Many additions:

Add several new achievements
Add price querying with ?b
Simplify commands' argument-handling
Some spooky stuff
New quackgrades
Stormy weather
Rebalanced some rare-event odds
!u buttons
Reworked prestige emojis
Save on every command
Add stonks
More temp poll triggers
Redemption upgrades more powerful
Fuzzy matching for usernames and buyables
This commit is contained in:
Sage Vaillancourt 2022-05-19 11:09:16 -04:00
parent 6dabe9d85a
commit ad021cf9a5
14 changed files with 1152 additions and 332 deletions

View File

@ -11,4 +11,4 @@ fetch(url, {
headers: headers headers: headers
// credentials: 'user:passwd' // credentials: 'user:passwd'
}).then(response => response.json()) }).then(response => response.json())
.then(json => console.log(json)) .then(json => console.log('json', json))

View File

@ -34,6 +34,16 @@ module.exports = {
description: 'I like big bets, and that\'s the truth', description: 'I like big bets, and that\'s the truth',
emoji: 'slot_machine' emoji: 'slot_machine'
}, },
hugeBets: {
name: 'Make a bet over 100T',
description: `That's so bonk`,
emoji: 'game_die'
},
mondoBets: {
name: 'Make a bet over 100 Quadrillion',
description: 'H I G H R O L L E R',
emoji: '8ball'
},
ignited: { ignited: {
name: 'You light my fire, baby', name: 'You light my fire, baby',
description: 'And you pay attention to descriptions!', description: 'And you pay attention to descriptions!',
@ -45,6 +55,72 @@ module.exports = {
description: 'I\'m beginning to feel like a rat god, rat god.', description: 'I\'m beginning to feel like a rat god, rat god.',
emoji: 'mouse2' emoji: 'mouse2'
}, },
mathematician: {
name: 'Own 100 Accountants',
description: 'They rejoice at the appearance of a third digit.',
emoji: 'male-office-worker'
},
iPod: {
name: 'Own 100 Whales',
description: `With the new iPod, you can hold 100's of songs.`,
emoji: 'whale'
},
fire100: {
name: 'Own 100 Fires',
description: `Wow, that's bright.`,
emoji: 'fire'
},
train100: {
name: 'Own 100 Trains',
description: `That's every train in America you've got there.`,
emoji: 'train2'
},
boom100: {
name: 'Own 100 Boomerangs',
description: `LOUD WOOSHING`,
emoji: 'boomerang'
},
moon100: {
name: 'Own 100 Moons',
description: `Space Cadet`,
emoji: 'new_moon_with_face'
},
mirror100: {
name: 'Own 100 Mirrors',
description: `Disco Ball`,
emoji: 'mirror'
},
butterfly100: {
name: 'Own 100 Butterflies',
description: `Delicate yet powerful.`,
emoji: 'butterfly'
},
quade100: {
name: 'Own 100 Quades',
description: `Your Ops are super Devved right now.`,
emoji: 'quade'
},
hvacker100: {
name: 'Own 100 Hvackers',
description: `Did Sage finally make his git repo public?`,
emoji: 'hvacker_angery'
},
creator100: {
name: 'Own 100 Creators',
description: `_Stern look_`,
emoji: 'question'
},
smallBusiness100: {
name: 'Own 100 Small Businesses',
description: `Enough to run a small city.`,
emoji: 'convenience_store'
},
bigBusiness100: {
name: 'Own 100 Big Businesses',
description: `I mean... that's basically all of them.`,
emoji: 'office'
},
weAllNeedHelp: { weAllNeedHelp: {
name: 'View the \'!coin\' help', name: 'View the \'!coin\' help',
description: 'We all need a little help sometimes', description: 'We all need a little help sometimes',
@ -85,5 +161,11 @@ module.exports = {
name: 'Take a peek at the lore', name: 'Take a peek at the lore',
description: 'It\'t gotta be worth your time somehow.', description: 'It\'t gotta be worth your time somehow.',
emoji: 'books' emoji: 'books'
},
theOtherSide: {
name: 'Die and be reborn',
description: 'You have seen the other side, and do not fear it.',
emoji: 'white_square'
} }
} }

View File

@ -1,5 +1,5 @@
const buyableItems = require('./buyableItems') const buyableItems = require('./buyableItems')
const { commas, saveGame, setHighestCoins, addAchievement, getUser, singleItemCps, chaosFilter } = require('./utils') const { commas, setHighestCoins, addAchievement, getUser, singleItemCps, chaosFilter, fuzzyMatcher } = require('./utils')
const slack = require('../../slack') const slack = require('../../slack')
const calculateCost = ({ itemName, user, quantity = 1 }) => { const calculateCost = ({ itemName, user, quantity = 1 }) => {
@ -17,9 +17,9 @@ const getItemHeader = user => ([itemName, { baseCost, description, emoji }]) =>
const itemCps = Math.round(singleItemCps(user, itemName)) const itemCps = Math.round(singleItemCps(user, itemName))
return `*${itemName}* :${emoji}: - ${itemCost} HVAC Coins - ${commas(itemCps)} CPS\n_${description}_` return `*${itemName}* :${emoji}: - ${itemCost} HVAC Coins - ${commas(itemCps)} CPS\n_${description}_`
} }
const canView = highestCoins => ([, item]) => item.baseCost < (highestCoins || 1) * 101 const canView = (item, highestCoins) => item.baseCost < (highestCoins || 1) * 101
const buyableText = (highestCoins, user) => Object.entries(buyableItems) const buyableText = (highestCoins, user) => Object.entries(buyableItems)
.filter(canView(highestCoins)) .filter(([, item]) => canView(item, highestCoins))
.map(getItemHeader(user)) .map(getItemHeader(user))
.join('\n\n') + .join('\n\n') +
'\n\n:grey_question::grey_question::grey_question:' + '\n\n:grey_question::grey_question::grey_question:' +
@ -73,8 +73,8 @@ const buyText2 = (highestCoins, user) => {
return ({ return ({
text: buyableText(highestCoins, user), text: buyableText(highestCoins, user),
blocks: Object.entries(buyableItems) blocks: Object.entries(buyableItems)
.filter(canView(highestCoins)) .filter(([, item]) => canView(item, highestCoins))
.map(([itemName, item]) => { .map(([itemName]) => {
const cost = calculateCost({ itemName, user, quantity: 1 }) const cost = calculateCost({ itemName, user, quantity: 1 })
const cps = Math.round(singleItemCps(user, itemName)) const cps = Math.round(singleItemCps(user, itemName))
return ({ user, itemName, cost, cps }) return ({ user, itemName, cost, cps })
@ -90,58 +90,65 @@ const maxQuantity = ({ itemName, user, currentCoins }) => {
return quantity return quantity
} }
const buyRoute = async ({ event, say, words, user }) => { const buyRoute = async ({ event, say, args, user }) => {
const buying = words[1] const buying = args[0]
setHighestCoins(event.user) setHighestCoins(event.user)
const query = event?.text?.startsWith('?b ') || event?.text?.startsWith('?buy ')
if (!buying) { if (!buying) {
const highestCoins = user.highestEver || user.coins || 1 const highestCoins = user.highestEver || user.coins || 1
if (buyableItems.quade.baseCost < highestCoins * 100) { if (canView(buyableItems.quade, highestCoins)) {
addAchievement(user, 'seeTheQuade', say) addAchievement(user, 'seeTheQuade', say)
} }
await say(buyText2(highestCoins, user)) await say(buyText2(highestCoins, user))
return return
} }
const buyable = buyableItems[buying] const matcher = fuzzyMatcher(buying)
const buyable = Object.entries(buyableItems).find(([name]) => matcher.test(name))
if (!buyable) { if (!buyable) {
await say('That item does not exist!') await say('That item does not exist!')
return return
} }
const [buyableName, buyableItem] = buyable
let quantity let quantity
const currentCoins = user.coins const currentCoins = user.coins
const max = maxQuantity({ itemName: buying, user, currentCoins }) const max = maxQuantity({ itemName: buyableName, user, currentCoins })
if (words[2] === 'max') { if (!args[1]) {
quantity = 1
} else if (args[1] === 'max') {
quantity = max quantity = max
} else { } else {
quantity = Math.round(chaosFilter(parseInt(words[2] || '1'), 0.2, user, max) || 1) if (query) {
quantity = parseInt(args[1])
} else {
quantity = Math.round(chaosFilter(parseInt(args[1]), 0.2, user, max) || 1)
}
} }
if (!quantity || quantity < 1) { if (!quantity || quantity < 1) {
await say('Quantity must be a positive integer') await say('Quantity must be a positive integer')
return return
} }
const realCost = calculateCost({ itemName: buying, user, quantity }) const realCost = calculateCost({ itemName: buyableName, user, quantity })
if (query) {
return say(`Buying ${quantity} ${buyableName} would cost you ${commas(realCost)} HVAC`)
}
if (currentCoins < realCost) { if (currentCoins < realCost) {
await say(`You don't have enough coins! You have ${commas(currentCoins)}, but you need ${commas(realCost)}`) await say(`You don't have enough coins! You have ${commas(currentCoins)}, but you need ${commas(realCost)}`)
return return
} }
user.coins -= realCost user.coins -= realCost
user.items[buying] = user.items[buying] || 0 user.items[buyableName] = user.items[buyableName] || 0
user.items[buying] += quantity user.items[buyableName] += quantity
if (buying === 'mouse' && user.items.mouse >= 100) { if (user.items[buyableName] >= 100) {
addAchievement(user, 'ratGod', say) addAchievement(user, buyableItems[buyableName].own100Achievement, say)
} }
if (quantity === 1) { const countString = quantity === 1 ? 'one' : quantity
await say(`You bought one :${buyable.emoji}:`) await say(`You bought ${countString} :${buyableItem.emoji}:`)
} else {
await say(`You bought ${quantity} :${buyable.emoji}:`)
}
saveGame()
} }
const buyButton = async ({ body, ack, say, payload }) => { const buyButton = async ({ body, ack, say, payload }) => {
@ -153,7 +160,8 @@ const buyButton = async ({ body, ack, say, payload }) => {
} }
const user = getUser(event.user) const user = getUser(event.user)
const words = ['', buying, body.actions[0].text] const words = ['', buying, body.actions[0].text]
await buyRoute({ event, say, words, user }) const [commandName, ...args] = words
await buyRoute({ event, say, words, args, commandName, user })
const highestCoins = user.highestEver || user.coins || 1 const highestCoins = user.highestEver || user.coins || 1
await slack.app.client.chat.update({ await slack.app.client.chat.update({
channel: body.channel.id, channel: body.channel.id,

View File

@ -3,84 +3,98 @@ module.exports = {
baseCost: 100, baseCost: 100,
earning: 1, earning: 1,
emoji: 'mouse2', emoji: 'mouse2',
description: 'A mouse to steal coins for you.' description: 'A mouse to steal coins for you.',
own100Achievement: 'ratGod',
}, },
accountant: { accountant: {
baseCost: 1_100, baseCost: 1_100,
earning: 8, earning: 8,
emoji: 'male-office-worker', emoji: 'male-office-worker',
description: 'Legally make money from nothing!' description: 'Legally make money from nothing!',
own100Achievement: 'mathematician',
}, },
whale: { whale: {
baseCost: 12_000, baseCost: 12_000,
earning: 47, earning: 47,
emoji: 'whale', emoji: 'whale',
description: 'Someone to spend money on your HVAC Coin mining app.' description: 'Someone to spend money on your HVAC Coin mining app.',
own100Achievement: 'iPod',
}, },
train: { train: {
baseCost: 130_000, baseCost: 130_000,
earning: 260, earning: 260,
emoji: 'train2', emoji: 'train2',
description: 'Efficiently ship your most valuable coins.' description: 'Efficiently ship your most valuable coins.',
own100Achievement: 'fire100',
}, },
fire: { fire: {
baseCost: 1_400_000, baseCost: 1_400_000,
earning: 1_400, earning: 1_400,
emoji: 'fire', emoji: 'fire',
description: 'Return to the roots of HVAC.' description: 'Return to the roots of HVAC.',
own100Achievement: 'train100',
}, },
boomerang: { boomerang: {
baseCost: 20_000_000, baseCost: 20_000_000,
earning: 7_800, earning: 7_800,
emoji: 'boomerang', emoji: 'boomerang',
description: 'Your coin always seems to come back.' description: 'Your coin always seems to come back.',
own100Achievement: 'boom100',
}, },
moon: { moon: {
baseCost: 330_000_000, baseCost: 330_000_000,
earning: 44_000, earning: 44_000,
emoji: 'new_moon_with_face', emoji: 'new_moon_with_face',
description: 'Convert dark new-moon energy into HVAC Coins.' description: 'Convert dark new-moon energy into HVAC Coins.',
own100Achievement: 'mirror100',
}, },
butterfly: { butterfly: {
baseCost: 5_100_000_000, baseCost: 5_100_000_000,
earning: 260_000, earning: 260_000,
emoji: 'butterfly', emoji: 'butterfly',
description: 'Create the exact worldly chaos to bit-flip HVAC Coins into existence on your computer.' description: 'Create the exact worldly chaos to bit-flip HVAC Coins into existence on your computer.',
own100Achievement: 'butterfly100',
}, },
mirror: { mirror: {
baseCost: 75_000_000_000, baseCost: 75_000_000_000,
earning: 1_600_000, earning: 1_600_000,
emoji: 'mirror', emoji: 'mirror',
description: 'Only by gazing inward can you collect enough Coin to influence the thermostat.' description: 'Only by gazing inward can you collect enough Coin to influence the thermostat.',
own100Achievement: 'quade100',
}, },
quade: { quade: {
baseCost: 1_000_000_000_000, baseCost: 1_000_000_000_000,
earning: 10_000_000, earning: 10_000_000,
emoji: 'quade', emoji: 'quade',
description: 'Has thumbs capable of physically manipulating the thermostat.' description: 'Has thumbs capable of physically manipulating the thermostat.',
own100Achievement: 'hvacker100',
}, },
hvacker: { hvacker: {
baseCost: 14_000_000_000_000, baseCost: 14_000_000_000_000,
earning: 65_000_000, earning: 65_000_000,
emoji: 'hvacker_angery', emoji: 'hvacker_angery',
description: 'Harness the power of the mad god himself.' description: 'Harness the power of the mad god himself.',
own100Achievement: 'creator100',
}, },
creator: { creator: {
baseCost: 170_000_000_000_000, baseCost: 170_000_000_000_000,
earning: 430_000_000, earning: 430_000_000,
emoji: 'question', emoji: 'question',
description: 'The elusive creator of Hvacker takes a favorable look at your CPS.' description: 'The elusive creator of Hvacker takes a favorable look at your CPS.',
own100Achievement: 'smallBusiness100',
}, },
smallBusiness: { smallBusiness: {
baseCost: 2_210_000_000_000_000, baseCost: 2_210_000_000_000_000,
earning: 2_845_000_000, earning: 2_845_000_000,
emoji: 'convenience_store', emoji: 'convenience_store',
description: 'The place where the creator of Hvacker goes to work.' description: 'The place where the creator of Hvacker goes to work.',
own100Achievement: 'bigBusiness100',
}, },
bigBusiness: { bigBusiness: {
baseCost: 26_210_000_000_000_000, baseCost: 26_210_000_000_000_000,
earning: 23_650_000_000, earning: 23_650_000_000,
emoji: 'office', emoji: 'office',
description: 'The place where the smallBusiness goes to work.' description: 'The place where the smallBusiness goes to work.',
own100Achievement: 'ratGod',
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -39,6 +39,8 @@ const lore = [
l(`And the ninth...`), l(`And the ninth...`),
l(`Well, the ninth might actually amount to something.`), l(`Well, the ninth might actually amount to something.`),
l(`https://i.imgur.com/eFreg7Y.gif\n`), l(`https://i.imgur.com/eFreg7Y.gif\n`),
//l(`As you might imagine, the ninth egg was I, the almighty Hvacker.`)
] ]
slack.onReaction(async ({ event, say }) => { slack.onReaction(async ({ event, say }) => {
@ -56,11 +58,11 @@ slack.onReaction(async ({ event, say }) => {
} }
return return
} }
console.log(lore[user.lore]) console.log('lore:', lore[user.lore])
await say(lore[user.lore].correctResponse) await say(lore[user.lore].correctResponse)
user.lore += 1 user.lore += 1
saveGame() saveGame()
} catch (e) {console.error(e)} } catch (e) {console.error('onReaction error', e)}
}) })
const encodeLore = loreNumber => lore[loreNumber].text.startsWith(':') && lore[loreNumber].text.endsWith(':') ? '' : const encodeLore = loreNumber => lore[loreNumber].text.startsWith(':') && lore[loreNumber].text.endsWith(':') ? '' :
@ -77,35 +79,35 @@ const loreMessage = (user, say) => {
return `Sorry. I'd love to tell you more, but I'm tired. Please check back later.` return `Sorry. I'd love to tell you more, but I'm tired. Please check back later.`
} }
const loreRoute = async ({ say, words, user, isAdmin }) => { const loreRoute = async ({ say, args, user, isAdmin }) => {
user.lore ??= 0 user.lore ??= 0
if (!words[1]) { if (!args[0]) {
const message = loreMessage(user, say) const message = loreMessage(user, say)
await say(message) await say(message)
if (!lore[user.lore]?.correctReactions) { if (!lore[user.lore]?.correctReactions) {
user.lore += 1 user.lore += 1
} }
saveGame() //saveGame()
console.log('Sent ' + user.name + ':\n' + message) console.log('Sent ' + user.name + ':\n' + message)
return return
} }
if (words[1] === 'reset') { if (args[0] === 'reset') {
user.lore = 0 user.lore = 0
saveGame() //saveGame()
return say(`I have reset your place in the story.`) return say(`I have reset your place in the story.`)
} }
if (isAdmin) { if (isAdmin) {
if (words[1] === 'all') { if (args[0] === 'all') {
let loreMessage = '' let loreMessage = ''
for (let i = 0; i < user.lore; i++) { for (let i = 0; i < user.lore; i++) {
loreMessage += lore[i].text + (lore[i].correctResponse || '') + '\n' loreMessage += lore[i].text + (lore[i].correctResponse || '') + '\n'
} }
return say(loreMessage) return say(loreMessage)
} }
const jumpTo = parseInt(words[1]) const jumpTo = parseInt(args[0])
if (!isNaN(jumpTo)) { if (!isNaN(jumpTo)) {
user.lore = jumpTo user.lore = jumpTo
saveGame() //saveGame()
} }
} }
} }

View File

@ -1,4 +1,4 @@
const { commas, saveGame, quackGradeMultiplier, prestigeMultiplier, makeBackup } = require('./utils') const { commas, quackGradeMultiplier, prestigeMultiplier, makeBackup, userHasCheckedQuackgrade } = require('./utils')
const { quackStore } = require('./quackstore') const { quackStore } = require('./quackstore')
const possiblePrestige = coins => { const possiblePrestige = coins => {
@ -17,10 +17,10 @@ const totalCostForPrestige = prestigeLevel => {
return (tpcRecMemo[prestigeLevel]) || (tpcRecMemo[prestigeLevel] = 1_000_000_000_000 * Math.pow(prestigeLevel, 3) + totalCostForPrestige(prestigeLevel - 1)) return (tpcRecMemo[prestigeLevel]) || (tpcRecMemo[prestigeLevel] = 1_000_000_000_000 * Math.pow(prestigeLevel, 3) + totalCostForPrestige(prestigeLevel - 1))
} }
const prestigeRoute = async ({ say, words, user }) => { const prestigeRoute = async ({ say, args, user }) => {
const possible = possiblePrestige(user.coinsAllTime) const possible = possiblePrestige(user.coinsAllTime)
const current = user.prestige ??= 0 const current = user.prestige ??= 0
if (words[1] === 'me') { if (args[0] === 'me') {
await say( await say(
'This will permanently remove all of your items, upgrades, and coins!\n\n' + 'This will permanently remove all of your items, upgrades, and coins!\n\n' +
'Say \'!!prestige me\' to confirm.' 'Say \'!!prestige me\' to confirm.'
@ -53,18 +53,18 @@ const prestigeConfirmRoute = async ({ event, say, user }) => {
user.quacks += (possible - user.prestige) user.quacks += (possible - user.prestige)
user.prestige = possible user.prestige = possible
user.highestEver = 0
user.coins = 0 user.coins = 0
user.items = {} user.items = {};
const starterUpgrades = (user.quackUpgrades?.starter || [])
starterUpgrades.forEach(upgradeName => quackStore[upgradeName].effect(user))
user.upgrades = {} user.upgrades = {}
saveGame()
await say('You prestiged! Check out !quackstore to see what you can buy!') await say('You prestiged! Check out !quackstore to see what you can buy!')
} }
const quackStoreListing = (showCost = true) => ([name, upgrade]) => const quackStoreListing = (showCost = true) => ([name, upgrade]) =>
showCost `:${upgrade.emoji}: *${name}* - ${showCost ? 'Costs' : 'Worth'} *${upgrade.cost} Quack.*\n\n_${upgrade.description}_`
? `:${upgrade.emoji}: *${name}* - Costs *${upgrade.cost} Quack.*\n\n_${upgrade.description}_`
: `:${upgrade.emoji}: *${name}* - Worth *${upgrade.cost} Quack.*\n\n_${upgrade.description}_`
const allUserQuackUpgrades = user => const allUserQuackUpgrades = user =>
Object.entries(user.quackUpgrades || {}) Object.entries(user.quackUpgrades || {})
@ -75,6 +75,7 @@ const hasPreReqs = user => ([name, upgrade]) => {
return true return true
} }
const allUserUpgrades = allUserQuackUpgrades(user) const allUserUpgrades = allUserQuackUpgrades(user)
console.log('allUserUpgrades', allUserUpgrades)
return upgrade.preReqs.every(preReq => allUserUpgrades.includes(preReq)) return upgrade.preReqs.every(preReq => allUserUpgrades.includes(preReq))
} }
@ -92,27 +93,31 @@ const quackStoreText = user =>
`\n\nYou have ${user.quacks ??= 0} quacks to spend.` + `\n\nYou have ${user.quacks ??= 0} quacks to spend.` +
`\nQuackStore upgrades are currently boosting your CPS by ${commas((quackGradeMultiplier(user) - 1) * 100)}%` `\nQuackStore upgrades are currently boosting your CPS by ${commas((quackGradeMultiplier(user) - 1) * 100)}%`
const quackStoreRoute = async ({ user, say, words }) => { const quackStoreRoute = async ({ user, say, args }) => {
user.quackUpgrades ??= {} user.quackUpgrades ??= {}
const quacks = user.quacks ??= 0 const quacks = user.quacks ??= 0
if (!words[1]) { if (!args[0]) {
await say(quackStoreText(user)) await say(quackStoreText(user))
return return
} }
console.log(`Trying to buy ${words[1]}`) console.log(`Trying to buy ${args[0]}`)
const quackItem = quackStore[words[1]] const quackItem = quackStore[args[0]]
if (!quackItem || !unownedQuackItems(user).find(([name]) => name === words[1])) { if (!quackItem || !unownedQuackItems(user).find(([name]) => name === args[0])) {
await say(`'${words[1]}' is not available in the quack store!`) await say(`'${args[0]}' is not available in the quack store!`)
return return
} }
if (quackItem.cost > quacks) { if (quackItem.cost > quacks) {
await say(`${words[1]} costs ${quackItem.cost} Quacks, but you only have ${quacks}!`) await say(`${args[0]} costs ${quackItem.cost} Quacks, but you only have ${quacks}!`)
return return
} }
user.quacks -= quackItem.cost user.quacks -= quackItem.cost
user.quackUpgrades[quackItem.type] ??= [] user.quackUpgrades[quackItem.type] ??= []
user.quackUpgrades[quackItem.type].push(words[1]) user.quackUpgrades[quackItem.type].push(args[0])
saveGame() if (quackItem.type === 'starter') {
quackItem.effect(user)
}
await say(`You bought ${args[0]}!`)
//saveGame()
} }
const ownedQuacksText = user => const ownedQuacksText = user =>

View File

@ -31,9 +31,6 @@ const quackStore = {
//+ '_\n_Averages a 26% CPS boost.', //+ '_\n_Averages a 26% CPS boost.',
preReqs: ['nuclearFuel'], preReqs: ['nuclearFuel'],
effect: (cps, user) => { effect: (cps, user) => {
if (user.name !== 'Sage') {
console.log('Chaos Multiplier', getChaos(Math.round(user.interactions / 50)))
}
return cps * getChaos(Math.round(user.interactions / 50)) return cps * getChaos(Math.round(user.interactions / 50))
}, },
cost: 10 cost: 10
@ -44,10 +41,67 @@ const quackStore = {
type: 'lightning', type: 'lightning',
emoji: 'rose', emoji: 'rose',
description: 'Smells nice. Makes lightning twice as likely to strike.', description: 'Smells nice. Makes lightning twice as likely to strike.',
//+ '_\n_Averages a 26% CPS boost.',
effect: lightningOdds => lightningOdds * 2, effect: lightningOdds => lightningOdds * 2,
preReqs: ['nuclearFuel'], preReqs: ['nuclearFuel'],
cost: 10 cost: 10
},
// Checked Upgrades. Have no effect(), but their existence is referred to elsewhere.
theGift: {
name: 'The Gift',
type: 'checked',
emoji: 'eye-in-speech-bubble',
description: 'Become forewarned of certain events...',
preReqs: ['dryerSheet', 'chaos'],
cost: 10
},
theVoice: {
name: 'The Voice',
type: 'checked',
emoji: 'loud_sound',
description: 'Unlocks the !speak command',
preReqs: ['dryerSheet', 'chaos'],
cost: 50
},
cheeseBaby: {
name: 'cheeseBaby',
type: 'starter',
emoji: 'baby_symbol',
description: 'Start each prestige with 5 mice',
preReqs: ['dryerSheet', 'chaos'],
effect: user => {
user.items.mouse ??= 0
user.items.mouse += 5
},
cost: 5
},
silverSpoon: {
name: 'Silver Spoon',
type: 'starter',
emoji: 'spoon',
description: 'Start each prestige with 5 accountants',
preReqs: ['cheeseBaby'],
effect: user => {
user.items.accountant ??= 0
user.items.accountant += 5
},
cost: 10
},
oceanMan: {
name: 'Ocean Man',
type: 'starter',
emoji: 'ocean',
description: 'Start each prestige with 5 whales',
preReqs: ['silverSpoon'],
effect: user => {
user.items.whale ??= 0
user.items.whale += 5
},
cost: 20
} }
} }

View File

@ -1,3 +1,4 @@
module.exports = { module.exports = {
horrorEnabled: false horrorEnabled: false,
admins: ['Sage']
} }

View File

@ -14,20 +14,22 @@ const evil = ({ type, description, cost }) => basic({
extraCondition: (user, squadGrades) => squadGrades?.includes('discardHumanMorals'), extraCondition: (user, squadGrades) => squadGrades?.includes('discardHumanMorals'),
}) })
const heavenly = ({ type, description, cost }) => basic({ const heavenly = ({ type, description, cost, multiplier = 2 }) => ({
type, type,
description, description,
count: 60, condition: (user, squadGrades) => user.items[type] >= 60 && squadGrades?.includes('redemption'),
cost, cost,
extraCondition: (user, squadGrades) => squadGrades?.includes('redemption'), effect: cps => cps * multiplier
}) })
const disabled = () => false
const baby = ({ type, description, cost }) => basic({ const baby = ({ type, description, cost }) => basic({
type, type,
description, description,
count: 80, count: 70,
cost, cost,
extraCondition: (user, squadGrades) => squadGrades?.includes('redemption'), extraCondition: disabled
}) })
const geometry = ({ type, description, cost }) => basic({ const geometry = ({ type, description, cost }) => basic({
@ -35,7 +37,7 @@ const geometry = ({ type, description, cost }) => basic({
description, description,
count: 100, count: 100,
cost, cost,
extraCondition: (user, squadGrades) => squadGrades?.includes('redemption'), extraCondition: disabled
}) })
const universitality = ({ type, description, cost }) => basic({ const universitality = ({ type, description, cost }) => basic({
@ -43,7 +45,7 @@ const universitality = ({ type, description, cost }) => basic({
description, description,
count: 100, count: 100,
cost, cost,
extraCondition: (user, squadGrades) => squadGrades?.includes('redemption'), extraCondition: disabled
}) })
module.exports = { module.exports = {
@ -72,8 +74,14 @@ module.exports = {
}), }),
hoodedMice: heavenly({ hoodedMice: heavenly({
type: 'mouse', type: 'mouse',
description: 'These monks have nearly reached enlightenment.', description: 'These monks have nearly reached enlightenment. 10x Mouse CPS.',
cost: 1_000_000, cost: 1_000_000,
multiplier: 10,
}),
babyMouse: baby({
type: 'mouse',
description: 'Squeak!',
cost: 6_000_000,
}), }),
fasterComputers: basic({ fasterComputers: basic({
@ -101,8 +109,14 @@ module.exports = {
}), }),
charityFund: heavenly({ charityFund: heavenly({
type: 'accountant', type: 'accountant',
description: 'THIS one is more than just a tax break.', description: 'THIS one is more than just a tax break. 9x Accountant CPS.',
cost: 16_333_333, cost: 16_333_333,
multiplier: 9,
}),
mathBaby: baby({
type: 'accountant',
description: '2 + 2 = WAAH!',
cost: 99_999_999,
}), }),
biggerBlowhole: basic({ biggerBlowhole: basic({
@ -130,8 +144,14 @@ module.exports = {
}), }),
whaleChoir: heavenly({ whaleChoir: heavenly({
type: 'whale', type: 'whale',
description: `Their cleansing songs reverberate through the sea.`, description: `Their cleansing songs reverberate through the sea. 8x Whale CPS.`,
cost: 144_000_000 cost: 144_000_000,
multiplier: 8,
}),
smolWhales: baby({
type: 'whale',
description: ``,
cost: 8_400_000_000
}), }),
greasyTracks: basic({ greasyTracks: basic({
@ -159,7 +179,8 @@ module.exports = {
}), }),
toyTrain: heavenly({ toyTrain: heavenly({
type: 'train', type: 'train',
description: 'Something simple. Toot toot!', description: 'Toot toot! 8x Train CPS.',
multiplier: 8,
cost: 2_220_000_000 cost: 2_220_000_000
}), }),
@ -171,7 +192,7 @@ module.exports = {
}), }),
extremelyDryFuel: basic({ extremelyDryFuel: basic({
type: 'fire', type: 'fire',
description: 'Use the ignite command for a secret achievement.', description: 'Hey, psst, hey. Use the ignite command for a secret achievement.',
count: 10, count: 10,
cost: 163_000_000 cost: 163_000_000
}), }),
@ -188,9 +209,15 @@ module.exports = {
}), }),
blueFire: heavenly({ blueFire: heavenly({
type: 'fire', type: 'fire',
description: `You can hear it singing with delight.`, description: `You can hear it singing with delight. 7x Fire CPS.`,
multiplier: 7,
cost: 25_200_000_000 cost: 25_200_000_000
}), }),
cuteFire: baby({
type: 'fire',
description: `I just met my perfect match...`,
cost: 150_000_000_000
}),
spoonerang: basic({ spoonerang: basic({
type: 'boomerang', type: 'boomerang',
@ -217,7 +244,8 @@ module.exports = {
}), }),
youRang: heavenly({ youRang: heavenly({
type: 'boomerang', type: 'boomerang',
description: 'Your arms and legs recede into your body. You bend at the middle. You fly. And for a moment, you are free.', description: 'Your arms and legs recede into your body. You bend at the middle. You fly. And for a moment, you are free._\n_7x Boomerang CPS.',
multiplier: 7,
cost: 360_000_000_000 cost: 360_000_000_000
}), }),
@ -246,7 +274,8 @@ module.exports = {
}), }),
newMoon: heavenly({ newMoon: heavenly({
type: 'moon', type: 'moon',
description: `Build a second moon to provide space for affordable housing.`, description: `Build a second moon to provide space for affordable housing. 6x Moon CPS.`,
multiplier: 6,
cost: 5_190_000_000_000 cost: 5_190_000_000_000
}), }),
@ -275,7 +304,8 @@ module.exports = {
}), }),
quietingNectar: heavenly({ quietingNectar: heavenly({
type: 'butterfly', type: 'butterfly',
description: 'Calming and extra sweet. Soothes even human ails.', description: 'Calming and extra sweet. Soothes even human ails. 6x Butterfly CPS.',
multiplier: 6,
cost: 75_300_000_000_000 cost: 75_300_000_000_000
}), }),
@ -304,7 +334,8 @@ module.exports = {
}), }),
funHouseMirror: heavenly({ funHouseMirror: heavenly({
type: 'mirror', type: 'mirror',
description: `yoU LOok so siLLY IN thesE THINgs`, description: `yoU LOok so siLLY IN thesE THINgs. 5X mIRror CpS.`,
multiplier: 5,
cost: 1_330_000_000_000_000 cost: 1_330_000_000_000_000
}), }),
@ -333,7 +364,8 @@ module.exports = {
}), }),
hannahMontanaLinux: heavenly({ hannahMontanaLinux: heavenly({
type: 'quade', type: 'quade',
description: `The patrician's choice.`, description: `The patrician's choice. 4x Quade CPS.`,
multiplier: 4,
cost: 18_000_000_000_000_000 cost: 18_000_000_000_000_000
}), }),
@ -362,7 +394,8 @@ module.exports = {
}), }),
mutualUnderstanding: heavenly({ mutualUnderstanding: heavenly({
type: 'hvacker', type: 'hvacker',
description: `lol fat chance, dummy. Points for trying, though`, description: `lol fat chance, dummy. Points for trying, though. 3x Hvacker CPS`,
multiplier: 3,
cost: 250_000_000_000_000_000 cost: 250_000_000_000_000_000
}), }),
@ -391,7 +424,8 @@ module.exports = {
}), }),
goVegan: heavenly({ goVegan: heavenly({
type: 'creator', type: 'creator',
description: `Unlock your vegan powers.`, description: `Unlock your vegan powers. 3x Creator CPS.`,
multiplier: 3,
cost: 3_600_000_000_000_000_000 cost: 3_600_000_000_000_000_000
}), }),
@ -420,7 +454,8 @@ module.exports = {
}), }),
coop: heavenly({ coop: heavenly({
type: 'smallBusiness', type: 'smallBusiness',
description: `By the people, for the people.`, description: `By the people, for the people. 2x smallBusiness CPS`,
multiplier: 2,
cost: 5_140_000_000_000_000_000 cost: 5_140_000_000_000_000_000
}), }),
@ -449,7 +484,8 @@ module.exports = {
}), }),
makePublic: heavenly({ makePublic: heavenly({
type: 'bigBusiness', type: 'bigBusiness',
description: `Downplay immediate profit for more long-term benefits.`, description: `Downplay immediate profit for more long-term benefits. 2x bigBusiness CPS.`,
multiplier: 2,
cost: 42_000_000_000_000_000_000 cost: 42_000_000_000_000_000_000
}), }),

View File

@ -7,7 +7,7 @@ const { quackStore, getChaos } = require('./quackstore')
const saveFile = 'hvacoins.json' const saveFile = 'hvacoins.json'
const logError = msg => msg ? console.error(msg) : () => { /* Don't log empty message */ } const logError = msg => msg ? console.error('logError: ', msg) : () => { /* Don't log empty message */ }
const loadGame = () => { const loadGame = () => {
const game = parseOr(fs.readFileSync('./' + saveFile, 'utf-8'), const game = parseOr(fs.readFileSync('./' + saveFile, 'utf-8'),
@ -53,12 +53,15 @@ const makeBackup = () => {
} }
let saves = 0 let saves = 0
const saveGame = () => { const saveGame = (force = true) => {
if (saves % 100 === 0) { if (saves % 100 === 0) {
makeBackup() makeBackup()
} }
saves += 1 saves += 1
if (force || saves % 10 === 0) {
console.log('SAVING GAME')
fs.writeFileSync('./' + saveFile, JSON.stringify(game, null, 2)) fs.writeFileSync('./' + saveFile, JSON.stringify(game, null, 2))
}
} }
const maybeNews = say => { const maybeNews = say => {
@ -74,9 +77,10 @@ const maybeNews = say => {
const idFromWord = word => { const idFromWord = word => {
if (!word?.startsWith('<@') || !word.endsWith('>')) { if (!word?.startsWith('<@') || !word.endsWith('>')) {
return null return getIdFromName(word)
} } else {
return word.substring(2, word.length - 1) return word.substring(2, word.length - 1)
}
} }
const getSeconds = () => new Date().getTime() / 1000 const getSeconds = () => new Date().getTime() / 1000
@ -113,7 +117,7 @@ const parseAll = (str, allNum) => {
return NaN return NaN
} }
str = str.toLowerCase()?.replace(/,/g, '') str = str?.toLowerCase()?.replace(/,/g, '') || '1'
switch (str) { switch (str) {
case 'all': case 'all':
@ -143,12 +147,10 @@ const parseAll = (str, allNum) => {
console.log('STR', str) console.log('STR', str)
if (str.match(/^\d+$/)) { if (str.match(/^\d+$/)) {
console.log('parseInt()')
return parseInt(str) return parseInt(str)
} }
if (str.match(/^\d+\.\d+$/)) { if (str.match(/^\d+\.\d+$/)) {
console.log('parseFloat()')
return Math.round(parseFloat(str)) return Math.round(parseFloat(str))
} }
@ -185,6 +187,18 @@ const addAchievement = (user, achievementName, say) => {
}, 500) }, 500)
} }
const fuzzyMatcher = string => new RegExp((string?.toLowerCase() || '').split('').join('.*'), 'i')
let knownUsers = {}
const getIdFromName = name => {
const matcher = fuzzyMatcher(name?.toLowerCase())
const found = Object.entries(knownUsers).find(([id, knownName]) => matcher.test(knownName?.toLowerCase()))
if (found) {
return found[0]
}
return null;
}
const getUser = userId => { const getUser = userId => {
if (!users[userId]) { if (!users[userId]) {
users[userId] = { users[userId] = {
@ -205,20 +219,24 @@ const getUser = userId => {
return users[userId] return users[userId]
} }
const addCoins = (user, add) => {
user.coins += add
user.coinsAllTime += add
user.coinsAllTime = Math.floor(user.coinsAllTime)
user.coins = Math.floor(user.coins)
}
const getCoins = userId => { const getCoins = userId => {
const user = getUser(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
const increase = getCPS(user) * secondsPassed addCoins(user, getCPS(user) * secondsPassed)
user.coins += increase
user.coinsAllTime += increase
user.coins = Math.floor(user.coins)
user.lastCheck = currentTime user.lastCheck = currentTime
setHighestCoins(userId) setHighestCoins(userId)
saveGame() //saveGame()
return user.coins return user.coins
} }
@ -290,7 +308,8 @@ const singleItemCps = (user, itemName) => {
const itemUpgradeCps = itemUpgrades.reduce((totalCps, upgrade) => upgrade.effect(totalCps, user), 1) const itemUpgradeCps = itemUpgrades.reduce((totalCps, upgrade) => upgrade.effect(totalCps, user), 1)
// console.log('itemUpgradeCps', itemUpgradeCps) // console.log('itemUpgradeCps', itemUpgradeCps)
const userGeneralUpgrades = user.upgrades.general || [] user.upgrades.general ??= []
const userGeneralUpgrades = user.upgrades.general
const generalUpgradeCps = Object.entries(userGeneralUpgrades).reduce((total, [, upgradeName]) => upgrades[upgradeName].effect(total, user), 1) const generalUpgradeCps = Object.entries(userGeneralUpgrades).reduce((total, [, upgradeName]) => upgrades[upgradeName].effect(total, user), 1)
// console.log('generalUpgradeCps', generalUpgradeCps) // console.log('generalUpgradeCps', generalUpgradeCps)
@ -382,6 +401,41 @@ const addReactions = async ({ app, channelId, timestamp, reactions }) => {
} }
} }
} }
const daysSinceEpoch = () => {
const today = new Date().getTime()
const epoch = new Date(0).getTime()
return Math.floor((today - epoch) / (1000 * 60 * 60 * 24))
}
const dayOfYear = () => {
const date = new Date()
return ((Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) - Date.UTC(date.getFullYear(), 0, 0)) / 24 / 60 / 60 / 1000)
}
game.stonkMarket ??= {
lastDay: daysSinceEpoch(),
stonks: {
duk: {
pattern: "duk",
index: 0,
price: 1_410_911_983_728
},
quak: {
pattern: "quak",
index: 0,
price: 5_111_242_778_696
},
honk: {
pattern: "honk",
index: 0,
price: 511_915_144_009
},
}
}
const userHasCheckedQuackgrade = (user, quackGrade) => (user.quackUpgrades?.checked || []).includes(quackGrade)
module.exports = { module.exports = {
saveGame, saveGame,
makeBackup, makeBackup,
@ -408,5 +462,11 @@ module.exports = {
chaosFilter, chaosFilter,
addReactions, addReactions,
getCompletedSquadgradeNames, getCompletedSquadgradeNames,
game game,
dayOfYear,
daysSinceEpoch,
userHasCheckedQuackgrade,
fuzzyMatcher,
addCoins,
setKnownUsers: users => knownUsers = users
} }

View File

@ -4,10 +4,10 @@ const port = 3001
const crypto = require('crypto') const crypto = require('crypto')
const base64 = require('base-64') const base64 = require('base-64')
const slack = require('../../slack') const slack = require('../../slack')
const { game: { users } } = require('./utils') const { game: { users }, getUser, fuzzyMatcher } = require('./utils')
const apiGetUserId = hash => { const apiGetUserId = hash => {
return Object.entries(userGetter.users) return Object.entries(users)
.filter(([id, user]) => user.pwHash === hash) .filter(([id, user]) => user.pwHash === hash)
.map(([id, user]) => id)[0] .map(([id, user]) => id)[0]
} }
@ -17,12 +17,21 @@ const makeHash = pw =>
.update(pw) .update(pw)
.digest('hex') .digest('hex')
const illegalCommands = ['!', '!b']
const lastCalls = {}
const addCommand = ({ commandNames, helpText, action, condition, hidden }) => { 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 route = async (req, res) => {
const say = async msg => res.send(msg) const say = async msg => res.send(msg + '\n')
try { try {
const words = ['', ...Object.keys(req.query)] const words = ['', ...Object.keys(req.query)]
console.log('INCOMING API CALL:', name, words) const [commandName, ...args] = words
console.log('INCOMING API CALL:', commandName, words)
const encoded = req.header('Authorization').substring(5) const encoded = req.header('Authorization').substring(5)
const decoded = base64.decode(encoded).substring(1) const decoded = base64.decode(encoded).substring(1)
const event = { const event = {
@ -37,8 +46,8 @@ const addCommand = ({ commandNames, helpText, action, condition, hidden }) => {
console.log(' bad password') console.log(' bad password')
return return
} }
const lastCall = userGetter.users[event.user].lastApiCall || 0 const lastCall = lastCalls[event.user] || 0
const secondsBetweenCalls = 5 const secondsBetweenCalls = 30
const currentTime = Math.floor(new Date().getTime() / 1000) const currentTime = Math.floor(new Date().getTime() / 1000)
if (lastCall + secondsBetweenCalls > currentTime) { if (lastCall + secondsBetweenCalls > currentTime) {
res.status(400) res.status(400)
@ -47,12 +56,21 @@ const addCommand = ({ commandNames, helpText, action, condition, hidden }) => {
return return
} }
console.log(` went through for ${slack.users[event.user]}`) console.log(` went through for ${slack.users[event.user]}`)
userGetter.users[event.user].lastApiCall = currentTime lastCalls[event.user] = currentTime
await action({ event, say, words }) 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.Sage) })
if (!canUse) {
await say(`Command '${words[0]}' not found`)
return
}
await action({ event, say, trueSay: say, words, args, commandName, user, userId: event.user, haunted })
} catch (e) { } catch (e) {
console.error(e) console.error('route error', e)
await say(e.stack) 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\'`')
} }
} }
commandNames.forEach(name => commandNames.forEach(name =>

View File

@ -6,7 +6,7 @@ const getTrivia = async () => axios.get('https://opentdb.com/api.php?amount=10&c
} }
}) })
.then(res => res.data.results) .then(res => res.data.results)
.catch(console.error) .catch(e => console.error('trivia error', e))
module.exports = { module.exports = {
getTrivia getTrivia

View File

@ -1,6 +1,6 @@
const { App: SlackApp } = require('@slack/bolt') const { App: SlackApp } = require('@slack/bolt')
const config = require('../config') const config = require('../config')
const { addReactions } = require('../games/hvacoins/utils') const { addReactions, saveGame } = require('../games/hvacoins/utils')
const temperatureChannelId = 'C034156CE03' const temperatureChannelId = 'C034156CE03'
@ -27,7 +27,7 @@ try {
console.log('Failed to initialize SlackApp', e) console.log('Failed to initialize SlackApp', e)
} }
const pollTriggers = ['!temp', '!temperature', '!imhot', '!imcold'] const pollTriggers = ['!temp', '!temperature', '!imhot', '!imcold', '!imfreezing', '!idonthavemysweater']
const halfTriggers = ['change temperature', "i'm cold", "i'm hot", 'quack', 'hvacker', '<@U0344TFA7HQ>'] const halfTriggers = ['change temperature', "i'm cold", "i'm hot", 'quack', 'hvacker', '<@U0344TFA7HQ>']
const sendHelp = async (say, prefix) => { const sendHelp = async (say, prefix) => {
@ -53,6 +53,7 @@ const getMessage = async ({ channel, ts }) => app.client.conversations.history({
}) })
app.event('reaction_added', async ({ event, context, client, say }) => { app.event('reaction_added', async ({ event, context, client, say }) => {
console.log('reaction_added', event)
for (const listener of reactionListeners) { for (const listener of reactionListeners) {
listener({ event, say }) listener({ event, say })
} }
@ -70,6 +71,8 @@ const users = {
U0X0ZQCN6: 'Caleb', U0X0ZQCN6: 'Caleb',
U03BBTD4CQZ: 'Fernando', U03BBTD4CQZ: 'Fernando',
U03DF152WUV: 'Nik', U03DF152WUV: 'Nik',
U2X0SG7BP: 'John',
UR2H5KNHY: 'Jake',
Sage: 'U028BMEBWBV', Sage: 'U028BMEBWBV',
Adam: 'U02U15RFK4Y', Adam: 'U02U15RFK4Y',
@ -81,15 +84,34 @@ const users = {
Caleb: 'U0X0ZQCN6', Caleb: 'U0X0ZQCN6',
Hvacker: 'U0344TFA7HQ', Hvacker: 'U0344TFA7HQ',
Fernando: 'U03BBTD4CQZ', Fernando: 'U03BBTD4CQZ',
Nik: 'U03DF152WUV' John: 'U2X0SG7BP',
Jake: 'UR2H5KNHY',
} }
const buildSayPrepend = ({ say, prepend }) => async msg => {
if (typeof(msg) === 'string') {
return say(prepend + msg)
}
return say({
...msg,
text: prepend + msg.text
})
}
process.once('SIGINT', code => {
saveGame(true)
process.exit()
})
const activePolls = {} const activePolls = {}
const testId = 'U028BMEBWBV_TEST' const testId = 'U028BMEBWBV_TEST'
let testMode = false let testMode = false
app.event('message', async ({ event, context, client, say }) => { app.event('message', async ({ event, context, client, say }) => {
if (event.subtype !== 'message_changed') { if (event.subtype !== 'message_changed' && event?.text !== '!') {
console.log(event) console.log('message.event', {
...event,
userName: users[event.user]
})
} }
if (event?.user === users.Sage) { if (event?.user === users.Sage) {
if (event?.text.startsWith('!')) { if (event?.text.startsWith('!')) {
@ -116,6 +138,10 @@ app.event('message', async ({ event, context, client, say }) => {
} }
if (event.user === users.Sage && event.channel === 'D0347Q4H9FE') { if (event.user === users.Sage && event.channel === 'D0347Q4H9FE') {
if (event.text === '!!kill') { if (event.text === '!!kill') {
saveGame(true)
process.exit(1)
} else if (event.text === '!!restart') {
saveGame(true)
process.exit() process.exit()
} }
if (event.text?.startsWith('!say ') || event.text?.startsWith('!say\n')) { if (event.text?.startsWith('!say ') || event.text?.startsWith('!say\n')) {
@ -167,6 +193,7 @@ app.event('message', async ({ event, context, client, say }) => {
reactCounts[name] += 1 reactCounts[name] += 1
} }
}) })
console.log('REACT COUNTS', JSON.stringify(reactCounts))
const contentVotes = reactCounts[goodEmoji] || 0 const contentVotes = reactCounts[goodEmoji] || 0
let hotterVotes = reactCounts[hotterEmoji] || 0 let hotterVotes = reactCounts[hotterEmoji] || 0
@ -188,11 +215,11 @@ app.event('message', async ({ event, context, client, say }) => {
let text let text
if (hotterVotes > colderVotes && hotterVotes > contentVotes) { if (hotterVotes > colderVotes && hotterVotes > contentVotes) {
text = `<@${users.Quade}> The people have spoken, and would like to ` text = `<@${users.Adam}> The people have spoken, and would like to `
text += 'raise the temperature, quack.' text += 'raise the temperature, quack.'
requestTempChange('Hotter') requestTempChange('Hotter')
} else if (colderVotes > hotterVotes && colderVotes > contentVotes) { } else if (colderVotes > hotterVotes && colderVotes > contentVotes) {
text = `<@${users.Quade}> The people have spoken, and would like to ` text = `<@${users.Adam}> The people have spoken, and would like to `
text += 'lower the temperature, quack quack.' text += 'lower the temperature, quack quack.'
requestTempChange('Colder') requestTempChange('Colder')
} else { } else {
@ -233,7 +260,7 @@ const messageIn = async (channel, optionsOrText) => {
const startPoll = async () => { const startPoll = async () => {
const sent = await postToTechThermostatChannel({ const sent = await postToTechThermostatChannel({
text: `<!here|here> Temperature poll requested! In ${pollingMinutes} minutes the temperature will be adjusted.\n` + text: `<!here> Temperature poll requested! In ${pollingMinutes} minutes the temperature will be adjusted.\n` +
`Pick :${colderEmoji}: if you want it colder, :${hotterEmoji}: if you want it hotter, or :${goodEmoji}: if you like it how it is.` + `Pick :${colderEmoji}: if you want it colder, :${hotterEmoji}: if you want it hotter, or :${goodEmoji}: if you like it how it is.` +
'\n(Note that I can\'t actually change the temperature yet. Make Quade do it!)' '\n(Note that I can\'t actually change the temperature yet. Make Quade do it!)'
}) })
@ -269,15 +296,15 @@ const decodeData = (key, message) => {
const onReaction = listener => reactionListeners.push(listener) const onReaction = listener => reactionListeners.push(listener)
const channelIsIm = async channel => (await app.client.conversations.info({ channel }))?.channel?.is_im
onReaction(async ({ event }) => { onReaction(async ({ event }) => {
if (event.user === users.Sage) { if (event.reaction === 'x' && (event.user === users.Sage || await channelIsIm(event.item.channel))) {
if (event.reaction === 'x') {
try { try {
await app.client.chat.delete({ channel: event.item.channel, ts: event.item.ts }) await app.client.chat.delete({ channel: event.item.channel, ts: event.item.ts })
} catch (e) { } catch (e) {
} }
} }
}
}) })
module.exports = { module.exports = {
@ -296,5 +323,6 @@ module.exports = {
messageIn, messageIn,
testMode, testMode,
testId, testId,
users users,
buildSayPrepend
} }