diff --git a/src/games/hvacoins/index.js b/src/games/hvacoins/index.js index cbd7bb2..00f5db0 100644 --- a/src/games/hvacoins/index.js +++ b/src/games/hvacoins/index.js @@ -140,11 +140,9 @@ const defaultAccess = { hidden: false, condition: alwaysAccessible } */ const command = (commandNames, helpText, action, { hidden, condition } = defaultAccess) => { if (!hidden) { - console.log(`Initializing command '${commandNames[0]}'`) commandHelpText += `\n${commandNames.toString().replace(/,/g, ', ')} - ${helpText}\n` shortCommandHelpText += `\n${commandNames.toString().replace(/,/g, ', ')}` } else if (condition === adminOnly.condition) { - console.log(`Initializing admin command '${commandNames[0]}'`) } else { hiddenCommands++ } @@ -243,7 +241,6 @@ const cardGames = { help: 'Search for Yu-Gi-Oh cards: !ygo ', fetch: async name => { const url = `https://db.ygoprodeck.com/api/v7/cardinfo.php?fname=${name}`; - console.log('yugioh url', url) return url }, getCardData: ({ data }) => data, @@ -293,7 +290,6 @@ Object.entries(cardGames).forEach(async ([gameName, cardGame]) => { // return say('Please specify a card name!') // } arg = arg.trim() - console.log('arg', arg) if (cardGame.cards && !cardGame.fetch) { const fileName = cardGame.cards.find(name => name?.toLowerCase().replaceAll(/_/g, ' ').startsWith(arg.toLowerCase())) if (fileName) { @@ -326,7 +322,6 @@ Object.entries(cardGames).forEach(async ([gameName, cardGame]) => { const name = cardGame.getCardName(card) const fileName = gameName + '/' + name if (existsSync(fileName)) { - console.log(`Using cached file: ${fileName}`) return postCard(event, name, fileName) } const file = createWriteStream(fileName) @@ -334,7 +329,6 @@ Object.entries(cardGames).forEach(async ([gameName, cardGame]) => { response.pipe(file) file.on('finish', async () => { await file.close() - console.log(event.channel) await postCard(event, name, fileName) }).on('error', err => { console.error(err) @@ -448,21 +442,84 @@ const buildHorrorSay = ({ say, event, commandName, c }) => async message => { } const buildSayWithPayload = ({ say, event }) => async msg => { + const { user, text } = event + const payload = { event: { - text: event.text, - user: event.user + text, + user } } + if (typeof(msg) === 'string') { - return say(slack.encodeData('commandPayload', payload) + msg) + const userBuff = (messageBuffer[user] ??= { buffer: [], lastSendTs: [] }) + const currentTs = Date.now() + const lastSendWasRecent = userBuff.lastSendTs.every(ts => ts > (currentTs - falloffMs)) + + const doStringSay = currentMsg => { + let sending = '' + const append = text => { + if (sending) { + sending += '\n' + } + sending += text; + } + if (userBuff.buffer.length) { + let dupCount = 0 + let lastMessage = null + const close = () => { + if (dupCount) { + append('\n' + lastMessage + `\n`) + dupCount = 0 + } else if (lastMessage !== null) { + append('\n' + lastMessage) + } + } + userBuff.buffer.forEach(buffered => { + if (buffered === lastMessage) { + dupCount++ + return + } + + close() + lastMessage = buffered + }) + close() + } + if (currentMsg) { + append(currentMsg) + } + userBuff.buffer = [] + if (userBuff.lastSendTs.length > bucketSize) { + userBuff.lastSendTs.shift() + } + userBuff.lastSendTs.push(currentTs) + if (!sending) { + return + } + return say(slack.encodeData('commandPayload', payload) + sending) + } + + + if (lastSendWasRecent) { + // Note: Drops buffered payloads + userBuff.buffer.push(msg) + clearTimeout(userBuff.timeoutId) + userBuff.timeoutId = setTimeout(() => doStringSay(null), falloffMs) + return + } else { + return doStringSay(msg) + } } + return say({ ...msg, text: slack.encodeData('commandPayload', payload) + msg.text }) } +command(['!ping'], 'Ping', async ({ say }) => say('Hello!'), adminOnly) + const userHasTheGift = user => userHasCheckedQuackgrade(user, 'theGift') command( @@ -490,7 +547,6 @@ const noWinner = 'NO WINNER' const getPollWinner = async ({ channel, ts }) => { try { const msg = await slack.getMessage({ channel, ts }) - console.log('pollWinner message', JSON.stringify(msg.messages[0])) let texts = [] let maxVotes = 0 for (let i = 1; i < msg.messages[0].blocks.length; i++) { @@ -500,11 +556,8 @@ const getPollWinner = async ({ channel, ts }) => { continue } votes = votes.split('@').length - 1 - console.log(`${votes} votes for:`) text = text.replace(/^\s*:[a-z]*: /, '') text = text.replace(/\s+`\d+`$/, '') - console.log(`TEXT: '${text}'`) - console.log(``) if (votes > maxVotes) { maxVotes = votes texts = [text] @@ -512,7 +565,6 @@ const getPollWinner = async ({ channel, ts }) => { texts.push(text) } } - console.log('TEXTS', texts) if (texts.length === 1) { return [texts[0], false] } else if (texts.length > 1) { @@ -552,13 +604,11 @@ command( async ({ args, say, user }) => { try { const msg = await slack.getMessage({channel: slack.temperatureChannelId, ts: args[0]}) - console.log(JSON.stringify(msg?.messages[0])) } catch (e) {console.error('!getmsg error', e)} } ) const messageHandler = async ({ event, say, isRecycle = false, skipCounting }) => { - console.log('messageHandler') if (event?.subtype === 'bot_message') { return botMessageHandler({ event, say }) } @@ -566,7 +616,6 @@ const messageHandler = async ({ event, say, isRecycle = false, skipCounting }) = const words = event?.text?.split(/\s+/) || [] const [commandName, ...args] = words const c = commands.get(commandName) - console.log('getUser') let user = await getUser(event.user) if (user.isDisabled && c.condition !== alwaysAlwaysAccessible) { return @@ -633,7 +682,6 @@ const messageHandler = async ({ event, say, isRecycle = false, skipCounting }) = } } } - console.log('getCoins') Object.entries(users).forEach(([id, usr]) => usr.coins = getCoins(id)) //user.coins = getCoins(event.user) const isAdmin = event.user?.includes(slack.users.Admin) @@ -824,7 +872,7 @@ slack.app.action('lightningStrike', async ({ body, ack }) => { blocks: [] }) await ack() - return slack.messageAdmin(`Lighting bottled by <@${body.user.id}>`) + // return slack.messageAdmin(`Lighting bottled by <@${body.user.id}>`) }) slack.onMessage(async msg => { @@ -1139,7 +1187,7 @@ const doMine = async ({ user, userId, say }) => { diff = 500 + secondsOfCps(60 * 60, 0.2) prefix = `:gem: You found a lucky gem worth ${commas(diff)} HVAC!\n` addAchievement(user, 'luckyGem', say) - await slack.messageAdmin(`${slack.users[userId]} FOUND A LUCKY GEM COIN WORTH ${commas(diff)} HVAC!`) + // await slack.messageAdmin(`${slack.users[userId]} FOUND A LUCKY GEM COIN WORTH ${commas(diff)} HVAC!`) } else if (random > 0.986) { diff = 50 + secondsOfCps(60 * 5, 0.1) prefix = `:goldbrick: You found a lucky gold coin worth ${commas(diff)} HVAC!\n` @@ -1163,7 +1211,7 @@ command( 'Mine HVAC coins', async ({ say, user, userId }) => { await say(await doMine({ user, userId, say })) - if ((lbIndex++) % 20 == 0) { + if ((lbIndex++) % 100 == 0) { return updateAllLeaderboards() } } @@ -1257,7 +1305,6 @@ command( } else { outcome = 'lost' } - console.log(`They ${outcome}`) //saveGame() await say(`You bet ${commas(n)} coins and ${outcome}! You now have ${commas(user.coins)}.`) if (outcome === 'lost' && user.lostBetMessage) { @@ -1266,7 +1313,7 @@ command( await trueSay(user.wonBetMessage) } return updateAllLeaderboards() - } + }, dmsOnly ) const emojiRegex = /^:[^:\s]*:$/ @@ -1278,7 +1325,6 @@ const validEmoji = async emojiText => { const validEmojis = (await getEmojis()).emoji const noColons = emojiText.replace(/:/g, '') - // console.log('validEmojis', validEmojis) return !!validEmojis[noColons] } const getEmojis = async () => await slack.app.client.emoji.list() @@ -1368,7 +1414,6 @@ command( if (!args[0]) { return say(upgradeText2(user)) } - console.log({args: args.join(' ')}) const matcher = fuzzyMatcher(args.join(' ')) const u = Object.entries(upgrades).find(([name, upgrade]) => matcher.test(name) || matcher.test(upgrade.name)) if (!u) { @@ -1418,7 +1463,6 @@ const upgradeBlock = upgradeName => { const upgradeButton = async ({ body, ack, say, payload }) => { await ack() const upgrade = payload.action_id.substring(8) - console.log(`upgradeButton ${upgrade} clicked`) const event = { user: body.user.id } @@ -1574,7 +1618,6 @@ command( 'Donate coins to a fellow player\n' + ' Send coins by saying \'!gift @player coin_amount\'', async ({ event, args, say, user, haunted }) => { - return say(`I'm sorry, but you people can't be trusted anymore.`) if (haunted) { return say(`!give doesn't work while you're haunted.`) } @@ -1624,13 +1667,55 @@ command( const last = gifted.pop() recipients = gifted.map(t => users[t].name).join(', ') + ', and ' + users[last].name } else { - 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}`) - } + }, adminOnly +) + +command( + ['!take'], + 'Take coins from a player\n' + + ' Take coins by saying \'!take @player coin_amount\'', + async ({ event, args, say, user, haunted }) => { + if (haunted) { + return say(`!give doesn't work while you're haunted.`) + } + let [target, ...amountText] = args + amountText = amountText.join(' ') + const targetId = idFromWord(target) + const targets = [targetId] + if (targetId === event?.user) { + return say(':thonk:') + } + if (!targetId) { + return say('Target must be a valid @') + } + const individualAmount = parseAll(amountText, user.coins, user) + const totalAmount = individualAmount + + let victims = [] + for (const targetId of targets) { + const targetUser = await getUser(targetId) + targetUser.coins -= individualAmount + victims.push(targetId) + user.coins += individualAmount + } + if (!totalAmount || totalAmount < 0) { + return say('Amount must be a positive integer!') + } + if (user.coins < totalAmount) { + return say(`You don't have that many coins! You have ${commas(user.coins)} HVAC.`) + } + let losers + if (victims.length > 1) { + const last = victims.pop() + losers = victims.map(t => users[t].name).join(', ') + ', and ' + users[last].name + } else { + losers = users[victims[0]].name + } + await say(`Took ${commas(individualAmount)} HVAC from ${losers}`) + }, adminOnly ) const getChaosMessage = (user, { channel_type }, prefix = '', postfix = '') => { @@ -1862,7 +1947,6 @@ command( owner: null } nfts.push(newNft) - console.log('addedNft', newNft) }, adminOnly) command( @@ -1938,13 +2022,10 @@ command( return } let targetId = idFromWord(target) - console.log({ user: event.user, target, targetId }) if (event.user === targetId) { return say('What, are you trying to steal from yourself? What, are you stupid?') } - if (!targetId) { - targetId = slack.users.Admin - } + targetId = slack.users.Admin if (user.coins < amount) { return } @@ -2013,13 +2094,11 @@ command( args = rest } - console.log({args, channel}) const target = idFromWord(args[0]) const [, ...rest] = args const userInfo = await slack.app.client.users.info({ user: target }) - console.log(userInfo) return slack.app.client.chat.postMessage({ channel, text: rest.join(' '), @@ -2126,12 +2205,8 @@ const updateStonkPrices = () => { // TODO: Gotta take into account wrapping around to the end of the year Object.entries(stonkMarket.stonks).forEach(([, stonk]) => { - console.log(stonk.pattern) - console.log('try set') for (let i = stonkMarket.lastDay; i < today; i++) { - console.log('set lastPrice') stonk.lastPrice = stonk.price - console.log(stonk.pattern, stonkPatterns) stonk.price *= 1 + ((stonkPatterns[stonk.pattern] || stonkPatterns.duk)[stonk.index] / 100) stonk.index++ if (stonk.index >= stonkPatterns[stonk.pattern]?.length) { diff --git a/src/games/hvacoins/utils.js b/src/games/hvacoins/utils.js index 309aec3..adc608e 100644 --- a/src/games/hvacoins/utils.js +++ b/src/games/hvacoins/utils.js @@ -61,7 +61,13 @@ const parseOr = (parseable, fallback) => { } } -const makeBackup = () => { +let lastBackupTs = 0 +const makeBackup = (force) => { + const currentTs = Date.now() + if (lastBackupTs > (currentTs - 60000) && !force) { + return + } + lastBackupTs = currentTs 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)) @@ -209,6 +215,9 @@ const parseAll = (str, allNum, user) => { if (str.match(/^\d+$/)) { return parseInt(str) } + if (allNum && str.match(/^some$/)) { + return Math.floor(Math.random() * allNum) + } if (allNum && str.match(/^\d+%$/)) { const percent = parseFloat(str) / 100 if (percent > 1 || percent < 0) { diff --git a/src/slack/index.js b/src/slack/index.js index 9e8ca2b..bfac72d 100644 --- a/src/slack/index.js +++ b/src/slack/index.js @@ -122,9 +122,9 @@ app.event('message', async ({ event, context, client, say }) => { for (const listener of messageListeners) { listener({ event, say }) } - if (event.user) { - console.log('MSG', users[event.user], "'" + event.text + "'", new Date().toLocaleTimeString()) - } + // if (event.user) { + // console.log('MSG', users[event.user], "'" + event.text + "'", new Date().toLocaleTimeString()) + // } if (event.user === users.Admin && event.channel === 'D0347Q4H9FE') { if (event.text === '!!kill') { saveGame('!!kill', true) @@ -198,15 +198,17 @@ app.event('message', async ({ event, context, client, say }) => { })) const reactCounts = {} - Object.entries(reactPosters).forEach(([id, votes]) => { - console.log(`VOTES FROM ${id}:`, votes) - votes = votes.filter(v => [goodEmoji, hotterEmoji, colderEmoji].find(emoji => v.startsWith(emoji))) - if (votes.length === 1) { - const name = votes[0].replace(/:.*/g, '') - reactCounts[name] ??= 0 - reactCounts[name] += 1 - } - }) + Object.entries(reactPosters) + .filter(([id]) => !users.CANNOT_VOTE?.includes(users[id])) + .forEach(([id, votes]) => { + console.log(`VOTES FROM ${id}:`, votes) + votes = votes.filter(v => [goodEmoji, hotterEmoji, colderEmoji].find(emoji => v.startsWith(emoji))) + if (votes.length === 1) { + const name = votes[0].replace(/:.*/g, '') + reactCounts[name] ??= 0 + reactCounts[name] += 1 + } + }) console.log('REACT COUNTS', JSON.stringify(reactCounts)) const contentVotes = reactCounts[goodEmoji] || 0