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 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 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.'
const getUpgradeEmoji = upgrade => upgrade.emoji || buyableItems[upgrade.type].emoji
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')) +
const upgradeText = user => {
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\nJust type \'!upgrade upgrade_name\' to purchase'
}
const game = JSON.parse(fs.readFileSync(saveFile, 'utf-8'))
const { users, nfts } = game
const saveGame = () => fs.writeFile(saveFile, JSON.stringify(game), logError)
const getUser = userId => {
if (!users[userId]) {
users[userId] = {
coins: 0,
items: {},
upgrades: {},
achievements: {}
}
} else {
users[userId].items ??= {}
users[userId].upgrades ??= {}
users[userId].achievements ??= {}
}
return users[userId]
}
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}!`)
}
// I'm super not confident this will stay
// What's nice is that it centralizes
// * calling with event,say,words
// * checking for words[1]==='help'
const alwaysAccessible = () => true
const adminOnly = userId => userId === slack.sageUserId
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,
helpText,
action,
condition: condition || (() => true)
condition,
hidden
}))
}
command( // I don't like that command() is at the highest indentation level instead of the route name
['!t', '!test'],
'HelpText: Testing out new routing functionality',
async ({ event, say, words }) => {// This extra indendation sucks
await say('This is the route acton') // Also the function can't go after command() - not initialized
}
command(
['!help', '!h'],
'List available commands',
async ({ say }) => await say('```' + commandHelpText + '```')
)
slack.onMessage(async ({ event, say }) => {
const words = event?.text?.split(/\s+/) || []
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)) {
await say(`Command '${words[0]} not found'`)
return
@ -102,9 +136,11 @@ slack.onMessage(async ({ event, say }) => {
}
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 achievementCount = Object.keys(user.achievements).length
@ -116,7 +152,8 @@ const listAchievements = async ({ event, say }) => {
.join('\n')
const postfix = achievementCount ? `\n\n_Achievements are boosting your CPS by ${mult.toPrecision(3)}%_` : ''
await say(prefix + list + postfix)
}
}
)
const getItemCps = (user, itemName) => {
const achievements = Object.keys(user.achievements || {}).length
@ -143,7 +180,7 @@ const getCPS = userId => {
}
const getCoins = userId => {
const user = users[userId]
const user = getUser(userId)
const currentTime = getSeconds()
const lastCheck = user.lastCheck || currentTime
const secondsPassed = currentTime - lastCheck
@ -156,13 +193,15 @@ const getCoins = userId => {
return user.coins
}
const mineCoinHelp = 'Mine HVAC coins: `!coin` or `!c`'
const mineCoin = async ({ event, say, words }) => {
command(['!cps'], 'Display your current Coins Per Second', async ({ event, say }) => {
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)
if (words[1] === 'help') {
await say(mineCoinHelp)
return
}
maybeNews(say)
const random = Math.random()
const c = getCoins(event.user)
@ -190,51 +229,48 @@ const mineCoin = async ({ event, say, words }) => {
user.coins += diff
await say(`${prefix}You now have ${commas(user.coins)} HVAC coin` + (c !== 0 ? 's' : '') + '. Spend wisely.')
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 currentCoins = getCoins(event.user)
if (words[1].toLowerCase() === 'all') {
if (currentCoins === 0) {
if (user.coins === 0) {
await say('You don\'t have any coins!')
return
}
n = currentCoins
n = user.coins
} else {
n = parseInt(event.text.substring(7))
n = parseInt(words[1])
}
if (!n || n < 0) {
await say(`Invalid number '${n}'`)
return
}
if (currentCoins < n) {
await say(`You don\'t have that many coins! You have ${commas(currentCoins)}.`)
if (user.coins < n) {
await say(`You don\'t have that many coins! You have ${commas(user.coins)}.`)
return
}
users[event.user].coins -= n
user.coins -= n
let outcome
if (Math.random() > 0.5) {
users[event.user].coins += (2 * n)
user.coins += (2 * n)
outcome = 'won'
} else {
outcome = 'lost'
}
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()
}
}
)
const calculateCost = ({ itemName, user, quantity = 1 }) => {
let currentlyOwned = user.items[itemName] || 0
@ -246,16 +282,14 @@ const calculateCost = ({ itemName, user, quantity = 1 }) => {
return realCost
}
const buyNftHelp = 'Acquire high-quality art: `!buynft`\n'+
'To use, say `!buynft nft_name`'
const buyNft = async ({ event, say, words }) => {
if (words[1] === 'help') {
await say(buyNftHelp)
return
}
command(
['!buynft', '!bn'],
'Acquire high-quality art\n' +
' To use, say \'!buynft nft_name\'',
async ({ event, say, words }) => {
const nft = nfts.find(n => n.name.toLowerCase() === words[1])
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)
return
}
@ -273,31 +307,14 @@ const buyNft = async ({ event, say, words }) => {
nft.owner = event.user
saveGame()
await say('You bought ' + nft.name + '!')
}
}
)
const getUser = userId => {
if (!users[userId]) {
users[userId] = {
coins: 0,
items: {},
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
}
command(
['!upgrade', '!u'],
'Improve the performance of your HVAC-generators.\n' +
' Say \'!upgrade\' to list available upgrades, or \'!upgrade upgrade_name\' to purchase.',
async ({ userId, say, words }) => {
const user = getUser(userId)
const upgradeName = words[1]
if (!upgradeName) {
@ -329,15 +346,15 @@ const buyUpgrade = async ({ userId, say, words }) => {
user.upgrades[upgrade.type].push(upgradeName)
await saveGame()
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 buying = words[1]
const quantity = parseInt(words[2] || '1')
@ -375,9 +392,14 @@ const buyItem = async ({ event, say, words }) => {
await say(`You bought ${quantity} ${buying}`)
}
saveGame()
}
}
)
const getCoinCommand = async ({ target, say }) => {
command(
['!check', '!ch'],
'Check how many coins another player has',
async ({ target, say }) => {
if (!target?.startsWith('<@') || !target.endsWith('>')) {
await say('Target must be a valid @')
return
@ -391,9 +413,16 @@ const getCoinCommand = async ({ target, say }) => {
const humanMembers = members.filter(name => name.length === 11)
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 [, target, amountText] = words
const amount = amountText === 'all' ? (getCoins(userId)) : parseInt(amountText)
@ -414,74 +443,67 @@ const gift = async ({ userId, words, say }) => {
user.coins -= amount
targetUser.coins += amount
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})
case '!cps':
return say(`You are currently earning \`${commas(getCPS(event.user))}\` HVAC Coin per second.`)
case '!gift':
case '!give':
return gift({userId: event.user, words, say})
case '!check':
return getCoinCommand({target: words[1], say})
case '!s':
case '!status':
return say(
)
command(
['!status', '!s'],
'Print your current CPS, HVAC balance, and owned items',
async ({ event, say }) => {
await say(
`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` +
`${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 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
return say(nfts
await say(nfts
.filter(filter || (() => true))
.map(nftDisplay)
.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)
return say('```' +
await say('```' +
Object.entries(users)
.filter(([id]) => getCPS(id) !== 0)
.sort(([id], [id2]) => getCPS(id) > getCPS(id2) ? -1 : 1)
.map(([id]) => `${slack.ourUsers[id] || '???'} - ${commas(getCPS(id))} CPS - ${commas(getCoins(id))} HVAC`)
.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 rest = event.text.substring(event.text.indexOf('\n') + 1)
const desc = rest.substring(0, rest.indexOf('\n'))
@ -496,19 +518,17 @@ slack.onMessage(async ({ event, say }) => {
nfts.push(newNft)
console.log('addedNft', newNft)
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 targetId = target.substring(2, target.length - 1)
maybeNews(say)
return say(
await say(
`${target} are currently earning \`${commas(getCPS(targetId))}\` HVAC Coin per second.\n\n` +
`They currently have ${commas(getCoins(targetId))} HVAC Coins\n\n` +
`${collection(targetId)}\n\n`
)
} else if (firstWord === '!updatenft') {
const nft = nfts.find(n => n.name.toLowerCase() === 'quackers')
nft.price *= 100
return saveGame()
}
}
})
}, true, adminOnly)