Added errorh andling, organized commands
This commit is contained in:
@ -1,2 +1,4 @@
|
|||||||
DISCORD_TOKEN=
|
DISCORD_TOKEN=
|
||||||
DISCORD_CLIENT_ID=
|
DISCORD_CLIENT_ID=
|
||||||
|
DATA_FILE=./key_data.json
|
||||||
|
ERROR_FILE=./error_log.json
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -138,3 +138,4 @@ dist
|
|||||||
|
|
||||||
# Application-specific files
|
# Application-specific files
|
||||||
key_data.json
|
key_data.json
|
||||||
|
error_log.json
|
||||||
214
index.js
214
index.js
@ -11,10 +11,11 @@ import {
|
|||||||
EmbedBuilder
|
EmbedBuilder
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { NEWS_CHANNEL_MESSAGE, INELLIGIBLE_MESSAGE, KEY_DM_MESSAGE } from './messages.js';
|
import { NEWS_CHANNEL_MESSAGE, INELLIGIBLE_MESSAGE, formatNoKeysMessage, formatKeyMessage, formatFailedDMMessage } from './messages.js';
|
||||||
|
|
||||||
const TOKEN = process.env.DISCORD_TOKEN;
|
const TOKEN = process.env.DISCORD_TOKEN;
|
||||||
const DATA_FILE = './key_data.json';
|
const DATA_FILE = process.env.DATA_FILE;
|
||||||
|
const ERROR_FILE = process.env.ERROR_FILE;
|
||||||
|
|
||||||
function loadData() {
|
function loadData() {
|
||||||
if (!fs.existsSync(DATA_FILE)) {
|
if (!fs.existsSync(DATA_FILE)) {
|
||||||
@ -28,15 +29,55 @@ function saveData(data) {
|
|||||||
fs.writeFileSync(DATA_FILE, JSON.stringify(data, null, 2));
|
fs.writeFileSync(DATA_FILE, JSON.stringify(data, null, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadErrorLog() {
|
||||||
|
if (!fs.existsSync(ERROR_FILE)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return JSON.parse(fs.readFileSync(ERROR_FILE, 'utf8'));
|
||||||
|
}
|
||||||
|
function saveErrorLog(errors) {
|
||||||
|
fs.writeFileSync(ERROR_FILE, JSON.stringify(errors, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFailedLoggingChannel(tag, id, key, err) {
|
||||||
|
errors.push({
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
message: `Unable to send error message in logging channel when attempting to DM to user ${tag} (${id}) with key ${key}. Please see error log for details.`,
|
||||||
|
userId: interaction.user.id,
|
||||||
|
error: JSON.stringify(err, null, 2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
let data = loadData();
|
let data = loadData();
|
||||||
|
let errors = loadErrorLog();
|
||||||
|
|
||||||
const client = new Client({
|
const client = new Client({
|
||||||
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers, GatewayIntentBits.DirectMessages],
|
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers, GatewayIntentBits.DirectMessages],
|
||||||
partials: [Partials.Channel]
|
partials: [Partials.Channel]
|
||||||
});
|
});
|
||||||
|
|
||||||
client.once(Events.ClientReady, () => {
|
async function deleteLatestBotDMs(userId) {
|
||||||
|
try {
|
||||||
|
const user = await client.users.fetch(userId);
|
||||||
|
const dm = await user.createDM();
|
||||||
|
// Fetch up to 100 messages (Discord API limit per request)
|
||||||
|
const messages = await dm.messages.fetch({ limit: 100 });
|
||||||
|
let deleted = 0;
|
||||||
|
for (const msg of messages.values()) {
|
||||||
|
if (msg.author.id === client.user.id) {
|
||||||
|
await msg.delete();
|
||||||
|
deleted++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(`Deleted ${deleted} DMs sent by the bot.`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error deleting DMs:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client.once(Events.ClientReady, async () => {
|
||||||
console.log(`Logged in as ${client.user.tag}`);
|
console.log(`Logged in as ${client.user.tag}`);
|
||||||
|
await deleteLatestBotDMs('265334763324178433');
|
||||||
});
|
});
|
||||||
|
|
||||||
client.on(Events.InteractionCreate, async interaction => {
|
client.on(Events.InteractionCreate, async interaction => {
|
||||||
@ -74,32 +115,34 @@ client.on(Events.InteractionCreate, async interaction => {
|
|||||||
await interaction.reply({ content: 'Registration message sent!', ephemeral: true });
|
await interaction.reply({ content: 'Registration message sent!', ephemeral: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slash command: /load
|
// Slash command: /key
|
||||||
if (interaction.isChatInputCommand() && interaction.commandName === 'load') {
|
if (interaction.isChatInputCommand() && interaction.commandName === 'keys') {
|
||||||
if (!interaction.member.permissions.has(PermissionsBitField.Flags.Administrator)) {
|
if (!interaction.member.permissions.has(PermissionsBitField.Flags.Administrator)) {
|
||||||
return interaction.reply({ content: 'You must be an administrator.', ephemeral: true });
|
return interaction.reply({ content: 'You must be an administrator.', ephemeral: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sub = interaction.options.getSubcommand();
|
||||||
|
|
||||||
|
// /key load
|
||||||
|
if (sub === 'load') {
|
||||||
const file = interaction.options.getAttachment('file');
|
const file = interaction.options.getAttachment('file');
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return interaction.reply({ content: 'You must attach a file.', ephemeral: true });
|
return interaction.reply({ content: 'You must attach a file.', ephemeral: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Download the file
|
|
||||||
const response = await fetch(file.url);
|
const response = await fetch(file.url);
|
||||||
const text = await response.text();
|
const text = await response.text();
|
||||||
const keys = text.split('\n').map(line => line.trim()).filter(line => line.length > 0);
|
const keys = text.split('\n').map(line => line.trim()).filter(line => line.length > 0);
|
||||||
|
|
||||||
const yesButton = new ButtonBuilder()
|
const yesButton = new ButtonBuilder()
|
||||||
.setCustomId('load_yes')
|
.setCustomId('key_load_yes')
|
||||||
.setLabel('Yes')
|
.setLabel('Yes')
|
||||||
.setStyle(ButtonStyle.Success);
|
.setStyle(ButtonStyle.Success);
|
||||||
const noButton = new ButtonBuilder()
|
const noButton = new ButtonBuilder()
|
||||||
.setCustomId('load_no')
|
.setCustomId('key_load_no')
|
||||||
.setLabel('No')
|
.setLabel('No')
|
||||||
.setStyle(ButtonStyle.Danger);
|
.setStyle(ButtonStyle.Danger);
|
||||||
const row = new ActionRowBuilder().addComponents(yesButton, noButton);
|
|
||||||
|
|
||||||
// Store keys temporarily in memory for this user
|
const row = new ActionRowBuilder().addComponents(yesButton, noButton);
|
||||||
client.tempKeyLoads = client.tempKeyLoads || {};
|
client.tempKeyLoads = client.tempKeyLoads || {};
|
||||||
client.tempKeyLoads[interaction.user.id] = keys;
|
client.tempKeyLoads[interaction.user.id] = keys;
|
||||||
|
|
||||||
@ -108,24 +151,64 @@ client.on(Events.InteractionCreate, async interaction => {
|
|||||||
components: [row],
|
components: [row],
|
||||||
ephemeral: true
|
ephemeral: true
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle load confirmation buttons
|
// /key get
|
||||||
if (interaction.isButton() && (interaction.customId === 'load_yes' || interaction.customId === 'load_no')) {
|
if (sub === 'get') {
|
||||||
|
const user = interaction.options.getUser('user');
|
||||||
|
if (!user) {
|
||||||
|
return interaction.reply({ content: 'Invalid user.', ephemeral: true });
|
||||||
|
}
|
||||||
|
data = loadData();
|
||||||
|
const found = data.users.find(u => u.id === user.id);
|
||||||
|
if (found && found.key) {
|
||||||
|
return interaction.reply({ content: `User <@${user.id}> has registered key: \`${found.key}\``, ephemeral: true });
|
||||||
|
} else {
|
||||||
|
return interaction.reply({ content: `User <@${user.id}> does not have a registered key.`, ephemeral: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// /key check
|
||||||
|
if (sub === 'check') {
|
||||||
|
data = loadData();
|
||||||
|
const count = data.keys ? data.keys.length : 0;
|
||||||
|
return interaction.reply({ content: `There are ${count} keys left in the vault.`, ephemeral: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// /keys logging
|
||||||
|
if (sub === 'logging') {
|
||||||
|
if (!interaction.member.permissions.has(PermissionsBitField.Flags.Administrator)) {
|
||||||
|
return interaction.reply({ content: 'You must be an administrator.', ephemeral: true });
|
||||||
|
}
|
||||||
|
const channel = interaction.options.getChannel('channel');
|
||||||
|
if (!channel) {
|
||||||
|
return interaction.reply({ content: 'Invalid channel.', ephemeral: true });
|
||||||
|
}
|
||||||
|
data = loadData();
|
||||||
|
data.loggingChannel = channel.id;
|
||||||
|
saveData(data);
|
||||||
|
await channel.send(`Successfully set logging channel for all Swordcery Key Bot-related messages to <#${channel.id}>.`);
|
||||||
|
return interaction.reply({ content: `Logging channel set to <#${channel.id}>.`, ephemeral: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle key load confirmation buttons
|
||||||
|
if (interaction.isButton() && (interaction.customId === 'key_load_yes' || interaction.customId === 'key_load_no')) {
|
||||||
if (!interaction.member.permissions.has(PermissionsBitField.Flags.Administrator)) {
|
if (!interaction.member.permissions.has(PermissionsBitField.Flags.Administrator)) {
|
||||||
return interaction.reply({ content: 'You must be an administrator.', ephemeral: true });
|
return interaction.reply({ content: 'You must be an administrator.', ephemeral: true });
|
||||||
}
|
}
|
||||||
if (!client.tempKeyLoads || !client.tempKeyLoads[interaction.user.id]) {
|
if (!client.tempKeyLoads || !client.tempKeyLoads[interaction.user.id]) {
|
||||||
return interaction.reply({ content: 'No pending key load found.', ephemeral: true });
|
return interaction.reply({ content: 'No pending key load found.', ephemeral: true });
|
||||||
}
|
}
|
||||||
if (interaction.customId === 'load_no') {
|
if (interaction.customId === 'key_load_no') {
|
||||||
delete client.tempKeyLoads[interaction.user.id];
|
delete client.tempKeyLoads[interaction.user.id];
|
||||||
await interaction.update({ content: 'Key load cancelled.', components: [] });
|
await interaction.update({ content: 'Key load cancelled.', components: [] });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Yes pressed
|
// Yes pressed
|
||||||
const keys = client.tempKeyLoads[interaction.user.id];
|
const keys = client.tempKeyLoads[interaction.user.id];
|
||||||
data = loadData(); // Reload in case of concurrent changes
|
data = loadData();
|
||||||
data.keys = data.keys || [];
|
data.keys = data.keys || [];
|
||||||
data.keys.push(...keys);
|
data.keys.push(...keys);
|
||||||
saveData(data);
|
saveData(data);
|
||||||
@ -136,12 +219,12 @@ client.on(Events.InteractionCreate, async interaction => {
|
|||||||
// Button interaction for register
|
// Button interaction for register
|
||||||
if (interaction.isButton() && interaction.customId === 'register_button') {
|
if (interaction.isButton() && interaction.customId === 'register_button') {
|
||||||
if (interaction.message.id !== data.messageId) return;
|
if (interaction.message.id !== data.messageId) return;
|
||||||
|
const channel = await interaction.guild.channels.fetch(data.loggingChannel).catch(() => handleFailedLoggingChannel(interaction.user.tag, interaction.user.id, null, 'No logging channel found'));
|
||||||
|
|
||||||
// Already registered?
|
// Already registered?
|
||||||
let user = data.users.find(u => u.id === interaction.user.id);
|
let user = data.users.find(u => u.id === interaction.user.id);
|
||||||
if (user && user.key) {
|
if (user && user.key) {
|
||||||
const message = KEY_DM_MESSAGE.replace('{key}', `https://store.steampowered.com/account/registerkey?key=${user.key}`);
|
await interaction.user.send(formatKeyMessage(user.key));
|
||||||
await interaction.user.send(message);
|
|
||||||
return interaction.reply({ content: 'Key successfully re-sent! Please check your DMs.', ephemeral: true });
|
return interaction.reply({ content: 'Key successfully re-sent! Please check your DMs.', ephemeral: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,10 +233,9 @@ client.on(Events.InteractionCreate, async interaction => {
|
|||||||
const joinDate = member.joinedAt;
|
const joinDate = member.joinedAt;
|
||||||
const cutoffDate = data.joinCutoff ? new Date(data.joinCutoff) : null;
|
const cutoffDate = data.joinCutoff ? new Date(data.joinCutoff) : null;
|
||||||
if (cutoffDate) {
|
if (cutoffDate) {
|
||||||
// Compare only the date parts (YYYY-MM-DD)
|
|
||||||
const join = joinDate.toISOString().slice(0, 10);
|
const join = joinDate.toISOString().slice(0, 10);
|
||||||
const cutoff = cutoffDate.toISOString().slice(0, 10);
|
const cutoff = cutoffDate.toISOString().slice(0, 10);
|
||||||
if (new Date(join) > new Date(cutoff)) {
|
if (new Date(join).getTime() > new Date(cutoff).getTime()) {
|
||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
content: INELLIGIBLE_MESSAGE,
|
content: INELLIGIBLE_MESSAGE,
|
||||||
ephemeral: true
|
ephemeral: true
|
||||||
@ -161,14 +243,14 @@ client.on(Events.InteractionCreate, async interaction => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign key
|
// Check for available keys
|
||||||
data = loadData(); // Reload in case of concurrent changes
|
data = loadData();
|
||||||
if (!data.keys || data.keys.length === 0) {
|
const keyCount = data.keys ? data.keys.length : 0;
|
||||||
// No keys left
|
if (keyCount === 0) {
|
||||||
if (data.loggingChannel) {
|
if (data.loggingChannel) {
|
||||||
const channel = await interaction.guild.channels.fetch(data.loggingChannel).catch(() => console.log(`Failed to fetch logging channel ${data.loggingChannel}`));
|
|
||||||
if (channel) {
|
if (channel) {
|
||||||
channel.send('WARNING: No more Steam keys available for registration! Please run `/load` to add more keys.');
|
channel.send(formatNoKeysMessage(interaction.user.id, interaction.user.tag));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return interaction.reply({ content: 'There was an error retrieving your keys. Please reach out to <@404872989188816906> in <#580122303342313473>!', ephemeral: true });
|
return interaction.reply({ content: 'There was an error retrieving your keys. Please reach out to <@404872989188816906> in <#580122303342313473>!', ephemeral: true });
|
||||||
@ -180,12 +262,25 @@ client.on(Events.InteractionCreate, async interaction => {
|
|||||||
data.users.push(user);
|
data.users.push(user);
|
||||||
saveData(data);
|
saveData(data);
|
||||||
|
|
||||||
|
// Send warnings for low key count
|
||||||
|
if (data.loggingChannel) {
|
||||||
|
if (channel) {
|
||||||
|
if (keyCount <= 10) {
|
||||||
|
channel.send(`⚠️ Only ${data.keys.length} keys left in the vault!`);
|
||||||
|
} else if (keyCount <= 50) {
|
||||||
|
channel.send(`⚠️ Only ${data.keys.length} keys left in the vault.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await interaction.reply({ content: 'Successfully claimed key! Check your DMs.', ephemeral: true });
|
await interaction.reply({ content: 'Successfully claimed key! Check your DMs.', ephemeral: true });
|
||||||
try {
|
try {
|
||||||
const message = KEY_DM_MESSAGE.replace('{key}', `https://store.steampowered.com/account/registerkey?key=${key}`);
|
await interaction.user.send(formatKeyMessage(key));
|
||||||
await interaction.user.send(message);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return interaction.reply({ content: 'Unable to send DM. Please change your privacy settings or reach out for support!', ephemeral: true });
|
if (channel) {
|
||||||
|
channel.send(formatFailedDMMessage(interaction.user.id, interaction.user.tag));
|
||||||
|
}
|
||||||
|
return interaction.reply({ content: 'Unable to send DM. Please change your privacy settings or reach out to <@404872989188816906> in <#580122303342313473> for support!', ephemeral: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,8 +296,71 @@ client.on(Events.InteractionCreate, async interaction => {
|
|||||||
data = loadData();
|
data = loadData();
|
||||||
data.loggingChannel = channel.id;
|
data.loggingChannel = channel.id;
|
||||||
saveData(data);
|
saveData(data);
|
||||||
|
await channel.send(`Successfully set logging channel for all Swordcery Key Bot-related messages to <#${channel.id}>.`);
|
||||||
return interaction.reply({ content: `Logging channel set to <#${channel.id}>.`, ephemeral: true });
|
return interaction.reply({ content: `Logging channel set to <#${channel.id}>.`, ephemeral: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Slash command: /getkey
|
||||||
|
if (interaction.isChatInputCommand() && interaction.commandName === 'getkey') {
|
||||||
|
if (!interaction.member.permissions.has(PermissionsBitField.Flags.Administrator)) {
|
||||||
|
return interaction.reply({ content: 'You must be an administrator.', ephemeral: true });
|
||||||
|
}
|
||||||
|
const user = interaction.options.getUser('user');
|
||||||
|
if (!user) {
|
||||||
|
return interaction.reply({ content: 'Invalid user.', ephemeral: true });
|
||||||
|
}
|
||||||
|
data = loadData();
|
||||||
|
const found = data.users.find(u => u.id === user.id);
|
||||||
|
if (found && found.key) {
|
||||||
|
return interaction.reply({ content: `User <@${user.id}> has registered key: \`${found.key}\``, ephemeral: true });
|
||||||
|
} else {
|
||||||
|
return interaction.reply({ content: `User <@${user.id}> does not have a registered key.`, ephemeral: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
client.login(TOKEN);
|
client.login(TOKEN);
|
||||||
|
|
||||||
|
// Handle uncaught exceptions
|
||||||
|
process.on('uncaughtException', (err) => {
|
||||||
|
console.error('Uncaught Exception:', err);
|
||||||
|
errors.push({
|
||||||
|
userId: null,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
message: 'Uncaught Exception',
|
||||||
|
error: JSON.stringify(err, null, 2)
|
||||||
|
});
|
||||||
|
saveErrorLog(errors);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle unhandled promise rejections
|
||||||
|
process.on('unhandledRejection', (reason, promise) => {
|
||||||
|
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
||||||
|
errors.push({
|
||||||
|
userId: null,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
message: 'Unhandled Rejection',
|
||||||
|
error: JSON.stringify(reason, null, 2)
|
||||||
|
});
|
||||||
|
saveErrorLog(errors);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle SIGINT (Ctrl+C) & SIGTERM (kill command)
|
||||||
|
function gracefulShutdown() {
|
||||||
|
console.log('Shutting down gracefully...');
|
||||||
|
saveData(data);
|
||||||
|
saveErrorLog(errors);
|
||||||
|
client.destroy();
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.on('SIGINT', gracefulShutdown);
|
||||||
|
process.on('SIGTERM', gracefulShutdown);
|
||||||
|
|
||||||
|
let lastErrorCount = errors.length;
|
||||||
|
setInterval(() => {
|
||||||
|
if (errors.length > lastErrorCount) {
|
||||||
|
saveErrorLog(errors);
|
||||||
|
lastErrorCount = errors.length;
|
||||||
|
}
|
||||||
|
}, 10000); // Check every 10 seconds
|
||||||
23
messages.js
23
messages.js
@ -1,12 +1,31 @@
|
|||||||
|
// Message to be sent in news channel for users to see to claim the game key
|
||||||
export const NEWS_CHANNEL_MESSAGE = `# CLAIM YOUR SWORDCERY SECRET PLAYTEST STEAM KEY!
|
export const NEWS_CHANNEL_MESSAGE = `# CLAIM YOUR SWORDCERY SECRET PLAYTEST STEAM KEY!
|
||||||
Any additional playtests we run, can be accessed with this key as well.`;
|
Any additional playtests we run can be accessed with this key as well.`;
|
||||||
|
|
||||||
|
// Message users will see if they are inelligible for the playtest
|
||||||
export const INELLIGIBLE_MESSAGE = `**Sorry! You missed the cutoff date for this Swordcery Secret Playtest.**
|
export const INELLIGIBLE_MESSAGE = `**Sorry! You missed the cutoff date for this Swordcery Secret Playtest.**
|
||||||
Please stay tuned though as you'll be eligible for any subsequent playtests we run!`;
|
Please stay tuned though as you'll be eligible for any subsequent playtests we run!`;
|
||||||
|
|
||||||
export const KEY_DM_MESSAGE = `# Thanks so much for choosing to be part of the Swordcery Secret Playtest!
|
// Message to be sent in logging channel if no more keys are available
|
||||||
|
const NO_MORE_KEYS = `# **⚠️ WARNING - NO MORE DEMO KEYS ⚠️**
|
||||||
|
User {user} tried to register for their game key, but there are none left. Please run \`/load\` to add more keys!`;
|
||||||
|
export function formatNoKeysMessage(id, tag) {
|
||||||
|
return NO_MORE_KEYS.replace('{user}', `${tag} (<@${id}>)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message to be sent in DM to users when they successfully register for a key
|
||||||
|
const KEY_DM_MESSAGE = `# Thanks so much for choosing to be part of the Swordcery Secret Playtest!
|
||||||
|
|
||||||
We're excited for you to try the game! Please note that the game is still a work in progress so you may run into some bugs, but we hope the experience is enjoyable.
|
We're excited for you to try the game! Please note that the game is still a work in progress so you may run into some bugs, but we hope the experience is enjoyable.
|
||||||
Thanks again for your support!
|
Thanks again for your support!
|
||||||
|
|
||||||
Redeem your key: {key}`;
|
Redeem your key: {key}`;
|
||||||
|
export function formatKeyMessage(key) {
|
||||||
|
return KEY_DM_MESSAGE.replace('{key}', `https://store.steampowered.com/account/registerkey?key=${key} (Or enter manually: \`${key}\`)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const KEY_DM_FAILED = `# ** ⚠️ WARNING - UNABLE TO SEND DM ⚠️**
|
||||||
|
User {user} tried to register for their game key, but Swordcery Key Bot was unable to send them a DM.`
|
||||||
|
export function formatFailedDMMessage(id, tag) {
|
||||||
|
return KEY_DM_FAILED.replace('{user}', `${tag} (<@${id}>)`);
|
||||||
|
}
|
||||||
38
register.js
38
register.js
@ -3,13 +3,21 @@ import { REST, Routes } from 'discord.js';
|
|||||||
|
|
||||||
const TOKEN = process.env.DISCORD_TOKEN;
|
const TOKEN = process.env.DISCORD_TOKEN;
|
||||||
const CLIENT_ID = process.env.DISCORD_CLIENT_ID;
|
const CLIENT_ID = process.env.DISCORD_CLIENT_ID;
|
||||||
|
const ADMIN_PERM = 0x00000008;
|
||||||
|
|
||||||
const commands = [
|
const commands = [
|
||||||
{
|
{
|
||||||
name: 'register',
|
name: 'register',
|
||||||
description: 'Send a registration message with a button'
|
description: 'Send a registration message with a button',
|
||||||
|
default_member_permissions: ADMIN_PERM.toString()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
name: 'keys',
|
||||||
|
description: 'Manage Steam keys',
|
||||||
|
default_member_permissions: ADMIN_PERM.toString(),
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
type: 1, // SUB_COMMAND
|
||||||
name: 'load',
|
name: 'load',
|
||||||
description: 'Load a list of Steam keys from a file',
|
description: 'Load a list of Steam keys from a file',
|
||||||
options: [
|
options: [
|
||||||
@ -22,7 +30,26 @@ const commands = [
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'setlogging',
|
type: 1, // SUB_COMMAND
|
||||||
|
name: 'get',
|
||||||
|
description: 'Get a user\'s registered Steam key',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'user',
|
||||||
|
description: 'User to look up',
|
||||||
|
type: 6, // USER type
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 1, // SUB_COMMAND
|
||||||
|
name: 'check',
|
||||||
|
description: 'Check how many keys are left'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 1, // SUB_COMMAND
|
||||||
|
name: 'logging',
|
||||||
description: 'Set the logging channel for key warnings',
|
description: 'Set the logging channel for key warnings',
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
@ -33,26 +60,23 @@ const commands = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const rest = new REST({ version: '10' }).setToken(TOKEN);
|
const rest = new REST({ version: '10' }).setToken(TOKEN);
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
// Fetch all current global commands
|
|
||||||
const currentCommands = await rest.get(
|
const currentCommands = await rest.get(
|
||||||
Routes.applicationCommands(CLIENT_ID)
|
Routes.applicationCommands(CLIENT_ID)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Delete each existing command
|
|
||||||
for (const cmd of currentCommands) {
|
for (const cmd of currentCommands) {
|
||||||
await rest.delete(
|
await rest.delete(
|
||||||
Routes.applicationCommand(CLIENT_ID, cmd.id)
|
Routes.applicationCommand(CLIENT_ID, cmd.id)
|
||||||
);
|
);
|
||||||
console.log(`Deleted command: ${cmd.name}`);
|
console.log(`Deleted command: ${cmd.name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register new commands
|
|
||||||
await rest.put(
|
await rest.put(
|
||||||
Routes.applicationCommands(CLIENT_ID),
|
Routes.applicationCommands(CLIENT_ID),
|
||||||
{ body: commands }
|
{ body: commands }
|
||||||
|
|||||||
Reference in New Issue
Block a user