diff --git a/src/commands/ownedTanks.tsx b/src/commands/ownedTanks.tsx index 9873f01c1..29f081c67 100644 --- a/src/commands/ownedTanks.tsx +++ b/src/commands/ownedTanks.tsx @@ -1,14 +1,13 @@ -import CommandWrapper from '../components/CommandWrapper'; -import NoData from '../components/NoData'; -import * as Tanks from '../components/Tanks'; -import TitleBar from '../components/TitleBar'; +import { chunk } from 'lodash'; +import markdownEscape from 'markdown-escape'; +import { TANK_CLASSES } from '../components/Tanks/components/Item/constants'; import { getAccountInfo } from '../core/blitz/getAccountInfo'; -import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo'; import getTankStats from '../core/blitz/getTankStats'; -import { emblemIdToURL } from '../core/blitzkrieg/emblemIdToURL'; import { + NATIONS, TankDefinition, Tier, + flags, tankDefinitions, } from '../core/blitzkrieg/tankDefinitions'; import { TIER_ROMAN_NUMERALS } from '../core/blitzkrieg/tankDefinitions/constants'; @@ -20,6 +19,8 @@ import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; import { translator } from '../core/localization/translator'; import { CommandRegistry } from '../events/interactionCreate'; +const TANKS_PER_MESSAGE = 64; + export const ownedTanksCommand = new Promise((resolve) => { resolve({ inProduction: true, @@ -43,7 +44,6 @@ export const ownedTanksCommand = new Promise((resolve) => { })), ) ).filter((tank) => tank.tankDefinitions?.tier === tier); - const clanAccountInfo = await getClanAccountInfo(region, id, ['clan']); const groupedTanks: Record = {}; const nations: string[] = []; @@ -56,71 +56,28 @@ export const ownedTanksCommand = new Promise((resolve) => { } }); - nations.sort(); + const awaitedTankDefinitions = await tankDefinitions; + const awaitedNations = await NATIONS; + const lines = tankStats + .map(({ tank_id }) => awaitedTankDefinitions[tank_id]) + .filter((tank) => tank.tier === tier) + .sort( + (a, b) => + TANK_CLASSES.indexOf(a.class) - TANK_CLASSES.indexOf(b.class), + ) + .sort( + (a, b) => + awaitedNations.indexOf(a.nation) - awaitedNations.indexOf(b.nation), + ) + .map((tank) => `${flags[tank.nation]} ${markdownEscape(tank.name)}`); - return ( - - + index === 0 + ? `${translate('bot.commands.owned_tanks.body.title', [ + markdownEscape(accountInfo.nickname), TIER_ROMAN_NUMERALS[tier as Tier], - ])} - /> - - {filteredTanks.length === 0 && ( - - )} - - {filteredTanks.length > 0 && - (await Promise.all( - nations.map(async (nation) => { - const tanks = groupedTanks[nation]; - const leftColumnSize = Math.ceil(tanks.length / 2); - const leftColumn = tanks.slice(0, leftColumnSize); - const rightColumn = tanks.slice(leftColumnSize); - - return ( - - - {translate(`common.nations.${nation}`)} - - - - - {await Promise.all( - leftColumn.map(async (tank) => ( - - )), - )} - - - {await Promise.all( - rightColumn.map(async (tank) => ( - - )), - )} - - - - ); - }), - ))} - + ])}${lines.join('\n')}` + : lines.join('\n'), ); }, diff --git a/src/commands/permissions.ts b/src/commands/permissions.ts index c2916f13f..ab5052cf1 100644 --- a/src/commands/permissions.ts +++ b/src/commands/permissions.ts @@ -27,6 +27,10 @@ export const permissionsCommand = new Promise((resolve) => { interaction.appPermissions?.has('AttachFiles'), t`bot.commands.permissions.body.attach_files`, ], + [ + interaction.appPermissions?.has('UseExternalEmojis'), + t`bot.commands.permissions.body.external_emojis`, + ], ]; const body = permissions .map(([hasPermission, description]) => { diff --git a/src/core/blitzkrieg/tankDefinitions/index.ts b/src/core/blitzkrieg/tankDefinitions/index.ts index 8d3afa5b8..63e6cac4b 100644 --- a/src/core/blitzkrieg/tankDefinitions/index.ts +++ b/src/core/blitzkrieg/tankDefinitions/index.ts @@ -185,3 +185,15 @@ export const NATIONS = tanksDefinitionsArray.then((tanks) => { return nations; }); + +export const flags: Record = { + ussr: '<:ussr:1218421042033197197>', + germany: 'πŸ‡©πŸ‡ͺ', + usa: 'πŸ‡ΊπŸ‡Έ', + china: 'πŸ‡¨πŸ‡³', + uk: 'πŸ‡¬πŸ‡§', + france: 'πŸ‡«πŸ‡·', + japan: 'πŸ‡―πŸ‡΅', + european: 'πŸ‡ͺπŸ‡Ί', + other: '<:other:1218421572243558482>', +}; diff --git a/src/core/discord/normalizeInteractionReturnable.ts b/src/core/discord/normalizeInteractionReturnable.ts index 5642f5564..c555bd67d 100644 --- a/src/core/discord/normalizeInteractionReturnable.ts +++ b/src/core/discord/normalizeInteractionReturnable.ts @@ -4,6 +4,7 @@ import { ButtonBuilder, EmbedBuilder, InteractionEditReplyOptions, + InteractionReplyOptions, } from 'discord.js'; import { InteractionReturnable } from '../../events/interactionCreate'; import jsxToPngThreaded from '../blitzkrieg/jsxToPngThreaded'; @@ -12,7 +13,7 @@ export default async function normalizeInteractionReturnable( returnable: InteractionReturnable, ) { const images: [number, Buffer][] = []; - const reply: InteractionEditReplyOptions = {}; + const reply: InteractionEditReplyOptions & InteractionReplyOptions = {}; const awaitedReturnable = await returnable; const normalizedReturnable = Array.isArray(awaitedReturnable) diff --git a/src/events/interactionCreate/handlers/chatInputCommand.ts b/src/events/interactionCreate/handlers/chatInputCommand.ts index a5842327b..9d796176b 100644 --- a/src/events/interactionCreate/handlers/chatInputCommand.ts +++ b/src/events/interactionCreate/handlers/chatInputCommand.ts @@ -7,7 +7,7 @@ import { ChatInputCommandInteraction, InteractionReplyOptions, } from 'discord.js'; -import { commands } from '..'; +import { InteractionRawReturnable, commands } from '..'; import buttonLink from '../../../core/discord/buttonLink'; import embedWarning from '../../../core/discord/embedWarning'; import normalizeInteractionReturnable from '../../../core/discord/normalizeInteractionReturnable'; @@ -28,70 +28,91 @@ export default async function handleChatInputCommand( if (registry.handlesInteraction) return; - const reply = await normalizeInteractionReturnable(returnable!); + const chunks: InteractionRawReturnable[][] = [[]]; - if ( - psa.data && - !psa.data.secret && - (psa.data.commands === undefined || - psa.data.commands.includes(interaction.commandName)) - ) { - if (psa.data.type === 'embed') { - if (!reply.embeds) reply.embeds = []; - reply.embeds.push(embedWarning(psa.data.title, psa.data.description)); - } else { - if (!reply.files) reply.files = []; - reply.files.push(new AttachmentBuilder(psa.data.image)); - reply.content = reply.content ?? psa.data.title; - } + (Array.isArray(returnable) ? returnable : [returnable]).forEach( + (object) => { + if (typeof object === 'string') { + chunks.push([object]); + } else chunks.at(-1)?.push(object!); + }, + ); - if (psa.data.links) { - if (!reply.components?.[0]) { - reply.components = [new ActionRowBuilder()]; + let index = 0; + for (const chunk of chunks.filter((chunk) => chunk.length > 0)) { + const reply = await normalizeInteractionReturnable(chunk); + + if ( + psa.data && + !psa.data.secret && + (psa.data.commands === undefined || + psa.data.commands.includes(interaction.commandName)) + ) { + if (psa.data.type === 'embed') { + if (!reply.embeds) reply.embeds = []; + reply.embeds.push(embedWarning(psa.data.title, psa.data.description)); + } else { + if (!reply.files) reply.files = []; + reply.files.push(new AttachmentBuilder(psa.data.image)); + reply.content = reply.content ?? psa.data.title; } - (reply.components[0] as ActionRowBuilder).addComponents( - psa.data.links.map(({ title, url }) => buttonLink(url, title)), - ); + if (psa.data.links) { + if (!reply.components?.[0]) { + reply.components = [new ActionRowBuilder()]; + } + + ( + reply.components[0] as ActionRowBuilder + ).addComponents( + psa.data.links.map(({ title, url }) => buttonLink(url, title)), + ); + } } - } - await interaction.editReply(reply); - - if ( - psa.data && - psa.data.secret && - (psa.data.commands === undefined || - psa.data.commands.includes(interaction.commandName)) - ) { - const followUp: InteractionReplyOptions = { - ephemeral: true, - }; - - if (psa.data.type === 'embed') { - if (!followUp.embeds) followUp.embeds = []; - followUp.embeds.push( - embedWarning(psa.data.title, psa.data.description), - ); + if (index === 0) { + await interaction.editReply(reply); } else { - if (!followUp.files) followUp.files = []; - followUp.files.push(new AttachmentBuilder(psa.data.image)); - followUp.content = followUp.content ?? psa.data.title; + await interaction.followUp(reply); } - if (psa.data.links) { - if (!followUp.components?.[0]) { - followUp.components = [new ActionRowBuilder()]; + if ( + psa.data && + psa.data.secret && + (psa.data.commands === undefined || + psa.data.commands.includes(interaction.commandName)) + ) { + const followUp: InteractionReplyOptions = { + ephemeral: true, + }; + + if (psa.data.type === 'embed') { + if (!followUp.embeds) followUp.embeds = []; + followUp.embeds.push( + embedWarning(psa.data.title, psa.data.description), + ); + } else { + if (!followUp.files) followUp.files = []; + followUp.files.push(new AttachmentBuilder(psa.data.image)); + followUp.content = followUp.content ?? psa.data.title; } - ( - followUp.components[0] as ActionRowBuilder - ).addComponents( - psa.data.links.map(({ title, url }) => buttonLink(url, title)), - ); + if (psa.data.links) { + if (!followUp.components?.[0]) { + followUp.components = [new ActionRowBuilder()]; + } + + ( + followUp.components[0] as ActionRowBuilder + ).addComponents( + psa.data.links.map(({ title, url }) => buttonLink(url, title)), + ); + } + + interaction.followUp(followUp); } - interaction.followUp(followUp); + index++; } } catch (error) { const { t } = translator(interaction.locale); diff --git a/src/lang/en-US.json b/src/lang/en-US.json index 9fbb91c0d..ed9751a7a 100644 --- a/src/lang/en-US.json +++ b/src/lang/en-US.json @@ -486,7 +486,7 @@ "description": "Shows all own tanks in a tier", "body": { - "subtitle": "Tier %s tanks" + "title": "# %s's owned tier %s tanks\n\n" } }, @@ -496,9 +496,10 @@ "body": { "title": "# Permissions\n\n%s", - "view_channels": "View channels: needed for the refresh button to work", - "read_message_history": "Read message history: needed for the refresh button to find the message to refresh", - "attach_files": "Attach files: needed for image-based commands to work" + "view_channels": "View channels: needed to find the message the refresh button was used on", + "read_message_history": "Read message history: needed to find the message to refresh", + "attach_files": "Attach files: needed to add images to image-based commands", + "external_emojis": "External emojis: needed to fill add national flags and tank class emojis" } },