Switch to new command() routing.

This commit is contained in:
Sage Vaillancourt 2022-03-03 14:19:58 -05:00
parent a3c838438e
commit b4ce41eb6a
1 changed files with 355 additions and 335 deletions

View File

@ -5,9 +5,31 @@ const buyableItems = require('./buyableItems')
const upgrades = require('./upgrades') const upgrades = require('./upgrades')
const achievements = require('./achievements') const achievements = require('./achievements')
const saveFile = './hvacoins.json' const saveFile = 'hvacoins.json'
const logError = msg => msg ? console.error(msg) : undefined const parseOr = (parseable, orFunc) => {
try {
return JSON.parse(parseable)
} catch (e) {
console.error(e)
return orFunc()
}
}
const game = parseOr(fs.readFileSync('./' + saveFile, 'utf-8'),
() => ({ users: {}, nfts: [] }))
const { users, nfts } = game
const logError = msg => msg ? console.error(msg) : () => {}
let saves = 0
const saveGame = () => {
if (saves % 100 === 0) {
fs.writeFileSync('./backups/' + saveFile + new Date().toLocaleString().replace(/[^a-z0-9]/gi, '_'), JSON.stringify(game))
}
saves += 1
fs.writeFileSync('./' + saveFile, JSON.stringify(game))
}
const maybeNews = say => { const maybeNews = say => {
const random = Math.random() const random = Math.random()
@ -35,15 +57,27 @@ const helpText = (highestCoins, user) => Object.entries(buyableItems)
'\n\nNote: Listed prices are _base costs_ and will increase as you buy more.' '\n\nNote: Listed prices are _base costs_ and will increase as you buy more.'
const getUpgradeEmoji = upgrade => upgrade.emoji || buyableItems[upgrade.type].emoji const getUpgradeEmoji = upgrade => upgrade.emoji || buyableItems[upgrade.type].emoji
const upgradeText = user => const upgradeText = user => {
(!user ? '' : '\n\n' + Object.entries(upgrades).filter(([upgradeName, upgrade]) => !hasUpgrade(user, upgrade, upgradeName)).filter(([, upgrade]) => upgrade.condition(user)).map(([key, value]) => `:${getUpgradeEmoji(value)}: *${key}* - ${commas(value.cost)}\n_${value.description}_`).join('\n\n')) + return (!user ? '' : '\n\n' + Object.entries(upgrades).filter(([upgradeName, upgrade]) => !hasUpgrade(user, upgrade, upgradeName)).filter(([, upgrade]) => upgrade.condition(user)).map(([key, value]) => `:${getUpgradeEmoji(value)}: *${key}* - ${commas(value.cost)}\n_${value.description}_`).join('\n\n')) +
'\n\n:grey_question::grey_question::grey_question:' + '\n\n:grey_question::grey_question::grey_question:' +
'\n\nJust type \'!upgrade upgrade_name\' to purchase' '\n\nJust type \'!upgrade upgrade_name\' to purchase'
}
const game = JSON.parse(fs.readFileSync(saveFile, 'utf-8')) const getUser = userId => {
const { users, nfts } = game if (!users[userId]) {
users[userId] = {
const saveGame = () => fs.writeFile(saveFile, JSON.stringify(game), logError) coins: 0,
items: {},
upgrades: {},
achievements: {}
}
} else {
users[userId].items ??= {}
users[userId].upgrades ??= {}
users[userId].achievements ??= {}
}
return users[userId]
}
const getSeconds = () => new Date().getTime() / 1000 const getSeconds = () => new Date().getTime() / 1000
@ -65,33 +99,33 @@ const addAchievement = async (user, achievementName, say) => {
await say(`You earned the achievement ${achievements[achievementName].name}!`) await say(`You earned the achievement ${achievements[achievementName].name}!`)
} }
// I'm super not confident this will stay const alwaysAccessible = () => true
// What's nice is that it centralizes const adminOnly = userId => userId === slack.sageUserId
// * calling with event,say,words
// * checking for words[1]==='help'
const commands = new Map() const commands = new Map()
const command = (commandNames, helpText, action, condition) => { let commandHelpText = ''
const command = (commandNames, helpText, action, hidden = false, condition = alwaysAccessible) => {
if (!hidden) {
commandHelpText += `\n${commandNames.toString().replace(/,/g, ', ')} - ${helpText}\n`
}
commandNames.forEach(name => commands.set(name, { commandNames.forEach(name => commands.set(name, {
commandNames,
helpText, helpText,
action, action,
condition: condition || (() => true) condition,
hidden
})) }))
} }
command( // I don't like that command() is at the highest indentation level instead of the route name command(
['!t', '!test'], ['!help', '!h'],
'HelpText: Testing out new routing functionality', 'List available commands',
async ({ event, say, words }) => {// This extra indendation sucks async ({ say }) => await say('```' + commandHelpText + '```')
await say('This is the route acton') // Also the function can't go after command() - not initialized
}
) )
slack.onMessage(async ({ event, say }) => { slack.onMessage(async ({ event, say }) => {
const words = event?.text?.split(/\s+/) || [] const words = event?.text?.split(/\s+/) || []
const c = commands.get(words[0]) const c = commands.get(words[0])
if (event.user !== slack.sageUserId) {
return // Don't do anything while this is incubating
}
if (!c?.condition(event.user)) { if (!c?.condition(event.user)) {
await say(`Command '${words[0]} not found'`) await say(`Command '${words[0]} not found'`)
return return
@ -102,9 +136,11 @@ slack.onMessage(async ({ event, say }) => {
} }
await c.action({ event, say, words }) await c.action({ event, say, words })
}) })
// End of new command action
const listAchievements = async ({ event, say }) => { command(
['!a', '!ach', '!achievements'],
'List your glorious achievements',
async ({ event, say }) => {
const user = getUser(event.user) const user = getUser(event.user)
const achievementCount = Object.keys(user.achievements).length const achievementCount = Object.keys(user.achievements).length
@ -116,7 +152,8 @@ const listAchievements = async ({ event, say }) => {
.join('\n') .join('\n')
const postfix = achievementCount ? `\n\n_Achievements are boosting your CPS by ${mult.toPrecision(3)}%_` : '' const postfix = achievementCount ? `\n\n_Achievements are boosting your CPS by ${mult.toPrecision(3)}%_` : ''
await say(prefix + list + postfix) await say(prefix + list + postfix)
} }
)
const getItemCps = (user, itemName) => { const getItemCps = (user, itemName) => {
const achievements = Object.keys(user.achievements || {}).length const achievements = Object.keys(user.achievements || {}).length
@ -143,7 +180,7 @@ const getCPS = userId => {
} }
const getCoins = userId => { const getCoins = userId => {
const user = users[userId] const user = getUser(userId)
const currentTime = getSeconds() const currentTime = getSeconds()
const lastCheck = user.lastCheck || currentTime const lastCheck = user.lastCheck || currentTime
const secondsPassed = currentTime - lastCheck const secondsPassed = currentTime - lastCheck
@ -156,13 +193,15 @@ const getCoins = userId => {
return user.coins return user.coins
} }
const mineCoinHelp = 'Mine HVAC coins: `!coin` or `!c`' command(['!cps'], 'Display your current Coins Per Second', async ({ event, say }) => {
const mineCoin = async ({ event, say, words }) => { await say(`You are currently earning \`${commas(getCPS(event.user))}\` HVAC Coin per second.`)
})
command(
['!c', '!coin', '!mine'],
'Mine HVAC coins',
async ({ event, say }) => {
const user = getUser(event.user) const user = getUser(event.user)
if (words[1] === 'help') {
await say(mineCoinHelp)
return
}
maybeNews(say) maybeNews(say)
const random = Math.random() const random = Math.random()
const c = getCoins(event.user) const c = getCoins(event.user)
@ -190,51 +229,48 @@ const mineCoin = async ({ event, say, words }) => {
user.coins += diff user.coins += diff
await say(`${prefix}You now have ${commas(user.coins)} HVAC coin` + (c !== 0 ? 's' : '') + '. Spend wisely.') await say(`${prefix}You now have ${commas(user.coins)} HVAC coin` + (c !== 0 ? 's' : '') + '. Spend wisely.')
saveGame() saveGame()
} }
)
command(
['!g', '!gamble'],
'Gamble away your HVAC\n' +
' To use, say \'gamble coin_amount\' or \'!gamble all\'',
async ({ event, say, words }) => {
getCoins(event.user) // Update coin counts
const user = getUser(event.user)
const gambleCoinHelp = 'Gamble away your HVAC: `!gamble` or `!g`\n'+
'To use, say `!gamble coin_amount` or `!gamble all`'
const gambleCoin = async ({ event, say, words }) => {
if (words[1] === 'help') {
await say(gambleCoinHelp)
return
}
if (!users[event.user]) {
users[event.user] = {
coins: 0
}
}
let n let n
let currentCoins = getCoins(event.user)
if (words[1].toLowerCase() === 'all') { if (words[1].toLowerCase() === 'all') {
if (currentCoins === 0) { if (user.coins === 0) {
await say('You don\'t have any coins!') await say('You don\'t have any coins!')
return return
} }
n = currentCoins n = user.coins
} else { } else {
n = parseInt(event.text.substring(7)) n = parseInt(words[1])
} }
if (!n || n < 0) { if (!n || n < 0) {
await say(`Invalid number '${n}'`) await say(`Invalid number '${n}'`)
return return
} }
if (currentCoins < n) { if (user.coins < n) {
await say(`You don\'t have that many coins! You have ${commas(currentCoins)}.`) await say(`You don\'t have that many coins! You have ${commas(user.coins)}.`)
return return
} }
users[event.user].coins -= n user.coins -= n
let outcome let outcome
if (Math.random() > 0.5) { if (Math.random() > 0.5) {
users[event.user].coins += (2 * n) user.coins += (2 * n)
outcome = 'won' outcome = 'won'
} else { } else {
outcome = 'lost' outcome = 'lost'
} }
console.log(`They ${outcome}`) console.log(`They ${outcome}`)
await say(`You bet ${commas(n)} coins and ${outcome}! You now have ${commas(users[event.user].coins)}.`) await say(`You bet ${commas(n)} coins and ${outcome}! You now have ${commas(user.coins)}.`)
saveGame() saveGame()
} }
)
const calculateCost = ({ itemName, user, quantity = 1 }) => { const calculateCost = ({ itemName, user, quantity = 1 }) => {
let currentlyOwned = user.items[itemName] || 0 let currentlyOwned = user.items[itemName] || 0
@ -246,16 +282,14 @@ const calculateCost = ({ itemName, user, quantity = 1 }) => {
return realCost return realCost
} }
const buyNftHelp = 'Acquire high-quality art: `!buynft`\n'+ command(
'To use, say `!buynft nft_name`' ['!buynft', '!bn'],
const buyNft = async ({ event, say, words }) => { 'Acquire high-quality art\n' +
if (words[1] === 'help') { ' To use, say \'!buynft nft_name\'',
await say(buyNftHelp) async ({ event, say, words }) => {
return
}
const nft = nfts.find(n => n.name.toLowerCase() === words[1]) const nft = nfts.find(n => n.name.toLowerCase() === words[1])
if (!nft) { if (!nft) {
const suffix = words[1].match(/[^a-z0-9]/i) ? '. And I\'m unhackable, so cut it out.' : '' const suffix = words[1]?.match(/[^a-z0-9]/i) ? '. And I\'m unhackable, so cut it out.' : ''
await say('No NFT with that name found' + suffix) await say('No NFT with that name found' + suffix)
return return
} }
@ -273,31 +307,14 @@ const buyNft = async ({ event, say, words }) => {
nft.owner = event.user nft.owner = event.user
saveGame() saveGame()
await say('You bought ' + nft.name + '!') await say('You bought ' + nft.name + '!')
} }
)
const getUser = userId => { command(
if (!users[userId]) { ['!upgrade', '!u'],
users[userId] = { 'Improve the performance of your HVAC-generators.\n' +
coins: 0, ' Say \'!upgrade\' to list available upgrades, or \'!upgrade upgrade_name\' to purchase.',
items: {}, async ({ userId, say, words }) => {
upgrades: {},
achievements: {}
}
} else {
users[userId].items ??= {}
users[userId].upgrades ??= {}
users[userId].achievements ??= {}
}
return users[userId]
}
const buyUpgradeHelp = 'Improve the performance of your HVAC-generators. `!upgrade` or `!u`\n'+
'Say `!upgrade` to list available upgrades, or `!upgrade upgrade_name` to purchase.'
const buyUpgrade = async ({ userId, say, words }) => {
if (words[1] === 'help') {
await say(buyUpgradeHelp)
return
}
const user = getUser(userId) const user = getUser(userId)
const upgradeName = words[1] const upgradeName = words[1]
if (!upgradeName) { if (!upgradeName) {
@ -329,15 +346,15 @@ const buyUpgrade = async ({ userId, say, words }) => {
user.upgrades[upgrade.type].push(upgradeName) user.upgrades[upgrade.type].push(upgradeName)
await saveGame() await saveGame()
await say(`You bought ${upgradeName}!`) await say(`You bought ${upgradeName}!`)
}
const buyItemHelp = 'Buy new items to earn HVAC with: `!buy` or `!b`\n'+
'Use `!buy` to list available items, and `!buy item_name optional_quantity` to get \'em.'
const buyItem = async ({ event, say, words }) => {
if (words[1] === 'help') {
await say(buyItemHelp)
return
} }
)
command(
['!buy', '!b'],
'Buy new items to earn HVAC with\n' +
' Use without arguments to list all available items.\n' +
' Say \'!buy item_name optional_quantity\' to make your purchase.',
async ({ event, say, words }) => {
const user = getUser(event.user) const user = getUser(event.user)
const buying = words[1] const buying = words[1]
const quantity = parseInt(words[2] || '1') const quantity = parseInt(words[2] || '1')
@ -375,9 +392,14 @@ const buyItem = async ({ event, say, words }) => {
await say(`You bought ${quantity} ${buying}`) await say(`You bought ${quantity} ${buying}`)
} }
saveGame() saveGame()
} }
)
const getCoinCommand = async ({ target, say }) => {
command(
['!check', '!ch'],
'Check how many coins another player has',
async ({ target, say }) => {
if (!target?.startsWith('<@') || !target.endsWith('>')) { if (!target?.startsWith('<@') || !target.endsWith('>')) {
await say('Target must be a valid @') await say('Target must be a valid @')
return return
@ -391,9 +413,16 @@ const getCoinCommand = async ({ target, say }) => {
const humanMembers = members.filter(name => name.length === 11) const humanMembers = members.filter(name => name.length === 11)
await say(`Hvacker owns ${humanMembers.length} souls.`) await say(`Hvacker owns ${humanMembers.length} souls.`)
} }
} }
)
const gift = async ({ userId, words, say }) => {
command(
['!gift', '!give', '!gi'],
'Donate coins to a fellow player\n' +
' Send coins by saying \'!gift @player coin_amount\'',
async ({ event, words, say }) => {
const userId = event.user
const user = getUser(userId) const user = getUser(userId)
const [, target, amountText] = words const [, target, amountText] = words
const amount = amountText === 'all' ? (getCoins(userId)) : parseInt(amountText) const amount = amountText === 'all' ? (getCoins(userId)) : parseInt(amountText)
@ -414,74 +443,67 @@ const gift = async ({ userId, words, say }) => {
user.coins -= amount user.coins -= amount
targetUser.coins += amount targetUser.coins += amount
await say(`Gifted ${commas(amount)} HVAC to <@${targetId}>`) await say(`Gifted ${commas(amount)} HVAC to <@${targetId}>`)
}
slack.onMessage(async ({ event, say }) => {
// if (event?.text?.startsWith('!') && event.user !== slack.sageUserId) {
// await say('Hvacker is taking a quick nap.')
// return
// }
const words = event?.text?.split(/\s+/) || []
console.log(event?.text, 'by', slack?.ourUsers[event?.user], 'at', new Date().toLocaleTimeString())
switch (words[0]) {
case '!c':
case '!coin':
return mineCoin({ event, say, words })
case '!g':
case '!gamble':
return gambleCoin({ event, say, words })
case '!b':
case '!buy': {
return buyItem({event, say, words})
} }
case '!u': )
case '!upgrade':
return buyUpgrade({userId: event.user, say, words}) command(
case '!cps': ['!status', '!s'],
return say(`You are currently earning \`${commas(getCPS(event.user))}\` HVAC Coin per second.`) 'Print your current CPS, HVAC balance, and owned items',
case '!gift': async ({ event, say }) => {
case '!give': await say(
return gift({userId: event.user, words, say})
case '!check':
return getCoinCommand({target: words[1], say})
case '!s':
case '!status':
return say(
`You are currently earning \`${commas(getCPS(event.user))}\` HVAC Coin per second.\n\n` + `You are currently earning \`${commas(getCPS(event.user))}\` HVAC Coin per second.\n\n` +
`You currently have ${commas(getCoins(event.user))} HVAC Coins\n\n` + `You currently have ${commas(getCoins(event.user))} HVAC Coins\n\n` +
`${collection(event.user)}\n\n` `${collection(event.user)}\n\n`
) )
case '!a': }
return listAchievements({ event, say }) )
case '!nfts':
command(
['!nfts', '!nft', '!n'],
'Show NFTs in the museum\n' +
' Call with no arguments to list all NFTs, or \'!nft nft_name\' to show a particular one.',
async ({ say, words }) => {
const owner = nft => `Owner: *${slack.ourUsers[nft.owner] || 'NONE'}*` const owner = nft => `Owner: *${slack.ourUsers[nft.owner] || 'NONE'}*`
const nftDisplay = nft => `_"${nft.name}"_\n\n${nft.description}\n\n${commas(nft.price)} HVAC.\n\n${nft.picture}\n\n${owner(nft)}` const nftDisplay = nft => `_"${nft.name}"_\n\n${nft.description}\n\n${commas(nft.price)} HVAC.\n\n${nft.picture}\n\n${owner(nft)}`
const filter = words[1] ? nft => words[1]?.toLowerCase() === nft.name : null const filter = words[1] ? nft => words[1]?.toLowerCase() === nft.name : null
return say(nfts await say(nfts
.filter(filter || (() => true)) .filter(filter || (() => true))
.map(nftDisplay) .map(nftDisplay)
.join('\n-------------------------\n') || (filter ? 'No NFTs with that name exist' : 'No NFTs currently exist.') .join('\n-------------------------\n') || (filter ? 'No NFTs with that name exist' : 'No NFTs currently exist.')
) )
case '!lb': }
)
command(
['!leaderboard', '!lb'],
'Show the top HVAC-earners, ranked by CPS',
async ({ event, say }) => {
const user = getUser(event.user) const user = getUser(event.user)
return say('```' + await say('```' +
Object.entries(users) Object.entries(users)
.filter(([id]) => getCPS(id) !== 0) .filter(([id]) => getCPS(id) !== 0)
.sort(([id], [id2]) => getCPS(id) > getCPS(id2) ? -1 : 1) .sort(([id], [id2]) => getCPS(id) > getCPS(id2) ? -1 : 1)
.map(([id]) => `${slack.ourUsers[id] || '???'} - ${commas(getCPS(id))} CPS - ${commas(getCoins(id))} HVAC`) .map(([id]) => `${slack.ourUsers[id] || '???'} - ${commas(getCPS(id))} CPS - ${commas(getCoins(id))} HVAC`)
.join('\n') + '```').then(() => addAchievement(user, 'leaderBoardViewer', say)) .join('\n') + '```').then(() => addAchievement(user, 'leaderBoardViewer', say))
case '!buynft':
return buyNft({event, say, words})
case '!ligma':
return say(':hvacker_angery:')
case '!pog':
return say('<https://i.imgur.com/XCg7WDz.png|poggers>')
} }
if (event.user === slack.sageUserId) { )
const firstWord = words[0]
if (firstWord === '!addnft') { command(['!pog'], 'Displays a poggers hvacker', async ({ say }) => {
await say('<https://i.imgur.com/XCg7WDz.png|poggers>')
}, true)
command(['!ligma'], 'GRRRR', async ({ say }) => {
await say(':hvacker_angery:')
}, true)
command(
['!addnft'],
'Arguments 1 and 2 should be on the first line as name (one word!) and integer price.\n' +
' The second line should be the description of the pieces\n' +
' the picture is everything after the second line',
async ({ event }) => {
const [, name, price] = event.text.substring(0, event.text.indexOf('\n')).split(' ') const [, name, price] = event.text.substring(0, event.text.indexOf('\n')).split(' ')
const rest = event.text.substring(event.text.indexOf('\n') + 1) const rest = event.text.substring(event.text.indexOf('\n') + 1)
const desc = rest.substring(0, rest.indexOf('\n')) const desc = rest.substring(0, rest.indexOf('\n'))
@ -496,19 +518,17 @@ slack.onMessage(async ({ event, say }) => {
nfts.push(newNft) nfts.push(newNft)
console.log('addedNft', newNft) console.log('addedNft', newNft)
return saveGame() return saveGame()
} else if (firstWord === '!s') { }, true, adminOnly)
command(
['!ss'],
'Show the status for another player: !ss @player',
async ({ words, say }) => {
const target = words[1] const target = words[1]
const targetId = target.substring(2, target.length - 1) const targetId = target.substring(2, target.length - 1)
maybeNews(say) await say(
return say(
`${target} are currently earning \`${commas(getCPS(targetId))}\` HVAC Coin per second.\n\n` + `${target} are currently earning \`${commas(getCPS(targetId))}\` HVAC Coin per second.\n\n` +
`They currently have ${commas(getCoins(targetId))} HVAC Coins\n\n` + `They currently have ${commas(getCoins(targetId))} HVAC Coins\n\n` +
`${collection(targetId)}\n\n` `${collection(targetId)}\n\n`
) )
} else if (firstWord === '!updatenft') { }, true, adminOnly)
const nft = nfts.find(n => n.name.toLowerCase() === 'quackers')
nft.price *= 100
return saveGame()
}
}
})