139 lines
4.6 KiB
JavaScript
139 lines
4.6 KiB
JavaScript
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 }, getUser, fuzzyMatcher } = require('./utils')
|
|
|
|
const apiGetUserId = hash => {
|
|
return Object.entries(users)
|
|
.filter(([id, user]) => user.pwHash === hash)
|
|
.map(([id, user]) => id)[0]
|
|
}
|
|
|
|
const makeHash = pw =>
|
|
crypto.createHash('md5')
|
|
.update(pw)
|
|
.digest('hex')
|
|
|
|
const emojiMaps = [
|
|
['mouse2', '🐭'],
|
|
['male-office-worker', '🧑💼'],
|
|
['whale', '🐋'],
|
|
['train2', '🚂'],
|
|
['fire', '🔥'],
|
|
['boomerang', '🪃'],
|
|
['new_moon_with_face', '🌚'],
|
|
['butterfly', '🦋'],
|
|
['mirror', '🪞'],
|
|
['quade', '🧔♂️'],
|
|
['hvacker_angery', '🦆'],
|
|
['grey_question', '❓'],
|
|
['convenience_store', '🏪'],
|
|
['office', '🏢'],
|
|
['japanese_castle', '🏯']
|
|
]
|
|
|
|
app.get('/alive', (req, res) => {
|
|
res.send('OK')
|
|
})
|
|
|
|
const illegalCommands = ['!', '!c', '!coin', '!mine']
|
|
const lastCalls = {}
|
|
const addCommand = ({ commandNames, helpText, action, condition, hidden }) => {
|
|
const route = async (req, res) => {
|
|
let sent
|
|
const say = async msg => {
|
|
if (sent) {
|
|
console.log('attempted double-send of', { msg })
|
|
return
|
|
}
|
|
const isObj = typeof msg === 'object'
|
|
if (isObj) {
|
|
msg = JSON.stringify(msg, null, 2)
|
|
}
|
|
for (const entry of emojiMaps) {
|
|
msg = msg.replaceAll(`:${entry[0]}:`, entry[1])
|
|
}
|
|
msg = msg.replaceAll(/\*([^*]+)\*/g, '$1')
|
|
.replaceAll(/_([^_]+)_/g, '$1')
|
|
res.send((typeof msg === 'object' ? JSON.stringify(msg, null, 2) : msg) + '\n')
|
|
sent = true
|
|
}
|
|
try {
|
|
const keys = [...Object.keys(req.query)]
|
|
keys.reverse()
|
|
const words = [commandNames[0], ...keys]
|
|
console.log({ words })
|
|
const [commandName, ...args] = words
|
|
console.log('INCOMING API CALL:', commandName, words)
|
|
const encoded = req.header('Authorization').substring(5)
|
|
const decoded = base64.decode(encoded).substring(1)
|
|
const hash = makeHash(decoded)
|
|
console.log({ hash })
|
|
const event = {
|
|
user: apiGetUserId(hash)
|
|
}
|
|
const user = getUser(event.user)
|
|
if (user.name !== 'TEST-USER' && illegalCommands.includes(commandName?.toLowerCase())) {
|
|
res.send('Command is illegal over the web api!')
|
|
return
|
|
}
|
|
if (!event.user) {
|
|
res.status(400)
|
|
res.send(
|
|
'User with that password does not exist, or user does not have a password.\n' +
|
|
'See \'!setpw help\' for assistance.\n'
|
|
)
|
|
console.log(' bad password')
|
|
return
|
|
}
|
|
// const lastCall = lastCalls[event.user] || 0
|
|
// const secondsBetweenCalls = 2
|
|
// 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.users[event.user]}`)
|
|
// lastCalls[event.user] = currentTime
|
|
|
|
if (words[1] === 'help') {
|
|
await say(commandNames.map(name => `\`${name}\``).join(', ') + ': ' + helpText)
|
|
if (commandNames.includes('!coin')) {
|
|
addAchievement(user, 'weAllNeedHelp', say)
|
|
}
|
|
return
|
|
}
|
|
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.Admin) })
|
|
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) {
|
|
console.error('route error', e)
|
|
const example = "`curl --location-trusted -u ':your-pw' quacker.sagev.space/lb`"
|
|
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 ${example}\n` +
|
|
`N.b. --location-trusted is needed because quacker.sagev.space is technically a redirect, and your headers need to be forwarded.`)
|
|
}
|
|
}
|
|
commandNames.forEach(name => name !== '!!help' &&
|
|
app.get('/' + name.replace(/!/gi, ''), async (req, res) => console.log('route', name.replace('/' + /!/gi, '')) || await route(req, res))
|
|
)
|
|
}
|
|
|
|
module.exports = {
|
|
addCommand,
|
|
makeHash,
|
|
launch: () => app.listen(port, () => {
|
|
console.log(`Express listening on port ${port}`)
|
|
})
|
|
}
|