diff --git a/script.sh b/script.sh index bae4375..6119cfb 100755 --- a/script.sh +++ b/script.sh @@ -2,11 +2,13 @@ export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion -cd /home/sagevaillancourt/git/hvacker +cd /home/sage/projects/hvacker || exit 1 whereis npm npm start | while read line; do echo "$line" - if [[ "$line" == *"app http request failed getaddrinfo ENOTFOUND slack.com"* ]]; then + if [[ "$line" == *"app http request failed getaddrinfo ENOTFOUND slack.com"* ]] || + [[ "$line" == *"node:internal/errors"* ]] || + [[ "$line" == *"Cannot set headers after"* ]]; then systemctl restart hvacker exit 1 fi diff --git a/src/games/hvacoins/index.js b/src/games/hvacoins/index.js index f210178..f6ea28f 100644 --- a/src/games/hvacoins/index.js +++ b/src/games/hvacoins/index.js @@ -210,38 +210,38 @@ const cardGames = { getCardName: card => card.name, getCardImageUrl: card => card.card_images[0].image_url }, - lotrOld: { - names: ['!lotrOld'], - help: 'Search for Lord of the Rings cards: !lotrOld ', - cards: async () => JSON.parse(readFileSync('lotrOld/lotr_cards.json').toString()), - fetch: async name => ({ json: () => { - const matcher = fuzzyMatcher(name?.toLowerCase()) - const exact = cardGames.lotrOld.cards.filter(card => card.name?.toLowerCase() === name.toLowerCase()) - if (exact.length) { - return exact - } - return cardGames.lotrOld.cards.filter(card => matcher.test(card.name)) - } }), - getCardData: data => data, - getCardName: card => card.name, - getCardImageUrl: card => 'https://ringsdb.com' + card.imagesrc - }, - lotr: { - names: ['!lotr'], - help: 'Search for Lord of the Rings cards: !lotr ', - cards: async () => JSON.parse(readFileSync('lotr/cards.json').toString()), - fetch: async name => ({ json: () => { - const matcher = fuzzyMatcher(name?.toLowerCase()) - const exact = cardGames.lotr.cards.filter(card => card.name?.toLowerCase() === name.toLowerCase()) - if (exact.length) { - return [exact[0]] - } - return cardGames.lotr.cards.filter(card => matcher.test(card.name)).filter(card => !card.name.includes('(')) - } }), - getCardData: data => data, - getCardName: card => card.name, - getCardImageUrl: card => 'https://lotrtcgwiki.com/wiki/_media/cards:' + card.id + '.jpg' - }, + // lotrOld: { + // names: ['!lotrOld'], + // help: 'Search for Lord of the Rings cards: !lotrOld ', + // cards: async () => JSON.parse(readFileSync('lotrOld/lotr_cards.json').toString()), + // fetch: async name => ({ json: () => { + // const matcher = fuzzyMatcher(name?.toLowerCase()) + // const exact = cardGames.lotrOld.cards.filter(card => card.name?.toLowerCase() === name.toLowerCase()) + // if (exact.length) { + // return exact + // } + // return cardGames.lotrOld.cards.filter(card => matcher.test(card.name)) + // } }), + // getCardData: data => data, + // getCardName: card => card.name, + // getCardImageUrl: card => 'https://ringsdb.com' + card.imagesrc + // }, + // lotr: { + // names: ['!lotr'], + // help: 'Search for Lord of the Rings cards: !lotr ', + // cards: async () => JSON.parse(readFileSync('lotr/cards.json').toString()), + // fetch: async name => ({ json: () => { + // const matcher = fuzzyMatcher(name?.toLowerCase()) + // const exact = cardGames.lotr.cards.filter(card => card.name?.toLowerCase() === name.toLowerCase()) + // if (exact.length) { + // return [exact[0]] + // } + // return cardGames.lotr.cards.filter(card => matcher.test(card.name)).filter(card => !card.name.includes('(')) + // } }), + // getCardData: data => data, + // getCardName: card => card.name, + // getCardImageUrl: card => 'https://lotrtcgwiki.com/wiki/_media/cards:' + card.id + '.jpg' + // }, playingCards: { names: ['!playing', '!pc'], help: 'Search for playing cards cards: !pc ', @@ -1122,7 +1122,7 @@ command( 'Mine HVAC coins', async ({ say, user, userId }) => { await say(await doMine({ user, userId, say })) - if ((lbIndex++) % 5 == 0) { + if ((lbIndex++) % 20 == 0) { return updateAllLeaderboards() } } @@ -1573,7 +1573,10 @@ command( const last = gifted.pop() recipients = gifted.map(t => users[t].name).join(', ') + ', and ' + users[last].name } else { - recipients = users[recipients[0]].name + console.log('gifted', gifted) + console.log('users[gifted[0]]', users[gifted[0]]) + recipients = users[gifted[0]].name + console.log('recipients', recipients) } await say(`Gifted ${commas(individualAmount)} HVAC to ${recipients}`) } @@ -2257,7 +2260,7 @@ command( adminOnly ) -//webapi.launch() +webapi.launch() module.exports = { command, diff --git a/src/games/hvacoins/utils.js b/src/games/hvacoins/utils.js index 787b582..d64f7d3 100644 --- a/src/games/hvacoins/utils.js +++ b/src/games/hvacoins/utils.js @@ -5,17 +5,22 @@ const buyableItems = require('./buyableItems') const { quackStore, getChaos } = require('./quackstore') const slack = require("../../slack"); +let slackUsers +const setSlackUsers = users => { + slackUsers = users +} let upgrades const setUpgrades = upg => { upgrades = upg } const saveFile = 'hvacoins.json' +const saveDir = '/hvacker-saves/' const logError = msg => msg ? console.error('logError: ', msg) : () => { /* Don't log empty message */ } const loadGame = () => { - const game = parseOr(fs.readFileSync('./' + saveFile, 'utf-8'), + const game = parseOr(fs.readFileSync(saveDir + saveFile, 'utf-8'), () => ({ users: {}, nfts: [], @@ -55,7 +60,7 @@ const parseOr = (parseable, fallback) => { } const makeBackup = () => { - const fileName = './backups/' + saveFile + new Date().toLocaleString().replace(/[^a-z0-9]/gi, '_') + const fileName = saveDir + 'backups/' + saveFile + new Date().toLocaleString().replace(/[^a-z0-9]/gi, '_') console.log(`Making backup file: ${fileName}`) fs.writeFileSync(fileName, JSON.stringify(game)) } @@ -73,7 +78,7 @@ const saveGame = (after, force = true) => { console.log('SAVING GAME') } - fs.writeFileSync('./' + saveFile, JSON.stringify(game, null, 2)) + fs.writeFileSync(saveDir + saveFile, JSON.stringify(game, null, 2)) } } @@ -258,6 +263,7 @@ const getUser = (userId, updateCoins = false) => { users[userId].coinsAllTime ??= users[userId].coins users[userId].prestige ??= 0 users[userId].startDate ??= new Date() + // users[userId].name ??= slack.users[userId] if (updateCoins) { users[userId].coins = getCoins(userId) } @@ -610,5 +616,6 @@ module.exports = { petBoost, updateAll, setSlackAppClientChatUpdate: update => slackAppClientChatUpdate = update, - setUpgrades + setUpgrades, + setSlackUsers } diff --git a/src/games/hvacoins/webapi.js b/src/games/hvacoins/webapi.js index e927de2..712def9 100644 --- a/src/games/hvacoins/webapi.js +++ b/src/games/hvacoins/webapi.js @@ -17,48 +17,97 @@ const makeHash = pw => .update(pw) .digest('hex') -const illegalCommands = ['!', '!b'] +const emojiMaps = [ + ['mouse2', '🐭'], + ['male-office-worker', '🧑‍💼'], + ['whale', '🐋'], + ['train2', '🚂'], + ['fire', '🔥'], + ['boomerang', '🪃'], + ['new_moon_with_face', '🌚'], + ['butterfly', '🦋'], + ['mirror', '🪞'], + ['quade', '🧔‍♂️'], + ['hvacker_angery', '🦆'], + ['grey_question', '❓'], + ['convenience_store', '🏪'], + ['office', '🏢'], + ['japanese_castle', '🏯'] +] + +app.get('/alive', (req, res) => { + res.send('OK') +}) + +const illegalCommands = ['!', '!c', '!coin', '!mine'] const lastCalls = {} const addCommand = ({ commandNames, helpText, action, condition, hidden }) => { - if (illegalCommands.find(command => commandNames.includes(command))) { - commandNames.forEach(name => - app.get('/' + name.replace(/!/gi, ''), async (req, res) => res.send('Command is illegal over the web api.')) - ) - return - } const route = async (req, res) => { - const say = async msg => res.send(msg + '\n') + let sent + const say = async msg => { + if (sent) { + console.log('attempted double-send of', { msg }) + return + } + const isObj = typeof msg === 'object' + if (isObj) { + msg = JSON.stringify(msg, null, 2) + } + for (const entry of emojiMaps) { + msg = msg.replaceAll(`:${entry[0]}:`, entry[1]) + } + msg = msg.replaceAll(/\*([^*]+)\*/g, '$1') + .replaceAll(/_([^_]+)_/g, '$1') + res.send((typeof msg === 'object' ? JSON.stringify(msg, null, 2) : msg) + '\n') + sent = true + } try { - const words = ['', ...Object.keys(req.query)] + const keys = [...Object.keys(req.query)] + keys.reverse() + const words = [commandNames[0], ...keys] + console.log({ words }) const [commandName, ...args] = words console.log('INCOMING API CALL:', commandName, words) const encoded = req.header('Authorization').substring(5) const decoded = base64.decode(encoded).substring(1) + const hash = makeHash(decoded) + console.log({ hash }) const event = { - user: apiGetUserId(makeHash(decoded)) + user: apiGetUserId(hash) + } + const user = getUser(event.user) + if (user.name !== 'TEST-USER' && illegalCommands.includes(commandName?.toLowerCase())) { + res.send('Command is illegal over the web api!') + return } if (!event.user) { res.status(400) res.send( - 'User does not exist, or does not have a password.\n' + - 'See \'!setpw help\' for assistance.' + 'User with that password does not exist, or user does not have a password.\n' + + 'See \'!setpw help\' for assistance.\n' ) console.log(' bad password') return } - const lastCall = lastCalls[event.user] || 0 - const secondsBetweenCalls = 30 - const currentTime = Math.floor(new Date().getTime() / 1000) - if (lastCall + secondsBetweenCalls > currentTime) { - res.status(400) - res.send(`Must have at least ${secondsBetweenCalls}s between api calls`) - console.log(' rate limited') + // const lastCall = lastCalls[event.user] || 0 + // const secondsBetweenCalls = 2 + // const currentTime = Math.floor(new Date().getTime() / 1000) + // if (lastCall + secondsBetweenCalls > currentTime) { + // res.status(400) + // res.send(`Must have at least ${secondsBetweenCalls}s between api calls`) + // console.log(' rate limited') + // return + // } + // console.log(` went through for ${slack.users[event.user]}`) + // lastCalls[event.user] = currentTime + + if (words[1] === 'help') { + await say(commandNames.map(name => `\`${name}\``).join(', ') + ': ' + helpText) + if (commandNames.includes('!coin')) { + addAchievement(user, 'weAllNeedHelp', say) + } return } - console.log(` went through for ${slack.users[event.user]}`) - lastCalls[event.user] = currentTime - - const user = getUser(event.user) const haunted = false //await action({ event, say, words, args, commandName }) const canUse = await condition({ event, say, words, commandName, args, user, userId: event.user, isAdmin: event.user.includes(slack.users.Admin) }) @@ -69,12 +118,14 @@ const addCommand = ({ commandNames, helpText, action, condition, hidden }) => { await action({ event, say, trueSay: say, words, args, commandName, user, userId: event.user, haunted }) } catch (e) { console.error('route error', e) + const example = "`curl --location-trusted -u ':your-pw' quacker.sagev.space/lb`" await say(`Routing error. Make sure you've set up API access with the !setpw command in slack!\n` + - 'Then you can use calls like `curl -u ":yourpw" \'http://10.3.0.48:3001/stonks\'`') + `Then you can use calls like ${example}\n` + + `N.b. --location-trusted is needed because quacker.sagev.space is technically a redirect, and your headers need to be forwarded.`) } } - commandNames.forEach(name => - app.get('/' + name.replace(/!/gi, ''), route) + commandNames.forEach(name => name !== '!!help' && + app.get('/' + name.replace(/!/gi, ''), async (req, res) => console.log('route', name.replace('/' + /!/gi, '')) || await route(req, res)) ) } diff --git a/src/slack/index.js b/src/slack/index.js index 1f81d86..72899df 100644 --- a/src/slack/index.js +++ b/src/slack/index.js @@ -1,7 +1,7 @@ const { App: SlackApp } = require('@slack/bolt') const config = require('../config') const fs = require('fs') -const { addReactions, saveGame, setSlackAppClientChatUpdate, parseOr } = require('../games/hvacoins/utils') +const { addReactions, saveGame, setSlackAppClientChatUpdate, parseOr, setSlackUsers } = require('../games/hvacoins/utils') const temperatureChannelId = 'C034156CE03' const dailyStandupChannelId = 'C03L533AU3Z' @@ -333,6 +333,7 @@ onReaction(async ({ event }) => { }) setSlackAppClientChatUpdate(app.client.chat.update) +setSlackUsers(users) module.exports = { app,