fix & refactor: centralize event logging, add DB indexes, fix mute timeout, role menu & owner permission bugs
All checks were successful
Auto Build and Push Docker Image / build (push) Successful in 20s

This commit is contained in:
sarah
2026-08-02 22:53:32 +02:00
parent 0e1630f80b
commit 5c40faa5c9
20 changed files with 245 additions and 261 deletions

View File

@@ -1,27 +1,7 @@
import { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder, GuildMember } from 'discord.js';
import { Command } from '../../structures/Command.js';
import { ExtendedClient } from '../../structures/ExtendedClient.js';
async function sendModerationLog(client: ExtendedClient, guildId: string, event: string, embed: EmbedBuilder) {
const DB = client.DB;
const settings = DB.getSettings(guildId);
if (!settings) return;
if (!settings.log_events) return;
const activeEvents = settings.log_events.split(',').filter(Boolean);
if (!activeEvents.includes(event)) return;
if (!settings.log_channel) return;
const channel = client.guilds.cache.get(guildId)?.channels.cache.get(settings.log_channel);
if (!channel || !channel.isTextBased()) return;
try {
await (channel as any).send({ embeds: [embed] });
} catch (error) {
console.error(`[LOG] Error sending moderation log (${event}):`, error);
}
}
import { EventLogger } from '../../structures/EventLogger.js';
const command: Command = {
data: new SlashCommandBuilder()
@@ -256,7 +236,7 @@ const command: Command = {
{ name: 'Grund', value: reason, inline: false }
)
.setTimestamp();
await sendModerationLog(interaction.client, guildId, 'kicks', embed);
await EventLogger.sendLog(interaction.client, guildId, 'kicks', embed);
} catch (error) {
console.error(error);
await interaction.reply({ content: '❌ Fehler beim Kicken.', ephemeral: true });
@@ -330,7 +310,7 @@ const command: Command = {
{ name: 'Warnungen', value: `${warnCount}/${warnThreshold}`, inline: true }
)
.setTimestamp();
await sendModerationLog(interaction.client, guildId, 'warns', embed);
await EventLogger.sendLog(interaction.client, guildId, 'warns', embed);
return;
}
@@ -373,10 +353,11 @@ const command: Command = {
// Parse duration
let durationMs: number | null = null;
let durationText = '';
const MAX_TIMEOUT_MS = 28 * 24 * 60 * 60 * 1000; // 28 days max Discord timeout
if (unit === 'perm') {
durationMs = null; // Permanent = null
durationText = 'Permanent';
durationMs = MAX_TIMEOUT_MS;
durationText = '28 Tage (Maximaler Discord Timeout)';
} else if (duration) {
const unitSeconds: Record<string, number> = {
sec: 1,
@@ -395,18 +376,21 @@ const command: Command = {
}
durationMs = duration * multiplier * 1000;
// Human-readable duration
const unitNames: Record<string, string> = {
sec: 'Sekunden',
min: 'Minuten',
hour: 'Stunden',
day: 'Tage',
week: 'Wochen',
month: 'Monate',
year: 'Jahre'
};
durationText = `${duration} ${unitNames[unit!]}`;
if (durationMs > MAX_TIMEOUT_MS) {
durationMs = MAX_TIMEOUT_MS;
durationText = `${duration} ${unit} (auf 28 Tage beschränkt)`;
} else {
const unitNames: Record<string, string> = {
sec: 'Sekunden',
min: 'Minuten',
hour: 'Stunden',
day: 'Tage',
week: 'Wochen',
month: 'Monate',
year: 'Jahre'
};
durationText = `${duration} ${unitNames[unit!] || unit}`;
}
} else {
await interaction.reply({ content: '❌ Dauer oder Zeiteinheit angeben.', ephemeral: true });
return;
@@ -426,7 +410,7 @@ const command: Command = {
{ name: 'Grund', value: reason, inline: false }
)
.setTimestamp();
await sendModerationLog(interaction.client, guildId, 'mutes', embed);
await EventLogger.sendLog(interaction.client, guildId, 'mutes', embed);
} catch (error) {
console.error(error);
await interaction.reply({ content: '❌ Fehler beim Muten.', ephemeral: true });
@@ -481,7 +465,7 @@ const command: Command = {
{ name: 'Nachrichten gelöscht', value: `${deleteDays} Tage`, inline: true }
)
.setTimestamp();
await sendModerationLog(interaction.client, guildId, 'bans', embed);
await EventLogger.sendLog(interaction.client, guildId, 'bans', embed);
} catch (error) {
console.error(error);
await interaction.reply({ content: '❌ Fehler beim Bannen.', ephemeral: true });
@@ -659,7 +643,7 @@ const command: Command = {
purgeSubcommand === 'user' ? 'Nach Nutzer' : 'Alle', inline: true }
)
.setTimestamp();
await sendModerationLog(interaction.client, guildId, 'messages', embed);
await EventLogger.sendLog(interaction.client, guildId, 'messages', embed);
}
} catch (error) {
console.error('[ADMIN] Purge error:', error);

View File

@@ -10,8 +10,23 @@ const command: Command = {
async execute(interaction: any, client: any) {
if (!client.application?.owner) await client.application?.fetch();
const isOwner = interaction.user.id === client.application?.owner?.id;
const isAdmin = interaction.memberPermissions?.has(PermissionFlagsBits.Administrator);
const owner = client.application?.owner;
const envOwnerId = process.env.BOT_OWNER_ID;
let isOwner = false;
if (envOwnerId && interaction.user.id === envOwnerId) {
isOwner = true;
} else if (owner) {
if ('members' in owner && owner.members) {
isOwner = owner.members.has(interaction.user.id);
} else {
isOwner = interaction.user.id === owner.id;
}
}
const isAdmin = interaction.memberPermissions?.has(PermissionFlagsBits.ModerateMembers) ||
interaction.memberPermissions?.has(PermissionFlagsBits.Administrator) ||
interaction.memberPermissions?.has(PermissionFlagsBits.ManageGuild);
const embed = new EmbedBuilder()
.setTitle('📖 Hilfe-Menü')

View File

@@ -50,7 +50,8 @@ const command: Command = {
.addSubcommand(subcommand =>
subcommand
.setName('help')
.setDescription('Hilfe zu Log-Befehlen (Mod).')),
.setDescription('Hilfe zu Log-Befehlen (Mod).'))
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
category: 'Admin',
async execute(interaction: any) {
const subcommand = interaction.options.getSubcommand();

View File

@@ -27,8 +27,22 @@ const command: Command = {
async execute(interaction: any, client: any) {
if (!client.application?.owner) await client.application?.fetch();
if (interaction.user.id !== client.application?.owner?.id) {
await interaction.reply({ content: 'Keine Berechtigung.', ephemeral: true });
const owner = client.application?.owner;
const envOwnerId = process.env.BOT_OWNER_ID;
let isOwner = false;
if (envOwnerId && interaction.user.id === envOwnerId) {
isOwner = true;
} else if (owner) {
if ('members' in owner && owner.members) {
isOwner = owner.members.has(interaction.user.id);
} else {
isOwner = interaction.user.id === owner.id;
}
}
if (!isOwner) {
await interaction.reply({ content: 'Keine Berechtigung. Nur für den Bot-Owner.', ephemeral: true });
return;
}

View File

@@ -19,12 +19,15 @@ export async function updateRoleMessage(client: any, category: any) {
return;
}
const roleOptions = options.map((opt: any) =>
new StringSelectMenuOptionBuilder()
const roleOptions = options.map((opt: any) => {
const menuOption = new StringSelectMenuOptionBuilder()
.setLabel(opt.label.replace(/^[^\s]+\s/, ''))
.setValue(opt.role_id)
.setEmoji(opt.emoji || '')
);
.setValue(opt.role_id);
if (opt.emoji && opt.emoji.trim().length > 0) {
menuOption.setEmoji(opt.emoji.trim());
}
return menuOption;
});
roleOptions.push(
new StringSelectMenuOptionBuilder()
@@ -144,7 +147,8 @@ const command: Command = {
.addSubcommand(subcommand =>
subcommand
.setName('list')
.setDescription('Listet alle Rollen-Kategorien auf')),
.setDescription('Listet alle Rollen-Kategorien auf'))
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
category: 'Admin',
async execute(interaction: any) {
const subcommand = interaction.options.getSubcommand();

View File

@@ -32,7 +32,8 @@ const command: Command = {
.addSubcommand(subcommand =>
subcommand
.setName('help')
.setDescription('Zeigt Hilfe zu den Trigger-Befehlen an.')),
.setDescription('Zeigt Hilfe zu den Trigger-Befehlen an.'))
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
category: 'Admin',
async execute(interaction: any) {
const subcommand = interaction.options.getSubcommand();

View File

@@ -34,7 +34,8 @@ const command: Command = {
.addSubcommand(subcommand =>
subcommand
.setName('help')
.setDescription('Zeigt Hilfe zu den TwitchMonitor-Befehlen')),
.setDescription('Zeigt Hilfe zu den TwitchMonitor-Befehlen'))
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
category: 'Admin',
async execute(interaction: any) {
const subcommand = interaction.options.getSubcommand();

View File

@@ -48,7 +48,8 @@ const command: Command = {
.addSubcommand(subcommand =>
subcommand
.setName('help')
.setDescription('Hilfe zu Welcome-Befehlen.')),
.setDescription('Hilfe zu Welcome-Befehlen.'))
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
category: 'Admin',
async execute(interaction: any) {
const subcommand = interaction.options.getSubcommand();

View File

@@ -1,21 +1,5 @@
import { Events, GuildChannel, EmbedBuilder } from 'discord.js';
async function sendLog(client: any, guildId: string, event: string, embed: EmbedBuilder) {
const settings = client.DB.getSettings(guildId);
if (!settings?.log_events || !settings?.log_channel) return;
const activeEvents = settings.log_events.split(',').filter(Boolean);
if (!activeEvents.includes(event)) return;
const channel = client.guilds.cache.get(guildId)?.channels.cache.get(settings.log_channel);
if (!channel || !channel.isTextBased()) return;
try {
await (channel as any).send({ embeds: [embed] });
} catch (error) {
console.error(`[LOG] Error sending log (${event}):`, error);
}
}
import { EventLogger } from '../structures/EventLogger.js';
export default {
name: Events.ChannelCreate,
@@ -31,6 +15,6 @@ export default {
)
.setTimestamp();
await sendLog(channel.client, channel.guild.id, 'channels', embed);
await EventLogger.sendLog(channel.client as any, channel.guild.id, 'channels', embed);
},
};

View File

@@ -1,21 +1,5 @@
import { Events, GuildChannel, EmbedBuilder } from 'discord.js';
async function sendLog(client: any, guildId: string, event: string, embed: EmbedBuilder) {
const settings = client.DB.getSettings(guildId);
if (!settings?.log_events || !settings?.log_channel) return;
const activeEvents = settings.log_events.split(',').filter(Boolean);
if (!activeEvents.includes(event)) return;
const channel = client.guilds.cache.get(guildId)?.channels.cache.get(settings.log_channel);
if (!channel || !channel.isTextBased()) return;
try {
await (channel as any).send({ embeds: [embed] });
} catch (error) {
console.error(`[LOG] Error sending log (${event}):`, error);
}
}
import { EventLogger } from '../structures/EventLogger.js';
export default {
name: Events.ChannelDelete,
@@ -31,6 +15,6 @@ export default {
)
.setTimestamp();
await sendLog(channel.client, channel.guild.id, 'channels', embed);
await EventLogger.sendLog(channel.client as any, channel.guild.id, 'channels', embed);
},
};

View File

@@ -1,24 +1,5 @@
import { Events, GuildMember, TextChannel, EmbedBuilder } from 'discord.js';
async function sendLog(client: any, guildId: string, event: string, embed: EmbedBuilder) {
const DB = client.DB;
const settings = DB.getSettings(guildId);
if (!settings?.log_events) return;
const activeEvents = settings.log_events.split(',').filter(Boolean);
if (!activeEvents.includes(event)) return;
if (!settings?.log_channel) return;
const channel = client.guilds.cache.get(guildId)?.channels.cache.get(settings.log_channel);
if (!channel || !channel.isTextBased()) return;
try {
await (channel as any).send({ embeds: [embed] });
} catch (error) {
console.error(`[LOG] Error sending log (${event}):`, error);
}
}
import { EventLogger } from '../structures/EventLogger.js';
export default {
name: Events.GuildMemberRemove,
@@ -36,7 +17,7 @@ export default {
)
.setTimestamp();
await sendLog(member.client, guildId, 'leaves', embed);
await EventLogger.sendLog(member.client as any, guildId, 'leaves', embed);
// Send goodbye message if configured
const settings = DB.getSettings(guildId);

View File

@@ -1,21 +1,5 @@
import { Events, GuildMember, EmbedBuilder } from 'discord.js';
async function sendLog(client: any, guildId: string, event: string, embed: EmbedBuilder) {
const settings = client.DB.getSettings(guildId);
if (!settings?.log_events || !settings?.log_channel) return;
const activeEvents = settings.log_events.split(',').filter(Boolean);
if (!activeEvents.includes(event)) return;
const channel = client.guilds.cache.get(guildId)?.channels.cache.get(settings.log_channel);
if (!channel || !channel.isTextBased()) return;
try {
await (channel as any).send({ embeds: [embed] });
} catch (error) {
console.error(`[LOG] Error sending log (${event}):`, error);
}
}
import { EventLogger } from '../structures/EventLogger.js';
export default {
name: Events.GuildMemberUpdate,
@@ -36,7 +20,7 @@ export default {
)
.setTimestamp();
await sendLog(newMember.client, guildId, 'nicks', embed);
await EventLogger.sendLog(newMember.client as any, guildId, 'nicks', embed);
}
// Role changes
@@ -46,30 +30,30 @@ export default {
const addedRoles = newRoles.filter(r => !oldRoles.has(r.id));
const removedRoles = oldRoles.filter(r => !newRoles.has(r.id));
for (const role of addedRoles) {
for (const role of addedRoles.values()) {
const embed = new EmbedBuilder()
.setTitle('🎭 Rolle hinzugefügt')
.setColor(0x27ae60)
.addFields(
{ name: 'Nutzer', value: `${newMember.user.tag} (<@${newMember.id}>)`, inline: true },
{ name: 'Rolle', value: `${role[1].name} (<@&${role[1].id}>)`, inline: true }
{ name: 'Rolle', value: `${role.name} (<@&${role.id}>)`, inline: true }
)
.setTimestamp();
await sendLog(newMember.client, guildId, 'roles', embed);
await EventLogger.sendLog(newMember.client as any, guildId, 'roles', embed);
}
for (const role of removedRoles) {
for (const role of removedRoles.values()) {
const embed = new EmbedBuilder()
.setTitle('🎭 Rolle entfernt')
.setColor(0xe74c3c)
.addFields(
{ name: 'Nutzer', value: `${newMember.user.tag} (<@${newMember.id}>)`, inline: true },
{ name: 'Rolle', value: `${role[1].name} (<@&${role[1].id}>)`, inline: true }
{ name: 'Rolle', value: `${role.name} (<@&${role.id}>)`, inline: true }
)
.setTimestamp();
await sendLog(newMember.client, guildId, 'roles', embed);
await EventLogger.sendLog(newMember.client as any, guildId, 'roles', embed);
}
},
};

View File

@@ -29,93 +29,104 @@ export default {
return;
}
if (selectedValue.startsWith('remove_')) {
const options: any[] = client.DB.all('SELECT * FROM role_options WHERE category_id = ?', category.id);
let removedCount = 0;
for (const opt of options) {
if (member.roles.cache.has(opt.role_id)) {
await member.roles.remove(opt.role_id);
removedCount++;
}
}
if (removedCount > 0) {
await interaction.reply({
content: `Alle Rollen aus "${categoryName}" wurden entfernt.`,
ephemeral: true
});
} else {
await interaction.reply({
content: `Du hast keine Rollen aus "${categoryName}".`,
ephemeral: true
});
}
return;
}
const option: any = client.DB.get(
'SELECT * FROM role_options WHERE category_id = ? AND role_id = ?',
category.id,
selectedValue
);
if (!option) {
await interaction.reply({ content: 'Rolle nicht gefunden.', ephemeral: true });
return;
}
const discordRole = guild.roles.cache.get(selectedValue);
if (!discordRole) {
await interaction.reply({ content: 'Diese Rolle existiert nicht mehr.', ephemeral: true });
return;
}
const hasRole = member.roles.cache.has(selectedValue);
if (category.exclusive) {
if (hasRole) {
await member.roles.remove(selectedValue);
await interaction.reply({
content: `Die Rolle ${option.emoji ? option.emoji + ' ' : ''}${discordRole.name} wurde entfernt.`,
ephemeral: true
});
} else {
try {
if (selectedValue.startsWith('remove_')) {
const options: any[] = client.DB.all('SELECT * FROM role_options WHERE category_id = ?', category.id);
let removedCount = 0;
for (const opt of options) {
if (opt.role_id !== selectedValue && member.roles.cache.has(opt.role_id)) {
if (member.roles.cache.has(opt.role_id)) {
await member.roles.remove(opt.role_id);
removedCount++;
}
}
await member.roles.add(selectedValue);
await interaction.reply({
content: `Du hast jetzt die Rolle ${option.emoji ? option.emoji + ' ' : ''}${discordRole.name}!`,
ephemeral: true
});
}
} else {
if (hasRole) {
await member.roles.remove(selectedValue);
await interaction.reply({
content: `Die Rolle ${option.emoji ? option.emoji + ' ' : ''}${discordRole.name} wurde entfernt.`,
ephemeral: true
});
} else {
const options: any[] = client.DB.all('SELECT * FROM role_options WHERE category_id = ?', category.id);
const userCategoryRoles = options.filter((opt: any) => member.roles.cache.has(opt.role_id)).length;
if (category.max_roles > 0 && userCategoryRoles >= category.max_roles) {
if (removedCount > 0) {
await interaction.reply({
content: `Du kannst maximal ${category.max_roles} Rollen aus dieser Kategorie haben.`,
content: `Alle Rollen aus "${categoryName}" wurden entfernt.`,
ephemeral: true
});
} else {
await interaction.reply({
content: `Du hast keine Rollen aus "${categoryName}".`,
ephemeral: true
});
return;
}
await member.roles.add(selectedValue);
await interaction.reply({
content: `Du hast jetzt die Rolle ${option.emoji ? option.emoji + ' ' : ''}${discordRole.name}!`,
ephemeral: true
});
return;
}
const option: any = client.DB.get(
'SELECT * FROM role_options WHERE category_id = ? AND role_id = ?',
category.id,
selectedValue
);
if (!option) {
await interaction.reply({ content: 'Rolle nicht gefunden.', ephemeral: true });
return;
}
const discordRole = guild.roles.cache.get(selectedValue) || await guild.roles.fetch(selectedValue).catch(() => null);
if (!discordRole) {
await interaction.reply({ content: 'Diese Rolle existiert nicht mehr.', ephemeral: true });
return;
}
const hasRole = member.roles.cache.has(selectedValue);
if (category.exclusive) {
if (hasRole) {
await member.roles.remove(selectedValue);
await interaction.reply({
content: `Die Rolle ${option.emoji ? option.emoji + ' ' : ''}${discordRole.name} wurde entfernt.`,
ephemeral: true
});
} else {
const options: any[] = client.DB.all('SELECT * FROM role_options WHERE category_id = ?', category.id);
for (const opt of options) {
if (opt.role_id !== selectedValue && member.roles.cache.has(opt.role_id)) {
await member.roles.remove(opt.role_id);
}
}
await member.roles.add(selectedValue);
await interaction.reply({
content: `Du hast jetzt die Rolle ${option.emoji ? option.emoji + ' ' : ''}${discordRole.name}!`,
ephemeral: true
});
}
} else {
if (hasRole) {
await member.roles.remove(selectedValue);
await interaction.reply({
content: `Die Rolle ${option.emoji ? option.emoji + ' ' : ''}${discordRole.name} wurde entfernt.`,
ephemeral: true
});
} else {
const options: any[] = client.DB.all('SELECT * FROM role_options WHERE category_id = ?', category.id);
const userCategoryRoles = options.filter((opt: any) => member.roles.cache.has(opt.role_id)).length;
if (category.max_roles > 0 && userCategoryRoles >= category.max_roles) {
await interaction.reply({
content: `Du kannst maximal ${category.max_roles} Rollen aus dieser Kategorie haben.`,
ephemeral: true
});
return;
}
await member.roles.add(selectedValue);
await interaction.reply({
content: `Du hast jetzt die Rolle ${option.emoji ? option.emoji + ' ' : ''}${discordRole.name}!`,
ephemeral: true
});
}
}
} catch (error) {
console.error('[ROLESETUP] Role assignment error:', error);
const errorMsg = '❌ Fehler beim Ändern der Rolle. Mir fehlen eventuell die Rechte dazu.';
if (interaction.replied || interaction.deferred) {
await interaction.followUp({ content: errorMsg, ephemeral: true });
} else {
await interaction.reply({ content: errorMsg, ephemeral: true });
}
}
return;
return;
}
}

View File

@@ -10,10 +10,16 @@ export default {
if (triggers.length === 0) return;
const contentLower = message.content.toLowerCase();
function escapeRegex(str: string) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
for (const trigger of triggers) {
if (contentLower.includes(trigger.trigger_word.toLowerCase())) {
const word = trigger.trigger_word.trim().toLowerCase();
if (!word) continue;
const pattern = new RegExp(`(?:^|\\s|\\b)${escapeRegex(word)}(?:$|\\s|\\b)`, 'i');
if (pattern.test(message.content)) {
await message.channel.send(trigger.response_text);
return;
}

View File

@@ -1,44 +1,33 @@
import { Events, Message, TextChannel, EmbedBuilder } from 'discord.js';
async function sendLog(client: any, guildId: string, event: string, embed: EmbedBuilder) {
const settings = client.DB.getSettings(guildId);
if (!settings?.log_events || !settings?.log_channel) return;
const activeEvents = settings.log_events.split(',').filter(Boolean);
if (!activeEvents.includes(event)) return;
const channel = client.guilds.cache.get(guildId)?.channels.cache.get(settings.log_channel);
if (!channel || !channel.isTextBased()) return;
try {
await (channel as any).send({ embeds: [embed] });
} catch (error) {
console.error(`[LOG] Error sending log (${event}):`, error);
}
}
import { Events, Message, EmbedBuilder } from 'discord.js';
import { EventLogger } from '../structures/EventLogger.js';
export default {
name: Events.MessageDelete,
async execute(message: Message) {
if (!message.guild || !message.author || message.author.bot) return;
if (!message.guild) return;
if (message.author?.bot) return;
const authorTag = message.author ? `${message.author.tag} (<@${message.author.id}>)` : 'Unbekannter Nutzer (Nicht im Cache)';
const embed = new EmbedBuilder()
.setTitle('🗑️ Nachricht gelöscht')
.setColor(0xe74c3c)
.addFields(
{ name: 'Autor', value: `${message.author.tag} (<@${message.author.id}>)`, inline: true },
{ name: 'Autor', value: authorTag, inline: true },
{ name: 'Kanal', value: `<#${message.channelId}>`, inline: true }
)
.setTimestamp();
if (message.content) {
embed.addFields({ name: 'Inhalt', value: message.content.substring(0, 1024), inline: false });
} else {
embed.addFields({ name: 'Inhalt', value: '*(Nachricht nicht im Cache oder enthielt nur Medien)*', inline: false });
}
if (message.attachments.size > 0) {
if (message.attachments && message.attachments.size > 0) {
embed.addFields({ name: 'Anhänge', value: `${message.attachments.size} Datei(en)`, inline: true });
}
await sendLog(message.client, message.guildId!, 'messages', embed);
await EventLogger.sendLog(message.client as any, message.guildId!, 'messages', embed);
},
};

View File

@@ -1,21 +1,5 @@
import { Events, Message, EmbedBuilder } from 'discord.js';
async function sendLog(client: any, guildId: string, event: string, embed: EmbedBuilder) {
const settings = client.DB.getSettings(guildId);
if (!settings?.log_events || !settings?.log_channel) return;
const activeEvents = settings.log_events.split(',').filter(Boolean);
if (!activeEvents.includes(event)) return;
const channel = client.guilds.cache.get(guildId)?.channels.cache.get(settings.log_channel);
if (!channel || !channel.isTextBased()) return;
try {
await (channel as any).send({ embeds: [embed] });
} catch (error) {
console.error(`[LOG] Error sending log (${event}):`, error);
}
}
import { EventLogger } from '../structures/EventLogger.js';
export default {
name: Events.MessageUpdate,
@@ -37,6 +21,6 @@ export default {
)
.setTimestamp();
await sendLog(newMessage.client, newMessage.guildId!, 'messages', embed);
await EventLogger.sendLog(newMessage.client as any, newMessage.guildId!, 'messages', embed);
},
};

View File

@@ -151,6 +151,11 @@ export class DB {
)
`).run();
// Performance Indexes
db.prepare('CREATE INDEX IF NOT EXISTS idx_mod_logs_guild_user ON mod_logs(guild_id, user_id, action)').run();
db.prepare('CREATE INDEX IF NOT EXISTS idx_reminders_target ON reminders(target_time)').run();
db.prepare('CREATE INDEX IF NOT EXISTS idx_twitch_monitors_guild ON twitch_monitors(guild_id)').run();
// Migration logic
const columns = db.prepare("PRAGMA table_info(guild_settings)").all() as any[];
const columnNames = columns.map((c: any) => c.name);

View File

@@ -0,0 +1,26 @@
import { EmbedBuilder, TextChannel } from 'discord.js';
import { ExtendedClient } from './ExtendedClient.js';
export class EventLogger {
static async sendLog(client: ExtendedClient, guildId: string, event: string, embed: EmbedBuilder) {
if (!guildId) return;
const settings = client.DB?.getSettings(guildId);
if (!settings?.log_events || !settings?.log_channel) return;
const activeEvents = settings.log_events.split(',').filter(Boolean);
if (!activeEvents.includes(event)) return;
const guild = client.guilds.cache.get(guildId);
if (!guild) return;
const channel = guild.channels.cache.get(settings.log_channel);
if (!channel || !channel.isTextBased()) return;
try {
await (channel as TextChannel).send({ embeds: [embed] });
} catch (error) {
console.error(`[LOG] Error sending log (${event}) in guild ${guildId}:`, error);
}
}
}