fixed themes and ui added new features

This commit is contained in:
2025-10-04 08:39:54 -04:00
parent 0b1a6cdea4
commit 834e77a93e
7 changed files with 373 additions and 24 deletions

View File

@@ -0,0 +1,54 @@
const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js');
const { readDb, writeDb } = require('../../backend/db');
module.exports = {
name: 'create-invite',
description: 'Create a Discord invite with options (channel optional, maxAge seconds, maxUses, temporary).',
enabled: true,
builder: new SlashCommandBuilder()
.setName('create-invite')
.setDescription('Create a Discord invite with options (channel optional, maxAge seconds, maxUses, temporary).')
.addChannelOption(opt => opt.setName('channel').setDescription('Channel to create invite in').setRequired(false))
.addIntegerOption(opt => opt.setName('maxage').setDescription('Duration in seconds (0 means never expire)').setRequired(false))
.addIntegerOption(opt => opt.setName('maxuses').setDescription('Number of uses allowed (0 means unlimited)').setRequired(false))
.addBooleanOption(opt => opt.setName('temporary').setDescription('Temporary membership?').setRequired(false))
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
async execute(interaction) {
try {
const channel = interaction.options.getChannel('channel');
const maxAge = interaction.options.getInteger('maxage') || 0;
const maxUses = interaction.options.getInteger('maxuses') || 0;
const temporary = interaction.options.getBoolean('temporary') || false;
const targetChannel = channel || interaction.guild.channels.cache.find(c => c.type === 0);
if (!targetChannel) {
await interaction.reply({ content: 'No valid channel found to create an invite.', ephemeral: true });
return;
}
const invite = await targetChannel.createInvite({ maxAge, maxUses, temporary, unique: true });
const db = readDb();
if (!db[interaction.guildId]) db[interaction.guildId] = {};
if (!db[interaction.guildId].invites) db[interaction.guildId].invites = [];
const item = {
code: invite.code,
url: invite.url,
channelId: targetChannel.id,
createdAt: new Date().toISOString(),
maxUses: invite.maxUses || maxUses || 0,
maxAge: invite.maxAge || maxAge || 0,
temporary: !!invite.temporary,
};
db[interaction.guildId].invites.push(item);
writeDb(db);
await interaction.reply({ content: `Invite created: ${invite.url}`, ephemeral: true });
} catch (error) {
console.error('Error in create-invite:', error);
await interaction.reply({ content: 'Failed to create invite.', ephemeral: true });
}
},
};

View File

@@ -0,0 +1,42 @@
const { SlashCommandBuilder, PermissionFlagsBits, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js');
const { readDb } = require('../../backend/db');
module.exports = {
name: 'list-invites',
description: 'List invites created by the bot for this guild',
enabled: true,
builder: new SlashCommandBuilder()
.setName('list-invites')
.setDescription('List invites created by the bot for this guild')
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
async execute(interaction) {
try {
const db = readDb();
const invites = (db[interaction.guildId] && db[interaction.guildId].invites) ? db[interaction.guildId].invites : [];
if (!invites.length) {
await interaction.reply({ content: 'No invites created by the bot in this server.', ephemeral: true });
return;
}
// Build a message with invite details and action buttons
for (const inv of invites) {
const created = inv.createdAt || 'Unknown';
const uses = inv.uses || inv.maxUses || 0;
const temporary = inv.temporary ? 'Yes' : 'No';
const content = `Invite: ${inv.url}\nCreated: ${created}\nUses: ${uses}\nMax Uses: ${inv.maxUses || 0}\nMax Age (s): ${inv.maxAge || 0}\nTemporary: ${temporary}`;
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder().setLabel('Copy Invite').setStyle(ButtonStyle.Secondary).setCustomId(`copy_inv_${inv.code}`),
new ButtonBuilder().setLabel('Delete Invite').setStyle(ButtonStyle.Danger).setCustomId(`delete_inv_${inv.code}`),
);
await interaction.reply({ content, components: [row], ephemeral: true });
}
} catch (error) {
console.error('Error in list-invites:', error);
await interaction.reply({ content: 'Failed to list invites.', ephemeral: true });
}
},
};

View File

@@ -15,6 +15,41 @@ commandHandler(client);
eventHandler(client);
client.on('interactionCreate', async interaction => {
// Handle button/component interactions for invites
if (interaction.isButton && interaction.isButton()) {
const id = interaction.customId || '';
if (id.startsWith('copy_inv_')) {
const code = id.replace('copy_inv_', '');
const db = readDb();
const invites = (db[interaction.guildId] && db[interaction.guildId].invites) ? db[interaction.guildId].invites : [];
const inv = invites.find(i => i.code === code);
if (inv) {
await interaction.reply({ content: `Invite: ${inv.url}`, ephemeral: true });
} else {
await interaction.reply({ content: 'Invite not found.', ephemeral: true });
}
} else if (id.startsWith('delete_inv_')) {
const code = id.replace('delete_inv_', '');
// permission check: admin only
const member = interaction.member;
if (!member.permissions.has('Administrator')) {
await interaction.reply({ content: 'You must be an administrator to delete invites.', ephemeral: true });
return;
}
try {
// call backend delete endpoint
const fetch = require('node-fetch');
const url = `http://localhost:${process.env.PORT || 3002}/api/servers/${interaction.guildId}/invites/${code}`;
await fetch(url, { method: 'DELETE' });
await interaction.reply({ content: 'Invite deleted.', ephemeral: true });
} catch (e) {
console.error('Error deleting invite via API:', e);
await interaction.reply({ content: 'Failed to delete invite.', ephemeral: true });
}
}
return;
}
if (!interaction.isCommand()) return;
const command = client.commands.get(interaction.commandName);