feat: core chunk 6

This commit is contained in:
TrèsAbhi committed 2023-10-14 11:32:02 -05:00
1 parent 4f3e868367
commit d726005c38
40 files changed
+83 -80

No files matched your search

+1 -1
View File
@@ -1,5 +1,5 @@
import { PeriodSize } from '../../LEGACY_core/discord/addPeriodSubCommands';
import { Region } from '../../constants/regions';
import { PeriodSize } from '../discord/addPeriodSubCommands';
import getTimeDaysAgo from './getTimeDaysAgo';
export default function getPeriodStart(region: Region, period: PeriodSize) {
+10
View File
@@ -0,0 +1,10 @@
import { SlashCommandStringOption } from 'discord.js';
export default function addClanChoices(option: SlashCommandStringOption) {
return option
.setName('clan')
.setDescription('The clan name or tag you are checking')
.setAutocomplete(true)
.setMinLength(2)
.setRequired(true);
}
+76
View File
@@ -0,0 +1,76 @@
import {
SlashCommandBuilder,
SlashCommandSubcommandBuilder,
SlashCommandSubcommandGroupBuilder,
} from 'discord.js';
import { TREE_TYPE_NAMES, TreeTypeString } from '../../components/Tanks';
import { tankopediaInfo } from '../blitzstars/tankopedia';
import addPeriodSubCommands from './addPeriodSubCommands';
import addTankChoices from './addTankChoices';
import addTierChoices from './addTierChoices';
export default async function addFilterOptions<
OptionType extends SlashCommandBuilder | SlashCommandSubcommandGroupBuilder,
>(
option: OptionType,
extra: (
option: SlashCommandSubcommandBuilder,
) => SlashCommandSubcommandBuilder = (option) => option,
): Promise<OptionType> {
const awaitedTankopediaInfo = await tankopediaInfo;
addPeriodSubCommands(option, (option) => {
extra(option);
return option
.addStringOption((option) =>
option
.setName('nation')
.setDescription('Nation')
.addChoices(
...Object.entries(awaitedTankopediaInfo.vehicle_nations).map(
([type, name]) => ({ name, value: type }),
),
)
.setRequired(false),
)
.addStringOption((option) => addTierChoices(option).setRequired(false))
.addStringOption((option) =>
option
.setName('tank-type')
.setDescription('Tank type')
.addChoices(
...Object.entries(awaitedTankopediaInfo.vehicle_types).map(
([type, name]) => ({
name: name.replace(' Tank', ''),
value: type,
}),
),
)
.setRequired(false),
)
.addStringOption((option) =>
option
.setName('tree-type')
.setDescription('Tech tree type')
.addChoices(
{
name: TREE_TYPE_NAMES['tech-tree'],
value: 'tech-tree' satisfies TreeTypeString,
},
{
name: TREE_TYPE_NAMES.premium,
value: 'premium' satisfies TreeTypeString,
},
{
name: TREE_TYPE_NAMES.collector,
value: 'collector' satisfies TreeTypeString,
},
)
.setRequired(false),
)
.addStringOption((option) => addTankChoices(option).setRequired(false));
});
return option;
}
+51
View File
@@ -0,0 +1,51 @@
import {
SlashCommandBuilder,
SlashCommandSubcommandBuilder,
SlashCommandSubcommandGroupBuilder,
} from 'discord.js';
export type PeriodType = 'today' | '30' | '60' | '90' | 'career' | 'custom';
export type PeriodSize = 'today' | `${number}` | 'career';
export default function addPeriodSubCommands(
option: SlashCommandBuilder | SlashCommandSubcommandGroupBuilder,
extra: (
option: SlashCommandSubcommandBuilder,
) => SlashCommandSubcommandBuilder = (option) => option,
) {
option
.addSubcommand((option) =>
extra(option.setName('today').setDescription('Today')),
)
.addSubcommand((option) =>
extra(option.setName('30').setDescription('30 days')),
)
.addSubcommand((option) =>
extra(option.setName('60').setDescription('60 days')),
)
.addSubcommand((option) =>
extra(option.setName('90').setDescription('90 days')),
)
.addSubcommand((option) =>
extra(option.setName('career').setDescription('Career')),
)
.addSubcommand((option) =>
extra(
option
.setName('custom')
.setDescription('Custom range')
.addIntegerOption((option) =>
option
.setName('start')
.setDescription('Start in number of days ago from today')
.setRequired(true),
)
.addIntegerOption((option) =>
option
.setName('end')
.setDescription('End in number of days')
.setRequired(true),
),
),
);
}
+14
View File
@@ -0,0 +1,14 @@
import { SlashCommandStringOption } from 'discord.js';
import { REGION_NAMES } from '../../constants/regions';
export default function addRegionChoices(option: SlashCommandStringOption) {
return option
.setName('region')
.setDescription('The Blitz region you are in')
.addChoices(
{ name: REGION_NAMES.com, value: 'com' },
{ name: REGION_NAMES.eu, value: 'eu' },
{ name: REGION_NAMES.asia, value: 'asia' },
)
.setRequired(true);
}
+9
View File
@@ -0,0 +1,9 @@
import { SlashCommandStringOption } from 'discord.js';
export default function addTankChoices(option: SlashCommandStringOption) {
return option
.setName('tank')
.setDescription('The tank to get stats for')
.setAutocomplete(true)
.setRequired(true);
}
+16
View File
@@ -0,0 +1,16 @@
import { SlashCommandStringOption } from 'discord.js';
import { range } from 'lodash';
import numberToRomanNumeral from '../math/numberToRomanNumeral';
export default function addTierChoices(option: SlashCommandStringOption) {
return option
.setName('tier')
.setDescription('The tier you want to see')
.setChoices(
...range(10, 0).map((tier) => ({
name: `Tier ${tier} - ${numberToRomanNumeral(tier)}`,
value: `${tier}`,
})),
)
.setRequired(true);
}
+10
View File
@@ -0,0 +1,10 @@
import { SlashCommandStringOption } from 'discord.js';
export default function addUsernameChoices(option: SlashCommandStringOption) {
return option
.setName('username')
.setDescription(
'The username you use in Blitz (inferred from Discord nickname by default)',
)
.setAutocomplete(true);
}
+28
View File
@@ -0,0 +1,28 @@
import {
ApplicationCommandOptionChoiceData,
AutocompleteInteraction,
CacheType,
} from 'discord.js';
import { REGION_NAMES } from '../../constants/regions';
import searchClansAcrossRegions from '../blitz/searchClansAcrossRegions';
export default async function autocompleteClan(
interaction: AutocompleteInteraction<CacheType>,
) {
const focusedOption = interaction.options.getFocused(true);
if (focusedOption.name !== 'clan') return;
if (focusedOption.value.length < 2) return interaction.respond([]);
const players = await searchClansAcrossRegions(focusedOption.value);
await interaction.respond(
players
? players.map(
(player) =>
({
name: `${player.name} (${REGION_NAMES[player.region]})`,
value: `${player.region}/${player.clan_id}`,
}) satisfies ApplicationCommandOptionChoiceData<string>,
)
: [],
);
}
+31
View File
@@ -0,0 +1,31 @@
import {
ApplicationCommandOptionChoiceData,
AutocompleteInteraction,
CacheType,
} from 'discord.js';
import { go } from 'fuzzysort';
import { TANK_NAMES_DIACRITICS } from '../blitzstars/tankopedia';
export default async function autocompleteTanks(
interaction: AutocompleteInteraction<CacheType>,
) {
const focusedOption = interaction.options.getFocused(true);
if (focusedOption.name !== 'tank') return;
await interaction.respond(
focusedOption.value
? await Promise.all(
go(focusedOption.value, await TANK_NAMES_DIACRITICS, {
keys: ['combined'],
limit: 10,
}).map(
async (item) =>
({
name: item.obj.original,
value: `${item.obj.id}`,
}) satisfies ApplicationCommandOptionChoiceData<string>,
),
)
: [],
);
}
+29
View File
@@ -0,0 +1,29 @@
import {
ApplicationCommandOptionChoiceData,
AutocompleteInteraction,
CacheType,
} from 'discord.js';
import { REGION_NAMES } from '../../constants/regions';
import searchPlayersAcrossRegions from '../blitz/searchPlayersAcrossRegions';
export default async function autocompleteUsername(
interaction: AutocompleteInteraction<CacheType>,
) {
const focusedOption = interaction.options.getFocused(true);
if (focusedOption.name !== 'username') return;
const players = await searchPlayersAcrossRegions(focusedOption.value);
try {
await interaction.respond(
players
? players.map(
(player) =>
({
name: `${player.nickname} (${REGION_NAMES[player.region]})`,
value: `${player.region}/${player.account_id}`,
}) satisfies ApplicationCommandOptionChoiceData<string>,
)
: [],
);
} catch (error) {}
}
+11
View File
@@ -0,0 +1,11 @@
import { Client, GatewayIntentBits } from 'discord.js';
import guildMemberAdd from '../../events/guildMemberAdd';
import interactionCreate from '../../events/interactionCreate';
import ready from '../../events/ready';
export const client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers],
})
.on('ready', ready)
.on('guildMemberAdd', guildMemberAdd)
.on('interactionCreate', interactionCreate);
+24
View File
@@ -0,0 +1,24 @@
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
export interface Parameters {
[key: string]: string | number | boolean | undefined | null;
}
export default function commandToURL(
interaction: ChatInputCommandInteraction<CacheType>,
parameters?: Parameters,
) {
const pathname = [
interaction.commandName,
interaction.options.getSubcommandGroup(false),
interaction.options.getSubcommand(false),
]
.filter(Boolean)
.join('/');
const params = (parameters ? Object.entries(parameters) : [])
.filter(([, value]) => value !== undefined && value !== null)
.map(([key, value]) => `${key}=${value}`)
.join('&');
return `${pathname}${params.length > 0 ? `?${params}` : ''}`;
}
+12
View File
@@ -0,0 +1,12 @@
import { EmbedBuilder } from 'discord.js';
import { ACCENT_COLOR } from '../../constants/colors';
export default function embedInfo(title: string, description: string) {
const embed = new EmbedBuilder()
.setColor(ACCENT_COLOR)
.setDescription(description);
if (title) embed.setTitle(title);
return embed;
}
+9
View File
@@ -0,0 +1,9 @@
import { EmbedBuilder } from 'discord.js';
import { NEGATIVE_COLOR } from '../../constants/colors';
export default function embedNegative(title: string, description: string) {
return new EmbedBuilder()
.setColor(NEGATIVE_COLOR)
.setTitle(title)
.setDescription(description);
}
+9
View File
@@ -0,0 +1,9 @@
import { EmbedBuilder } from 'discord.js';
import { POSITIVE_COLOR } from '../../constants/colors';
export default function embedPositive(title: string, description: string) {
return new EmbedBuilder()
.setColor(POSITIVE_COLOR)
.setTitle(title)
.setDescription(description);
}
+9
View File
@@ -0,0 +1,9 @@
import { EmbedBuilder } from 'discord.js';
import { WARNING_COLOR } from '../../constants/colors';
export default function embedWarning(title: string, description: string) {
return new EmbedBuilder()
.setColor(WARNING_COLOR)
.setTitle(title)
.setDescription(description);
}
+14
View File
@@ -0,0 +1,14 @@
import { ChatInputCommandInteraction } from 'discord.js';
import { PeriodType } from './addPeriodSubCommands';
export function getCustomPeriodParams(
interaction: ChatInputCommandInteraction,
) {
if ((interaction.options.getSubcommand() as PeriodType) === 'custom')
return {
start: interaction.options.getInteger('start', true),
end: interaction.options.getInteger('end', true),
};
return undefined;
}
+17
View File
@@ -0,0 +1,17 @@
import { ButtonInteraction } from 'discord.js';
import { TreeTypeString } from '../../components/Tanks';
import { StatFilters } from '../statistics/filterStats';
export function getFiltersFromButton(interaction: ButtonInteraction) {
const url = new URL(`https://example.com/${interaction.customId}`);
return {
nation: url.searchParams.get('nation') ?? undefined,
tank: parseInt(url.searchParams.get('tank') ?? '0') || undefined,
tankType: url.searchParams.get('tank-type') ?? undefined,
treeType:
(url.searchParams.get('tree-type') as TreeTypeString | undefined) ??
undefined,
tier: parseInt(url.searchParams.get('tier') ?? '0') || undefined,
} satisfies StatFilters;
}
+21
View File
@@ -0,0 +1,21 @@
import { ChatInputCommandInteraction } from 'discord.js';
import { TreeTypeString } from '../../components/Tanks';
import resolveTankId from '../blitz/resolveTankId';
import { StatFilters } from '../statistics/filterStats';
export async function getFiltersFromCommand(
interaction: ChatInputCommandInteraction,
) {
const tankRaw = interaction.options.getString('tank');
return {
nation: interaction.options.getString('nation') ?? undefined,
tier: parseInt(interaction.options.getString('tier') ?? '0') || undefined,
tankType: interaction.options.getString('tank-type') ?? undefined,
treeType:
(interaction.options.getString('tree-type') as
| TreeTypeString
| undefined) ?? undefined,
tank: tankRaw === null ? undefined : await resolveTankId(tankRaw),
} satisfies StatFilters;
}