mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 20:19:49 +02:00
feat: core chunk 7
This commit is contained in:
1 parent
d726005c38
commit
01dd7ad9c6
39 files changed
+83
-87
No files matched your search
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
ComponentEmojiResolvable,
|
||||
} from 'discord.js';
|
||||
|
||||
export default function buttonLink(
|
||||
url: string,
|
||||
label: string,
|
||||
emoji?: ComponentEmojiResolvable,
|
||||
) {
|
||||
const button = new ButtonBuilder()
|
||||
.setStyle(ButtonStyle.Link)
|
||||
.setURL(url)
|
||||
.setLabel(label);
|
||||
|
||||
if (emoji) button.setEmoji(emoji);
|
||||
|
||||
return button;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
ComponentEmojiResolvable,
|
||||
} from 'discord.js';
|
||||
|
||||
export default function buttonPrimary(
|
||||
id: string,
|
||||
label: string,
|
||||
emoji?: ComponentEmojiResolvable,
|
||||
) {
|
||||
const button = new ButtonBuilder()
|
||||
.setCustomId(id)
|
||||
.setStyle(ButtonStyle.Primary)
|
||||
.setLabel(label);
|
||||
|
||||
if (emoji) button.setEmoji(emoji);
|
||||
|
||||
return button;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { ChatInputCommandInteraction } from 'discord.js';
|
||||
import buttonPrimary from './buttonPrimary';
|
||||
|
||||
export function buttonRefresh(
|
||||
interaction: ChatInputCommandInteraction,
|
||||
path: string,
|
||||
) {
|
||||
if (
|
||||
interaction.appPermissions?.has('AttachFiles') &&
|
||||
interaction.appPermissions?.has('ViewChannel')
|
||||
) {
|
||||
return buttonPrimary(path, 'Refresh');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -4,6 +4,9 @@ export interface Parameters {
|
||||
[key: string]: string | number | boolean | undefined | null;
|
||||
}
|
||||
|
||||
// TODO: automatically infer command options???
|
||||
// but how would that work with the region and id options?
|
||||
|
||||
export default function commandToURL(
|
||||
interaction: ChatInputCommandInteraction<CacheType>,
|
||||
parameters?: Parameters,
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
export type TableInputEntry = [string, string | number] | [];
|
||||
export type TableInput = TableInputEntry[];
|
||||
|
||||
export default function markdownTable(input: TableInput) {
|
||||
return input
|
||||
.filter((array) => array[1] !== undefined)
|
||||
.map((array) => (array.length === 0 ? '' : `**${array[0]}**: ${array[1]}`))
|
||||
.join('\n');
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import {
|
||||
ActionRowBuilder,
|
||||
AttachmentBuilder,
|
||||
ButtonBuilder,
|
||||
EmbedBuilder,
|
||||
InteractionEditReplyOptions,
|
||||
} from 'discord.js';
|
||||
import { InteractionReturnable } from '../../events/interactionCreate';
|
||||
import jsxToPng from '../blitzkrieg/jsxToPng';
|
||||
|
||||
export default async function normalizeInteractionReturnable(
|
||||
returnable: InteractionReturnable,
|
||||
) {
|
||||
const reply: InteractionEditReplyOptions = {};
|
||||
|
||||
const awaitedReturnable = await returnable;
|
||||
const normalizedReturnable = Array.isArray(awaitedReturnable)
|
||||
? awaitedReturnable
|
||||
: [awaitedReturnable];
|
||||
|
||||
await Promise.all(
|
||||
normalizedReturnable.map(async (item) => {
|
||||
if (typeof item === 'string') {
|
||||
reply.content = item;
|
||||
} else 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<ButtonBuilder>()];
|
||||
(reply.components[0] as ActionRowBuilder<ButtonBuilder>).addComponents(
|
||||
item,
|
||||
);
|
||||
} else if (item instanceof AttachmentBuilder) {
|
||||
if (!reply.files) reply.files = [];
|
||||
reply.files.push(item);
|
||||
} else if (item === null) {
|
||||
return;
|
||||
} else {
|
||||
const image = await jsxToPng(item);
|
||||
if (!reply.files) reply.files = [];
|
||||
reply.files.push(image);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
return reply;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
export interface PSAData {
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface PSA {
|
||||
data: PSAData | null;
|
||||
}
|
||||
|
||||
export const psa: PSA = { data: null };
|
||||
|
||||
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');
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
import { ChatInputCommandInteraction } from 'discord.js';
|
||||
import throwError from '../../LEGACY_core/node/throwError';
|
||||
import { Region } from '../../constants/regions';
|
||||
import searchClansAcrossRegions from '../blitz/searchClansAcrossRegions';
|
||||
import { serverAndIdPattern } from './resolvePlayerFromCommand';
|
||||
|
||||
export default async function resolveClanFromCommand(
|
||||
interaction: ChatInputCommandInteraction,
|
||||
) {
|
||||
const clan = interaction.options.getString('clan', true);
|
||||
|
||||
if (serverAndIdPattern.test(clan)) {
|
||||
const [server, accountId] = clan.split('/');
|
||||
return { region: server as Region, id: Number(accountId) };
|
||||
} else {
|
||||
const accounts = await searchClansAcrossRegions(clan);
|
||||
|
||||
if (accounts[0]) {
|
||||
return { region: accounts[0].region, id: accounts[0].clan_id };
|
||||
} else {
|
||||
throw throwError(
|
||||
'Could not find clan',
|
||||
`I couldn't find clan \`${clan}\`. Try selecting a username from the search result.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { ButtonInteraction } from 'discord.js';
|
||||
import { Region } from '../../constants/regions';
|
||||
import getPeriodNow from '../blitzkrieg/getPeriodNow';
|
||||
import getPeriodStart from '../blitzkrieg/getPeriodStart';
|
||||
import getTimeDaysAgo from '../blitzkrieg/getTimeDaysAgo';
|
||||
import { PeriodType } from './addPeriodSubCommands';
|
||||
import {
|
||||
ResolvedPeriod,
|
||||
getPeriodOptionName,
|
||||
} from './resolvePeriodFromCommand';
|
||||
|
||||
export default function resolvePeriodFromButton(
|
||||
region: Region,
|
||||
interaction: ButtonInteraction,
|
||||
) {
|
||||
let name: string;
|
||||
let start: number;
|
||||
let end: number;
|
||||
const url = new URL(`https://example.com/${interaction.customId}`);
|
||||
const path = url.pathname.split('/');
|
||||
const periodSubcommand = path.at(-1) as PeriodType;
|
||||
|
||||
if (periodSubcommand === 'custom') {
|
||||
const startRaw = parseInt(url!.searchParams.get('start')!);
|
||||
const endRaw = parseInt(url!.searchParams.get('end')!);
|
||||
const startDaysAgoMin = Math.min(startRaw, endRaw);
|
||||
const endDaysAgoMax = Math.max(startRaw, endRaw);
|
||||
|
||||
name = `${startDaysAgoMin} - ${endDaysAgoMax} days`;
|
||||
start = getTimeDaysAgo(region, endDaysAgoMax);
|
||||
end = getTimeDaysAgo(region, startDaysAgoMin);
|
||||
} else {
|
||||
name = getPeriodOptionName(periodSubcommand);
|
||||
start = getPeriodStart(region, periodSubcommand);
|
||||
end = getPeriodNow();
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
start,
|
||||
end,
|
||||
} satisfies ResolvedPeriod;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { Region } from '../../constants/regions';
|
||||
import getPeriodNow from '../blitzkrieg/getPeriodNow';
|
||||
import getPeriodStart from '../blitzkrieg/getPeriodStart';
|
||||
import getTimeDaysAgo from '../blitzkrieg/getTimeDaysAgo';
|
||||
import { PeriodSize, PeriodType } from './addPeriodSubCommands';
|
||||
|
||||
export function getPeriodOptionName(period: PeriodSize) {
|
||||
if (period === 'career') return 'Career';
|
||||
if (period === 'today') return 'Today';
|
||||
return `${period} days`;
|
||||
}
|
||||
|
||||
export interface ResolvedPeriod {
|
||||
name: string;
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
|
||||
export default function resolvePeriodFromCommand(
|
||||
region: Region,
|
||||
interaction: ChatInputCommandInteraction<CacheType>,
|
||||
) {
|
||||
let name: string;
|
||||
let start: number;
|
||||
let end: number;
|
||||
const periodSubcommand = interaction.options.getSubcommand() as PeriodType;
|
||||
|
||||
if (periodSubcommand === 'custom') {
|
||||
const startOption = interaction.options.getInteger('start', true);
|
||||
const endOption = interaction.options.getInteger('end', true);
|
||||
const startDaysAgoMin = Math.min(startOption, endOption);
|
||||
const endDaysAgoMax = Math.max(startOption, endOption);
|
||||
|
||||
name = `${startDaysAgoMin} - ${endDaysAgoMax} days`;
|
||||
start = getTimeDaysAgo(region, endDaysAgoMax);
|
||||
end = getTimeDaysAgo(region, startDaysAgoMin);
|
||||
} else {
|
||||
name = getPeriodOptionName(periodSubcommand);
|
||||
start = getPeriodStart(region, periodSubcommand);
|
||||
end = getPeriodNow();
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
start,
|
||||
end,
|
||||
} satisfies ResolvedPeriod;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { ButtonInteraction } from 'discord.js';
|
||||
import { Region } from '../../constants/regions';
|
||||
import { ResolvedPlayer } from './resolvePlayerFromCommand';
|
||||
|
||||
export default async function resolvePlayerFromButton(
|
||||
interaction: ButtonInteraction,
|
||||
) {
|
||||
const url = new URL(`https://exmaple.com/${interaction.customId}`);
|
||||
|
||||
return {
|
||||
id: parseInt(url.searchParams.get('id')!),
|
||||
region: url.searchParams.get('region') as Region,
|
||||
} satisfies ResolvedPlayer;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
||||
import markdownEscape from 'markdown-escape';
|
||||
import throwError from '../../LEGACY_core/node/throwError';
|
||||
import { Region } from '../../constants/regions';
|
||||
import searchPlayersAcrossRegions from '../blitz/searchPlayersAcrossRegions';
|
||||
import { getBlitzFromDiscord } from '../blitzkrieg/discordBlitz';
|
||||
|
||||
export const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/;
|
||||
|
||||
export interface ResolvedPlayer {
|
||||
region: Region;
|
||||
id: number;
|
||||
}
|
||||
|
||||
export default async function resolvePlayerFromCommand(
|
||||
interaction: ChatInputCommandInteraction<CacheType>,
|
||||
) {
|
||||
const commandUsername = interaction.options.getString('username');
|
||||
|
||||
if (commandUsername) {
|
||||
if (serverAndIdPattern.test(commandUsername)) {
|
||||
const [server, accountId] = commandUsername.split('/');
|
||||
|
||||
return {
|
||||
region: server as Region,
|
||||
id: Number(accountId),
|
||||
} satisfies ResolvedPlayer;
|
||||
} else {
|
||||
const accounts = await searchPlayersAcrossRegions(commandUsername);
|
||||
|
||||
if (accounts[0]) {
|
||||
return {
|
||||
region: accounts[0].region,
|
||||
id: accounts[0].account_id,
|
||||
} satisfies ResolvedPlayer;
|
||||
} else {
|
||||
throw throwError(
|
||||
'Could not find user',
|
||||
`I couldn't find user "${markdownEscape(
|
||||
commandUsername,
|
||||
)}". Try picking an user from the search result, typing in a valid username, or using the \`/link\` command.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const account = await getBlitzFromDiscord(parseInt(interaction.user.id));
|
||||
|
||||
if (account) {
|
||||
return { region: account.region, id: account.blitz };
|
||||
} else {
|
||||
throw throwError(
|
||||
"You're not linked",
|
||||
'Use the `/link` command to link your Discord and Blitz accounts.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user