379 lines
12 KiB
JavaScript
379 lines
12 KiB
JavaScript
const { App } = require('@slack/bolt');
|
|
const postgres = require('postgres');
|
|
|
|
require('dotenv').config()
|
|
const sql = postgres({
|
|
host: 'hackclub.app',
|
|
port: 5432,
|
|
database: 'haroon_slackmaster',
|
|
username: 'haroon',
|
|
password: process.env.PGSQL_PASSWORD,
|
|
ssl: 'require'
|
|
})
|
|
|
|
const BeginnerOpponents = require('./opponents/beginner.json');
|
|
|
|
const app = new App({
|
|
token: process.env.SLACK_BOT_TOKEN,
|
|
signingSecret: process.env.SLACK_SIGNING_SECRET
|
|
});
|
|
|
|
async function initializeUser(slackUserId) {
|
|
let a = await sql`SELECT * FROM users WHERE slack_id = ${slackUserId};`
|
|
|
|
if (a.length === 0) {
|
|
a = await sql`INSERT INTO users (slack_id) VALUES (${slackUserId}) RETURNING *;`
|
|
}
|
|
|
|
return a[0];
|
|
}
|
|
|
|
app.use(async (ctx) => {
|
|
await initializeUser(ctx.body.user_id)
|
|
|
|
await ctx.next()
|
|
})
|
|
|
|
app.command('/chooseopponent', async (ctx) => {
|
|
await ctx.ack();
|
|
|
|
await ctx.client.views.open({
|
|
trigger_id: ctx.body.trigger_id,
|
|
view: {
|
|
"type": "modal",
|
|
"callback_id": "chooseopponent",
|
|
"title": {
|
|
"type": "plain_text",
|
|
"text": "Choose an opponent",
|
|
"emoji": true
|
|
},
|
|
"submit": {
|
|
"type": "plain_text",
|
|
"text": "Choose",
|
|
"emoji": true
|
|
},
|
|
"close": {
|
|
"type": "plain_text",
|
|
"text": "Never mind",
|
|
"emoji": true
|
|
},
|
|
"blocks": [
|
|
{
|
|
"type": "section",
|
|
"text": {
|
|
"type": "mrkdwn",
|
|
"text": `*Battle Support*: Hiya <@${ctx.body.user_id}>! What rank opponent would you like to battle against?\n\nNot sure yet? Don't worry! Just cancel out and view what opponents you can fight with /viewopponents!`
|
|
}
|
|
},
|
|
{
|
|
"type": "divider"
|
|
},
|
|
{
|
|
"type": "input",
|
|
"element": {
|
|
"type": "radio_buttons",
|
|
"options": [
|
|
{
|
|
"text": {
|
|
"type": "plain_text",
|
|
"text": "Special",
|
|
"emoji": true
|
|
},
|
|
"value": "SPECIAL"
|
|
},
|
|
{
|
|
"text": {
|
|
"type": "plain_text",
|
|
"text": "Beginner",
|
|
"emoji": true
|
|
},
|
|
"value": "BEGINNER"
|
|
},
|
|
{
|
|
"text": {
|
|
"type": "plain_text",
|
|
"text": "Casual",
|
|
"emoji": true
|
|
},
|
|
"value": "CASUAL"
|
|
}
|
|
],
|
|
"action_id": "rank-selection"
|
|
},
|
|
"label": {
|
|
"type": "plain_text",
|
|
"text": "Choose a rank:",
|
|
"emoji": true
|
|
}
|
|
}
|
|
]
|
|
}
|
|
})
|
|
});
|
|
|
|
function generateProfile(dbUser, slackUser) {
|
|
return [
|
|
{
|
|
"type": "section",
|
|
"fields": [
|
|
{
|
|
"type": "mrkdwn",
|
|
"text": "*User:* " + slackUser.display_name_normalized
|
|
},
|
|
{
|
|
"type": "mrkdwn",
|
|
"text": "*Rank:* " + dbUser.rank
|
|
},
|
|
{
|
|
"type": "mrkdwn",
|
|
"text": "*Battle Power*: " + (dbUser.health + dbUser.mindmg + dbUser.maxdmg)
|
|
},
|
|
{
|
|
"type": "mrkdwn",
|
|
"text": `*Victories/Losses:* ${dbUser.victories}/${dbUser.losses} (${dbUser.victories / (dbUser.victories + dbUser.losses) || 0}%)`
|
|
},
|
|
{
|
|
"type": "mrkdwn",
|
|
"text": "*Current Win Streak:* " + dbUser.curstreak
|
|
},
|
|
{
|
|
"type": "mrkdwn",
|
|
"text": "*Highest Win Streak*: " + dbUser.highstreak
|
|
}
|
|
],
|
|
"accessory": {
|
|
"type": "image",
|
|
"image_url": slackUser.image_1024,
|
|
"alt_text": "user profile"
|
|
}
|
|
},
|
|
{
|
|
"type": "section",
|
|
"text": {
|
|
"type": "mrkdwn",
|
|
"text": `*Base Health:* ${dbUser.health}\n*Base Min Damage:* ${dbUser.mindmg}\n*Base Max Damage:* ${dbUser.maxdmg}`
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
app.command('/profile', async (ctx) => {
|
|
await ctx.ack();
|
|
|
|
const args = ctx.body.text.slice().split(/ +/g).filter(x => x);
|
|
|
|
let match;
|
|
|
|
// If there is an argument and the first one is a Slack ping
|
|
if (args.length && (match = args[0].match(/\<\@(.+)\|(.+)>/))) {
|
|
const mentionedUser = match[1];
|
|
|
|
const dbUser = await initializeUser(mentionedUser);
|
|
const slackUser = (await ctx.client.users.info({ user: mentionedUser })).user.profile;
|
|
|
|
ctx.say({
|
|
"text": `@${slackUser.display_name_normalized}'s profile`,
|
|
"blocks": [
|
|
{
|
|
"type": "section",
|
|
"text": {
|
|
"type": "mrkdwn",
|
|
"text": `<@${ctx.body.user_id}> ran \`/profile @${slackUser.display_name_normalized}\``
|
|
}
|
|
},
|
|
...generateProfile(dbUser, slackUser)
|
|
]
|
|
})
|
|
}
|
|
// If there is an argument but it isn't a Slack ping
|
|
else if (args.length) {
|
|
ctx.respond({
|
|
blocks: [
|
|
{
|
|
type: 'section',
|
|
text: {
|
|
type: 'mrkdwn',
|
|
text: `*Battle Master:* Greetings <@${ctx.body.user_id}>. You have tried to view the profile of an invalid user. Please ensure you either send a user ping as an argument or provide no argument at all.`
|
|
}
|
|
}
|
|
]
|
|
})
|
|
}
|
|
// There is no argument
|
|
else {
|
|
const dbUser = await initializeUser(ctx.body.user_id);
|
|
const slackUser = (await ctx.client.users.info({ user: ctx.body.user_id })).user.profile;
|
|
|
|
ctx.say({
|
|
"text": `@${slackUser.display_name_normalized}'s profile`,
|
|
"blocks": [
|
|
{
|
|
"type": "section",
|
|
"text": {
|
|
"type": "mrkdwn",
|
|
"text": `<@${ctx.body.user_id}> ran \`/profile\``
|
|
}
|
|
},
|
|
...generateProfile(dbUser, slackUser)
|
|
]
|
|
})
|
|
}
|
|
})
|
|
|
|
app.view("chooseopponent", async (ctx) => {
|
|
const { selected_option } = Object.values(ctx.view.state.values)[0]['rank-selection'];
|
|
|
|
const rank = selected_option.value;
|
|
|
|
const messages = {
|
|
"SPECIAL": `*Battle Special*: Wasn't expecting to see ya here <@${ctx.body.user.id}>... I guess you're up for a challenge huh? Alright then, who do ya wanna annoy today?`,
|
|
"BEGINNER": `*Battle Beginner*: Hey there <@${ctx.body.user.id}>! Let's keep things simple, who do you want to battle against?`,
|
|
"CASUAL": `*Battle Casual*: Alright <@${ctx.body.user.id}>, things are about to get a little bit tougher from here... Who do you feel like taking on today?`
|
|
}
|
|
|
|
await ctx.ack({
|
|
response_action: 'update',
|
|
view: {
|
|
"type": "modal",
|
|
"callback_id": "chooseopponent-" + rank,
|
|
"title": {
|
|
"type": "plain_text",
|
|
"text": "Choose an opponent",
|
|
"emoji": true
|
|
},
|
|
"submit": {
|
|
"type": "plain_text",
|
|
"text": "Battle",
|
|
"emoji": true
|
|
},
|
|
"close": {
|
|
"type": "plain_text",
|
|
"text": "Never mind",
|
|
"emoji": true
|
|
},
|
|
"blocks": [
|
|
{
|
|
"type": "section",
|
|
"text": {
|
|
"type": "mrkdwn",
|
|
"text": messages[rank] || `*Battle Master*: Greetings <@${ctx.body.user.id}>. Please choose an opponent from below to battle.`
|
|
}
|
|
},
|
|
{
|
|
"type": "divider"
|
|
},
|
|
{
|
|
"type": "input",
|
|
"element": {
|
|
"type": "static_select",
|
|
"options": BeginnerOpponents.map(opponent =>
|
|
({
|
|
"text": {
|
|
"type": "plain_text",
|
|
"text": `${opponent.name} // ${opponent.stats.health + opponent.stats.min + opponent.stats.max} Battle Power`,
|
|
"emoji": true
|
|
},
|
|
"value": opponent.rawId
|
|
})
|
|
),
|
|
"action_id": "opponents"
|
|
},
|
|
"label": {
|
|
"type": "plain_text",
|
|
"text": "Choose an opponent:",
|
|
"emoji": true
|
|
}
|
|
}
|
|
]
|
|
}
|
|
})
|
|
})
|
|
|
|
app.view("chooseopponent-BEGINNER", async (ctx) => {
|
|
await ctx.ack();
|
|
|
|
// const userId =
|
|
|
|
// ctx.client.chat.postMessage({
|
|
// channel: ""
|
|
// })
|
|
})
|
|
|
|
app.command('/bm-eval', async (ctx) => {
|
|
await ctx.ack();
|
|
|
|
const resp = require('util').inspect(await eval(ctx.body.text), undefined, 1)
|
|
|
|
ctx.respond({
|
|
text: resp,
|
|
response_type: 'ephemeral',
|
|
blocks: [
|
|
{
|
|
type: 'section',
|
|
text: {
|
|
type: 'mrkdwn',
|
|
text: "```" + resp + "```"
|
|
}
|
|
}
|
|
]
|
|
})
|
|
})
|
|
|
|
app.command('/viewopponents', async (ctx) => {
|
|
await ctx.ack();
|
|
|
|
const args = ctx.body.text.slice().split(/ +/g);
|
|
|
|
switch (args[0].toUpperCase()) {
|
|
case "SPECIAL":
|
|
ctx.respond({
|
|
text: "You're trying to view Special opponents.",
|
|
response_type: 'ephemeral'
|
|
})
|
|
break;
|
|
case "BEGINNER":
|
|
const mappedBeginner = BeginnerOpponents.map(opponent =>
|
|
({
|
|
name: opponent.name,
|
|
battlePower:
|
|
opponent.stats.health +
|
|
opponent.stats.min +
|
|
opponent.stats.max
|
|
})
|
|
)
|
|
|
|
ctx.respond({
|
|
response_type: 'ephemeral',
|
|
text: `*Battle Master:* Greetings battler. Here are the avaliable *Beginner* opponents for you to battle.`,
|
|
blocks: [
|
|
{
|
|
type: 'section',
|
|
text: {
|
|
type: 'mrkdwn',
|
|
text: `*Battle Master:* Greetings <@${ctx.body.user_id}>. Here are the avaliable *Beginner* opponents for you to battle.`
|
|
}
|
|
},
|
|
{
|
|
type: 'section',
|
|
text: {
|
|
type: 'mrkdwn',
|
|
text: mappedBeginner.map(opponent => `*${opponent.name}:*\n\n${opponent.battlePower} Battle Power`).join('\n\n\n')
|
|
}
|
|
}
|
|
]
|
|
})
|
|
break;
|
|
case "CASUAL":
|
|
ctx.say("You're trying to view Casual opponents.")
|
|
break;
|
|
default:
|
|
ctx.say("You either wrote nothing or just chose a rank that doesn't exist.")
|
|
}
|
|
});
|
|
|
|
; (async () => {
|
|
// Start your app
|
|
await app.start(process.env.PORT);
|
|
|
|
console.log('⚡️ Bolt app is running!');
|
|
})(); |