Initial bot code
This commit is contained in:
208
index.js
Normal file
208
index.js
Normal file
@ -0,0 +1,208 @@
|
||||
import 'dotenv/config';
|
||||
import {
|
||||
Client,
|
||||
GatewayIntentBits,
|
||||
Partials,
|
||||
Events,
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
ActionRowBuilder,
|
||||
PermissionsBitField,
|
||||
EmbedBuilder
|
||||
} from 'discord.js';
|
||||
import fs from 'fs';
|
||||
import { NEWS_CHANNEL_MESSAGE, INELLIGIBLE_MESSAGE, KEY_DM_MESSAGE } from './messages.js';
|
||||
|
||||
const TOKEN = process.env.DISCORD_TOKEN;
|
||||
const DATA_FILE = './key_data.json';
|
||||
|
||||
function loadData() {
|
||||
if (!fs.existsSync(DATA_FILE)) {
|
||||
return { messageId: null, joinCutoff: null, users: [], keys: [], loggingChannel: null };
|
||||
}
|
||||
const data = JSON.parse(fs.readFileSync(DATA_FILE, 'utf8'));
|
||||
if (!data.keys) data.keys = [];
|
||||
return data;
|
||||
}
|
||||
function saveData(data) {
|
||||
fs.writeFileSync(DATA_FILE, JSON.stringify(data, null, 2));
|
||||
}
|
||||
|
||||
let data = loadData();
|
||||
|
||||
const client = new Client({
|
||||
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers, GatewayIntentBits.DirectMessages],
|
||||
partials: [Partials.Channel]
|
||||
});
|
||||
|
||||
client.once(Events.ClientReady, () => {
|
||||
console.log(`Logged in as ${client.user.tag}`);
|
||||
});
|
||||
|
||||
client.on(Events.InteractionCreate, async interaction => {
|
||||
// Slash command: /register
|
||||
if (interaction.isChatInputCommand() && interaction.commandName === 'register') {
|
||||
if (!interaction.member.permissions.has(PermissionsBitField.Flags.Administrator)) {
|
||||
return interaction.reply({ content: 'You must be an administrator.', ephemeral: true });
|
||||
}
|
||||
|
||||
// Check if loggingChannel is set
|
||||
if (!data.loggingChannel) {
|
||||
return interaction.reply({
|
||||
content: 'You must set a logging channel first using `/setlogging`. Registration message not sent.',
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setDescription('**NOTE**: Click "Claim Key" again if you need your key re-sent.')
|
||||
.setColor(0xff0000);
|
||||
|
||||
const button = new ButtonBuilder()
|
||||
.setCustomId('register_button')
|
||||
.setLabel('Claim Key')
|
||||
.setStyle(ButtonStyle.Success);
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(button);
|
||||
|
||||
const sent = await interaction.channel.send({
|
||||
content: NEWS_CHANNEL_MESSAGE, embeds: [embed], components: [row]
|
||||
});
|
||||
data.messageId = sent.id;
|
||||
saveData(data);
|
||||
|
||||
await interaction.reply({ content: 'Registration message sent!', ephemeral: true });
|
||||
}
|
||||
|
||||
// Slash command: /load
|
||||
if (interaction.isChatInputCommand() && interaction.commandName === 'load') {
|
||||
if (!interaction.member.permissions.has(PermissionsBitField.Flags.Administrator)) {
|
||||
return interaction.reply({ content: 'You must be an administrator.', ephemeral: true });
|
||||
}
|
||||
const file = interaction.options.getAttachment('file');
|
||||
if (!file) {
|
||||
return interaction.reply({ content: 'You must attach a file.', ephemeral: true });
|
||||
}
|
||||
|
||||
// Download the file
|
||||
const response = await fetch(file.url);
|
||||
const text = await response.text();
|
||||
const keys = text.split('\n').map(line => line.trim()).filter(line => line.length > 0);
|
||||
|
||||
const yesButton = new ButtonBuilder()
|
||||
.setCustomId('load_yes')
|
||||
.setLabel('Yes')
|
||||
.setStyle(ButtonStyle.Success);
|
||||
const noButton = new ButtonBuilder()
|
||||
.setCustomId('load_no')
|
||||
.setLabel('No')
|
||||
.setStyle(ButtonStyle.Danger);
|
||||
const row = new ActionRowBuilder().addComponents(yesButton, noButton);
|
||||
|
||||
// Store keys temporarily in memory for this user
|
||||
client.tempKeyLoads = client.tempKeyLoads || {};
|
||||
client.tempKeyLoads[interaction.user.id] = keys;
|
||||
|
||||
await interaction.reply({
|
||||
content: `Found ${keys.length} steam keys. Do you wish to load these into the key vault?`,
|
||||
components: [row],
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
|
||||
// Handle load confirmation buttons
|
||||
if (interaction.isButton() && (interaction.customId === 'load_yes' || interaction.customId === 'load_no')) {
|
||||
if (!interaction.member.permissions.has(PermissionsBitField.Flags.Administrator)) {
|
||||
return interaction.reply({ content: 'You must be an administrator.', ephemeral: true });
|
||||
}
|
||||
if (!client.tempKeyLoads || !client.tempKeyLoads[interaction.user.id]) {
|
||||
return interaction.reply({ content: 'No pending key load found.', ephemeral: true });
|
||||
}
|
||||
if (interaction.customId === 'load_no') {
|
||||
delete client.tempKeyLoads[interaction.user.id];
|
||||
await interaction.update({ content: 'Key load cancelled.', components: [] });
|
||||
return;
|
||||
}
|
||||
// Yes pressed
|
||||
const keys = client.tempKeyLoads[interaction.user.id];
|
||||
data = loadData(); // Reload in case of concurrent changes
|
||||
data.keys = data.keys || [];
|
||||
data.keys.push(...keys);
|
||||
saveData(data);
|
||||
delete client.tempKeyLoads[interaction.user.id];
|
||||
await interaction.update({ content: `Loaded ${keys.length} keys into the key vault.`, components: [] });
|
||||
}
|
||||
|
||||
// Button interaction for register
|
||||
if (interaction.isButton() && interaction.customId === 'register_button') {
|
||||
if (interaction.message.id !== data.messageId) return;
|
||||
|
||||
// Already registered?
|
||||
let user = data.users.find(u => u.id === interaction.user.id);
|
||||
if (user && user.key) {
|
||||
const message = KEY_DM_MESSAGE.replace('{key}', `https://store.steampowered.com/account/registerkey?key=${user.key}`);
|
||||
await interaction.user.send(message);
|
||||
return interaction.reply({ content: 'Key successfully re-sent! Please check your DMs.', ephemeral: true });
|
||||
}
|
||||
|
||||
// Check join date
|
||||
const member = await interaction.guild.members.fetch(interaction.user.id);
|
||||
const joinDate = member.joinedAt;
|
||||
const cutoffDate = data.joinCutoff ? new Date(data.joinCutoff) : null;
|
||||
if (cutoffDate) {
|
||||
// Compare only the date parts (YYYY-MM-DD)
|
||||
const join = joinDate.toISOString().slice(0, 10);
|
||||
const cutoff = cutoffDate.toISOString().slice(0, 10);
|
||||
if (new Date(join) > new Date(cutoff)) {
|
||||
return interaction.reply({
|
||||
content: INELLIGIBLE_MESSAGE,
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Assign key
|
||||
data = loadData(); // Reload in case of concurrent changes
|
||||
if (!data.keys || data.keys.length === 0) {
|
||||
// No keys left
|
||||
if (data.loggingChannel) {
|
||||
const channel = await interaction.guild.channels.fetch(data.loggingChannel).catch(() => console.log(`Failed to fetch logging channel ${data.loggingChannel}`));
|
||||
if (channel) {
|
||||
channel.send('WARNING: No more Steam keys available for registration! Please run `/load` to add more keys.');
|
||||
}
|
||||
}
|
||||
return interaction.reply({ content: 'There was an error retrieving your keys. Please reach out to <@404872989188816906> in <#580122303342313473>!', ephemeral: true });
|
||||
}
|
||||
|
||||
// Assign and remove key from vault
|
||||
const key = data.keys.shift();
|
||||
user = { id: interaction.user.id, key };
|
||||
data.users.push(user);
|
||||
saveData(data);
|
||||
|
||||
await interaction.reply({ content: 'Successfully claimed key! Check your DMs.', ephemeral: true });
|
||||
try {
|
||||
const message = KEY_DM_MESSAGE.replace('{key}', `https://store.steampowered.com/account/registerkey?key=${key}`);
|
||||
await interaction.user.send(message);
|
||||
} catch (e) {
|
||||
return interaction.reply({ content: 'Unable to send DM. Please change your privacy settings or reach out for support!', ephemeral: true });
|
||||
}
|
||||
}
|
||||
|
||||
// Slash command: /setlogging
|
||||
if (interaction.isChatInputCommand() && interaction.commandName === 'setlogging') {
|
||||
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);
|
||||
return interaction.reply({ content: `Logging channel set to <#${channel.id}>.`, ephemeral: true });
|
||||
}
|
||||
});
|
||||
|
||||
client.login(TOKEN);
|
||||
Reference in New Issue
Block a user