Several tweaks.

Buffer responses on repeated requests (especially useful for '!')
Remove some noisy logging
Add the admin-only !take
Add CANNOT_VOTE to users object
This commit is contained in:
Sage Vaillancourt 2024-11-09 16:59:05 -05:00
parent 2d2e0c9368
commit 35e401cd0b
3 changed files with 141 additions and 55 deletions

View File

@ -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 <card name>',
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<message duplicated ${dupCount} time${dupCount === 1 ? '' : 's'}>`)
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) {

View File

@ -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) {

View File

@ -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