diff --git a/.vscode/settings.json b/.vscode/settings.json
index e44870599..57e6e0dab 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -8,13 +8,18 @@
"kranvagn",
"outfile",
"ownedtanks",
+ "playerachievements",
+ "playerinfo",
"Resvg",
+ "searchclans",
"searchplayers",
+ "searchtanks",
"SKLLD",
"Smri",
"startcase",
"strvk",
"tankopedia",
+ "tankstats",
"Unicum",
"Unregistering",
"vickers",
diff --git a/package.json b/package.json
index b7e103244..eb623b4c1 100644
--- a/package.json
+++ b/package.json
@@ -5,8 +5,8 @@
"main": "src/index.ts",
"type": "module",
"scripts": {
- "dev": "yarn build && yarn start",
- "start": "node --enable-source-maps dist/index.cjs",
+ "dev": "yarn build --dev && yarn start",
+ "start": "node --enable-source-maps --trace-warnings dist/index.cjs",
"zip": "zip -r dist.zip dist",
"build": "node scripts/build",
"lint": "tsc"
diff --git a/scripts/build.js b/scripts/build.js
index 975ff801b..64d95a31f 100644
--- a/scripts/build.js
+++ b/scripts/build.js
@@ -1,10 +1,14 @@
import { build } from 'esbuild';
import { rmSync } from 'fs';
+import { argv } from 'process';
+
+const isDev = argv.includes('--dev');
+const isProd = !isDev;
console.log('Removing dist...');
rmSync('dist', { recursive: true, force: true });
-console.log('Creating context...');
+console.log('Building...');
build({
entryPoints: ['src/index.ts'],
outfile: 'dist/index.cjs',
@@ -15,9 +19,9 @@ build({
'.ttf': 'file',
},
- bundle: true,
- sourcemap: true,
+ bundle: isProd,
+ sourcemap: isProd,
minifyIdentifiers: false, // causes errors
- minifySyntax: true,
- minifyWhitespace: true,
+ minifySyntax: isProd,
+ minifyWhitespace: isProd,
});
diff --git a/src/commands/debug.ts b/src/commands/debug.ts
index 278060b45..9b7175480 100644
--- a/src/commands/debug.ts
+++ b/src/commands/debug.ts
@@ -20,31 +20,27 @@ export default {
.setName(cmdName('debug'))
.setDescription('Debug information about the bot'),
- async execute(interaction) {
+ execute(interaction) {
const currentTime = new Date().getTime();
const uptime = currentTime - executionStart;
- await interaction.editReply({
- embeds: [
- infoEmbed(
- `${client.user?.username} debug information`,
- cleanTable([
- ['Version', packageJSON.version],
- ['Client ID', getClientId()],
- ['Tag', interaction.client.user.tag],
- [
- 'Uptime',
- `${Math.floor(uptime / 1000 / 60 / 60 / 24)}d ${Math.floor(
- (uptime / 1000 / 60 / 60) % 24,
- )}h ${Math.floor((uptime / 1000 / 60) % 60)}m ${Math.floor(
- (uptime / 1000) % 60,
- )}s ${Math.floor(uptime % 1000)}ms`,
- ],
- ['Tankopedia cached', `${tankopedia !== undefined}`],
- ['Tank averages cached', `${tankAverages !== undefined}`],
- ]),
- ),
- ],
- });
+ return infoEmbed(
+ `${client.user?.username} debug information`,
+ cleanTable([
+ ['Version', packageJSON.version],
+ ['Client ID', getClientId()],
+ ['Tag', interaction.client.user.tag],
+ [
+ 'Uptime',
+ `${Math.floor(uptime / 1000 / 60 / 60 / 24)}d ${Math.floor(
+ (uptime / 1000 / 60 / 60) % 24,
+ )}h ${Math.floor((uptime / 1000 / 60) % 60)}m ${Math.floor(
+ (uptime / 1000) % 60,
+ )}s ${Math.floor(uptime % 1000)}ms`,
+ ],
+ ['Tankopedia cached', `${tankopedia !== undefined}`],
+ ['Tank averages cached', `${tankAverages !== undefined}`],
+ ]),
+ );
},
} satisfies CommandRegistry;
diff --git a/src/commands/eligible.ts b/src/commands/eligible.ts
index f6a65bd33..8dc80c208 100644
--- a/src/commands/eligible.ts
+++ b/src/commands/eligible.ts
@@ -166,10 +166,6 @@ export default {
)}`;
}
- await interaction.editReply({
- embeds: isEligible
- ? [positiveEmbed(title, body)]
- : [negativeEmbed(title, body)],
- });
+ return (isEligible ? positiveEmbed : negativeEmbed)(title, body);
},
} satisfies CommandRegistry;
diff --git a/src/commands/help.ts b/src/commands/help.ts
index 327e465bc..292c3d379 100644
--- a/src/commands/help.ts
+++ b/src/commands/help.ts
@@ -1,7 +1,7 @@
-import { EmbedBuilder, SlashCommandBuilder } from 'discord.js';
+import { SlashCommandBuilder } from 'discord.js';
import discord from '../../discord.json' assert { type: 'json' };
-import { ACCENT_COLOR } from '../constants/colors.js';
import cmdName from '../core/interaction/cmdName.js';
+import infoEmbed from '../core/interaction/infoEmbed.js';
import {
CommandRegistry,
guildCommands,
@@ -11,33 +11,27 @@ import { client } from '../index.js';
export default {
inProduction: true,
- inDevelopment: true,
+ inDevelopment: false,
inPublic: true,
command: new SlashCommandBuilder()
.setName(cmdName('help'))
.setDescription('All the help you need about the bot'),
- async execute(interaction) {
- await interaction.editReply({
- embeds: [
- new EmbedBuilder()
- .setTitle(`${client.user?.username} help`)
- .setColor(ACCENT_COLOR)
- .setDescription(
- `**About**\n${
- client.user?.username
- } automates many mundane tasks and provide statistics in numerous flexible ways.\n\nMade by TresAbhi from the Skilled [SKLLD] clan.\nGitHub: https://github.com/tresabhi/blitzkrieg\nDiscord server: https://discord.gg/nDt7AjGJQH\nSkilled server: https://discord.gg/ZPvcEG7DS8\n\n**Commands**\n${(interaction.guildId ===
- discord.sklld_guild_id
- ? [...guildCommands, ...publicCommands]
- : publicCommands
- )
- .map((command) => `**/${command.name}**: ${command.description}`)
- .join(
- '\n',
- )}\n\n*I am a new bot; I may make mistakes and go offline unexpectedly from time to time.*`,
- ),
- ],
- });
+ execute(interaction) {
+ return infoEmbed(
+ `${client.user?.username} help`,
+ `**About**\n${
+ client.user?.username
+ } automates many mundane tasks and provide statistics in numerous flexible ways.\n\nMade by TresAbhi from the Skilled [SKLLD] clan.\nGitHub: https://github.com/tresabhi/blitzkrieg\nDiscord server: https://discord.gg/nDt7AjGJQH\nSkilled server: https://discord.gg/ZPvcEG7DS8\n\n**Commands**\n${(interaction.guildId ===
+ discord.sklld_guild_id
+ ? [...guildCommands, ...publicCommands]
+ : publicCommands
+ )
+ .map((command) => `**/${command.name}**: ${command.description}`)
+ .join(
+ '\n',
+ )}\n\n*I am a new bot; I may make mistakes and go offline unexpectedly from time to time.*`,
+ );
},
} satisfies CommandRegistry;
diff --git a/src/commands/inactive.tsx b/src/commands/inactive.tsx
index 9b91fa8eb..8c781ee85 100644
--- a/src/commands/inactive.tsx
+++ b/src/commands/inactive.tsx
@@ -11,7 +11,6 @@ import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
import cmdName from '../core/interaction/cmdName.js';
import addClanChoices from '../core/options/addClanChoices.js';
import { WARGAMING_APPLICATION_ID } from '../core/process/args.js';
-import render from '../core/ui/render.js';
import { CommandRegistry } from '../events/interactionCreate.js';
import { AccountInfo } from '../types/accountInfo.js';
import { ClanInfo } from '../types/clanInfo.js';
@@ -67,7 +66,7 @@ export default {
);
const hasInactiveMembers = inactive.length > 0;
- const image = await render(
+ return (
}
- ,
+
);
-
- await interaction.editReply({ files: [image] });
},
autocomplete: clanAutocomplete,
diff --git a/src/commands/ownedtanks.tsx b/src/commands/ownedtanks.tsx
index 98df0be0f..15d4deb09 100644
--- a/src/commands/ownedtanks.tsx
+++ b/src/commands/ownedtanks.tsx
@@ -13,7 +13,6 @@ import { TIER_ROMAN_NUMERALS, tankopedia } from '../core/blitz/tankopedia.js';
import cmdName from '../core/interaction/cmdName.js';
import addUsernameOption from '../core/options/addUsernameOption.js';
import { WARGAMING_APPLICATION_ID } from '../core/process/args.js';
-import render from '../core/ui/render.js';
import { CommandRegistry } from '../events/interactionCreate.js';
import { AccountInfo } from '../types/accountInfo.js';
import { PlayerClanData } from '../types/playerClanData.js';
@@ -41,24 +40,34 @@ const COMP_TANKS = [
export default {
inProduction: true,
- inDevelopment: true,
+ inDevelopment: false,
inPublic: true,
command: new SlashCommandBuilder()
.setName(cmdName('ownedtanks'))
.setDescription("Shows a player's owned tanks")
- .addIntegerOption((option) =>
+ .addStringOption((option) =>
option
.setName('tier')
.setDescription('The tier you want to see')
- .setMinValue(1)
- .setMaxValue(10)
+ .setChoices(
+ { name: 'Tier I', value: '1' },
+ { name: 'Tier II', value: '2' },
+ { name: 'Tier III', value: '3' },
+ { name: 'Tier IV', value: '4' },
+ { name: 'Tier V', value: '5' },
+ { name: 'Tier VI', value: '6' },
+ { name: 'Tier VII', value: '7' },
+ { name: 'Tier VIII', value: '8' },
+ { name: 'Tier IX', value: '9' },
+ { name: 'Tier X', value: '10' },
+ )
.setRequired(true),
)
.addStringOption(addUsernameOption),
async execute(interaction) {
- const tier = interaction.options.getInteger('tier')!;
+ const tier = Number(interaction.options.getString('tier'));
const account = await getBlitzAccount(interaction);
const { id, server } = account;
const accountInfo = await getWargamingResponse(
@@ -72,7 +81,7 @@ export default {
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
);
- const image = await render(
+ return (
{/* TODO: integrate some of these into title bar */}
- ,
+
);
-
- await interaction.editReply({ files: [image] });
},
autocomplete: usernameAutocomplete,
diff --git a/src/commands/playerachievements.ts b/src/commands/playerachievements.ts
index cbe3626fd..9846a2abc 100644
--- a/src/commands/playerachievements.ts
+++ b/src/commands/playerachievements.ts
@@ -51,30 +51,26 @@ export default {
...accountAchievements.max_series,
};
- interaction.editReply({
- embeds: [
- infoEmbed(
- `${markdownEscape(accounts[id].nickname)}'s information`,
+ return infoEmbed(
+ `${markdownEscape(accounts[id].nickname)}'s information`,
- cleanTable(
- Object.keys(compound)
- .filter((achievement) => compound[achievement] > 0)
- .sort(
- sortBy === 'count'
- ? (a, b) => compound[b] - compound[a]
- : undefined,
- )
- .map(
- (achievement) =>
- [
- startCase(achievement),
- compound[achievement],
- ] as TableInputEntry,
- ),
+ cleanTable(
+ Object.keys(compound)
+ .filter((achievement) => compound[achievement] > 0)
+ .sort(
+ sortBy === 'count'
+ ? (a, b) => compound[b] - compound[a]
+ : undefined,
+ )
+ .map(
+ (achievement) =>
+ [
+ startCase(achievement),
+ compound[achievement],
+ ] as TableInputEntry,
),
- ),
- ],
- });
+ ),
+ );
},
autocomplete: usernameAutocomplete,
diff --git a/src/commands/playerinfo.ts b/src/commands/playerinfo.ts
index 46a3eefb8..77b339508 100644
--- a/src/commands/playerinfo.ts
+++ b/src/commands/playerinfo.ts
@@ -29,36 +29,32 @@ export default {
);
const accountInfo = accounts[id];
- interaction.editReply({
- embeds: [
- infoEmbed(
- `${markdownEscape(accountInfo.nickname)}'s information`,
+ return infoEmbed(
+ `${markdownEscape(accountInfo.nickname)}'s information`,
- cleanTable([
- ['Nickname', `${accountInfo.nickname}`],
- ['Battles', `${accountInfo.statistics.all.battles}`],
- [
- 'Winrate',
- `${(
- 100 *
- (accountInfo.statistics.all.wins /
- accountInfo.statistics.all.battles)
- ).toFixed(2)}%`,
- ],
- [],
- ['Account ID', `${accountInfo.account_id}`],
- [
- 'Created',
- `${new Date(accountInfo.created_at * 1000).toDateString()}`,
- ],
- [
- 'Last battle',
- `${new Date(accountInfo.last_battle_time * 1000).toDateString()}`,
- ],
- ]),
- ),
- ],
- });
+ cleanTable([
+ ['Nickname', `${accountInfo.nickname}`],
+ ['Battles', `${accountInfo.statistics.all.battles}`],
+ [
+ 'Winrate',
+ `${(
+ 100 *
+ (accountInfo.statistics.all.wins /
+ accountInfo.statistics.all.battles)
+ ).toFixed(2)}%`,
+ ],
+ [],
+ ['Account ID', `${accountInfo.account_id}`],
+ [
+ 'Created',
+ `${new Date(accountInfo.created_at * 1000).toDateString()}`,
+ ],
+ [
+ 'Last battle',
+ `${new Date(accountInfo.last_battle_time * 1000).toDateString()}`,
+ ],
+ ]),
+ );
},
autocomplete: usernameAutocomplete,
diff --git a/src/commands/searchclans.ts b/src/commands/searchclans.ts
index 28216932d..ef65b10b3 100644
--- a/src/commands/searchclans.ts
+++ b/src/commands/searchclans.ts
@@ -36,19 +36,13 @@ export default {
`https://api.wotblitz.${server}/wotb/clans/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${clan}&limit=${limit}`,
);
- await interaction.editReply({
- embeds: [
- infoEmbed(
- `Clan search for "${markdownEscape(clan)}" in ${
- BLITZ_SERVERS[server]
- }`,
- clanList.length === 0
- ? 'No clans found.'
- : `\`\`\`\n${clanList
- .map((clan) => `${clan.name} [${clan.tag}]`)
- .join('\n')}\n\`\`\``,
- ),
- ],
- });
+ return infoEmbed(
+ `Clan search for "${markdownEscape(clan)}" in ${BLITZ_SERVERS[server]}`,
+ clanList.length === 0
+ ? 'No clans found.'
+ : `\`\`\`\n${clanList
+ .map((clan) => `${clan.name} [${clan.tag}]`)
+ .join('\n')}\n\`\`\``,
+ );
},
} satisfies CommandRegistry;
diff --git a/src/commands/searchplayers.ts b/src/commands/searchplayers.ts
index 1f29125e5..63a61d61a 100644
--- a/src/commands/searchplayers.ts
+++ b/src/commands/searchplayers.ts
@@ -44,19 +44,13 @@ export default {
)
: [];
- await interaction.editReply({
- embeds: [
- infoEmbed(
- `Player search for "${markdownEscape(name)}" in ${
- BLITZ_SERVERS[server]
- }`,
- `\`\`\`${
- players.length === 0
- ? 'No players found.'
- : players.map((player) => player.nickname).join('\n')
- }\`\`\``,
- ),
- ],
- });
+ return infoEmbed(
+ `Player search for "${markdownEscape(name)}" in ${BLITZ_SERVERS[server]}`,
+ `\`\`\`${
+ players.length === 0
+ ? 'No players found.'
+ : players.map((player) => player.nickname).join('\n')
+ }\`\`\``,
+ );
},
} satisfies CommandRegistry;
diff --git a/src/commands/searchtanks.ts b/src/commands/searchtanks.ts
index 2d525cdc0..c9760df1c 100644
--- a/src/commands/searchtanks.ts
+++ b/src/commands/searchtanks.ts
@@ -31,15 +31,11 @@ export default {
(result) => result.target,
);
- await interaction.editReply({
- embeds: [
- infoEmbed(
- `Tank search for "${markdownEscape(tank)}"`,
- results.length === 0
- ? 'No tanks found.'
- : `\`\`\`\n${results.join('\n')}\n\`\`\``,
- ),
- ],
- });
+ return infoEmbed(
+ `Tank search for "${markdownEscape(tank)}"`,
+ results.length === 0
+ ? 'No tanks found.'
+ : `\`\`\`\n${results.join('\n')}\n\`\`\``,
+ );
},
} satisfies CommandRegistry;
diff --git a/src/commands/stats.tsx b/src/commands/stats.tsx
index 9939e2c20..6c541451a 100644
--- a/src/commands/stats.tsx
+++ b/src/commands/stats.tsx
@@ -1,8 +1,4 @@
-import {
- ActionRowBuilder,
- ButtonBuilder,
- SlashCommandBuilder,
-} from 'discord.js';
+import { SlashCommandBuilder } from 'discord.js';
import GenericAllStats from '../components/GenericAllStats.js';
import NoData, { NoDataType } from '../components/NoData.js';
import PoweredByBlitzStars from '../components/PoweredByBlitzStars.js';
@@ -29,7 +25,6 @@ import addStatPeriodChoices, {
} from '../core/options/addStatPeriodChoices.js';
import addUsernameOption from '../core/options/addUsernameOption.js';
import { WARGAMING_APPLICATION_ID } from '../core/process/args.js';
-import render from '../core/ui/render.js';
import { CommandRegistry } from '../events/interactionCreate.js';
import {
AccountInfo,
@@ -134,7 +129,7 @@ export default {
if (!hasWN8AccumulatedAtAll) supplementaryStats.WN8 = undefined;
- const image = await render(
+ return [
,
- );
-
- const actionRow = new ActionRowBuilder().addComponents(
fullBlitzStarsStats(server, accountInfo[id].nickname),
supportBlitzStars,
- );
-
- await interaction.editReply({
- files: [image],
- components: [actionRow],
- });
+ ];
},
autocomplete: usernameAutocomplete,
diff --git a/src/commands/tankstats.tsx b/src/commands/tankstats.tsx
index baae75a9c..6585a2fca 100644
--- a/src/commands/tankstats.tsx
+++ b/src/commands/tankstats.tsx
@@ -1,8 +1,4 @@
-import {
- ActionRowBuilder,
- ButtonBuilder,
- SlashCommandBuilder,
-} from 'discord.js';
+import { SlashCommandBuilder } from 'discord.js';
import GenericAllStats from '../components/GenericAllStats.js';
import NoData, { NoDataType } from '../components/NoData.js';
import PoweredByBlitzStars from '../components/PoweredByBlitzStars.js';
@@ -34,13 +30,12 @@ import addStatPeriodChoices, {
import addTankChoices from '../core/options/addTankChoices.js';
import addUsernameOption from '../core/options/addUsernameOption.js';
import { WARGAMING_APPLICATION_ID } from '../core/process/args.js';
-import render from '../core/ui/render.js';
import { CommandRegistry } from '../events/interactionCreate.js';
import { AccountInfo, AllStats } from '../types/accountInfo.js';
export default {
inProduction: true,
- inDevelopment: true,
+ inDevelopment: false,
inPublic: true,
command: new SlashCommandBuilder()
@@ -81,7 +76,7 @@ export default {
stats = tankStatsOverTime[tankId] ?? emptyAllStats;
}
- const image = await render(
+ return [
,
- );
-
- const actionRow = new ActionRowBuilder().addComponents(
fullBlitzStarsStats(server, accountInfo[id].nickname),
supportBlitzStars,
- );
-
- await interaction.editReply({
- files: [image],
- components: [actionRow],
- });
+ ];
},
autocomplete: (interaction) => {
diff --git a/src/commands/today.tsx b/src/commands/today.tsx
index 887e3f27a..34fe10b29 100644
--- a/src/commands/today.tsx
+++ b/src/commands/today.tsx
@@ -1,8 +1,4 @@
-import {
- ActionRowBuilder,
- ButtonBuilder,
- SlashCommandBuilder,
-} from 'discord.js';
+import { SlashCommandBuilder } from 'discord.js';
import * as Breakdown from '../components/Breakdown/index.js';
import NoData, { NoDataType } from '../components/NoData.js';
import PoweredByBlitzStars from '../components/PoweredByBlitzStars.js';
@@ -25,7 +21,6 @@ import fullBlitzStarsStats from '../core/interaction/fullBlitzStarsStats.js';
import { supportBlitzStars } from '../core/interaction/supportBlitzStars.js';
import addUsernameOption from '../core/options/addUsernameOption.js';
import { WARGAMING_APPLICATION_ID } from '../core/process/args.js';
-import render from '../core/ui/render.js';
import { CommandRegistry } from '../events/interactionCreate.js';
import { AccountInfo, AllStats } from '../types/accountInfo.js';
import { PlayerClanData } from '../types/playerClanData.js';
@@ -159,7 +154,7 @@ export default {
);
});
- const image = await render(
+ return [
,
- );
-
- const actionRow = new ActionRowBuilder().addComponents(
fullBlitzStarsStats(server, accountInfo[id].nickname),
supportBlitzStars,
- );
-
- await interaction.editReply({
- files: [image],
- components: [actionRow],
- });
+ ];
},
autocomplete: usernameAutocomplete,
diff --git a/src/commands/verify.ts b/src/commands/verify.ts
index 7fbfdeabc..cbc0643f8 100644
--- a/src/commands/verify.ts
+++ b/src/commands/verify.ts
@@ -1,11 +1,12 @@
-import { EmbedBuilder, SlashCommandBuilder } from 'discord.js';
+import { SlashCommandBuilder } from 'discord.js';
import markdownEscape from 'markdown-escape';
import discord from '../../discord.json' assert { type: 'json' };
-import { NEGATIVE_COLOR, POSITIVE_COLOR } from '../constants/colors.js';
import usernameAutocomplete from '../core/autocomplete/username.js';
import getBlitzAccount from '../core/blitz/getBlitzAccount.js';
import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
import cmdName from '../core/interaction/cmdName.js';
+import negativeEmbed from '../core/interaction/negativeEmbed.js';
+import positiveEmbed from '../core/interaction/positiveEmbed.js';
import addUsernameOption from '../core/options/addUsernameOption.js';
import { WARGAMING_APPLICATION_ID } from '../core/process/args.js';
import { CommandRegistry } from '../events/interactionCreate.js';
@@ -33,84 +34,49 @@ export default {
);
const clan = clanData[id]?.clan;
const clanTag = clan ? ` [${clan!.tag}]` : '';
+ const member =
+ interaction.member &&
+ interaction.guild?.members.cache.get(interaction.member?.user.id);
if (!interaction.guild?.members.me?.permissions.has('ManageNicknames')) {
- interaction.editReply({
- embeds: [
- new EmbedBuilder()
- .setColor(NEGATIVE_COLOR)
- .setTitle(
- `${markdownEscape(interaction.user.username)} failed to verify`,
- )
- .setDescription(
- "I don't have the permission to change your nickname.",
- ),
- ],
- });
-
- return;
+ return negativeEmbed(
+ `${markdownEscape(interaction.user.username)} failed to verify`,
+ "I don't have the permission to change your nickname.",
+ );
+ }
+ if (!interaction.member || !member) {
+ return negativeEmbed(
+ `${interaction.user.username} is not in this server`,
+ "I couldn't find this person in this server. Did they leave?",
+ );
}
- if (interaction.member) {
- const member = interaction.guild?.members.cache.get(
- interaction.member?.user.id,
- );
+ try {
+ await member.setNickname(`${accountInfo[id].nickname}${clanTag}`);
- if (member) {
- try {
- await member.setNickname(`${accountInfo[id].nickname}${clanTag}`);
-
- if (interaction.guildId === discord.sklld_guild_id) {
- if (
- !interaction.guild?.members.me?.permissions.has('ManageRoles')
- ) {
- await interaction.editReply({
- embeds: [
- new EmbedBuilder()
- .setColor(NEGATIVE_COLOR)
- .setTitle(
- `${markdownEscape(
- interaction.user.username,
- )} failed to verify`,
- )
- .setDescription(
- "I don't have the permission to change your manage roles.",
- ),
- ],
- });
-
- return;
- }
-
- await member.roles.remove(discord.sklld_verify_role);
- await member.roles.add(discord.sklld_peasant_role);
- }
-
- await interaction.editReply({
- embeds: [
- new EmbedBuilder()
- .setColor(POSITIVE_COLOR)
- .setTitle(`${interaction.user.username} is verified`)
- .setDescription(
- `The user is now verified as ${markdownEscape(
- accountInfo[id].nickname,
- )}${markdownEscape(clanTag)}`,
- ),
- ],
- });
- } catch (error) {
- await interaction.editReply({
- embeds: [
- new EmbedBuilder()
- .setColor(NEGATIVE_COLOR)
- .setTitle(`${interaction.user.username} failed to verify`)
- .setDescription(
- "I can't change your nickname because you have higher permissions than me. Try setting your nickname to your Blitz username manually.",
- ),
- ],
- });
+ if (interaction.guildId === discord.sklld_guild_id) {
+ if (!interaction.guild?.members.me?.permissions.has('ManageRoles')) {
+ return negativeEmbed(
+ `${markdownEscape(interaction.user.username)} failed to verify`,
+ "I don't have the permission to change your manage roles.",
+ );
}
+
+ await member.roles.remove(discord.sklld_verify_role);
+ await member.roles.add(discord.sklld_peasant_role);
}
+
+ return positiveEmbed(
+ `${interaction.user.username} is verified`,
+ `The user is now verified as ${markdownEscape(
+ accountInfo[id].nickname,
+ )}${markdownEscape(clanTag)}`,
+ );
+ } catch (error) {
+ return negativeEmbed(
+ `${interaction.user.username} failed to verify`,
+ "I can't change your nickname because you have higher permissions than me. Try setting your nickname to your Blitz username manually.",
+ );
}
},
diff --git a/src/constants/colors.ts b/src/constants/colors.ts
index ba9d56931..bb2c7f51d 100644
--- a/src/constants/colors.ts
+++ b/src/constants/colors.ts
@@ -1,3 +1,4 @@
-export const ACCENT_COLOR = '#ecedee'; // theme.colors.textHighContrast
+export const ACCENT_COLOR = '#ecedee';
export const POSITIVE_COLOR = '#82ff29';
export const NEGATIVE_COLOR = '#ff4747';
+export const WARNING_COLOR = '#ffab00';
diff --git a/src/core/interaction/warningEmbed.ts b/src/core/interaction/warningEmbed.ts
new file mode 100644
index 000000000..b8006d1c2
--- /dev/null
+++ b/src/core/interaction/warningEmbed.ts
@@ -0,0 +1,9 @@
+import { EmbedBuilder } from 'discord.js';
+import { WARNING_COLOR } from '../../constants/colors.js';
+
+export default function warningEmbed(title: string, description: string) {
+ return new EmbedBuilder()
+ .setColor(WARNING_COLOR)
+ .setTitle(title)
+ .setDescription(description);
+}
diff --git a/src/core/process/psa.ts b/src/core/process/psa.ts
new file mode 100644
index 000000000..a08e90812
--- /dev/null
+++ b/src/core/process/psa.ts
@@ -0,0 +1,20 @@
+export interface PSAData {
+ title: string;
+ description: string;
+}
+
+export interface PSA {
+ data?: PSAData;
+}
+
+export const psa: PSA = {};
+
+console.log('Caching PSA...');
+fetch(
+ 'https://gist.githubusercontent.com/tresabhi/ed4b136f08856e615212e573aabe1968/raw',
+)
+ .then((response) => response.json())
+ .then((data) => {
+ psa.data = data as PSAData;
+ console.log('PSA cached');
+ });
diff --git a/src/events/interactionCreate.ts b/src/events/interactionCreate.ts
index 37ec47d85..dc71678e3 100644
--- a/src/events/interactionCreate.ts
+++ b/src/events/interactionCreate.ts
@@ -5,7 +5,9 @@ import {
ButtonStyle,
CacheType,
ChatInputCommandInteraction,
+ EmbedBuilder,
Interaction,
+ InteractionEditReplyOptions,
REST,
RESTPostAPIChatInputApplicationCommandsJSONBody,
Routes,
@@ -27,39 +29,49 @@ import tankstats from '../commands/tankstats.js';
import today from '../commands/today.js';
import verify from '../commands/verify.js';
import negativeEmbed from '../core/interaction/negativeEmbed.js';
+import warningEmbed from '../core/interaction/warningEmbed.js';
import { DISCORD_TOKEN } from '../core/process/args.js';
import getClientId from '../core/process/getClientId.js';
import isDev from '../core/process/isDev.js';
+import { psa } from '../core/process/psa.js';
+import render from '../core/ui/render.js';
import { handleError } from './error.js';
+export type CommandReturnable = EmbedBuilder | ButtonBuilder | JSX.Element;
+export type RichCommandReturnable = CommandReturnable | CommandReturnable[];
+
export interface CommandRegistry {
inDevelopment: boolean;
inProduction: boolean;
inPublic: boolean;
command: Omit;
- execute: (interaction: ChatInputCommandInteraction) => void;
+ execute: (
+ interaction: ChatInputCommandInteraction,
+ ) => RichCommandReturnable | Promise;
autocomplete?: (interaction: AutocompleteInteraction) => void;
}
const rest = new REST().setToken(DISCORD_TOKEN);
-const commands = [
- debug,
- eligible,
- help,
- inactive,
- ownedtanks,
- playerachievements,
- playerinfo,
- searchclans,
- searchplayers,
- searchtanks,
- stats,
- tankstats,
- today,
- verify,
-].reduce>((accumulator, registry) => {
+const commands = (
+ [
+ debug,
+ eligible,
+ help,
+ inactive,
+ ownedtanks,
+ playerachievements,
+ playerinfo,
+ searchclans,
+ searchplayers,
+ searchtanks,
+ stats,
+ tankstats,
+ today,
+ verify,
+ ] as CommandRegistry[]
+).reduce>((accumulator, registry) => {
return { ...accumulator, [registry.command.name]: registry };
}, {});
@@ -138,7 +150,36 @@ export default async function interactionCreate(
await interaction.deferReply();
try {
- await command.execute(interaction);
+ const result = await command.execute(interaction);
+ const normalizedResult = Array.isArray(result) ? result : [result];
+ const reply: InteractionEditReplyOptions = {};
+
+ if (psa.data) {
+ normalizedResult.push(
+ warningEmbed(psa.data.title, psa.data.description),
+ );
+ }
+
+ await Promise.all(
+ normalizedResult.map(async (item) => {
+ if (item instanceof EmbedBuilder) {
+ if (!reply.embeds) reply.embeds = [];
+ reply.embeds.push(item);
+ } else if (item instanceof ButtonBuilder) {
+ if (!reply.components)
+ reply.components = [new ActionRowBuilder()];
+ (
+ reply.components[0] as ActionRowBuilder
+ ).addComponents(item);
+ } else {
+ const image = await render(item);
+ if (!reply.files) reply.files = [];
+ reply.files.push(image);
+ }
+ }),
+ );
+
+ await interaction.editReply(reply);
} catch (error) {
const actionRow = new ActionRowBuilder().addComponents(
new ButtonBuilder()