AeThex-Bot-Master/aethex-bot/commands/announce.js
sirpiglr 6f5c37959f Add new commands and improve existing ones for better user experience
Introduces new commands like `/automod`, `/giveaway`, `/rolepanel`, and `/schedule`. Enhances existing commands such as `/announce`, `/help`, `/leaderboard`, `/profile`, and `/serverinfo` with new features and improved embed designs. Updates welcome and goodbye listeners with rich embeds. Fixes a critical issue in the `/rolepanel` command regarding channel fetching. Adds interaction handling for role buttons and giveaway entries.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: aed2e46d-25bb-4b73-81a1-bb9e8437c261
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: eefee140-1301-4b6f-9439-2b0b883aa40a
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/aed2e46d-25bb-4b73-81a1-bb9e8437c261/qAaysIh
Replit-Helium-Checkpoint-Created: true
2025-12-08 07:24:49 +00:00

155 lines
5.4 KiB
JavaScript

const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } = require('discord.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('announce')
.setDescription('Send an announcement to all realms')
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
.addStringOption(option =>
option.setName('title')
.setDescription('Announcement title')
.setRequired(true)
.setMaxLength(256)
)
.addStringOption(option =>
option.setName('message')
.setDescription('Announcement message')
.setRequired(true)
.setMaxLength(2000)
)
.addStringOption(option =>
option.setName('type')
.setDescription('Announcement type (changes color and style)')
.setRequired(false)
.addChoices(
{ name: '📢 General (Purple)', value: 'general' },
{ name: '🆕 Update (Green)', value: 'update' },
{ name: '🎉 Event (Orange)', value: 'event' },
{ name: '⚠️ Important (Red)', value: 'important' },
{ name: '⭐ Highlight (Gold)', value: 'highlight' },
{ name: '💡 Info (Blue)', value: 'info' }
)
)
.addStringOption(option =>
option.setName('image')
.setDescription('Image URL to include')
.setRequired(false)
)
.addBooleanOption(option =>
option.setName('ping')
.setDescription('Ping @everyone with this announcement')
.setRequired(false)
),
async execute(interaction, supabase, client) {
await interaction.deferReply({ ephemeral: true });
const title = interaction.options.getString('title');
const message = interaction.options.getString('message');
const type = interaction.options.getString('type') || 'general';
const imageUrl = interaction.options.getString('image');
const ping = interaction.options.getBoolean('ping') || false;
const typeConfig = {
general: { color: 0x7c3aed, emoji: '📢', label: 'Announcement' },
update: { color: 0x22c55e, emoji: '🆕', label: 'Update' },
event: { color: 0xf97316, emoji: '🎉', label: 'Event' },
important: { color: 0xef4444, emoji: '⚠️', label: 'Important' },
highlight: { color: 0xfbbf24, emoji: '⭐', label: 'Highlight' },
info: { color: 0x3b82f6, emoji: '💡', label: 'Info' }
};
const config = typeConfig[type];
const embed = new EmbedBuilder()
.setColor(config.color)
.setAuthor({
name: `${config.emoji} ${config.label}`,
iconURL: interaction.guild.iconURL({ size: 64 })
})
.setTitle(title)
.setDescription(message)
.setFooter({
text: `Announced by ${interaction.user.tag}${interaction.guild.name}`,
iconURL: interaction.user.displayAvatarURL({ size: 32 })
})
.setTimestamp();
if (imageUrl) {
embed.setImage(imageUrl);
}
const results = [];
const REALM_GUILDS = client.REALM_GUILDS;
for (const [realm, guildId] of Object.entries(REALM_GUILDS)) {
if (!guildId) continue;
const guild = client.guilds.cache.get(guildId);
if (!guild) {
results.push({ realm, status: 'offline', emoji: '⚫' });
continue;
}
try {
const announcementChannel = guild.channels.cache.find(
c => c.isTextBased() && !c.isThread() && !c.isVoiceBased() &&
(c.name.includes('announcement') || c.name.includes('general'))
);
if (announcementChannel && announcementChannel.isTextBased()) {
const content = ping ? '@everyone' : null;
await announcementChannel.send({ content, embeds: [embed] });
results.push({ realm, status: 'sent', channel: announcementChannel.name, emoji: '✅' });
} else {
results.push({ realm, status: 'no_channel', emoji: '⚠️' });
}
} catch (error) {
console.error(`Announce error for ${realm}:`, error);
results.push({ realm, status: 'error', error: error.message, emoji: '❌' });
}
}
if (supabase) {
try {
await supabase.from('audit_logs').insert({
action: 'announce',
user_id: interaction.user.id,
username: interaction.user.tag,
guild_id: interaction.guildId,
details: { title, message, type, results },
});
} catch (e) {
console.warn('Failed to log announcement:', e.message);
}
}
const successCount = results.filter(r => r.status === 'sent').length;
const failCount = results.filter(r => r.status !== 'sent' && r.status !== 'offline').length;
const resultEmbed = new EmbedBuilder()
.setColor(successCount > 0 ? 0x22c55e : 0xef4444)
.setTitle(`${config.emoji} Announcement Results`)
.setDescription(
results.length > 0
? results.map(r =>
`${r.emoji} **${capitalizeFirst(r.realm)}**: ${r.status}${r.channel ? ` (#${r.channel})` : ''}`
).join('\n')
: 'No realms configured'
)
.addFields({
name: '📊 Summary',
value: `✅ Sent: ${successCount} | ⚠️ Failed: ${failCount} | ⚫ Offline: ${results.filter(r => r.status === 'offline').length}`,
inline: false
})
.setFooter({ text: `Type: ${config.label}` })
.setTimestamp();
await interaction.editReply({ embeds: [resultEmbed] });
},
};
function capitalizeFirst(str) {
if (!str) return str;
return str.charAt(0).toUpperCase() + str.slice(1);
}