mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: link command basic functionaility
This commit is contained in:
1 parent
97d427a6a8
commit
19298b9ad6
19 files changed
+225
-166
No files matched your search
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { client } from './core/discord/client';
|
||||
import { registerProcesses } from './core/node/processes';
|
||||
import { secrets } from './core/node/secrets';
|
||||
import { registerErrorHandlers } from './events/error';
|
||||
|
||||
registerErrorHandlers();
|
||||
registerProcesses();
|
||||
client.login(secrets.DISCORD_TOKEN);
|
||||
@@ -0,0 +1,98 @@
|
||||
import { SlashCommandBuilder } from 'discord.js';
|
||||
import { Region } from '../constants/regions';
|
||||
import { linkBlitzAndDiscord } from '../core/cockroackdb/discordBlitz';
|
||||
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||
import autocompleteUsername from '../core/discord/autocompleteUsername';
|
||||
import embedNegative from '../core/discord/embedNegative';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
|
||||
const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/;
|
||||
|
||||
export const verifyCommand: CommandRegistry = {
|
||||
inProduction: true,
|
||||
inPublic: true,
|
||||
|
||||
command: new SlashCommandBuilder()
|
||||
.setName('link')
|
||||
.setDescription('Links your Blitz and Discord account')
|
||||
.addStringOption((option) => addUsernameChoices(option).setRequired(true)),
|
||||
|
||||
async handler(interaction) {
|
||||
// const blitzAccount = await resolvePlayerFromCommand(interaction);
|
||||
// const { id, region: server } = blitzAccount;
|
||||
// const accountInfo = await getWargamingResponse<AccountInfo>(
|
||||
// `https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
// );
|
||||
// const clanData = await getWargamingResponse<PlayerClanData>(
|
||||
// `https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
|
||||
// );
|
||||
// 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')) {
|
||||
// return embedNegative(
|
||||
// `${markdownEscape(interaction.user.username)} failed to verify`,
|
||||
// "I don't have the permission to change your nickname.",
|
||||
// );
|
||||
// }
|
||||
// if (!interaction.member || !member) {
|
||||
// return embedNegative(
|
||||
// `${interaction.user.username} is not in this server`,
|
||||
// "I couldn't find this person in this server. Did they leave?",
|
||||
// );
|
||||
// }
|
||||
|
||||
// try {
|
||||
// await member.setNickname(`${accountInfo[id].nickname}${clanTag}`);
|
||||
|
||||
// if (interaction.guildId === discord.sklld_guild_id) {
|
||||
// if (!interaction.guild?.members.me?.permissions.has('ManageRoles')) {
|
||||
// return embedNegative(
|
||||
// `${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 embedPositive(
|
||||
// `${interaction.user.username} is verified`,
|
||||
// `The user is now verified as ${markdownEscape(
|
||||
// accountInfo[id].nickname,
|
||||
// )}${markdownEscape(clanTag)}`,
|
||||
// );
|
||||
// } catch (error) {
|
||||
// return embedNegative(
|
||||
// `${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.",
|
||||
// );
|
||||
// }
|
||||
|
||||
const username = interaction.options.getString('username', true);
|
||||
|
||||
if (serverAndIdPattern.test(username)) {
|
||||
const [region, idString] = username.split('/');
|
||||
const id = parseInt(idString);
|
||||
const discordId = parseInt(interaction.user.id);
|
||||
|
||||
await linkBlitzAndDiscord(discordId, region as Region, id);
|
||||
|
||||
return 'success';
|
||||
} else {
|
||||
return [
|
||||
embedNegative(
|
||||
'Please select from the options',
|
||||
'Click on the user you desire after the search results show up (like the image below).',
|
||||
),
|
||||
'https://i.imgur.com/ncJyV1F.png',
|
||||
];
|
||||
}
|
||||
},
|
||||
|
||||
autocomplete: autocompleteUsername,
|
||||
};
|
||||
@@ -1,82 +0,0 @@
|
||||
import { SlashCommandBuilder } from 'discord.js';
|
||||
import markdownEscape from 'markdown-escape';
|
||||
import discord from '../../discord.json' assert { type: 'json' };
|
||||
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
|
||||
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||
import autocompleteUsername from '../core/discord/autocompleteUsername';
|
||||
import embedNegative from '../core/discord/embedNegative';
|
||||
import embedPositive from '../core/discord/embedPositive';
|
||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { AccountInfo } from '../types/accountInfo';
|
||||
import { PlayerClanData } from '../types/playerClanData';
|
||||
|
||||
export const verifyCommand: CommandRegistry = {
|
||||
inProduction: true,
|
||||
inPublic: true,
|
||||
|
||||
command: new SlashCommandBuilder()
|
||||
.setName('verify')
|
||||
.setDescription("Set's the user's username to their in-game name")
|
||||
.addStringOption((option) => addUsernameChoices(option).setRequired(true)),
|
||||
|
||||
async handler(interaction) {
|
||||
const blitzAccount = await resolvePlayerFromCommand(interaction);
|
||||
const { id, region: server } = blitzAccount;
|
||||
const accountInfo = await getWargamingResponse<AccountInfo>(
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
);
|
||||
const clanData = await getWargamingResponse<PlayerClanData>(
|
||||
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
|
||||
);
|
||||
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')) {
|
||||
return embedNegative(
|
||||
`${markdownEscape(interaction.user.username)} failed to verify`,
|
||||
"I don't have the permission to change your nickname.",
|
||||
);
|
||||
}
|
||||
if (!interaction.member || !member) {
|
||||
return embedNegative(
|
||||
`${interaction.user.username} is not in this server`,
|
||||
"I couldn't find this person in this server. Did they leave?",
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
await member.setNickname(`${accountInfo[id].nickname}${clanTag}`);
|
||||
|
||||
if (interaction.guildId === discord.sklld_guild_id) {
|
||||
if (!interaction.guild?.members.me?.permissions.has('ManageRoles')) {
|
||||
return embedNegative(
|
||||
`${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 embedPositive(
|
||||
`${interaction.user.username} is verified`,
|
||||
`The user is now verified as ${markdownEscape(
|
||||
accountInfo[id].nickname,
|
||||
)}${markdownEscape(clanTag)}`,
|
||||
);
|
||||
} catch (error) {
|
||||
return embedNegative(
|
||||
`${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.",
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
autocomplete: autocompleteUsername,
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { Region } from '../../constants/regions';
|
||||
|
||||
export const discordBlitzDB = new PrismaClient();
|
||||
|
||||
export function linkBlitzAndDiscord(
|
||||
discord: number,
|
||||
region: Region,
|
||||
blitz: number,
|
||||
) {
|
||||
return discordBlitzDB.discord_blitz.upsert({
|
||||
where: { discord },
|
||||
update: { region, blitz },
|
||||
create: { discord, region, blitz },
|
||||
});
|
||||
}
|
||||
|
||||
export function getBlitzFromDiscord(discord: number) {
|
||||
return discordBlitzDB.discord_blitz.findUnique({
|
||||
where: { discord },
|
||||
select: { region: true, blitz: true },
|
||||
}) as Promise<null | { region: Region; blitz: number }>;
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { Region } from '../../constants/regions';
|
||||
import listPlayers, {
|
||||
usernamePatternWithoutPosition,
|
||||
} from '../blitz/listPlayers';
|
||||
import { getBlitzFromDiscord } from '../cockroackdb/discordBlitz';
|
||||
import throwError from '../node/throwError';
|
||||
|
||||
export const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/;
|
||||
@@ -15,38 +13,14 @@ export interface ResolvedPlayer {
|
||||
export default async function resolvePlayerFromCommand(
|
||||
interaction: ChatInputCommandInteraction<CacheType>,
|
||||
) {
|
||||
const commandUsername = interaction.options.getString('username');
|
||||
const displayName = interaction.guild?.members.cache
|
||||
.get(interaction.user.id)
|
||||
?.displayName.match(usernamePatternWithoutPosition)?.[0];
|
||||
const username = commandUsername ?? displayName;
|
||||
const account = await getBlitzFromDiscord(parseInt(interaction.user.id));
|
||||
|
||||
if (username === undefined) {
|
||||
if (account) {
|
||||
return { region: account.region, id: account.blitz };
|
||||
} else {
|
||||
throw throwError(
|
||||
'Username could not be inferred',
|
||||
"You didn't provide me a username in the command nor was I able to infer your Blitz username from your Discord nickname. Try providing a username manually in the command or using the `/verify` command to set your Discord nickname to your Blitz username.",
|
||||
"You're not linked",
|
||||
'Use the `/link` command to link your Discord and Blitz accounts.',
|
||||
);
|
||||
}
|
||||
|
||||
if (serverAndIdPattern.test(username)) {
|
||||
const [server, accountId] = username.split('/');
|
||||
return {
|
||||
region: server as Region,
|
||||
id: Number(accountId),
|
||||
} satisfies ResolvedPlayer;
|
||||
} else {
|
||||
const accounts = await listPlayers(username);
|
||||
|
||||
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 \`${username}\`. Try providing a valid username manually or using the \`/verify\` command to set your Discord nickname to your Blitz username.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { discordBlitzDB } from '../cockroackdb/discordBlitz';
|
||||
import { client } from '../discord/client';
|
||||
|
||||
const EXIT_EVENTS = [
|
||||
'beforeExit',
|
||||
'exit',
|
||||
'SIGINT',
|
||||
'SIGTERM',
|
||||
'SIGUSR1',
|
||||
'SIGUSR2',
|
||||
];
|
||||
|
||||
async function cleanup() {
|
||||
console.log('Cleaning up...');
|
||||
|
||||
await Promise.all([discordBlitzDB.$disconnect(), client.destroy()]);
|
||||
|
||||
console.log('Gracefully exiting...');
|
||||
|
||||
EXIT_EVENTS.forEach((event) => process.off(event, cleanup));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
export function registerProcesses() {
|
||||
process.on('uncaughtException', console.error);
|
||||
client.on('error', console.error);
|
||||
|
||||
EXIT_EVENTS.forEach((event) => process.on(event, cleanup));
|
||||
}
|
||||
@@ -2,7 +2,11 @@ import { config } from 'dotenv';
|
||||
|
||||
config();
|
||||
|
||||
export const SECRET_KEYS = ['DISCORD_TOKEN', 'GH_TOKEN'] as const;
|
||||
export const SECRET_KEYS = [
|
||||
'DISCORD_TOKEN',
|
||||
'GH_TOKEN',
|
||||
'DISCORD_BLITZ_DB',
|
||||
] as const;
|
||||
|
||||
export type SECRET = typeof SECRET_KEYS extends readonly (infer T)[]
|
||||
? T
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import { EmbedBuilder, TextChannel } from 'discord.js';
|
||||
import discord from '../../discord.json' assert { type: 'json' };
|
||||
import { NEGATIVE_COLOR } from '../constants/colors';
|
||||
import { client } from '../core/discord/client';
|
||||
import isDev from '../core/node/isDev';
|
||||
|
||||
const PROCESS_ERROR_EVENTS = ['uncaughtException'];
|
||||
const CLIENT_ERROR_EVENTS = ['error'];
|
||||
|
||||
export function handleError(error: Error, command: string) {
|
||||
console.error(error);
|
||||
|
||||
(
|
||||
client.guilds.cache
|
||||
.get(discord.tres_guild_id)
|
||||
?.channels.cache.get(discord.tres_log_channel) as TextChannel
|
||||
).send({
|
||||
embeds: [
|
||||
new EmbedBuilder()
|
||||
.setTitle(`${client.user?.username} ran into an error`)
|
||||
.setColor(NEGATIVE_COLOR)
|
||||
.setDescription(
|
||||
`\`\`\`${[command, error.name, error.message, error.stack]
|
||||
.filter((item) => item !== undefined)
|
||||
.join('\n\n')}\`\`\``,
|
||||
),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
export function registerErrorHandlers() {
|
||||
if (!isDev()) {
|
||||
PROCESS_ERROR_EVENTS.forEach((name) =>
|
||||
process.on(name, (error) => handleError(error, 'process error')),
|
||||
);
|
||||
CLIENT_ERROR_EVENTS.forEach((name) =>
|
||||
client.on(name, (error) => handleError(error, 'client error')),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ import embedNegative from '../../../core/discord/embedNegative';
|
||||
import embedWarning from '../../../core/discord/embedWarning';
|
||||
import normalizeInteractionReturnable from '../../../core/discord/normalizeInteractionReturnable';
|
||||
import { psa } from '../../../core/discord/psa';
|
||||
import { handleError } from '../../error';
|
||||
|
||||
export default async function handleChatInputCommand(
|
||||
interaction: ChatInputCommandInteraction<CacheType>,
|
||||
@@ -55,6 +54,8 @@ export default async function handleChatInputCommand(
|
||||
.setStyle(ButtonStyle.Link),
|
||||
);
|
||||
|
||||
console.error(interaction.commandName, error);
|
||||
|
||||
await interaction.editReply({
|
||||
embeds: [
|
||||
embedNegative(
|
||||
@@ -64,7 +65,5 @@ export default async function handleChatInputCommand(
|
||||
],
|
||||
components: [actionRow],
|
||||
});
|
||||
|
||||
handleError(error as Error, interaction.commandName);
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ import { searchPlayersCommand } from '../../commands/searchPlayers';
|
||||
import { searchTanksCommand } from '../../commands/searchTanks';
|
||||
import { statsCommand } from '../../commands/stats';
|
||||
import { todayCommand } from '../../commands/today';
|
||||
import { verifyCommand } from '../../commands/verify';
|
||||
import { verifyCommand } from '../../commands/link';
|
||||
import getClientId from '../../core/node/getClientId';
|
||||
import isDev from '../../core/node/isDev';
|
||||
import { secrets } from '../../core/node/secrets';
|
||||
|
||||
Reference in new issue
Block a user