1154 lines
45 KiB
JavaScript
1154 lines
45 KiB
JavaScript
const { App } = require('@slack/bolt');
|
|
const postgres = require('postgres');
|
|
|
|
require('dotenv').config()
|
|
const sql = postgres({
|
|
host: '/var/run/postgresql',
|
|
database: 'haroon_slackmaster',
|
|
username: 'haroon'
|
|
})
|
|
|
|
const BeginnerOpponents = require('./opponents/beginner');
|
|
|
|
const AllOpponents = [
|
|
...BeginnerOpponents
|
|
]
|
|
|
|
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 *;`
|
|
await sql`INSERT INTO cooldowns (slack_id) VALUES (${slackUserId});`
|
|
}
|
|
|
|
return a[0];
|
|
}
|
|
|
|
app.use(async (ctx) => {
|
|
await initializeUser(ctx.context.userId)
|
|
|
|
await ctx.next()
|
|
})
|
|
|
|
app.command('/chooseopponent', async (ctx) => {
|
|
await ctx.ack();
|
|
|
|
const user = await initializeUser(ctx.body.user_id);
|
|
|
|
if (user.currentopponent != "None") {
|
|
const opponent = AllOpponents.find(x => x.rawId == user.currentopponent)
|
|
|
|
return await ctx.respond({
|
|
response_type: 'ephemeral',
|
|
text: `You are already in a battle with ${opponent.name}.`,
|
|
blocks: [
|
|
{
|
|
type: 'section',
|
|
text: {
|
|
type: 'mrkdwn',
|
|
text: `*Battle Master:* You are already in a battle with *${opponent.name}*. Please finish your battle with them before proceeding.
|
|
${user.battlemessage}`
|
|
}
|
|
}
|
|
]
|
|
})
|
|
}
|
|
|
|
await ctx.client.views.open({
|
|
trigger_id: ctx.body.trigger_id,
|
|
view: {
|
|
"private_metadata": ctx.payload.channel_id,
|
|
"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)) * 100 || 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 user = await initializeUser(ctx.context.userId);
|
|
|
|
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?`
|
|
}
|
|
|
|
const canRankUp = (() => {
|
|
if (user.rank == 'Beginner') {
|
|
return (user.health + user.mindmg + user.maxdmg) >= 50 && user.cshards >= 50
|
|
} else if (user.rank == 'Casual') {
|
|
return (user.health + user.mindmg + user.maxdmg) >= 200 && user.cshards >= 250
|
|
} else {
|
|
return false
|
|
}
|
|
})();
|
|
|
|
await ctx.ack({
|
|
response_action: 'update',
|
|
view: {
|
|
"private_metadata": ctx.payload.private_metadata,
|
|
"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
|
|
}
|
|
},
|
|
...( canRankUp ? [
|
|
{
|
|
type: 'divider',
|
|
},
|
|
{
|
|
"type": "section",
|
|
"text": {
|
|
"type": "mrkdwn",
|
|
"text": `You can also now participate in a rank up battle against *Battle ${user.rank}*. Click the button below to initiate the battle.`
|
|
},
|
|
"accessory": {
|
|
"type": "button",
|
|
"text": {
|
|
"type": "plain_text",
|
|
"text": "Click Me",
|
|
"emoji": true
|
|
},
|
|
"value": ctx.context.userId,
|
|
"action_id": "start-rankup-battle"
|
|
}
|
|
}
|
|
] : [])
|
|
]
|
|
}
|
|
})
|
|
})
|
|
|
|
app.view("chooseopponent-BEGINNER", async (ctx) => {
|
|
await ctx.ack();
|
|
|
|
const channelId = ctx.view.private_metadata;
|
|
const userId = ctx.context.userId;
|
|
|
|
const slackUser = (await ctx.client.users.info({ user: userId })).user.profile;
|
|
|
|
const opponent = BeginnerOpponents.find(o => o.rawId == Object.values(ctx.payload.state.values)[0].opponents.selected_option.value);
|
|
|
|
const player = await initializeUser(userId);
|
|
|
|
await sql`UPDATE users
|
|
SET playerhealth = ${player.health},
|
|
playermin = ${player.mindmg},
|
|
playermax = ${player.maxdmg},
|
|
|
|
currentOpponent = ${opponent.rawId},
|
|
|
|
opponenthealth = ${opponent.stats.health},
|
|
opponentmin = ${opponent.stats.min},
|
|
opponentmax = ${opponent.stats.max}
|
|
|
|
WHERE slack_id = ${userId};`
|
|
|
|
const msg = await ctx.client.chat.postMessage({
|
|
channel: channelId,
|
|
text: `${slackUser.display_name_normalized} started a battle against ${opponent.name}.`,
|
|
blocks: [
|
|
{
|
|
type: 'section',
|
|
text: {
|
|
type: 'mrkdwn',
|
|
text: opponent.intro.replaceAll("{player}", slackUser.display_name_normalized)
|
|
},
|
|
"accessory": {
|
|
"type": "image",
|
|
"image_url": opponent.image,
|
|
"alt_text": opponent.name
|
|
}
|
|
},
|
|
{
|
|
"type": "actions",
|
|
"elements": [
|
|
{
|
|
"type": "button",
|
|
|
|
"text": {
|
|
"type": "plain_text",
|
|
"text": "Continue",
|
|
"emoji": true
|
|
},
|
|
"value": userId,
|
|
"action_id": "continue"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
});
|
|
|
|
await sql`UPDATE users SET battlemessage = ${`https://hackclub.slack.com/archives/${channelId}/p${msg.ts.replace('.', '')}`} WHERE slack_id = ${userId};`
|
|
})
|
|
|
|
async function checkButton(ctx) {
|
|
await ctx.ack();
|
|
|
|
if (ctx.payload.value != ctx.context.userId) {
|
|
return ctx.respond({
|
|
replace_original: false,
|
|
response_type: 'ephemeral',
|
|
text: "Battle Support: Please do not click other battlers buttons!",
|
|
blocks: [
|
|
{
|
|
type: 'section',
|
|
text: {
|
|
type: 'mrkdwn',
|
|
text: `*Battle Support:* Erm, <@${ctx.context.userId}>?... Please don't press other battlers buttons!
|
|
|
|
If you want to choose an opponent yourself, simply use \`/chooseopponent\` to get started!`
|
|
}
|
|
}
|
|
]
|
|
})
|
|
}
|
|
|
|
await ctx.next();
|
|
}
|
|
|
|
function chooseAction(chances) {
|
|
const random = Math.random();
|
|
let cumulative = 0;
|
|
|
|
for (const action in chances) {
|
|
cumulative += chances[action];
|
|
if (random < cumulative) {
|
|
return action;
|
|
}
|
|
}
|
|
}
|
|
|
|
async function takeOutOfBattle(slack_id) {
|
|
return await sql`UPDATE users SET
|
|
currentopponent = 'None',
|
|
|
|
playerhealth = NULL,
|
|
playermin = NULL,
|
|
playermax = NULL,
|
|
playerdefense = NULL,
|
|
playerdefendcount = 0,
|
|
|
|
opponenthealth = NULL,
|
|
opponentmin = NULL,
|
|
opponentmax = NULL,
|
|
opponentdefense = NULL,
|
|
opponentdefendcount = 0,
|
|
|
|
battlemessage = NULL
|
|
|
|
WHERE slack_id = ${slack_id};
|
|
`
|
|
}
|
|
|
|
async function playerLoss(ctx) {
|
|
const user = await initializeUser(ctx.context.userId);
|
|
const slackUser = (await ctx.client.users.info({ user: ctx.context.userId })).user.profile;
|
|
|
|
await takeOutOfBattle(ctx.context.userId);
|
|
|
|
await sql`
|
|
UPDATE users
|
|
SET spoints = ${user.spoints - 1},
|
|
cshards = ${user.cshards - 5},
|
|
defeats = ${user.defeats + 1}
|
|
WHERE slack_id = ${ctx.context.userId};
|
|
`
|
|
|
|
ctx.respond({
|
|
replace_original: true,
|
|
text: `${slackUser.display_name_normalized} lost against ${AllOpponents.find(x => x.rawId == user.currentopponent).name}`,
|
|
"blocks": [
|
|
{
|
|
"type": "context",
|
|
"elements": [
|
|
{
|
|
"type": "mrkdwn",
|
|
"text": "*DEFEAT :trophy:*"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"type": "section",
|
|
"text": {
|
|
"type": "mrkdwn",
|
|
"text": `Oh no ${slackUser.display_name_normalized}... You've been defeated by ${AllOpponents.find(x => x.rawId == user.currentopponent).name}... Don't give up! There's always room for improvement!\n\n\nLosses:*\n> -1 Skill Point\n>-5 Creation Shards\n> 1 Defeat`
|
|
},
|
|
"accessory": {
|
|
"type": "image",
|
|
"image_url": AllOpponents.find(x => x.rawId == user.currentopponent).image,
|
|
"alt_text": AllOpponents.find(x => x.rawId == user.currentopponent).name
|
|
}
|
|
}
|
|
]
|
|
})
|
|
}
|
|
|
|
async function playerWin(ctx) {
|
|
const user = await initializeUser(ctx.context.userId);
|
|
const slackUser = (await ctx.client.users.info({ user: ctx.context.userId })).user.profile;
|
|
|
|
await takeOutOfBattle(ctx.context.userId);
|
|
|
|
await sql`
|
|
UPDATE users
|
|
SET spoints = ${user.spoints + 1},
|
|
cshards = ${user.cshards + 5},
|
|
victories = ${user.victories + 1}
|
|
WHERE slack_id = ${ctx.context.userId};
|
|
`
|
|
|
|
ctx.respond({
|
|
replace_original: true,
|
|
text: `${slackUser.display_name_normalized} won against ${AllOpponents.find(x => x.rawId == user.currentopponent).name}`,
|
|
"blocks": [
|
|
{
|
|
"type": "context",
|
|
"elements": [
|
|
{
|
|
"type": "mrkdwn",
|
|
"text": "*YOU WON! :trophy:*"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"type": "section",
|
|
"text": {
|
|
"type": "mrkdwn",
|
|
"text": `Congratulations ${slackUser.display_name_normalized}! You have defeated ${AllOpponents.find(x => x.rawId == user.currentopponent).name} in battle! Good job!\n\n\n*Rewards:*\n> 1 Skill Point\n>5 Creation Shards\n> 1 Victory`
|
|
},
|
|
"accessory": {
|
|
"type": "image",
|
|
"image_url": AllOpponents.find(x => x.rawId == user.currentopponent).image,
|
|
"alt_text": AllOpponents.find(x => x.rawId == user.currentopponent).name
|
|
}
|
|
}
|
|
]
|
|
})
|
|
}
|
|
|
|
app.action("continue", checkButton, async (ctx) => {
|
|
const user = await initializeUser(ctx.context.userId);
|
|
const slackUser = (await ctx.client.users.info({ user: ctx.context.userId })).user.profile;
|
|
|
|
if (user.playerhealth <= 0) {
|
|
return playerLoss(ctx)
|
|
}
|
|
|
|
ctx.respond({
|
|
replace_original: true,
|
|
text: "",
|
|
"blocks": [
|
|
{
|
|
"type": "section",
|
|
"fields": [
|
|
{
|
|
"type": "mrkdwn",
|
|
"text": `${slackUser.display_name_normalized}:\n\n*Health:* ${user.playerhealth}\n*Min Damage:* ${user.playermin}\n*Max Damage:* ${user.playermax}`
|
|
},
|
|
{
|
|
"type": "mrkdwn",
|
|
"text": `${AllOpponents.find(x => x.rawId == user.currentopponent).name}:\n\n*Health:* ${user.opponenthealth}\n*Min Damage:* ${user.opponentmin}\n*Max Damage:* ${user.opponentmax}`
|
|
}
|
|
],
|
|
"accessory": {
|
|
"type": "image",
|
|
"image_url": AllOpponents.find(x => x.rawId == user.currentopponent).image,
|
|
"alt_text": AllOpponents.find(x => x.rawId == user.currentopponent).name
|
|
}
|
|
},
|
|
{
|
|
"type": "context",
|
|
"elements": [
|
|
{
|
|
"type": "mrkdwn",
|
|
"text": `*${slackUser.display_name_normalized}* vs *${AllOpponents.find(x => x.rawId == user.currentopponent).name}* | Your Turn`
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"type": "actions",
|
|
"elements": [
|
|
{
|
|
"type": "button",
|
|
"text": {
|
|
"type": "plain_text",
|
|
"text": "⚔️ Attack",
|
|
"emoji": true
|
|
},
|
|
"value": ctx.context.userId,
|
|
"action_id": "attack"
|
|
},
|
|
{
|
|
"type": "button",
|
|
"text": {
|
|
"type": "plain_text",
|
|
"text": "🛡️ Defend",
|
|
"emoji": true
|
|
},
|
|
"value": ctx.context.userId,
|
|
"action_id": "defend"
|
|
},
|
|
{
|
|
"type": "button",
|
|
"text": {
|
|
"type": "plain_text",
|
|
"text": " 🎁 Item",
|
|
"emoji": true
|
|
},
|
|
"value": ctx.context.userId,
|
|
"action_id": "item"
|
|
},
|
|
{
|
|
"type": "button",
|
|
"text": {
|
|
"type": "plain_text",
|
|
"text": "☠️ Forfeit",
|
|
"emoji": true
|
|
},
|
|
"style": "danger",
|
|
"value": ctx.context.userId,
|
|
"action_id": "forfeit"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
})
|
|
})
|
|
|
|
app.action('forfeit', checkButton, async (ctx) => {
|
|
ctx.respond({
|
|
replace_original: false,
|
|
response_type: 'ephemeral',
|
|
text: 'Not yet...',
|
|
})
|
|
})
|
|
|
|
app.action(/attack|defend|item/, checkButton, async (ctx) => {
|
|
let response = "This error message isn't meant to show up. If it does, contact Haroon.";
|
|
const user = await initializeUser(ctx.context.userId);
|
|
const slackUser = (await ctx.client.users.info({ user: ctx.context.userId })).user.profile;
|
|
|
|
switch (ctx.payload.action_id) {
|
|
case 'attack':
|
|
if (user.opponentdefense == 'Strong') {
|
|
response = `*_${slackUser.display_name_normalized} attacks ${AllOpponents.find(x => x.rawId == user.currentopponent).name}_*\n\n\`\`\`NO DAMAGE\`\`\``
|
|
} else if (user.opponentdefense == 'Moderate') {
|
|
if (Math.random() < 0.5) {
|
|
response = `*_${slackUser.display_name_normalized} attacks ${AllOpponents.find(x => x.rawId == user.currentopponent).name}_*\n\n\`\`\`NO DAMAGE\`\`\``
|
|
} else {
|
|
const damage = Math.floor(Math.random() * (user.playermax - user.playermin + 1)) + user.playermin;
|
|
await sql`UPDATE users SET opponenthealth = ${user.opponenthealth - damage} WHERE slack_id = ${ctx.context.userId};`
|
|
response = `*_${slackUser.display_name_normalized} attacks ${AllOpponents.find(x => x.rawId == user.currentopponent).name}_*\n\n\`\`\`${damage.toLocaleString()} DAMAGE\`\`\``
|
|
}
|
|
} else if (user.opponentdefense == 'Weak') {
|
|
if (Math.random() < 0.25) {
|
|
response = `*_${slackUser.display_name_normalized} attacks ${AllOpponents.find(x => x.rawId == user.currentopponent).name}_*\n\n\`\`\`NO DAMAGE\`\`\``
|
|
} else {
|
|
const damage = Math.floor(Math.random() * (user.playermax - user.playermin + 1)) + user.playermin;
|
|
await sql`UPDATE users SET opponenthealth = ${user.opponenthealth - damage} WHERE slack_id = ${ctx.context.userId};`
|
|
response = `*_${slackUser.display_name_normalized} attacks ${AllOpponents.find(x => x.rawId == user.currentopponent).name}_*\n\n\`\`\`${damage.toLocaleString()} DAMAGE\`\`\``
|
|
}
|
|
} else {
|
|
const damage = Math.floor(Math.random() * (user.playermax - user.playermin + 1)) + user.playermin;
|
|
await sql`UPDATE users SET opponenthealth = ${user.opponenthealth - damage} WHERE slack_id = ${ctx.context.userId};`
|
|
response = `*_${slackUser.display_name_normalized} attacks ${AllOpponents.find(x => x.rawId == user.currentopponent).name}_*\n\n\`\`\`${damage.toLocaleString()} DAMAGE\`\`\``
|
|
}
|
|
break;
|
|
case 'defend':
|
|
if (user.playerdefendcount < 3) {
|
|
await sql`UPDATE users SET playerdefense = 'Strong' WHERE slack_id = ${ctx.context.userId};`
|
|
response = `*_A blue forcefield magically appears around ${slackUser.display_name_normalized}_*\n\n\`\`\`STRONG DEFENCE\`\`\``
|
|
} else {
|
|
const type = ["Strong", "Moderate", "Weak"][Math.floor(Math.random() * 3)];
|
|
await sql`UPDATE users SET playerdefense = ${type} WHERE slack_id = ${ctx.context.userId};`
|
|
response = `*_A blue forcefield magically appears around ${slackUser.display_name_normalized}_*\n\n\`\`\`${type.toUpperCase()} DEFENCE\`\`\``
|
|
}
|
|
await sql`UPDATE users SET playerdefendcount = ${user.opponentdefendcount + 1} WHERE slack_id = ${ctx.context.userId};`
|
|
break;
|
|
case 'item':
|
|
const increase = Math.floor(Math.random() * 5) + 1;
|
|
switch (['health', 'min', 'max', 'nothing'][Math.floor(Math.random() * 4)]) {
|
|
case 'health':
|
|
await sql`UPDATE users SET playerhealth = ${user.playerhealth + increase} WHERE slack_id = ${ctx.context.userId};`
|
|
response = `*_${slackUser.display_name_normalized} drinks some squash. U N D I L U T E D._*\n\n\`\`\`+ ${increase} HEALTH\`\`\``
|
|
break;
|
|
case 'min':
|
|
if ((user.playermin + increase) < user.playermax) {
|
|
response = `*_${slackUser.display_name_normalized} touches some grass_*\n\n\`\`\`NOTHING HAPPENED\`\`\``
|
|
break;
|
|
}
|
|
await sql`UPDATE users SET playermin = ${user.playermin + increase} WHERE slack_id = ${ctx.context.userId};`
|
|
response = `*_${slackUser.display_name_normalized} drinks some squash. U N D I L U T E D._*\n\n\`\`\`+ ${increase} MIN DAMAGE\`\`\``
|
|
break;
|
|
case 'max':
|
|
await sql`UPDATE users SET playermax = ${user.playermax + increase} WHERE slack_id = ${ctx.context.userId};`
|
|
response = `*_${slackUser.display_name_normalized} drinks some squash. U N D I L U T E D._*\n\n\`\`\`+ ${increase} MAX DAMAGE\`\`\``
|
|
break;
|
|
case 'nothing':
|
|
response = `*_${slackUser.display_name_normalized} touches some grass_*\n\n\`\`\`NOTHING HAPPENED\`\`\``
|
|
break;
|
|
}
|
|
}
|
|
|
|
await sql`UPDATE users SET opponentdefense = 'None' WHERE slack_id = ${ctx.context.userId};`
|
|
|
|
await ctx.respond({
|
|
"blocks": [
|
|
{
|
|
"type": "section",
|
|
"text": {
|
|
"type": "mrkdwn",
|
|
"text": response
|
|
}
|
|
},
|
|
{
|
|
"type": "actions",
|
|
"elements": [
|
|
{
|
|
"type": "button",
|
|
"text": {
|
|
"type": "plain_text",
|
|
"text": "Continue",
|
|
"emoji": true
|
|
},
|
|
"value": ctx.context.userId,
|
|
"action_id": "continue-opponent"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
})
|
|
})
|
|
|
|
app.action("continue-opponent", checkButton, async (ctx) => {
|
|
const user = await initializeUser(ctx.context.userId);
|
|
const slackUser = (await ctx.client.users.info({ user: ctx.context.userId })).user.profile;
|
|
|
|
if (user.opponenthealth <= 0) {
|
|
return playerWin(ctx)
|
|
}
|
|
|
|
ctx.respond({
|
|
replace_original: true,
|
|
text: "",
|
|
"blocks": [
|
|
{
|
|
"type": "section",
|
|
"fields": [
|
|
{
|
|
"type": "mrkdwn",
|
|
"text": `${slackUser.display_name_normalized}:\n\n*Health:* ${user.playerhealth}\n*Min Damage:* ${user.playermin}\n*Max Damage:* ${user.playermax}`
|
|
},
|
|
{
|
|
"type": "mrkdwn",
|
|
"text": `${AllOpponents.find(x => x.rawId == user.currentopponent).name}:\n\n*Health:* ${user.opponenthealth}\n*Min Damage:* ${user.opponentmin}\n*Max Damage:* ${user.opponentmax}`
|
|
}
|
|
],
|
|
"accessory": {
|
|
"type": "image",
|
|
"image_url": AllOpponents.find(x => x.rawId == user.currentopponent).image,
|
|
"alt_text": AllOpponents.find(x => x.rawId == user.currentopponent).name
|
|
}
|
|
},
|
|
{
|
|
"type": "context",
|
|
"elements": [
|
|
{
|
|
"type": "mrkdwn",
|
|
"text": `*${slackUser.display_name_normalized}* vs *${AllOpponents.find(x => x.rawId == user.currentopponent).name}* | ${AllOpponents.find(x => x.rawId == user.currentopponent).name}'s Turn`
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"type": "actions",
|
|
"elements": [
|
|
{
|
|
"type": "button",
|
|
"text": {
|
|
"type": "plain_text",
|
|
"text": "Continue",
|
|
"emoji": true
|
|
},
|
|
"value": ctx.context.userId,
|
|
"action_id": "viewaction-opponent"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
})
|
|
})
|
|
|
|
app.action('viewaction-opponent', checkButton, async (ctx) => {
|
|
let response = "This error message isn't meant to show up. If it does, contact Haroon.";
|
|
const user = await initializeUser(ctx.context.userId);
|
|
const slackUser = (await ctx.client.users.info({ user: ctx.context.userId })).user.profile;
|
|
|
|
const chances = AllOpponents.find(x => x.rawId == user.currentopponent).chances;
|
|
|
|
const action = chooseAction(chances);
|
|
|
|
switch (action) {
|
|
case 'attack':
|
|
if (user.playerdefense == 'Strong') {
|
|
response = `*_${AllOpponents.find(x => x.rawId == user.currentopponent).name} attacks ${slackUser.display_name_normalized}_*\n\n\`\`\`NO DAMAGE\`\`\``
|
|
} else if (user.playerdefense == 'Moderate') {
|
|
if (Math.random() < 0.5) {
|
|
response = `*_${AllOpponents.find(x => x.rawId == user.currentopponent).name} attacks ${slackUser.display_name_normalized}_*\n\n\`\`\`NO DAMAGE\`\`\``
|
|
} else {
|
|
const damage = Math.floor(Math.random() * (user.opponentmax - user.opponentmin + 1)) + user.opponentmin;
|
|
await sql`UPDATE users SET opponenthealth = ${user.opponenthealth - damage} WHERE slack_id = ${ctx.context.userId};`
|
|
response = `*_${AllOpponents.find(x => x.rawId == user.currentopponent).name} attacks ${slackUser.display_name_normalized}_*\n\n\`\`\`${damage.toLocaleString()} DAMAGE\`\`\``
|
|
}
|
|
} else if (user.playerdefense == 'Weak') {
|
|
if (Math.random() < 0.25) {
|
|
response = `*_${AllOpponents.find(x => x.rawId == user.currentopponent).name} attacks ${lackUser.display_name_normalized}_*\n\n\`\`\`NO DAMAGE\`\`\``
|
|
} else {
|
|
const damage = Math.floor(Math.random() * (user.playermax - user.playermin + 1)) + user.playermin;
|
|
await sql`UPDATE users SET opponenthealth = ${user.opponenthealth - damage} WHERE slack_id = ${ctx.context.userId};`
|
|
response = `*_${AllOpponents.find(x => x.rawId == user.currentopponent).name} attacks ${slackUser.display_name_normalized}_*\n\n\`\`\`${damage.toLocaleString()} DAMAGE\`\`\``
|
|
}
|
|
} else {
|
|
const damage = Math.floor(Math.random() * (user.playermax - user.playermin + 1)) + user.playermin;
|
|
await sql`UPDATE users SET opponenthealth = ${user.opponenthealth - damage} WHERE slack_id = ${ctx.context.userId};`
|
|
response = `*_${AllOpponents.find(x => x.rawId == user.currentopponent).name} attacks ${slackUser.display_name_normalized}_*\n\n\`\`\`${damage.toLocaleString()} DAMAGE\`\`\``
|
|
}
|
|
break;
|
|
case 'defend':
|
|
if (user.opponentdefendcount < 3) {
|
|
await sql`UPDATE users SET opponentdefense = 'Strong' WHERE slack_id = ${ctx.context.userId};`
|
|
response = `*_A blue forcefield magically appears around ${AllOpponents.find(x => x.rawId == user.currentopponent).name}_*\n\n\`\`\`STRONG DEFENCE\`\`\``
|
|
} else {
|
|
const type = ["Strong", "Moderate", "Weak"][Math.floor(Math.random() * 3)];
|
|
await sql`UPDATE users SET opponentdefense = ${type} WHERE slack_id = ${ctx.context.userId};`
|
|
response = `*_A blue forcefield magically appears around ${AllOpponents.find(x => x.rawId == user.currentopponent).name}_*\n\n\`\`\`${type.toUpperCase()} DEFENCE\`\`\``
|
|
}
|
|
await sql`UPDATE users SET opponentdefendcount = ${user.opponentdefendcount + 1} WHERE slack_id = ${ctx.context.userId};`
|
|
break;
|
|
case 'item':
|
|
const increase = Math.floor(Math.random() * 5) + 1;
|
|
switch (['health', 'health', 'min', 'min', 'max', 'nothing', 'nothing', 'nothing'][Math.floor(Math.random() * 8)]) {
|
|
case 'health':
|
|
await sql`UPDATE users SET opponenthealth = ${user.opponenthealth + increase} WHERE slack_id = ${ctx.context.userId};`
|
|
response = `*_${AllOpponents.find(x => x.rawId == user.currentopponent).name} drinks some squash. U N D I L U T E D._*\n\n\`\`\`+ ${increase} HEALTH\`\`\``
|
|
break;
|
|
case 'min':
|
|
if ((user.opponentmin + increase) < user.opponentmax) {
|
|
response = `*_${AllOpponents.find(x => x.rawId == user.currentopponent).name} touches some grass_*\n\n\`\`\`NOTHING HAPPENED\`\`\``
|
|
break;
|
|
}
|
|
await sql`UPDATE users SET opponentmin = ${user.opponentmin + increase} WHERE slack_id = ${ctx.context.userId};`
|
|
response = `*_${AllOpponents.find(x => x.rawId == user.currentopponent).name} drinks some squash. U N D I L U T E D._*\n\n\`\`\`+ ${increase} MIN DAMAGE\`\`\``
|
|
break;
|
|
case 'max':
|
|
await sql`UPDATE users SET opponentmax = ${user.opponentmax + increase} WHERE slack_id = ${ctx.context.userId};`
|
|
response = `*_${AllOpponents.find(x => x.rawId == user.currentopponent).name} drinks some squash. U N D I L U T E D._*\n\n\`\`\`+ ${increase} MAX DAMAGE\`\`\``
|
|
break;
|
|
case 'nothing':
|
|
response = `*_${AllOpponents.find(x => x.rawId == user.currentopponent).name} touches some grass_*\n\n\`\`\`NOTHING HAPPENED\`\`\``
|
|
break;
|
|
}
|
|
}
|
|
|
|
await sql`UPDATE users SET playerdefense = 'None' WHERE slack_id = ${ctx.context.userId};`
|
|
|
|
await ctx.respond({
|
|
"blocks": [
|
|
{
|
|
"type": "section",
|
|
"text": {
|
|
"type": "mrkdwn",
|
|
"text": response
|
|
}
|
|
},
|
|
{
|
|
"type": "actions",
|
|
"elements": [
|
|
{
|
|
"type": "button",
|
|
"text": {
|
|
"type": "plain_text",
|
|
"text": "Continue",
|
|
"emoji": true
|
|
},
|
|
"value": ctx.context.userId,
|
|
"action_id": "continue"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
})
|
|
})
|
|
|
|
app.command('/bm-eval', async (ctx) => {
|
|
await ctx.ack();
|
|
|
|
if (ctx.context.userId != 'U06TBP41C3E') return;
|
|
|
|
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',
|
|
fields: mappedBeginner.map(opponent => ({
|
|
type: 'mrkdwn',
|
|
text: `*${opponent.name}* // ${opponent.battlePower} Battle Power`
|
|
}))
|
|
}
|
|
]
|
|
})
|
|
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.")
|
|
}
|
|
});
|
|
|
|
function getTimeDifference(date1, date2) {
|
|
// Ensure both dates are Date objects
|
|
const start = new Date(date1);
|
|
const end = new Date(date2);
|
|
|
|
// Calculate the difference in milliseconds
|
|
const diff = Math.abs(end - start);
|
|
|
|
// Calculate the difference in weeks, days, hours, minutes, and seconds
|
|
const weeks = Math.floor(diff / (1000 * 60 * 60 * 24 * 7));
|
|
const days = Math.floor(diff / (1000 * 60 * 60 * 24)) % 7;
|
|
const hours = Math.floor(diff / (1000 * 60 * 60)) % 24;
|
|
const minutes = Math.floor(diff / (1000 * 60)) % 60;
|
|
const seconds = Math.floor(diff / 1000) % 60;
|
|
|
|
// Determine which unit to use for the output
|
|
if (weeks > 0) {
|
|
return `${weeks} Week${weeks > 1 ? 's' : ''}`;
|
|
} else if (days > 0) {
|
|
return `${days} Day${days > 1 ? 's' : ''}`;
|
|
} else if (hours > 0) {
|
|
return `${hours} Hour${hours > 1 ? 's' : ''}`;
|
|
} else if (minutes > 0) {
|
|
return `${minutes} Minute${minutes > 1 ? 's' : ''}`;
|
|
} else {
|
|
return `${seconds} Second${seconds > 1 ? 's' : ''}`;
|
|
}
|
|
}
|
|
|
|
app.command('/b-daily', async (ctx) => {
|
|
await ctx.ack();
|
|
const [ cooldown ] = await sql`SELECT * FROM cooldowns WHERE slack_id = ${ctx.context.userId};`
|
|
const user = await initializeUser(ctx.context.userId)
|
|
const slackUser = (await ctx.client.users.info({ user: ctx.context.userId })).user.profile;
|
|
|
|
const now = new Date();
|
|
|
|
if (!cooldown.daily || (cooldown.daily.getTime() <= now.getTime())) {
|
|
const cshards = Math.floor(Math.random() * 10) + 1;
|
|
const spoints = Math.floor(Math.random() * 5) + 1;
|
|
|
|
|
|
ctx.respond({
|
|
response_type: 'ephemeral',
|
|
blocks: [
|
|
{
|
|
type: 'section',
|
|
text: {
|
|
type: 'mrkdwn',
|
|
text: `*Battle Master:* Oh ${slackUser.display_name_normalized}, apologies for the wait. Here is your reward for today.\n\n> ${cshards} Creation Shards\n> ${spoints} Skill Points`
|
|
}
|
|
}
|
|
]
|
|
})
|
|
|
|
await sql`UPDATE users SET cshards = ${user.cshards + cshards}, spoints = ${user.spoints + spoints} WHERE slack_id = ${ctx.context.userId};`
|
|
|
|
await sql`UPDATE cooldowns SET daily = ${new Date(now.getTime() + (24 * 60 * 60 * 1000))};`
|
|
} else {
|
|
ctx.respond({
|
|
response_type: 'ephemeral',
|
|
blocks: [
|
|
{
|
|
type: 'section',
|
|
text: {
|
|
type: 'mrkdwn',
|
|
text: `*Battle Master:* Apologies battler. Unfortunately you must wait *${getTimeDifference(now, cooldown.daily)}* before you can collect your next daily reward.`
|
|
}
|
|
}
|
|
]
|
|
})
|
|
}
|
|
})
|
|
|
|
app.command('/b-weekly', async (ctx) => {
|
|
await ctx.ack();
|
|
const [ cooldown ] = await sql`SELECT * FROM cooldowns WHERE slack_id = ${ctx.context.userId};`
|
|
const user = await initializeUser(ctx.context.userId)
|
|
const slackUser = (await ctx.client.users.info({ user: ctx.context.userId })).user.profile;
|
|
|
|
const now = new Date();
|
|
|
|
if (!cooldown.weekly || (cooldown.weekly.getTime() <= now.getTime())) {
|
|
const cshards = Math.floor(Math.random() * 20) + 1;
|
|
const dshards = Math.floor(Math.random() * 15) + 1;
|
|
const spoints = Math.floor(Math.random() * 10) + 1;
|
|
|
|
ctx.respond({
|
|
response_type: 'ephemeral',
|
|
blocks: [
|
|
{
|
|
type: 'section',
|
|
text: {
|
|
type: 'mrkdwn',
|
|
text: `*Battle Master:* It's that time again ${slackUser.display_name_normalized}. Enjoy your weekly reward.\n\n> ${cshards} Creation Shards\n> ${dshards} Destruction Shards\n> ${spoints} Skill Points`
|
|
}
|
|
}
|
|
]
|
|
})
|
|
|
|
await sql`UPDATE users SET cshards = ${user.cshards + cshards}, dshards = ${user.dshards + dshards}, spoints = ${user.spoints + spoints} WHERE slack_id = ${ctx.context.userId};`
|
|
|
|
await sql`UPDATE cooldowns SET weekly = ${new Date(now.getTime() + (7 * 24 * 60 * 60 * 1000))};`
|
|
} else {
|
|
ctx.respond({
|
|
response_type: 'ephemeral',
|
|
blocks: [
|
|
{
|
|
type: 'section',
|
|
text: {
|
|
type: 'mrkdwn',
|
|
text: `*Battle Master:* Apologies battler. Unfortunately you must wait *${getTimeDifference(now, cooldown.weekly)}* before you can collect your next weekly reward.`
|
|
}
|
|
}
|
|
]
|
|
})
|
|
}
|
|
})
|
|
|
|
app.command('/b-monthly', async (ctx) => {
|
|
await ctx.ack();
|
|
const [ cooldown ] = await sql`SELECT * FROM cooldowns WHERE slack_id = ${ctx.context.userId};`
|
|
const user = await initializeUser(ctx.context.userId)
|
|
const slackUser = (await ctx.client.users.info({ user: ctx.context.userId })).user.profile;
|
|
|
|
const now = new Date();
|
|
|
|
if (!cooldown.monthly || (cooldown.monthly.getTime() <= now.getTime())) {
|
|
const cshards = Math.floor(Math.random() * 30) + 1;
|
|
const dshards = Math.floor(Math.random() * 25) + 1;
|
|
const spoints = Math.floor(Math.random() * 15) + 1;
|
|
|
|
ctx.respond({
|
|
response_type: 'ephemeral',
|
|
blocks: [
|
|
{
|
|
type: 'section',
|
|
text: {
|
|
type: 'mrkdwn',
|
|
text: `*Battle Master:* Another month, another reward. You have my appreciation ${slackUser.display_name_normalized}.\n\n> ${cshards} Creation Shards\n> ${dshards} Destruction Shards\n> ${spoints} Skill Points`
|
|
}
|
|
}
|
|
]
|
|
})
|
|
|
|
await sql`UPDATE users SET cshards = ${user.cshards + cshards}, dshards = ${user.dshards + dshards}, spoints = ${user.spoints + spoints} WHERE slack_id = ${ctx.context.userId};`
|
|
|
|
await sql`UPDATE cooldowns SET monthly = ${new Date(now.getTime() + (30 * 7 * 24 * 60 * 60 * 1000))};`
|
|
} else {
|
|
ctx.respond({
|
|
response_type: 'ephemeral',
|
|
blocks: [
|
|
{
|
|
type: 'section',
|
|
text: {
|
|
type: 'mrkdwn',
|
|
text: `*Battle Master:* Apologies battler. Unfortunately you must wait *${getTimeDifference(now, cooldown.monthly)}* before you can collect your next monthly reward.`
|
|
}
|
|
}
|
|
]
|
|
})
|
|
}
|
|
})
|
|
|
|
; (async () => {
|
|
await app.start(process.env.PORT);
|
|
|
|
console.log('⚡️ Bolt app is running!');
|
|
})(); |