Many improvements and additions.
Several new achievements. Separate files for buy route, webapi, and shared utils. Add Creator buyable item. Access control now object-based. Toying with Trivia and a lottery system. !cleanusers, !setpw, !rach, !myupgrades, !squad, !gimme, !prestige, !quack, !whois, !ngift, !message, !!kill, Add simple test user system. Add several oneShot commands.
This commit is contained in:
parent
f054154717
commit
e971f7e7c2
|
@ -673,6 +673,16 @@
|
||||||
"promise.allsettled": "^1.0.2",
|
"promise.allsettled": "^1.0.2",
|
||||||
"raw-body": "^2.3.3",
|
"raw-body": "^2.3.3",
|
||||||
"tsscmp": "^1.0.6"
|
"tsscmp": "^1.0.6"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": {
|
||||||
|
"version": "0.21.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
|
||||||
|
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
|
||||||
|
"requires": {
|
||||||
|
"follow-redirects": "^1.14.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@slack/logger": {
|
"@slack/logger": {
|
||||||
|
@ -1082,11 +1092,11 @@
|
||||||
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
|
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
|
||||||
},
|
},
|
||||||
"axios": {
|
"axios": {
|
||||||
"version": "0.21.4",
|
"version": "0.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.26.0.tgz",
|
||||||
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
|
"integrity": "sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"follow-redirects": "^1.14.0"
|
"follow-redirects": "^1.14.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"babel-jest": {
|
"babel-jest": {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@slack/bolt": "^3.9.0",
|
"@slack/bolt": "^3.9.0",
|
||||||
|
"axios": "^0.26.0",
|
||||||
"base-64": "^1.0.0",
|
"base-64": "^1.0.0",
|
||||||
"express": "^4.17.3",
|
"express": "^4.17.3",
|
||||||
"fs": "0.0.1-security",
|
"fs": "0.0.1-security",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
let base64 = require('base-64')
|
const base64 = require('base-64')
|
||||||
let config = require('../config')
|
const config = require('../config')
|
||||||
|
|
||||||
const url = ''
|
const url = ''
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,42 @@ module.exports = {
|
||||||
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!',
|
||||||
emoji: 'fire'
|
emoji: 'fire'
|
||||||
|
},
|
||||||
|
|
||||||
|
ratGod: {
|
||||||
|
name: 'Own 100 Mice',
|
||||||
|
description: 'I\'m beginning to feel like a rat god, rat god.',
|
||||||
|
emoji: 'mouse2'
|
||||||
|
},
|
||||||
|
weAllNeedHelp: {
|
||||||
|
name: `View the '!coin' help`,
|
||||||
|
description: 'We all need a little help sometimes',
|
||||||
|
emoji: 'grey_question'
|
||||||
|
},
|
||||||
|
showReverence: { // Not implemented
|
||||||
|
name: 'Show your reverence in the chat',
|
||||||
|
description: 'What a good little worshipper.',
|
||||||
|
emoji: 'blush'
|
||||||
|
},
|
||||||
|
walmartGiftCard: {
|
||||||
|
name: 'Walmart Gift Card',
|
||||||
|
description: 'May or may not be expired',
|
||||||
|
emoji: 'credit_card'
|
||||||
|
},
|
||||||
|
|
||||||
|
hvackerAfterDark: {
|
||||||
|
name: 'Hvacker after dark',
|
||||||
|
description: 'You might be taking this a little far.',
|
||||||
|
emoji: 'night_with_stars'
|
||||||
|
},
|
||||||
|
certifiedCoolGuy: {
|
||||||
|
name: 'Certified Cool Guy',
|
||||||
|
description: 'You absolutely know how to party.',
|
||||||
|
emoji: 'sunglasses'
|
||||||
|
},
|
||||||
|
youDisgustMe: {
|
||||||
|
name: 'You disgust me',
|
||||||
|
description: 'Like, wow.',
|
||||||
|
emoji: 'nauseated_face'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
const buyableItems = require('./buyableItems');
|
||||||
|
const { commas, saveGame, setHighestCoins, addAchievement, getCoins, getUser, singleItemCps } = require('./utils');
|
||||||
|
const slack = require('../../slack')
|
||||||
|
|
||||||
|
const calculateCost = ({ itemName, user, quantity = 1 }) => {
|
||||||
|
let currentlyOwned = user.items[itemName] || 0
|
||||||
|
let realCost = 0
|
||||||
|
for (let i = 0; i < quantity; i++) {
|
||||||
|
realCost += Math.ceil(buyableItems[itemName].baseCost * Math.pow(1.15, currentlyOwned || 0))
|
||||||
|
currentlyOwned += 1
|
||||||
|
}
|
||||||
|
return realCost
|
||||||
|
}
|
||||||
|
|
||||||
|
const getItemHeader = user => ([itemName, { baseCost, description, emoji }]) => {
|
||||||
|
const itemCost = commas(user ? calculateCost({ itemName, user }) : baseCost)
|
||||||
|
const itemCps = Math.round(singleItemCps(user, itemName))
|
||||||
|
return `*${itemName}* :${emoji}: - ${itemCost} HVAC Coins - ${commas(itemCps)} CPS\n_${description}_`
|
||||||
|
}
|
||||||
|
const canView = highestCoins => ([, item]) => item.baseCost < (highestCoins || 1) * 101
|
||||||
|
const buyableText = (highestCoins, user) => Object.entries(buyableItems)
|
||||||
|
.filter(canView(highestCoins))
|
||||||
|
.map(getItemHeader(user))
|
||||||
|
.join('\n\n') +
|
||||||
|
'\n\n:grey_question::grey_question::grey_question:' +
|
||||||
|
'\n\nJust type \'!buy item_name\' to purchase'
|
||||||
|
|
||||||
|
const buildBlock = ({ user, itemName, cost, cps }) => ({
|
||||||
|
type: 'section',
|
||||||
|
text: {
|
||||||
|
type: 'mrkdwn',
|
||||||
|
text: `${itemName} :${buyableItems[itemName].emoji}:x${user.items[itemName] || 0} - 𝕳${commas(cost)} - ${commas(cps)} CPS\n_${buyableItems[itemName].description}_`
|
||||||
|
},
|
||||||
|
accessory: {
|
||||||
|
type: 'button',
|
||||||
|
text: {
|
||||||
|
type: 'plain_text',
|
||||||
|
text: 'Buy 1',
|
||||||
|
emoji: true
|
||||||
|
},
|
||||||
|
value: 'buy_' + itemName,
|
||||||
|
action_id: 'buy_' + itemName
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const buyText2 = (highestCoins, user) => {
|
||||||
|
return ({
|
||||||
|
text: buyableText(highestCoins, user),
|
||||||
|
blocks: Object.entries(buyableItems)
|
||||||
|
.filter(canView(highestCoins))
|
||||||
|
.map(([itemName, item]) => {
|
||||||
|
const cost = calculateCost({ itemName, user, quantity: 1 })
|
||||||
|
const cps = Math.round(singleItemCps(user, itemName))
|
||||||
|
return ({ user, itemName, cost, cps })
|
||||||
|
}).map(buildBlock)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const buyRoute = async ({ event, say, words }) => {
|
||||||
|
const user = getUser(event.user)
|
||||||
|
const buying = words[1]
|
||||||
|
setHighestCoins(event.user)
|
||||||
|
if (!buying) {
|
||||||
|
const highestCoins = user.highestEver || user.coins || 1
|
||||||
|
if (buyableItems.quade.baseCost < highestCoins * 100) {
|
||||||
|
addAchievement(user, 'seeTheQuade', say)
|
||||||
|
}
|
||||||
|
await say(buyText2(highestCoins, user))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const buyable = buyableItems[buying]
|
||||||
|
if (!buyable) {
|
||||||
|
await say('That item does not exist!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let quantity = 1
|
||||||
|
const currentCoins = getCoins(event.user)
|
||||||
|
if (words[2] === 'max') {
|
||||||
|
while (calculateCost({ itemName: buying, user, quantity: quantity + 1 }) <= currentCoins) {
|
||||||
|
quantity++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quantity = parseInt(words[2] || '1')
|
||||||
|
}
|
||||||
|
if (!quantity || quantity < 1) {
|
||||||
|
await say('Quantity must be a positive integer')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const realCost = calculateCost({ itemName: buying, user, quantity })
|
||||||
|
if (currentCoins < realCost) {
|
||||||
|
await say(`You don't have enough coins! You have ${commas(currentCoins)}, but you need ${commas(realCost)}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user.coins -= realCost
|
||||||
|
user.items[buying] = user.items[buying] || 0
|
||||||
|
user.items[buying] += quantity
|
||||||
|
console.log(buying, user.items.mouse)
|
||||||
|
if (buying === 'mouse' && user.items.mouse >= 100) {
|
||||||
|
addAchievement(user, 'ratGod', say)
|
||||||
|
}
|
||||||
|
if (quantity === 1) {
|
||||||
|
await say(`You bought one :${buyable.emoji}:`)
|
||||||
|
} else {
|
||||||
|
await say(`You bought ${quantity} :${buyable.emoji}:`)
|
||||||
|
}
|
||||||
|
saveGame()
|
||||||
|
}
|
||||||
|
|
||||||
|
const buyButton = async ({ body, ack, say, payload }) => {
|
||||||
|
await ack()
|
||||||
|
const buying = payload.action_id.substring(4)
|
||||||
|
console.log(`buyButton ${buying} clicked`)
|
||||||
|
const event = {
|
||||||
|
user: body.user.id,
|
||||||
|
}
|
||||||
|
const user = getUser(event.user)
|
||||||
|
const words = ['', buying, '1']
|
||||||
|
await buyRoute({ event, say, words })
|
||||||
|
const highestCoins = user.highestEver || user.coins || 1
|
||||||
|
await slack.app.client.chat.update({
|
||||||
|
channel: body.channel.id,
|
||||||
|
ts: body.message.ts,
|
||||||
|
...buyText2(highestCoins, user)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(buyableItems).forEach(itemName => slack.app.action('buy_' + itemName, buyButton))
|
||||||
|
|
||||||
|
module.exports = buyRoute
|
|
@ -64,5 +64,11 @@ module.exports = {
|
||||||
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.'
|
||||||
}
|
},
|
||||||
|
creator: {
|
||||||
|
baseCost: 170_000_000_000_000,
|
||||||
|
earning: 430_000_000,
|
||||||
|
emoji: 'question',
|
||||||
|
description: 'The elusive creator of Hvacker takes a favorable look at your CPS.'
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,24 @@
|
||||||
|
const {
|
||||||
|
saveGame,
|
||||||
|
logError,
|
||||||
|
getCPS,
|
||||||
|
squadUpgrades,
|
||||||
|
getItemCps,
|
||||||
|
squadIsMissing,
|
||||||
|
maybeNews,
|
||||||
|
idFromWord,
|
||||||
|
getCoins,
|
||||||
|
getUser,
|
||||||
|
commas,
|
||||||
|
addAchievement,
|
||||||
|
game: {nfts, squad, users}
|
||||||
|
} = require('./utils')
|
||||||
const slack = require('../../slack')
|
const slack = require('../../slack')
|
||||||
const fs = require('fs')
|
|
||||||
const jokes = require('./../jokes')
|
|
||||||
const buyableItems = require('./buyableItems')
|
const buyableItems = require('./buyableItems')
|
||||||
const upgrades = require('./upgrades')
|
const upgrades = require('./upgrades')
|
||||||
const achievements = require('./achievements')
|
const achievements = require('./achievements')
|
||||||
|
const webapi = require('./webapi')
|
||||||
|
const prestige = require('./prestige')
|
||||||
|
|
||||||
// const readline = require('readline').createInterface({
|
// const readline = require('readline').createInterface({
|
||||||
// input: process.stdin,
|
// input: process.stdin,
|
||||||
|
@ -19,60 +34,9 @@ const achievements = require('./achievements')
|
||||||
// read()
|
// read()
|
||||||
// })();
|
// })();
|
||||||
|
|
||||||
const saveFile = 'hvacoins.json'
|
|
||||||
|
|
||||||
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()
|
|
||||||
if (random > 0.98) {
|
|
||||||
const prefixedSay = msg => console.log(`Sent news update: '${msg}'`) || say('_Breaking news:_\n' + msg)
|
|
||||||
setTimeout(() => jokes.newsAlert(prefixedSay).catch(logError), 3000)
|
|
||||||
} else if (random > 0.96) {
|
|
||||||
setTimeout(async () => await say('_Say have you heard this one?_'), 3000)
|
|
||||||
setTimeout(() => jokes.tellJoke(say).catch(logError), 4000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const commas = num => num.toLocaleString()
|
|
||||||
|
|
||||||
const getItemHeader = user => ([itemName, {baseCost, description, earning, emoji}]) => {
|
|
||||||
const itemCost = commas(user ? calculateCost({itemName, user}) : baseCost)
|
|
||||||
return `*${itemName}* :${emoji}: - ${itemCost} HVAC Coins - ${commas(earning)} CPS\n_${description}_`
|
|
||||||
}
|
|
||||||
const helpText = (highestCoins, user) => Object.entries(buyableItems)
|
|
||||||
.filter(([, value]) => value.baseCost < (highestCoins || 1) * 101)
|
|
||||||
.map(getItemHeader(user))
|
|
||||||
.join('\n\n') +
|
|
||||||
'\n\n:grey_question::grey_question::grey_question:' +
|
|
||||||
'\n\nJust type \'!buy item_name\' to purchase' +
|
|
||||||
'\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, showOwned = false) => {
|
||||||
const userDoesNotHave = ([upgradeName, upgrade]) => !hasUpgrade(user, upgrade, upgradeName)
|
const userDoesNotHave = ([upgradeName, upgrade]) => hasUpgrade(user, upgrade, upgradeName) === showOwned
|
||||||
const userMeetsCondition = ([, upgrade]) => upgrade.condition(user)
|
const userMeetsCondition = ([, upgrade]) => upgrade.condition(user)
|
||||||
const format = ([key, value]) => `:${getUpgradeEmoji(value)}: *${key}* - ${commas(value.cost)}\n_${value.description}_`
|
const format = ([key, value]) => `:${getUpgradeEmoji(value)}: *${key}* - ${commas(value.cost)}\n_${value.description}_`
|
||||||
return '\n\n' +
|
return '\n\n' +
|
||||||
|
@ -85,151 +49,186 @@ const upgradeText = user => {
|
||||||
'\n\nJust type \'!upgrade upgrade_name\' to purchase'
|
'\n\nJust type \'!upgrade upgrade_name\' to purchase'
|
||||||
}
|
}
|
||||||
|
|
||||||
const getUser = userId => {
|
const hasUpgrade = (user, upgrade, upgradeName) => !!user.upgrades[upgrade.type]?.includes(upgradeName)
|
||||||
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
|
|
||||||
|
|
||||||
const setHighestCoins = userId => {
|
|
||||||
const prevMax = users[userId].highestEver || 0
|
|
||||||
if (prevMax < users[userId].coins) {
|
|
||||||
users[userId].highestEver = users[userId].coins
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasUpgrade = (user, upgrade, upgradeName) => user.upgrades[upgrade.type]?.includes(upgradeName)
|
|
||||||
|
|
||||||
const addAchievement = (user, achievementName, say) => {
|
|
||||||
if (user.achievements[achievementName]) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
setTimeout(async () => {
|
|
||||||
user.achievements[achievementName] = true
|
|
||||||
saveGame()
|
|
||||||
await say(`You earned the achievement ${achievements[achievementName].name}!`)
|
|
||||||
}, 500)
|
|
||||||
}
|
|
||||||
|
|
||||||
const alwaysAccessible = () => true
|
const alwaysAccessible = () => true
|
||||||
const adminOnly = userId => userId === slack.sageUserId
|
const adminOnly = {
|
||||||
|
hidden: true,
|
||||||
|
condition: ({ event, say }) => {
|
||||||
|
if (!event.user.startsWith(slack.sageUserId)) {
|
||||||
|
say(`This is an admin-only command!`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const testOnly = {
|
||||||
|
hidden: true,
|
||||||
|
condition: ({ event }) => event.user.includes('TEST')
|
||||||
|
}
|
||||||
|
const dmsOnly = {
|
||||||
|
hidden: false,
|
||||||
|
condition: ({ event, say, words }) => {
|
||||||
|
if (event.channel_type !== 'im') {
|
||||||
|
say(`Please use ${words[0]} in DMs only!`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const prestigeOnly = adminOnly // TODO ({ event }) => !!getUser(event.user).prestige
|
||||||
|
|
||||||
const commands = new Map()
|
const commands = new Map()
|
||||||
let commandHelpText = ''
|
let commandHelpText = ''
|
||||||
const command = (commandNames, helpText, action, hidden = false, condition = alwaysAccessible) => {
|
const defaultAccess = { hidden: false, condition: alwaysAccessible }
|
||||||
|
const command = (commandNames, helpText, action, { hidden, condition } = defaultAccess) => {
|
||||||
if (!hidden) {
|
if (!hidden) {
|
||||||
commandHelpText += `\n${commandNames.toString().replace(/,/g, ', ')} - ${helpText}\n`
|
commandHelpText += `\n${commandNames.toString().replace(/,/g, ', ')} - ${helpText}\n`
|
||||||
}
|
}
|
||||||
commandNames.forEach(name => commands.set(name, {
|
if (!condition) {
|
||||||
|
condition = alwaysAccessible
|
||||||
|
}
|
||||||
|
if (Array.isArray(condition)) {
|
||||||
|
const conditionList = condition
|
||||||
|
condition = arg => conditionList.every(c => c(arg))
|
||||||
|
}
|
||||||
|
const c = {
|
||||||
commandNames,
|
commandNames,
|
||||||
helpText,
|
helpText,
|
||||||
action,
|
action,
|
||||||
condition,
|
condition,
|
||||||
hidden
|
hidden
|
||||||
}))
|
}
|
||||||
|
webapi.addCommand(c)
|
||||||
|
commandNames.forEach(name => {
|
||||||
|
if (commands.get(name)) {
|
||||||
|
throw `Duplicate command '${name}' detected.`
|
||||||
|
}
|
||||||
|
commands.set(name, c)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const messageHandler = async ({ event, say }) => {
|
||||||
|
const words = event?.text?.split(/\s+/) || []
|
||||||
|
const c = commands.get(words[0])
|
||||||
|
if (!c && words[0]?.startsWith('!')) {
|
||||||
|
return slack.messageSage(`${slack.ourUsers[event.user]} tried to use \`${event.text}\`, if you wanted to add that.`)
|
||||||
|
}
|
||||||
|
const canUse = await c?.condition({ event, say, words })
|
||||||
|
if (!canUse) {
|
||||||
|
// await say(`Command '${words[0]}' not found`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (words[1] === 'help') {
|
||||||
|
await say(c.commandNames.map(name => '`' + name + '`').join(', ') + ': ' + c.helpText)
|
||||||
|
if (c.commandNames.includes('!coin')) {
|
||||||
|
addAchievement(getUser(event.user), 'weAllNeedHelp', say)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await c.action({ event, say, words })
|
||||||
|
}
|
||||||
|
|
||||||
|
slack.onMessage(async msg => {
|
||||||
|
try {
|
||||||
|
await messageHandler(msg)
|
||||||
|
} catch (e) {
|
||||||
|
logError(e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
command(
|
||||||
|
['!cleanusers'],
|
||||||
|
'Calls getUser() on all users, ensuring a valid state.',
|
||||||
|
async ({ say }) => {
|
||||||
|
Object.keys(users).forEach(userId => { getUser(userId) })
|
||||||
|
return say('```Cleaning ' + JSON.stringify(Object.keys(users), null, 2) + '```')
|
||||||
|
}, adminOnly)
|
||||||
|
|
||||||
|
command(
|
||||||
|
['!setpw'],
|
||||||
|
'Set your api password. May not contain spaces. *This is not secure!*\n' +
|
||||||
|
' To use, say !setpw your_password',
|
||||||
|
async ({ event, say, words }) => {
|
||||||
|
const user = getUser(event.user)
|
||||||
|
user.pwHash = webapi.makeHash(words[1])
|
||||||
|
await saveGame()
|
||||||
|
await say(`Password encoded as ${user.pwHash}`)
|
||||||
|
}
|
||||||
|
, { hidden: true })
|
||||||
|
|
||||||
command(
|
command(
|
||||||
['!help', '!h'],
|
['!help', '!h'],
|
||||||
'List available commands',
|
'List available commands',
|
||||||
async ({ say }) => await say('```' + commandHelpText + '```')
|
async ({ say }) => say('```' + commandHelpText + '```')
|
||||||
)
|
)
|
||||||
|
|
||||||
slack.onMessage(async ({ event, say }) => {
|
const removeAchievement = async (user, name, say) => {
|
||||||
const words = event?.text?.split(/\s+/) || []
|
if (user.achievements[name]) {
|
||||||
const c = commands.get(words[0])
|
user.achievements[name] = false
|
||||||
if (!words[0]?.startsWith('!')) {
|
await say('Achievement removed!')
|
||||||
return
|
} else {
|
||||||
|
await say('That user doesn\'t have that achievement!')
|
||||||
}
|
}
|
||||||
if (!c?.condition(event.user)) {
|
}
|
||||||
//await say(`Command '${words[0]}' not found`)
|
|
||||||
return
|
command(
|
||||||
}
|
['!rach'],
|
||||||
if (words[1] === 'help') {
|
'Remove achievement',
|
||||||
await say(c.helpText)
|
async ({ say, words }) => {
|
||||||
return
|
const achName = words[1]
|
||||||
}
|
const target = idFromWord(words[2])
|
||||||
await c.action({ event, say, words })
|
await removeAchievement(getUser(target), achName, say)
|
||||||
})
|
}, adminOnly)
|
||||||
|
|
||||||
command(
|
command(
|
||||||
['!a', '!ach', '!achievements'],
|
['!a', '!ach', '!achievements'],
|
||||||
'List your glorious achievements',
|
'List your glorious achievements',
|
||||||
async ({ event, say }) => {
|
async ({ event, say }) => {
|
||||||
if (event.channel_type !== 'im') {
|
|
||||||
await say('Please only use !ach in DMs!')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const user = getUser(event.user)
|
const user = getUser(event.user)
|
||||||
|
|
||||||
const achievementCount = Object.keys(user.achievements).length
|
const achievementCount = Object.keys(user.achievements).length
|
||||||
const prefix = `You have ${achievementCount} achievements!\n\n`
|
const prefix = `You have ${achievementCount} achievements!\n\n`
|
||||||
const mult = (Math.pow(1.01, achievementCount) - 1) * 100
|
const mult = (Math.pow(1.01, achievementCount) - 1) * 100
|
||||||
const list = Object.keys(user.achievements)
|
let list = ''
|
||||||
|
if (event.channel_type === 'im') {
|
||||||
|
list = Object.keys(user.achievements)
|
||||||
.map(name => achievements[name])
|
.map(name => achievements[name])
|
||||||
.map(({description, emoji, name}) => `:${emoji}: *${name}* - ${description}`)
|
.map(({ description, emoji, name }) => `:${emoji}: *${name}* - ${description}`)
|
||||||
.join('\n')
|
.join('\n') + '\n\n'
|
||||||
const postfix = achievementCount ? `\n\n_Achievements are boosting your CPS by ${mult.toPrecision(3)}%_` : ''
|
}
|
||||||
|
|
||||||
|
const postfix = achievementCount ? `_Achievements are boosting your CPS by ${mult.toPrecision(3)}%_` : ''
|
||||||
await say(prefix + list + postfix)
|
await say(prefix + list + postfix)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const getItemCps = (user, itemName) => {
|
const emojiLine2 = (itemName, countOwned) =>
|
||||||
const achievements = Object.keys(user.achievements || {}).length
|
((emoji = buyableItems[itemName].emoji) => countOwned < 5
|
||||||
const achievementMultiplier = Math.pow(1.01, achievements)
|
? `:${emoji}:`.repeat(countOwned)
|
||||||
const baseCps = (user.items[itemName] || 0) * buyableItems[itemName].earning
|
: `:${emoji}: x${countOwned}`)()
|
||||||
const itemUpgrades = (user.upgrades[itemName] || []).map(name => upgrades[name])
|
|
||||||
return achievementMultiplier * itemUpgrades.reduce((totalCps, upgrade) => upgrade.effect(totalCps, user), baseCps)
|
const emojiLine3 = (itemName, countOwned) =>
|
||||||
}
|
(countOwned < 5
|
||||||
|
? s => s.repeat(countOwned)
|
||||||
|
: s => s + ` x${countOwned}`)
|
||||||
|
(buyableItems[itemName].emoji)
|
||||||
|
|
||||||
const emojiLine = (itemName, countOwned) => countOwned < 5
|
const emojiLine = (itemName, countOwned) => countOwned < 5
|
||||||
? `:${buyableItems[itemName].emoji}:`.repeat(countOwned)
|
? `:${buyableItems[itemName].emoji}:`.repeat(countOwned)
|
||||||
: `:${buyableItems[itemName].emoji}: x${countOwned}`
|
: `:${buyableItems[itemName].emoji}: x${countOwned}`
|
||||||
const collection = userId =>
|
const collection = userId =>
|
||||||
Object.entries(users[userId]?.items || {})
|
Object.entries(users[userId]?.items || {})
|
||||||
.map(([itemName, countOwned]) => emojiLine(itemName, countOwned) + ' - ' + commas(getItemCps(getUser(userId), itemName)) + ' cps')
|
.map(([itemName, countOwned]) => emojiLine(itemName, countOwned) + ' - ' + commas(Math.round(getItemCps(getUser(userId), itemName))) + ' cps')
|
||||||
.join('\n')
|
.join('\n')
|
||||||
|
|
||||||
const getCPS = userId => {
|
command(['!cps'],
|
||||||
const user = getUser(userId)
|
'Display your current Coins Per Second',
|
||||||
const userItems = user?.items || {}
|
async ({ event, say }) =>
|
||||||
const userGeneralUpgrades = user?.upgrades?.general || []
|
say(`You are currently earning \`${commas(getCPS(event.user))}\` HVAC Coin per second.`))
|
||||||
const cpsFromItems = Object.keys(userItems).reduce((total, itemName) => total + getItemCps(getUser(userId), itemName), 0)
|
|
||||||
return Object.entries(userGeneralUpgrades).reduce((total, [, upgradeName]) => upgrades[upgradeName].effect(total, user), cpsFromItems)
|
|
||||||
}
|
|
||||||
|
|
||||||
const getCoins = userId => {
|
|
||||||
const user = getUser(userId)
|
|
||||||
const currentTime = getSeconds()
|
|
||||||
const lastCheck = user.lastCheck || currentTime
|
|
||||||
const secondsPassed = currentTime - lastCheck
|
|
||||||
user.coins += getCPS(userId) * secondsPassed
|
|
||||||
user.coins = Math.floor(user.coins)
|
|
||||||
|
|
||||||
user.lastCheck = currentTime
|
|
||||||
setHighestCoins(userId)
|
|
||||||
saveGame()
|
|
||||||
return user.coins
|
|
||||||
}
|
|
||||||
|
|
||||||
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(
|
command(
|
||||||
['!c', '!coin', '!mine'],
|
['!c', '!coin', '!mine', '!'],
|
||||||
'Mine HVAC coins',
|
'Mine HVAC coins',
|
||||||
async ({ event, say }) => {
|
async ({ event, say }) => {
|
||||||
const user = getUser(event.user)
|
const user = getUser(event.user)
|
||||||
|
@ -237,6 +236,10 @@ command(
|
||||||
const random = Math.random()
|
const random = Math.random()
|
||||||
const c = getCoins(event.user)
|
const c = getCoins(event.user)
|
||||||
const secondsOfCps = seconds => Math.floor(getCPS(event.user) * seconds)
|
const secondsOfCps = seconds => Math.floor(getCPS(event.user) * seconds)
|
||||||
|
const hour = new Date().getHours()
|
||||||
|
if (hour < 8 || hour > 18) {
|
||||||
|
addAchievement(user, 'hvackerAfterDark', say)
|
||||||
|
}
|
||||||
let diff
|
let diff
|
||||||
let prefix
|
let prefix
|
||||||
if (random > 0.9967) {
|
if (random > 0.9967) {
|
||||||
|
@ -247,18 +250,17 @@ command(
|
||||||
} else if (random > 0.986) {
|
} else if (random > 0.986) {
|
||||||
diff = 50 + Math.floor(c * 0.025) + secondsOfCps(60)
|
diff = 50 + Math.floor(c * 0.025) + secondsOfCps(60)
|
||||||
prefix = `:goldbrick: You found a lucky gold coin worth ${commas(diff)} HVAC!\n`
|
prefix = `:goldbrick: You found a lucky gold coin worth ${commas(diff)} HVAC!\n`
|
||||||
await slack.messageSage(`${slack.ourUsers[event.user]} found a lucky gold coin worth ${commas(diff)} HVAC!`)
|
|
||||||
addAchievement(user, 'goldBrick', say)
|
addAchievement(user, 'goldBrick', say)
|
||||||
} else if (random > 0.96) {
|
} else if (random > 0.96) {
|
||||||
diff = 10 + Math.floor(c * 0.01) + secondsOfCps(10)
|
diff = 10 + Math.floor(c * 0.01) + secondsOfCps(10)
|
||||||
prefix = `:money_with_wings: You found a lucky green coin worth ${commas(diff)} HVAC!\n`
|
prefix = `:money_with_wings: You found a lucky green coin worth ${commas(diff)} HVAC!\n`
|
||||||
addAchievement(user, 'greenCoin', say)
|
addAchievement(user, 'greenCoin', say)
|
||||||
} else {
|
} else {
|
||||||
prefix = `You mined one HVAC.\n`
|
prefix = 'You mined one HVAC.\n'
|
||||||
diff = 1
|
diff = 1
|
||||||
}
|
}
|
||||||
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 !== 1 ? 's' : '') + '. Spend wisely.')
|
||||||
saveGame()
|
saveGame()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -274,22 +276,19 @@ command(
|
||||||
let n
|
let n
|
||||||
if (words[1].toLowerCase() === 'all') {
|
if (words[1].toLowerCase() === 'all') {
|
||||||
if (user.coins === 0) {
|
if (user.coins === 0) {
|
||||||
await say('You don\'t have any coins!')
|
return say('You don\'t have any coins!')
|
||||||
return
|
|
||||||
}
|
}
|
||||||
n = user.coins
|
n = user.coins
|
||||||
} else {
|
} else {
|
||||||
n = parseInt(words[1])
|
n = parseInt(words[1])
|
||||||
}
|
}
|
||||||
if (!n || n < 0) {
|
if (!n || n < 0) {
|
||||||
await say(`Invalid number '${n}'`)
|
return say(`Invalid number '${n}'`)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if (user.coins < n) {
|
if (user.coins < n) {
|
||||||
await say(`You don\'t have that many coins! You have ${commas(user.coins)}.`)
|
return say(`You don\'t have that many coins! You have ${commas(user.coins)}.`)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if (n > 100_000_000_000) {
|
if (n >= 100_000_000_000) {
|
||||||
addAchievement(user, 'bigBets', say)
|
addAchievement(user, 'bigBets', say)
|
||||||
}
|
}
|
||||||
user.coins -= n
|
user.coins -= n
|
||||||
|
@ -306,16 +305,6 @@ command(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const calculateCost = ({ itemName, user, quantity = 1 }) => {
|
|
||||||
let currentlyOwned = user.items[itemName] || 0
|
|
||||||
let realCost = 0
|
|
||||||
for (let i = 0; i < quantity; i++) {
|
|
||||||
realCost += Math.ceil(buyableItems[itemName].baseCost * Math.pow(1.15, currentlyOwned || 0))
|
|
||||||
currentlyOwned += 1
|
|
||||||
}
|
|
||||||
return realCost
|
|
||||||
}
|
|
||||||
|
|
||||||
command(
|
command(
|
||||||
['!buynft', '!bn'],
|
['!buynft', '!bn'],
|
||||||
'Acquire high-quality art\n' +
|
'Acquire high-quality art\n' +
|
||||||
|
@ -323,18 +312,15 @@ command(
|
||||||
async ({ event, say, words }) => {
|
async ({ event, say, words }) => {
|
||||||
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)
|
return say('No NFT with that name found' + suffix)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if (nft.owner) {
|
if (nft.owner) {
|
||||||
await say('Someone already owns that NFT!')
|
return say('Someone already owns that NFT!')
|
||||||
return
|
|
||||||
}
|
}
|
||||||
const c = getCoins(event.user)
|
const c = getCoins(event.user)
|
||||||
if (c < nft.price) {
|
if (c < nft.price) {
|
||||||
await say('You don\'t have enough coin for this nft')
|
return say('You don\'t have enough coin for this nft')
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
users[event.user].coins -= nft.price
|
users[event.user].coins -= nft.price
|
||||||
|
@ -344,118 +330,148 @@ command(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
command(
|
||||||
|
['!myupgrades', '!myu'],
|
||||||
|
'List all the upgrades that you own.',
|
||||||
|
async ({ event, say }) => {
|
||||||
|
const user = getUser(event.user)
|
||||||
|
await say(upgradeText(user, true))
|
||||||
|
}, dmsOnly)
|
||||||
|
|
||||||
command(
|
command(
|
||||||
['!upgrade', '!u'],
|
['!upgrade', '!u'],
|
||||||
'Improve the performance of your HVAC-generators.\n' +
|
'Improve the performance of your HVAC-generators.\n' +
|
||||||
' Say \'!upgrade\' to list available upgrades, or \'!upgrade upgrade_name\' to purchase.',
|
' Say \'!upgrade\' to list available upgrades, or \'!upgrade upgrade_name\' to purchase.',
|
||||||
async ({ event, say, words }) => {
|
async ({ event, say, words }) => {
|
||||||
if (event.channel_type !== 'im') {
|
|
||||||
await say('Please only use !upgrade in DMs!')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const user = getUser(event.user)
|
const user = getUser(event.user)
|
||||||
const upgradeName = words[1]
|
const upgradeName = words[1]
|
||||||
if (!upgradeName) {
|
if (!upgradeName) {
|
||||||
await say(upgradeText(user))
|
return say(upgradeText(user))
|
||||||
return
|
|
||||||
}
|
}
|
||||||
const upgrade = upgrades[upgradeName]
|
const upgrade = upgrades[upgradeName]
|
||||||
if (!upgrade) {
|
if (!upgrade) {
|
||||||
await say('An upgrade with that name does not exist!')
|
return say('An upgrade with that name does not exist!')
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if (!user.upgrades[upgrade.type]) {
|
if (!user.upgrades[upgrade.type]) {
|
||||||
user.upgrades[upgrade.type] = []
|
user.upgrades[upgrade.type] = []
|
||||||
}
|
}
|
||||||
if (hasUpgrade(user, upgrade, upgradeName)) {
|
if (hasUpgrade(user, upgrade, upgradeName)) {
|
||||||
await say('You already have that upgrade!')
|
return say('You already have that upgrade!')
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if (!upgrade.condition(user)) {
|
if (!upgrade.condition(user)) {
|
||||||
await say('That item does not exist!')
|
return say('That item does not exist!')
|
||||||
return
|
|
||||||
}
|
}
|
||||||
const c = getCoins(event.user)
|
const c = getCoins(event.user)
|
||||||
if (c < upgrade.cost) {
|
if (c < upgrade.cost) {
|
||||||
await say(`You don't have enough coins! You have ${commas(c)}, but you need ${commas(upgrade.cost)}`)
|
return say(`You don't have enough coins! You have ${commas(c)}, but you need ${commas(upgrade.cost)}`)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
user.coins -= upgrade.cost
|
user.coins -= upgrade.cost
|
||||||
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}!`)
|
||||||
|
}, dmsOnly)
|
||||||
|
|
||||||
|
const getCurrentSquadgrade = () => {
|
||||||
|
const current = Object.entries(squadUpgrades).find(squadIsMissing)
|
||||||
|
if (!current) {
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
const [name, upgrade] = current
|
||||||
|
if (!squad.upgrades[name]) {
|
||||||
|
squad.upgrades[name] = upgrade.cost
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
remaining: squad.upgrades[name],
|
||||||
|
emoji: upgrade.emoji,
|
||||||
|
description: upgrade.description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const squadgradeText = ([name, { emoji, description }]) =>
|
||||||
|
`:${emoji}: *${name}*\n_${description}_`
|
||||||
|
|
||||||
|
const squadText = () => {
|
||||||
|
const current = getCurrentSquadgrade()
|
||||||
|
if (current) {
|
||||||
|
const currentUpgradeText = ({ name, remaining, emoji, description }) =>
|
||||||
|
`:${emoji}: *${name}* - ${commas(remaining)} HVAC remaining.\n_${description}_`
|
||||||
|
return currentUpgradeText(current)
|
||||||
|
}
|
||||||
|
return 'No more squadgrades currently available.'
|
||||||
|
// const squadIsMissing = ([name, upgrade]) => squad.upgrades[name] === false
|
||||||
|
// return squadgradeText(Object.entries(squadUpgrades).find(squadIsMissing))
|
||||||
|
}
|
||||||
|
|
||||||
|
command(
|
||||||
|
['!squad', '!sq'],
|
||||||
|
'Buy upgrades that help the whole team.\n' +
|
||||||
|
' Say !squad to list squad upgrades.\n' +
|
||||||
|
' Say \'!squad contrib_amount\' to make progress toward the current upgrade.',
|
||||||
|
async ({ event, say, words }) => {
|
||||||
|
if (!words[1]) {
|
||||||
|
return say(squadText())
|
||||||
|
}
|
||||||
|
const current = getCurrentSquadgrade()
|
||||||
|
if (!current) {
|
||||||
|
return say('No squadgrades are currently available')
|
||||||
|
}
|
||||||
|
const currentCoins = getCoins(event.user)
|
||||||
|
let amount
|
||||||
|
if (words[1] === 'all') {
|
||||||
|
amount = currentCoins
|
||||||
|
} else {
|
||||||
|
amount = parseInt(words[1])
|
||||||
|
if (amount > currentCoins) {
|
||||||
|
return say(`You don't have that much HVAC! You have ${currentCoins}.`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!amount || amount < 1) {
|
||||||
|
return say(`Invalid amount: '${words[1]}'`)
|
||||||
|
}
|
||||||
|
if (amount > squad.upgrades[current.name]) {
|
||||||
|
amount = squad.upgrades[current.name]
|
||||||
|
}
|
||||||
|
const user = getUser(event.user)
|
||||||
|
squad.upgrades[current.name] -= amount
|
||||||
|
user.coins -= amount
|
||||||
|
if (squad.upgrades[current.name] <= 0) {
|
||||||
|
squad.upgrades[current.name] = true
|
||||||
|
}
|
||||||
|
saveGame()
|
||||||
|
await say(`Thank you for your contribution of ${commas(amount)} HVAC! Current status:\n\n${squadText()}`)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const buyRoute = require('./buy')
|
||||||
command(
|
command(
|
||||||
['!buy', '!b'],
|
['!buy', '!b'],
|
||||||
'Buy new items to earn HVAC with\n' +
|
'Buy new items to earn HVAC with\n' +
|
||||||
' Use without arguments to list all available items.\n' +
|
' Use without arguments to list all available items.\n' +
|
||||||
' Say \'!buy item_name optional_quantity\' to make your purchase.',
|
' Say \'!buy item_name optional_quantity\' to make your purchase.',
|
||||||
async ({ event, say, words }) => {
|
buyRoute
|
||||||
const user = getUser(event.user)
|
|
||||||
const buying = words[1]
|
|
||||||
const quantity = parseInt(words[2] || '1')
|
|
||||||
if (!quantity || quantity < 1) {
|
|
||||||
await say('Quantity must be a positive integer')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
setHighestCoins(event.user)
|
|
||||||
if (!buying) {
|
|
||||||
const highestCoins = user.highestEver || user.coins || 1
|
|
||||||
if (buyableItems.quade.baseCost < highestCoins * 100) {
|
|
||||||
addAchievement(user, 'seeTheQuade', say)
|
|
||||||
}
|
|
||||||
await say(helpText(highestCoins, user))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const buyable = buyableItems[buying]
|
|
||||||
if (!buyable) {
|
|
||||||
await say('That item does not exist!')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const realCost = calculateCost({itemName: buying, user, quantity})
|
|
||||||
const currentCoins = getCoins(event.user)
|
|
||||||
if (currentCoins < realCost) {
|
|
||||||
await say(`You don't have enough coins! You have ${commas(currentCoins)}, but you need ${commas(realCost)}`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
user.coins -= realCost
|
|
||||||
user.items[buying] = user.items[buying] || 0
|
|
||||||
user.items[buying] += quantity
|
|
||||||
if (quantity === 1) {
|
|
||||||
await say(`You bought one ${buying}`)
|
|
||||||
} else {
|
|
||||||
await say(`You bought ${quantity} ${buying}`)
|
|
||||||
}
|
|
||||||
saveGame()
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
command(
|
command(
|
||||||
['!check', '!ch'],
|
['!check', '!ch'],
|
||||||
'Check how many coins another player has',
|
'Check how many coins another player has',
|
||||||
async ({ say, words }) => {
|
async ({ say, words }) => {
|
||||||
const target = words[1]
|
const targetId = idFromWord(words[1])
|
||||||
if (!target?.startsWith('<@') || !target.endsWith('>')) {
|
if (!targetId) {
|
||||||
await say('Target must be a valid @')
|
return say('Target must be a valid @')
|
||||||
return
|
|
||||||
}
|
}
|
||||||
const targetId = target.substring(2, target.length - 1)
|
|
||||||
if (targetId !== 'U0344TFA7HQ') {
|
if (targetId !== 'U0344TFA7HQ') {
|
||||||
const coins = getCoins(targetId)
|
const coins = getCoins(targetId)
|
||||||
await say(`<@${targetId}> has ${commas(coins)} HVAC.`)
|
await say(`<@${targetId}> has ${commas(coins)} HVAC.`)
|
||||||
} else {
|
} else {
|
||||||
const members = (await slack.app.client.conversations.members({channel: slack.temperatureChannelId})).members
|
const members = (await slack.app.client.conversations.members({ channel: slack.temperatureChannelId })).members
|
||||||
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.`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
command(
|
command(
|
||||||
['!gift', '!give', '!gi'],
|
['!gift', '!give', '!gi'],
|
||||||
'Donate coins to a fellow player\n' +
|
'Donate coins to a fellow player\n' +
|
||||||
|
@ -464,20 +480,20 @@ command(
|
||||||
const userId = event.user
|
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)
|
||||||
|
const targetId = idFromWord(target)
|
||||||
if (!amount || amount < 0) {
|
if (!amount || amount < 0) {
|
||||||
await say('Amount must be a positive integer!')
|
return say('Amount must be a positive integer!')
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if (!target?.startsWith('<@') || !target.endsWith('>')) {
|
if (!targetId) {
|
||||||
await say('Target must be a valid @')
|
return say('Target must be a valid @')
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if (user.coins < amount) {
|
if (user.coins < amount) {
|
||||||
await say(`You don't have that many coins! You have ${commas(user.coins)} HVAC.`)
|
return say(`You don't have that many coins! You have ${commas(user.coins)} HVAC.`)
|
||||||
return
|
}
|
||||||
|
if (amountText === 'all' && slack.ourUsers[targetId] === 'Tyler') {
|
||||||
|
addAchievement(user, 'walmartGiftCard', say)
|
||||||
}
|
}
|
||||||
const targetId = target.substring(2, target.length - 1)
|
|
||||||
const targetUser = getUser(targetId)
|
const targetUser = getUser(targetId)
|
||||||
user.coins -= amount
|
user.coins -= amount
|
||||||
targetUser.coins += amount
|
targetUser.coins += amount
|
||||||
|
@ -492,11 +508,22 @@ command(
|
||||||
await say(
|
await 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\nCoins collected all-time: ${commas(Math.round(users[event.user].coinsAllTime))}\n\n`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
command(
|
||||||
|
['!gimme'],
|
||||||
|
'Give self x coins',
|
||||||
|
async ({ event, say, words }) => {
|
||||||
|
const user = getUser(event.user)
|
||||||
|
const increase = parseInt(words[1].replace(/,/g, ''))
|
||||||
|
user.coins += increase
|
||||||
|
user.coinsAllTime += increase
|
||||||
|
await say(`You now have ${getCoins(event.user)} HVAC.`)
|
||||||
|
}, testOnly)
|
||||||
|
|
||||||
command(
|
command(
|
||||||
['!nfts', '!nft', '!n'],
|
['!nfts', '!nft', '!n'],
|
||||||
'Show NFTs in the museum\n' +
|
'Show NFTs in the museum\n' +
|
||||||
|
@ -525,17 +552,27 @@ command(
|
||||||
.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))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
command(['!pog'], 'Displays a poggers hvacker', async ({ say }) => {
|
const oneShot = (name, helpText, message, achievementName) =>
|
||||||
await say('<https://i.imgur.com/XCg7WDz.png|poggers>')
|
command([name], helpText, async ({ event, say }) => {
|
||||||
}, true)
|
await say(message)
|
||||||
|
await slack.messageSage(`Wow buddy they like your ${name} joke.`)
|
||||||
|
if (achievementName) {
|
||||||
|
addAchievement(getUser(event.user), achievementName, say)
|
||||||
|
}
|
||||||
|
}, { hidden: true })
|
||||||
|
|
||||||
command(['!ligma'], 'GRRRR', async ({ say }) => {
|
oneShot('!santa', 'Ho ho ho!', '<https://i.imgur.com/dBWgFfQ.png|I\'m Santa Quacks>')
|
||||||
await say(':hvacker_angery:')
|
oneShot('!sugma', 'GRRRR', ':hvacker_angery:')
|
||||||
}, true)
|
oneShot('!pog', 'Displays a poggers hvacker', '<https://i.imgur.com/XCg7WDz.png|poggers>')
|
||||||
|
oneShot('!ligma', 'Toy with me not, son.', '<https://i.imgur.com/i1YtW7m.png|no>')
|
||||||
|
oneShot('!dab', 'ACTIVATE COOL GUY MODE', '<https://i.imgur.com/FKYdeqo.jpg|I go XD style>', 'certifiedCoolGuy')
|
||||||
|
oneShot('!based', 'Sorry, it\'s a little hard to hear you!', '<https://i.imgur.com/IUX6R26.png|What?>')
|
||||||
|
oneShot('!shrek', 'Love and life, baby.', '<https://i.imgur.com/QwuCQZA.png|Donkey!>')
|
||||||
|
|
||||||
command(
|
command(
|
||||||
['!addnft'],
|
['!addnft'],
|
||||||
|
@ -557,20 +594,68 @@ command(
|
||||||
nfts.push(newNft)
|
nfts.push(newNft)
|
||||||
console.log('addedNft', newNft)
|
console.log('addedNft', newNft)
|
||||||
return saveGame()
|
return saveGame()
|
||||||
}, true, adminOnly)
|
}, adminOnly)
|
||||||
|
|
||||||
|
command(
|
||||||
|
['!prestige', '!p'],
|
||||||
|
'Show your current prestige status',
|
||||||
|
prestige.prestigeRoute,
|
||||||
|
adminOnly
|
||||||
|
)
|
||||||
|
|
||||||
|
command(
|
||||||
|
['!!prestige'],
|
||||||
|
'Confirm your prestige activation.',
|
||||||
|
prestige.prestigeConfirmRoute,
|
||||||
|
adminOnly
|
||||||
|
)
|
||||||
|
|
||||||
|
command(
|
||||||
|
['!quack', '!quackstore'],
|
||||||
|
'Spend your prestigious quackings',
|
||||||
|
prestige.quackStoreRoute,
|
||||||
|
prestigeOnly
|
||||||
|
)
|
||||||
|
|
||||||
command(
|
command(
|
||||||
['!ss'],
|
['!ss'],
|
||||||
'Show the status for another player: !ss @player',
|
'Show the status for another player: !ss @player',
|
||||||
async ({ words, say }) => {
|
async ({ words, say }) => {
|
||||||
const target = words[1]
|
const target = words[1]
|
||||||
const targetId = target.substring(2, target.length - 1)
|
const targetId = idFromWord(target)
|
||||||
await say(
|
await say(
|
||||||
`${target} are currently earning \`${commas(getCPS(targetId))}\` HVAC Coin per second.\n\n` +
|
`${target} is currently earning \`${commas(getCPS(targetId))}\` HVAC Coin per second.\n\n` +
|
||||||
`They currently have ${commas(getCoins(targetId))} HVAC Coins\n\n` +
|
`They have ${commas(getCoins(targetId))} HVAC Coins\n\n` +
|
||||||
`${collection(targetId)}\n\n`
|
`${collection(targetId)}\n\n` +
|
||||||
|
`${Object.entries(users[targetId]?.items || {}).reduce((total, [name, count]) => total + count, 0)} total items\n\n`
|
||||||
)
|
)
|
||||||
}, true, adminOnly)
|
}, adminOnly)
|
||||||
|
|
||||||
|
const exec = require('child_process').exec;
|
||||||
|
command(
|
||||||
|
['!pl'],
|
||||||
|
'!pl `code`',
|
||||||
|
async ({ event, say}) => {
|
||||||
|
const code = event.text.substring(4)
|
||||||
|
.replace(/`/g, '')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
console.log('PL CODE:', code)
|
||||||
|
const child = exec(`/home/sagevaillancourt/projects/pebblisp/src/pl '${code}'`)
|
||||||
|
let result = '```'
|
||||||
|
let errors = ''
|
||||||
|
child.stdout.on('data', data => result += data)
|
||||||
|
child.stderr.on('data', data => {
|
||||||
|
result += `\nERR:${data}\n`
|
||||||
|
errors += `\nERR:${data}`
|
||||||
|
})
|
||||||
|
child.on('close', async () => {
|
||||||
|
if (result.length > 2000) {
|
||||||
|
result = result.substring(0, 2000) + '...\nOUTPUT EXCEEDS MAXIMUM AND WAS TRUNCATED\n' +
|
||||||
|
'STDERR:\n' + errors.substring(0, 200)
|
||||||
|
}
|
||||||
|
await say(result + '```')
|
||||||
|
})
|
||||||
|
}, adminOnly)
|
||||||
|
|
||||||
command(
|
command(
|
||||||
['!steal', '!sagesteal'],
|
['!steal', '!sagesteal'],
|
||||||
|
@ -578,16 +663,14 @@ command(
|
||||||
async ({ event, words, say }) => {
|
async ({ event, words, say }) => {
|
||||||
const userId = event.user
|
const userId = event.user
|
||||||
const user = getUser(userId)
|
const user = getUser(userId)
|
||||||
let [, target] = words
|
const [, target] = words
|
||||||
const amount = getCoins(userId) / 100
|
const amount = getCoins(userId) / 100
|
||||||
if (!amount || amount < 0) {
|
if (!amount || amount < 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let targetId
|
let targetId = idFromWord(target)
|
||||||
if (!target?.startsWith('<@') || !target.endsWith('>')) {
|
if (!targetId) {
|
||||||
targetId = slack.sageUserId
|
targetId = slack.sageUserId
|
||||||
} else {
|
|
||||||
targetId = target.substring(2, target.length - 1)
|
|
||||||
}
|
}
|
||||||
if (user.coins < amount) {
|
if (user.coins < amount) {
|
||||||
return
|
return
|
||||||
|
@ -596,15 +679,22 @@ command(
|
||||||
user.coins -= amount
|
user.coins -= amount
|
||||||
targetUser.coins += amount
|
targetUser.coins += amount
|
||||||
await say(`Stealing is wrong. Gave ${commas(Math.floor(amount))} of your HVAC to ${slack.ourUsers[targetId]}`)
|
await say(`Stealing is wrong. Gave ${commas(Math.floor(amount))} of your HVAC to ${slack.ourUsers[targetId]}`)
|
||||||
}, true)
|
}, { hidden: true })
|
||||||
|
|
||||||
|
// TODO
|
||||||
command(
|
command(
|
||||||
['!lottery'],
|
['!lottery'],
|
||||||
'Once per day, try for a big win!',
|
'Once per day, try for a big win!',
|
||||||
async ({ event, say }) => {
|
async ({ event, say }) => {
|
||||||
const user = getUser(event.user)
|
const user = getUser(event.user)
|
||||||
addAchievement(user, 'ignited', say)
|
const currentDate = new Date().getDate()
|
||||||
}, true)
|
const lastLotto = user.lastLotto || currentDate - 1
|
||||||
|
if (lastLotto === currentDate) {
|
||||||
|
return say('Hey, only one lotto ticket per day, alright?')
|
||||||
|
}
|
||||||
|
user.lastLotto = currentDate
|
||||||
|
saveGame()
|
||||||
|
}, { hidden: true })
|
||||||
|
|
||||||
command(
|
command(
|
||||||
['!ignite'],
|
['!ignite'],
|
||||||
|
@ -612,15 +702,59 @@ command(
|
||||||
async ({ event, say }) => {
|
async ({ event, say }) => {
|
||||||
const user = getUser(event.user)
|
const user = getUser(event.user)
|
||||||
addAchievement(user, 'ignited', say)
|
addAchievement(user, 'ignited', say)
|
||||||
}
|
}, { hidden: true })
|
||||||
)
|
|
||||||
command(
|
command(
|
||||||
['!giveach'],
|
['!giveach'],
|
||||||
'!giveach @player ach_name',
|
'!giveach @player ach_name',
|
||||||
async ({ words, say }) => {
|
async ({ words, say }) => {
|
||||||
const targetId = words[1].substring(2, words[1].length - 1)
|
const targetId = idFromWord(words[1])
|
||||||
const user = getUser(targetId)
|
const user = getUser(targetId)
|
||||||
console.log(user, words[2], say)
|
console.log(user, words[2], say)
|
||||||
addAchievement(user, words[2], say)
|
addAchievement(user, words[2], say)
|
||||||
saveGame()
|
saveGame()
|
||||||
}, true, adminOnly)
|
}, adminOnly)
|
||||||
|
|
||||||
|
command(
|
||||||
|
['!whois'],
|
||||||
|
'!whois player_id',
|
||||||
|
async ({ words, say }) => say(`<@${words[1]}>`),
|
||||||
|
adminOnly)
|
||||||
|
|
||||||
|
command(
|
||||||
|
['!ngift'],
|
||||||
|
'!ngift player_id nft_name',
|
||||||
|
async ({ event, words, say }) => {
|
||||||
|
const targetId = idFromWord(words[1])
|
||||||
|
if (!targetId) {
|
||||||
|
return say(`Please specify a valid @ target!`)
|
||||||
|
}
|
||||||
|
const nft = nfts.find(nft => nft.name === words[2] && nft.owner === event.user)
|
||||||
|
if (!nft) {
|
||||||
|
return say(`You don't own an nft with that name!`)
|
||||||
|
}
|
||||||
|
nft.owner = targetId
|
||||||
|
}, { hidden: true })
|
||||||
|
|
||||||
|
command(
|
||||||
|
['!deletetest', '!dtest'],
|
||||||
|
'!deletetest',
|
||||||
|
async () => {
|
||||||
|
delete users[slack.testId]
|
||||||
|
saveGame()
|
||||||
|
}, adminOnly)
|
||||||
|
|
||||||
|
command(
|
||||||
|
['!message', '!msg', '!m'],
|
||||||
|
'!message player_id message',
|
||||||
|
async ({ event, words, say }) => {
|
||||||
|
const targetId = idFromWord(words[1])
|
||||||
|
if (!targetId) {
|
||||||
|
return say(`Please specify a valid @ target!`)
|
||||||
|
}
|
||||||
|
const msg = event.text.replace(/\S+ +\S+\s*/i, '')
|
||||||
|
await slack.messageIn(targetId, msg)
|
||||||
|
await slack.messageSage(msg)
|
||||||
|
}, adminOnly)
|
||||||
|
|
||||||
|
webapi.launch()
|
|
@ -0,0 +1,125 @@
|
||||||
|
const { getUser, getCoins, commas, saveGame } = require('./utils');
|
||||||
|
const quackStore = require('./quackstore');
|
||||||
|
|
||||||
|
const possiblePrestige = coins => {
|
||||||
|
let p = 0
|
||||||
|
while (tpcRec(p + 1) <= coins) {
|
||||||
|
p += 1
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalCostForPrestige = prestigeLevel => {
|
||||||
|
let cost = 0
|
||||||
|
while (prestigeLevel) {
|
||||||
|
cost += 1_000_000_000_000 * Math.pow(prestigeLevel, 3)
|
||||||
|
prestigeLevel -= 1
|
||||||
|
}
|
||||||
|
return cost
|
||||||
|
}
|
||||||
|
|
||||||
|
const tpcRecMemo = []
|
||||||
|
const tpcRec = prestigeLevel => {
|
||||||
|
if (prestigeLevel === 0) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return (tpcRecMemo[prestigeLevel]) || (tpcRecMemo[prestigeLevel] = 1_000_000_000_000 * Math.pow(prestigeLevel, 3) + tpcRec(prestigeLevel - 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
const prestigeRoute = async ({ event, say, words }) => {
|
||||||
|
const user = getUser(event.user)
|
||||||
|
getCoins(event.user)
|
||||||
|
const possible = possiblePrestige(user.coinsAllTime)
|
||||||
|
const current = user.prestige ??= 0
|
||||||
|
if (words[1] === 'me') {
|
||||||
|
await say(
|
||||||
|
'This will permanently remove all of your items, upgrades, and coins!\n\n' +
|
||||||
|
'Say \'!!prestige me\' to confirm.'
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
await say(
|
||||||
|
`Current Prestige: ${commas(current)}\n\n` +
|
||||||
|
`Quacks gained if you prestige now: ${commas(possible - current)}\n\n` +
|
||||||
|
`HVAC until next quack: ${commas(Math.round(tpcRec(possible + 1) - user.coinsAllTime))}\n\n` +
|
||||||
|
'Say \'!prestige me\' to start the prestige process.'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}//, true, adminOnly)
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
const prestigeConfirmRoute = async ({ event, say, words }) => {
|
||||||
|
const user = getUser(event.user)
|
||||||
|
getCoins(event.user)
|
||||||
|
const possible = possiblePrestige(user.coinsAllTime)
|
||||||
|
const current = user.prestige
|
||||||
|
if (possible <= current) {
|
||||||
|
await say('You don\'t have enough HVAC to prestige right now!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (event?.text !== '!!prestige me') {
|
||||||
|
await say('Say exactly \'!!prestige me\' to confirm')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log('possible', possible)
|
||||||
|
console.log('user.prestige', user.prestige)
|
||||||
|
user.quacks ??= 0
|
||||||
|
user.quacks += (possible - user.prestige)
|
||||||
|
console.log('user.quacks', user.quacks)
|
||||||
|
user.prestige = possible
|
||||||
|
user.coins = 0
|
||||||
|
user.items = {}
|
||||||
|
user.upgrades = {}
|
||||||
|
saveGame()
|
||||||
|
await say('You prestiged! Check out !quackstore to see what you can buy!')
|
||||||
|
}
|
||||||
|
|
||||||
|
const quackStoreListing = ([name, upgrade]) =>
|
||||||
|
`:${upgrade.emoji}: *${name}* - Costs *${upgrade.cost} Quack.*\n\n_${upgrade.description}_`
|
||||||
|
|
||||||
|
const allUserQuackUpgrades = user =>
|
||||||
|
Object.entries(user.quackUpgrades || {})
|
||||||
|
.map(([type, upgrades]) => upgrades)
|
||||||
|
|
||||||
|
const hasPreReqs = user => ([name, upgrade]) => {
|
||||||
|
if (!upgrade.preReqs) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
const allUserUpgrades = allUserQuackUpgrades(user)
|
||||||
|
console.log(allUserUpgrades)
|
||||||
|
return upgrade.preReqs.every(preReq => allUserUpgrades.includes(preReq))
|
||||||
|
}
|
||||||
|
|
||||||
|
const quackStoreText = user =>
|
||||||
|
Object.entries(quackStore)
|
||||||
|
.filter(hasPreReqs(user))
|
||||||
|
.map(quackStoreListing)
|
||||||
|
.join('\n\n')
|
||||||
|
|
||||||
|
const quackStoreRoute = async ({ event, say, words }) => {
|
||||||
|
const user = getUser(event.user)
|
||||||
|
user.quackUpgrades ??= {}
|
||||||
|
const quacks = user.quacks ??= 0
|
||||||
|
if (!words[1]) {
|
||||||
|
await say(quackStoreText(user))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const quackItem = quackStore[words[1]]
|
||||||
|
if (!quackItem) {
|
||||||
|
await say(`'${words[1]}' is not available in the quack store!`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (quackItem.cost > quacks) {
|
||||||
|
await say(`${words[1]} costs ${quackItem.cost} Quacks, but you only have ${quacks}!`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user.quackUpgrades[quackItem.type] ??= []
|
||||||
|
user.quackUpgrades[quackItem.type].push(words[1])
|
||||||
|
saveGame()
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
quackStoreRoute,
|
||||||
|
prestigeRoute,
|
||||||
|
prestigeConfirmRoute
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
const quackStore = {
|
||||||
|
ascent: {
|
||||||
|
name: 'Ascent',
|
||||||
|
type: 'cps',
|
||||||
|
emoji: 'rocket',
|
||||||
|
description: 'Welcome to level 2. Boosts all CPS by 20%',
|
||||||
|
effect: cps => cps * 1.2,
|
||||||
|
cost: 1
|
||||||
|
},
|
||||||
|
nuclearFuel: {
|
||||||
|
name: 'Nuclear Fuel',
|
||||||
|
type: 'cps',
|
||||||
|
emoji: 'atom_symbol',
|
||||||
|
description: 'The future is now. Boosts all CPS by 20%.',
|
||||||
|
preReqs: ['ascent'],
|
||||||
|
effect: cps => cps * 1.2,
|
||||||
|
cost: 5
|
||||||
|
},
|
||||||
|
}
|
||||||
|
module.exports = quackStore
|
|
@ -6,8 +6,6 @@ const basic = ({ type, description, count, cost }) => ({
|
||||||
effect: itemCps => itemCps * 2
|
effect: itemCps => itemCps * 2
|
||||||
})
|
})
|
||||||
|
|
||||||
const nothing = itemCps => itemCps
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
doubleClick: basic({
|
doubleClick: basic({
|
||||||
type: 'mouse',
|
type: 'mouse',
|
||||||
|
@ -173,6 +171,7 @@ module.exports = {
|
||||||
count: 25,
|
count: 25,
|
||||||
cost: 37_500_000_000_000,
|
cost: 37_500_000_000_000,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
fzero: basic({
|
fzero: basic({
|
||||||
type: 'quade',
|
type: 'quade',
|
||||||
description: 'Brings out his competitive spirit.',
|
description: 'Brings out his competitive spirit.',
|
||||||
|
@ -186,6 +185,19 @@ module.exports = {
|
||||||
cost: 500_000_000_000_000,
|
cost: 500_000_000_000_000,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
latestNode: basic({
|
||||||
|
type: 'hvacker',
|
||||||
|
description: 'The old one has terrible ergonomics, tsk tsk.',
|
||||||
|
count: 1,
|
||||||
|
cost: 140_000_000_000_000,
|
||||||
|
}),
|
||||||
|
gitCommits: basic({
|
||||||
|
type: 'hvacker',
|
||||||
|
description: 'The heads of multiple people in a company are better than, for example, merely one head.',
|
||||||
|
count: 25,
|
||||||
|
cost: 7_000_000_000_000_000,
|
||||||
|
}),
|
||||||
|
|
||||||
homage: {
|
homage: {
|
||||||
type: 'general',
|
type: 'general',
|
||||||
description: 'The power of original ideas increases your overall CPS by 10%',
|
description: 'The power of original ideas increases your overall CPS by 10%',
|
||||||
|
@ -194,6 +206,14 @@ module.exports = {
|
||||||
cost: 10_000_000_000,
|
cost: 10_000_000_000,
|
||||||
effect: (itemCps, user) => Math.ceil(itemCps * 1.1)
|
effect: (itemCps, user) => Math.ceil(itemCps * 1.1)
|
||||||
},
|
},
|
||||||
|
iLoveHvac: {
|
||||||
|
type: 'general',
|
||||||
|
description: 'The power of love increases your overall CPS by 10%',
|
||||||
|
condition: user => Object.entries(user.items).reduce((total, [, countOwned]) => countOwned + total, 0) >= 400,
|
||||||
|
emoji: 'heart',
|
||||||
|
cost: 100_000_000_000_000,
|
||||||
|
effect: (itemCps, user) => Math.ceil(itemCps * 1.1)
|
||||||
|
}
|
||||||
|
|
||||||
// moreUpgrades: {
|
// moreUpgrades: {
|
||||||
// type: 'general',
|
// type: 'general',
|
||||||
|
|
|
@ -0,0 +1,194 @@
|
||||||
|
const fs = require('fs')
|
||||||
|
const jokes = require('../jokes')
|
||||||
|
const achievements = require('./achievements');
|
||||||
|
const buyableItems = require("./buyableItems");
|
||||||
|
const upgrades = require("./upgrades");
|
||||||
|
const quackStore = require("./quackstore");
|
||||||
|
|
||||||
|
const saveFile = 'hvacoins.json'
|
||||||
|
|
||||||
|
const logError = msg => msg ? console.error(msg) : () => { /* Don't log empty message */ }
|
||||||
|
|
||||||
|
const loadGame = () => parseOr(fs.readFileSync('./' + saveFile, 'utf-8'),
|
||||||
|
() => ({
|
||||||
|
users: {},
|
||||||
|
nfts: [],
|
||||||
|
squad: {}
|
||||||
|
}))
|
||||||
|
|
||||||
|
const parseOr = (parseable, orFunc) => {
|
||||||
|
try {
|
||||||
|
return JSON.parse(parseable)
|
||||||
|
} catch (e) {
|
||||||
|
logError(e)
|
||||||
|
return orFunc()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, null, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
const maybeNews = say => {
|
||||||
|
const random = Math.random()
|
||||||
|
if (random > 0.98) {
|
||||||
|
const prefixedSay = msg => console.log(`Sent news update: '${msg}'`) || say('_Breaking news:_\n' + msg)
|
||||||
|
setTimeout(() => jokes.newsAlert(prefixedSay).catch(logError), 3000)
|
||||||
|
} else if (random > 0.96) {
|
||||||
|
setTimeout(async () => say('_Say have you heard this one?_'), 3000)
|
||||||
|
setTimeout(() => jokes.tellJoke(say).catch(logError), 4000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const idFromWord = word => {
|
||||||
|
if (!word?.startsWith('<@') || !word.endsWith('>')) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return word.substring(2, word.length - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSeconds = () => new Date().getTime() / 1000
|
||||||
|
|
||||||
|
const commas = num => num.toLocaleString()
|
||||||
|
|
||||||
|
const game = loadGame()
|
||||||
|
const { users, nfts, squad } = game
|
||||||
|
|
||||||
|
const setHighestCoins = userId => {
|
||||||
|
const prevMax = users[userId].highestEver || 0
|
||||||
|
if (prevMax < users[userId].coins) {
|
||||||
|
users[userId].highestEver = users[userId].coins
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const addAchievement = (user, achievementName, say) => {
|
||||||
|
if (!achievements[achievementName]) {
|
||||||
|
logError(`Achievement ${achievementName} does not exist!`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (user.achievements[achievementName]) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setTimeout(async () => {
|
||||||
|
user.achievements[achievementName] = true
|
||||||
|
saveGame()
|
||||||
|
await say(`You earned the achievement ${achievements[achievementName].name}!`)
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getUser = userId => {
|
||||||
|
if (!users[userId]) {
|
||||||
|
users[userId] = {
|
||||||
|
coins: 0,
|
||||||
|
items: {},
|
||||||
|
upgrades: {},
|
||||||
|
achievements: {},
|
||||||
|
coinsAllTime: 0,
|
||||||
|
prestige: 0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
users[userId].items ??= {}
|
||||||
|
users[userId].upgrades ??= {}
|
||||||
|
users[userId].achievements ??= {}
|
||||||
|
users[userId].coinsAllTime ??= users[userId].coins
|
||||||
|
users[userId].prestige ??= 0
|
||||||
|
}
|
||||||
|
return users[userId]
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCoins = userId => {
|
||||||
|
const user = getUser(userId)
|
||||||
|
const currentTime = getSeconds()
|
||||||
|
const lastCheck = user.lastCheck || currentTime
|
||||||
|
const secondsPassed = currentTime - lastCheck
|
||||||
|
|
||||||
|
const increase = getCPS(userId) * secondsPassed
|
||||||
|
user.coins += increase
|
||||||
|
user.coinsAllTime += increase
|
||||||
|
user.coins = Math.floor(user.coins)
|
||||||
|
|
||||||
|
user.lastCheck = currentTime
|
||||||
|
setHighestCoins(userId)
|
||||||
|
saveGame()
|
||||||
|
return user.coins
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCPS = userId => {
|
||||||
|
const user = getUser(userId)
|
||||||
|
const userItems = user?.items || {}
|
||||||
|
return Math.round(Object.keys(userItems).reduce((total, itemName) => total + getItemCps(user, itemName), 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
const getItemCps = (user, itemName) => (user.items[itemName] || 0) * singleItemCps(user, itemName)
|
||||||
|
|
||||||
|
const squadUpgrades = {
|
||||||
|
tastyKeyboards: {
|
||||||
|
name: 'Tasty Keyboards',
|
||||||
|
description: 'Delicious and sticky. Boosts CPS by 20% for everyone.',
|
||||||
|
effect: cps => Math.ceil(cps * 1.2),
|
||||||
|
cost: 10_000_000_000_000,
|
||||||
|
emoji: 'keyboard'
|
||||||
|
},
|
||||||
|
copyPasteMacro: {
|
||||||
|
name: 'Copy-Paste Macro.',
|
||||||
|
description: 'Don\'t actually use this. Boosts CPS by 20% for everyone.',
|
||||||
|
effect: cps => Math.ceil(cps * 1.2),
|
||||||
|
cost: 100_000_000_000_000,
|
||||||
|
emoji: 'printer'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const squadHas = ([name]) => squad.upgrades[name] === true
|
||||||
|
const squadIsMissing = name => !squadHas(name)
|
||||||
|
|
||||||
|
const getCompletedSquadgrades = () =>
|
||||||
|
Object.entries(squadUpgrades)
|
||||||
|
.filter(squadHas)
|
||||||
|
.map(([, upgrade]) => upgrade)
|
||||||
|
|
||||||
|
const singleItemCps = (user, itemName) => {
|
||||||
|
const baseCps = buyableItems[itemName].earning
|
||||||
|
|
||||||
|
const itemUpgrades = (user.upgrades[itemName] || []).map(name => upgrades[name])
|
||||||
|
const itemUpgradeCps = itemUpgrades.reduce((totalCps, upgrade) => upgrade.effect(totalCps, user), baseCps)
|
||||||
|
|
||||||
|
const userGeneralUpgrades = user.upgrades.general || []
|
||||||
|
const generalUpgradeCps = Object.entries(userGeneralUpgrades).reduce((total, [, upgradeName]) => upgrades[upgradeName].effect(total, user), itemUpgradeCps)
|
||||||
|
|
||||||
|
const achievementCount = Object.keys(user.achievements || {}).length
|
||||||
|
const achievementMultiplier = Math.pow(1.01, achievementCount)
|
||||||
|
|
||||||
|
const userQuackgrades = user.quackUpgrades?.cps || []
|
||||||
|
const quackMultiplier = userQuackgrades.reduce((total, upgrade) => quackStore[upgrade].effect(total, user), 1)
|
||||||
|
|
||||||
|
const prestigeMultiplier = 1 + ((user.prestige || 0) * 0.01)
|
||||||
|
|
||||||
|
return achievementMultiplier *
|
||||||
|
quackMultiplier *
|
||||||
|
prestigeMultiplier *
|
||||||
|
getCompletedSquadgrades().reduce((cps, upgrade) => upgrade.effect(cps), generalUpgradeCps)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
saveGame,
|
||||||
|
logError,
|
||||||
|
parseOr,
|
||||||
|
maybeNews,
|
||||||
|
idFromWord,
|
||||||
|
commas,
|
||||||
|
setHighestCoins,
|
||||||
|
addAchievement,
|
||||||
|
getCoins,
|
||||||
|
getUser,
|
||||||
|
singleItemCps,
|
||||||
|
getCPS,
|
||||||
|
getItemCps,
|
||||||
|
squadUpgrades,
|
||||||
|
squadIsMissing,
|
||||||
|
game
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
const express = require('express')
|
||||||
|
const app = express()
|
||||||
|
const port = 3001
|
||||||
|
const crypto = require('crypto')
|
||||||
|
const base64 = require('base-64')
|
||||||
|
const slack = require('../../slack')
|
||||||
|
const { game: { users } } = require('./utils')
|
||||||
|
|
||||||
|
const apiGetUserId = hash => {
|
||||||
|
return Object.entries(userGetter.users)
|
||||||
|
.filter(([id, user]) => user.pwHash === hash)
|
||||||
|
.map(([id, user]) => id)[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
const makeHash = pw =>
|
||||||
|
crypto.createHash('md5')
|
||||||
|
.update(pw)
|
||||||
|
.digest('hex')
|
||||||
|
|
||||||
|
const addCommand = ({ commandNames, helpText, action, condition, hidden }) => {
|
||||||
|
const route = async (req, res) => {
|
||||||
|
const say = async msg => res.send(msg)
|
||||||
|
try {
|
||||||
|
const words = ['', ...Object.keys(req.query)]
|
||||||
|
console.log('INCOMING API CALL:', name, words)
|
||||||
|
const encoded = req.header('Authorization').substring(5)
|
||||||
|
const decoded = base64.decode(encoded).substring(1)
|
||||||
|
const event = {
|
||||||
|
user: apiGetUserId(makeHash(decoded))
|
||||||
|
}
|
||||||
|
if (!event.user) {
|
||||||
|
res.status(400)
|
||||||
|
res.send(
|
||||||
|
'User does not exist, or does not have a password.\n' +
|
||||||
|
'See \'!setpw help\' for assistance.'
|
||||||
|
)
|
||||||
|
console.log(' bad password')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const lastCall = userGetter.users[event.user].lastApiCall || 0
|
||||||
|
const secondsBetweenCalls = 5
|
||||||
|
const currentTime = Math.floor(new Date().getTime() / 1000)
|
||||||
|
if (lastCall + secondsBetweenCalls > currentTime) {
|
||||||
|
res.status(400)
|
||||||
|
res.send(`Must have at least ${secondsBetweenCalls}s between api calls`)
|
||||||
|
console.log(' rate limited')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log(` went through for ${slack.ourUsers[event.user]}`)
|
||||||
|
userGetter.users[event.user].lastApiCall = currentTime
|
||||||
|
|
||||||
|
await action({event, say, words})
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
await say(e.stack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commandNames.forEach(name =>
|
||||||
|
app.get('/' + name.replace(/!/gi, ''), route)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
addCommand,
|
||||||
|
makeHash,
|
||||||
|
launch: () => app.listen(port, () => {
|
||||||
|
console.log(`Express listening on port ${port}`)
|
||||||
|
})
|
||||||
|
}
|
|
@ -2,3 +2,4 @@ require('./connect4')
|
||||||
require('./tictactoe')
|
require('./tictactoe')
|
||||||
require('./jokes')
|
require('./jokes')
|
||||||
require('./hvacoins')
|
require('./hvacoins')
|
||||||
|
require('./trivia')
|
||||||
|
|
|
@ -79,14 +79,14 @@ const tellJoke = async say => {
|
||||||
|
|
||||||
let timeout = 0
|
let timeout = 0
|
||||||
for (const part of joke) {
|
for (const part of joke) {
|
||||||
setTimeout(async () => await say(part), timeout);
|
setTimeout(async () => say(part), timeout)
|
||||||
timeout += 5000
|
timeout += 5000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const newsAlert = async say => {
|
const newsAlert = async say => {
|
||||||
let article = lastNews
|
let article = lastNews
|
||||||
while(article === lastNews) {
|
while (article === lastNews) {
|
||||||
article = Math.floor(Math.random() * news.length)
|
article = Math.floor(Math.random() * news.length)
|
||||||
}
|
}
|
||||||
lastNews = article
|
lastNews = article
|
||||||
|
@ -104,5 +104,5 @@ slack.onMessage(async ({ event, say }) => {
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
tellJoke,
|
tellJoke,
|
||||||
newsAlert,
|
newsAlert
|
||||||
}
|
}
|
|
@ -32,12 +32,12 @@ const buildGameStarter = ({ startTriggers, dataName, gameName, textFromBoard, in
|
||||||
player2: opponent
|
player2: opponent
|
||||||
})
|
})
|
||||||
const sent = await say(msg)
|
const sent = await say(msg)
|
||||||
await addChoiceEmojis({...sent, choices: turnChoiceEmojis})
|
await addChoiceEmojis({ ...sent, choices: turnChoiceEmojis })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const encodeGame = (dataKey, board, players) => slack.encodeData(dataKey, {board, players})
|
const encodeGame = (dataKey, board, players) => slack.encodeData(dataKey, { board, players })
|
||||||
|
|
||||||
const decodeGame = (dataKey, message) => slack.decodeData(dataKey, message)
|
const decodeGame = (dataKey, message) => slack.decodeData(dataKey, message)
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ const buildTurnHandler = ({ gameName, dataName, checkWinner, textFromBoard, turn
|
||||||
text: boardMessage + winnerMessages.opponent
|
text: boardMessage + winnerMessages.opponent
|
||||||
})
|
})
|
||||||
if (!winner) {
|
if (!winner) {
|
||||||
await addChoiceEmojis({...sentBoard, choices: turnChoiceEmojis})
|
await addChoiceEmojis({ ...sentBoard, choices: turnChoiceEmojis })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,9 +119,9 @@ module.exports = {
|
||||||
gameName,
|
gameName,
|
||||||
textFromBoard,
|
textFromBoard,
|
||||||
checkWinner,
|
checkWinner,
|
||||||
makeMove,
|
makeMove
|
||||||
}) => {
|
}) => {
|
||||||
const dataName = gameName.replace(/[^0-9a-zA-Z]/gi, '')
|
const dataName = gameName.replace(/[^0-9A-Z]/gi, '')
|
||||||
const gameStarter = buildGameStarter({
|
const gameStarter = buildGameStarter({
|
||||||
startTriggers,
|
startTriggers,
|
||||||
gameName,
|
gameName,
|
||||||
|
@ -129,7 +129,7 @@ module.exports = {
|
||||||
initialBoard,
|
initialBoard,
|
||||||
textFromBoard,
|
textFromBoard,
|
||||||
turnChoiceEmojis
|
turnChoiceEmojis
|
||||||
});
|
})
|
||||||
slack.onMessage(gameStarter)
|
slack.onMessage(gameStarter)
|
||||||
|
|
||||||
const turnHandler = buildTurnHandler({
|
const turnHandler = buildTurnHandler({
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
const axios = require('axios')
|
||||||
|
|
||||||
|
const getTrivia = async () => axios.get('https://opentdb.com/api.php?amount=10&category=9&difficulty=medium&type=multiple', {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json, text/plain, */*'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(res => res.data.results)
|
||||||
|
.catch(console.error)
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getTrivia
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
const postToHoneywell = options => {}
|
const postToHoneywell = options => {}
|
||||||
|
|
||||||
const postTemp = ({ heatSetpoint, coolSetpoint, mode}) => {
|
const postTemp = ({ heatSetpoint, coolSetpoint, mode }) => {
|
||||||
postToHoneywell({
|
postToHoneywell({
|
||||||
heatSetpoint,
|
heatSetpoint,
|
||||||
coolSetpoint,
|
coolSetpoint,
|
||||||
|
|
|
@ -5,7 +5,7 @@ const temperatureChannelId = 'C034156CE03'
|
||||||
const hvackerBotUserId = 'U0344TFA7HQ'
|
const hvackerBotUserId = 'U0344TFA7HQ'
|
||||||
const sageUserId = 'U028BMEBWBV'
|
const sageUserId = 'U028BMEBWBV'
|
||||||
|
|
||||||
const pollingMinutes = 10
|
const pollingMinutes = 5
|
||||||
const pollingPeriod = 1000 * 60 * pollingMinutes
|
const pollingPeriod = 1000 * 60 * pollingMinutes
|
||||||
|
|
||||||
const colderEmoji = 'snowflake'
|
const colderEmoji = 'snowflake'
|
||||||
|
@ -45,7 +45,7 @@ const sendHelp = async (say, prefix) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const getMessage = async ({channel, ts}) => app.client.conversations.history({
|
const getMessage = async ({ channel, ts }) => app.client.conversations.history({
|
||||||
channel: channel,
|
channel: channel,
|
||||||
latest: ts,
|
latest: ts,
|
||||||
inclusive: true,
|
inclusive: true,
|
||||||
|
@ -59,21 +59,46 @@ app.event('reaction_added', async ({ event, context, client, say }) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const ourUsers = {
|
const ourUsers = {
|
||||||
'U028BMEBWBV': 'Sage',
|
U028BMEBWBV: 'Sage',
|
||||||
'U02U15RFK4Y': 'Adam',
|
U02U15RFK4Y: 'Adam',
|
||||||
'U02AAB54V34': 'Houston',
|
U02AAB54V34: 'Houston',
|
||||||
'U02KYLVK1GV': 'Quade',
|
U02KYLVK1GV: 'Quade',
|
||||||
'U017PG4EL1Y': 'Max',
|
U017PG4EL1Y: 'Max',
|
||||||
'UTDLFGZA5': 'Tyler'
|
UTDLFGZA5: 'Tyler',
|
||||||
|
U017CB5L1K3: 'Andres'
|
||||||
}
|
}
|
||||||
|
|
||||||
let activePolls = {}
|
const activePolls = {}
|
||||||
|
const testId = 'U028BMEBWBV_TEST'
|
||||||
|
let testMode = false
|
||||||
app.event('message', async ({ event, context, client, say }) => {
|
app.event('message', async ({ event, context, client, say }) => {
|
||||||
|
console.log(event)
|
||||||
|
if (event.user === sageUserId) {
|
||||||
|
if (event?.text.startsWith('!')) {
|
||||||
|
if (testMode) {
|
||||||
|
await messageSage('Currently in test mode!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (event?.text === '!test') {
|
||||||
|
testMode = !testMode
|
||||||
|
await messageSage(`TestMode: ${testMode} with ID ${testId}`)
|
||||||
|
} else if (event?.text === '!notest') {
|
||||||
|
testMode = false
|
||||||
|
await messageSage(`TestMode: ${testMode}`)
|
||||||
|
}
|
||||||
|
if (testMode) {
|
||||||
|
event.user = testId
|
||||||
|
}
|
||||||
|
// console.log(event.blocks[0].elements[0])
|
||||||
|
}
|
||||||
for (const listener of messageListeners) {
|
for (const listener of messageListeners) {
|
||||||
listener({ event, say })
|
listener({ event, say })
|
||||||
}
|
}
|
||||||
console.log('MSG', ourUsers[event.user], "'" + event.text + "'", new Date().toLocaleTimeString())
|
console.log('MSG', ourUsers[event.user], "'" + event.text + "'", new Date().toLocaleTimeString())
|
||||||
if (event.user === 'U028BMEBWBV' && event.channel === 'D0347Q4H9FE') {
|
if (event.user === 'U028BMEBWBV' && event.channel === 'D0347Q4H9FE') {
|
||||||
|
if (event.text === '!!kill') {
|
||||||
|
process.exit()
|
||||||
|
}
|
||||||
if (event.text?.startsWith('!say ') || event.text?.startsWith('!say\n')) {
|
if (event.text?.startsWith('!say ') || event.text?.startsWith('!say\n')) {
|
||||||
await postToTechThermostatChannel(event.text.substring(4).trim())
|
await postToTechThermostatChannel(event.text.substring(4).trim())
|
||||||
return
|
return
|
||||||
|
@ -87,7 +112,7 @@ app.event('message', async ({ event, context, client, say }) => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let eventText = event.text?.toLowerCase() || ''
|
const eventText = event.text?.toLowerCase() || ''
|
||||||
|
|
||||||
if (eventText.startsWith('!help')) {
|
if (eventText.startsWith('!help')) {
|
||||||
await sendHelp(say)
|
await sendHelp(say)
|
||||||
|
@ -115,7 +140,7 @@ app.event('message', async ({ event, context, client, say }) => {
|
||||||
full: true
|
full: true
|
||||||
})
|
})
|
||||||
const reactCounts = {}
|
const reactCounts = {}
|
||||||
reactions.message.reactions.forEach(reaction => reactCounts[reaction.name] = reaction.count)
|
reactions.message.reactions.forEach(reaction => { reactCounts[reaction.name] = reaction.count })
|
||||||
|
|
||||||
const contentVotes = reactCounts[goodEmoji]
|
const contentVotes = reactCounts[goodEmoji]
|
||||||
const hotterVotes = reactCounts[hotterEmoji]
|
const hotterVotes = reactCounts[hotterEmoji]
|
||||||
|
@ -141,10 +166,10 @@ app.event('message', async ({ event, context, client, say }) => {
|
||||||
;(async () => {
|
;(async () => {
|
||||||
await app.start().catch(console.error)
|
await app.start().catch(console.error)
|
||||||
console.log('Slack Bolt has started')
|
console.log('Slack Bolt has started')
|
||||||
//setTimeout(async () => {
|
// setTimeout(async () => {
|
||||||
// await messageSage('<https://i.imgur.com/VCvfvdz.png|...>')
|
// await messageSage('<https://i.imgur.com/VCvfvdz.png|...>')
|
||||||
//}, 2000)
|
// }, 2000)
|
||||||
})();
|
})()
|
||||||
|
|
||||||
const postToTechThermostatChannel = async optionsOrText => {
|
const postToTechThermostatChannel = async optionsOrText => {
|
||||||
if (optionsOrText === null || typeof optionsOrText !== 'object') {
|
if (optionsOrText === null || typeof optionsOrText !== 'object') {
|
||||||
|
@ -152,7 +177,7 @@ const postToTechThermostatChannel = async optionsOrText => {
|
||||||
text: optionsOrText
|
text: optionsOrText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return await app.client.chat.postMessage({...optionsOrText, channel: temperatureChannelId})
|
return app.client.chat.postMessage({ ...optionsOrText, channel: temperatureChannelId })
|
||||||
}
|
}
|
||||||
|
|
||||||
const messageSage = async optionsOrText => messageIn(sageUserId, optionsOrText)
|
const messageSage = async optionsOrText => messageIn(sageUserId, optionsOrText)
|
||||||
|
@ -164,17 +189,17 @@ const messageIn = async (channel, optionsOrText) => {
|
||||||
text: optionsOrText
|
text: optionsOrText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return await app.client.chat.postMessage({...optionsOrText, channel})
|
return app.client.chat.postMessage({ ...optionsOrText, channel })
|
||||||
}
|
}
|
||||||
|
|
||||||
const startPoll = async () => {
|
const startPoll = async () => {
|
||||||
const sent = await postToTechThermostatChannel({
|
const sent = await postToTechThermostatChannel({
|
||||||
text: `<!here|here> Temperature poll requested! In ${pollingMinutes} minute(s) the temperature will be adjusted.\n` +
|
text: `<!here|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!)`
|
||||||
})
|
})
|
||||||
const addReaction = async emojiName =>
|
const addReaction = async emojiName =>
|
||||||
await app.client.reactions.add({
|
app.client.reactions.add({
|
||||||
channel: temperatureChannelId,
|
channel: temperatureChannelId,
|
||||||
timestamp: sent.ts,
|
timestamp: sent.ts,
|
||||||
name: emojiName
|
name: emojiName
|
||||||
|
@ -208,10 +233,14 @@ const decodeData = (key, message) => {
|
||||||
|
|
||||||
const onReaction = listener => reactionListeners.push(listener)
|
const onReaction = listener => reactionListeners.push(listener)
|
||||||
|
|
||||||
onReaction(async ({ event, say }) => {
|
onReaction(async ({ event }) => {
|
||||||
if (event.user === sageUserId && event.reaction === 'x') {
|
if (event.user === sageUserId && event.reaction === 'x') {
|
||||||
console.log(event)
|
console.log(event)
|
||||||
await app.client.chat.delete({ channel: event.item.channel, ts: event.item.ts })
|
try {
|
||||||
|
await app.client.chat.delete({channel: event.item.channel, ts: event.item.ts})
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -230,5 +259,8 @@ module.exports = {
|
||||||
decodeData,
|
decodeData,
|
||||||
sageUserId,
|
sageUserId,
|
||||||
messageSage,
|
messageSage,
|
||||||
|
messageIn,
|
||||||
|
testMode,
|
||||||
|
testId,
|
||||||
ourUsers
|
ourUsers
|
||||||
}
|
}
|
Loading…
Reference in New Issue