Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- const { Client, GatewayIntentBits, EmbedBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, PermissionsBitField, SlashCommandBuilder, REST, Routes, ChannelType } = require('discord.js');
- const dotenv = require('dotenv');
- const sqlite3 = require('sqlite3');
- const { open } = require('sqlite');
- dotenv.config();
- const TOKEN = process.env.DISCORD_HELPER_TOKEN;
- if (!TOKEN) throw new Error('שגיאה: לא נמצא טוקן. אנא צור קובץ .env עם DISCORD_HELPER_TOKEN');
- const client = new Client({
- intents: [
- GatewayIntentBits.Guilds,
- GatewayIntentBits.GuildMembers,
- GatewayIntentBits.GuildMessages,
- GatewayIntentBits.MessageContent,
- GatewayIntentBits.GuildVoiceStates,
- GatewayIntentBits.DirectMessages, // Added for DM handling
- ],
- });
- const databases = new Map();
- const muteTimers = new Map();
- const banTimers = new Map();
- const voiceMuteFlags = new Map();
- const guildData = {
- admin_role_mentions: new Map(),
- log_channel_id: new Map(),
- help_requests: new Map(),
- taken_requests: new Map(),
- last_help_request: new Map(),
- cooldown_time: new Map(),
- admin_help_counts: new Map(),
- mute_role_id: new Map(),
- ban_role_id: new Map(),
- appeal_channel_id: new Map(),
- auto_mute_links: new Map(), // New: Enable/disable auto-mute for links
- auto_mute_duration: new Map(), // New: Mute duration for links
- allowed_link_roles: new Map(), // New: Roles allowed to send links
- spam_message_limit: new Map(), // Number of messages before triggering anti-spam
- spam_time_window: new Map(), // Time window in seconds for spam detection
- spam_mute_duration: new Map(), // Mute duration for spam violations
- allowed_spam_roles: new Map(), // Roles exempt from anti-spam
- user_message_timestamps: new Map(), // Tracks message timestamps per user per guild
- };
- // פונקציות עזר (unchanged, kept for completeness)
- const parseDuration = duration => {
- if (duration === '0') return Infinity;
- const match = /^(\d+)([smhd])$/.exec(duration);
- if (!match) return null;
- const value = parseInt(match[1]);
- const unit = { s: 1, m: 60, h: 3600, d: 86400 }[match[2]];
- return value * unit;
- };
- const formatDuration = duration => {
- if (duration === '0') return 'תמידי';
- const match = /^(\d+)([smhd])$/.exec(duration);
- if (!match) return duration;
- const value = parseInt(match[1]);
- const unit = { s: 'שניות', m: 'דקות', h: 'שעות', d: 'ימים' }[match[2]];
- return `${value} ${unit}`;
- };
- const formatTimeLeft = seconds => {
- if (seconds === Infinity) return 'תמידי';
- if (seconds <= 0) return 'פג תוקף';
- const parts = [];
- const days = Math.floor(seconds / 86400);
- const hours = Math.floor((seconds % 86400) / 3600);
- const minutes = Math.floor((seconds % 3600) / 60);
- const secs = Math.floor(seconds % 60);
- if (days) parts.push(`${days} ימים`);
- if (hours) parts.push(`${hours} שעות`);
- if (minutes) parts.push(`${minutes} דקות`);
- if (secs && !days && !hours) parts.push(`${secs} שניות`);
- return parts.join(', ') || 'פחות מדקה';
- };
- const initializeDatabase = async guildId => {
- try {
- if (!/^\d+$/.test(guildId)) {
- throw new Error(`Invalid guildId: ${guildId}`);
- }
- const db = await open({
- filename: `./helper_${guildId}.db`,
- driver: sqlite3.Database,
- });
- await db.exec(`
- CREATE TABLE IF NOT EXISTS guilds (
- guild_id TEXT PRIMARY KEY,
- admin_role_mentions TEXT,
- log_channel_id TEXT,
- cooldown_time INTEGER DEFAULT 5,
- mute_role_id TEXT,
- ban_role_id TEXT,
- appeal_channel_id TEXT,
- auto_mute_links BOOLEAN DEFAULT 0,
- auto_mute_duration TEXT DEFAULT '1h',
- allowed_link_roles TEXT DEFAULT '[]',
- spam_message_limit INTEGER DEFAULT 5, -- New: Max messages before spam detection
- spam_time_window INTEGER DEFAULT 10, -- New: Time window in seconds
- spam_mute_duration TEXT DEFAULT '1h', -- New: Mute duration for spam
- allowed_spam_roles TEXT DEFAULT '[]' -- New: Roles exempt from spam
- );
- -- Other table definitions remain unchanged --
- `);
- databases.set(guildId, db);
- await ensureColumns(db, guildId);
- return db;
- } catch (error) {
- console.error(`Error in initializeDatabase for guild ${guildId}:`, error);
- throw error;
- }
- };
- const ensureColumns = async (db, guildId) => {
- try {
- const addColumnIfMissing = async (table, column, type) => {
- const columns = await db.all(`PRAGMA table_info(${table})`);
- if (!columns.some(c => c.name === column)) {
- const query = `ALTER TABLE ${table} ADD COLUMN ${column} ${type}`;
- await db.exec(query);
- }
- };
- await addColumnIfMissing('guilds', 'mute_role_id', 'TEXT');
- await addColumnIfMissing('guilds', 'ban_role_id', 'TEXT');
- await addColumnIfMissing('guilds', 'appeal_channel_id', 'TEXT');
- await addColumnIfMissing('guilds', 'auto_mute_links', 'BOOLEAN DEFAULT 0');
- await addColumnIfMissing('guilds', 'auto_mute_duration', 'TEXT DEFAULT "1h"');
- await addColumnIfMissing('guilds', 'allowed_link_roles', 'TEXT DEFAULT "[]"');
- await addColumnIfMissing('guilds', 'spam_message_limit', 'INTEGER DEFAULT 5');
- await addColumnIfMissing('guilds', 'spam_time_window', 'INTEGER DEFAULT 10');
- await addColumnIfMissing('guilds', 'spam_mute_duration', 'TEXT DEFAULT "1h"');
- await addColumnIfMissing('guilds', 'allowed_spam_roles', 'TEXT DEFAULT "[]"');
- for (const col of [
- ['muted_users', 'muted_by', 'TEXT'],
- ['muted_users', 'duration', 'TEXT'],
- ['muted_users', 'reason', 'TEXT'],
- ['voice_muted_users', 'muted_by', 'TEXT'],
- ['voice_muted_users', 'duration', 'TEXT'],
- ['voice_muted_users', 'reason', 'TEXT'],
- ['voice_muted_users', 'source', 'TEXT'],
- ['banned_users', 'banned_by', 'TEXT'],
- ['banned_users', 'duration', 'TEXT'],
- ['banned_users', 'reason', 'TEXT'],
- ]) {
- await addColumnIfMissing(...col);
- }
- } catch (error) {
- console.error(`Error ensuring columns for guild ${guildId}:`, error);
- throw error;
- }
- };
- const handleError = async (guildId, error, context) => {
- console.error(`[${context}] שגיאה:`, error);
- try {
- if (guildId && guildData.log_channel_id.get(guildId)) {
- const logChannel = client.channels.cache.get(guildData.log_channel_id.get(guildId));
- if (logChannel) {
- await logChannel.send(`שגיאה ב-${context}: ${error.message}`).catch(err => console.error(`Failed to send error to log channel: ${err}`));
- }
- }
- if (error.code === 10062 && guildId) {
- const channel = client.channels.cache.get(guildId);
- if (channel) {
- await channel.send('שגיאה: האינטראקציה פגה. אנא נסה שוב.').catch(err => console.error(`Failed to send interaction expiry message: ${err}`));
- }
- }
- } catch (err) {
- console.error(`Error in handleError for ${context}:`, err);
- }
- };
- const checkAdminRole = (member, guildId) =>
- member.roles.cache.some(role => guildData.admin_role_mentions.get(guildId)?.includes(role.toString()));
- const hasAdministratorPermission = interactionOrMember =>
- interactionOrMember.member.permissions.has(PermissionsBitField.Flags.Administrator);
- const getUserVoiceChannel = member => member.voice?.channel?.toString() || 'ללא שיחה';
- const sendWebhookMessage = async (channel, user, message) => {
- try {
- const webhooks = await channel.fetchWebhooks();
- let webhook = webhooks.find(w => w.name === 'HelpBotWebhook') || (await channel.createWebhook({ name: 'HelpBotWebhook' }));
- await webhook.send({ content: message, username: user.displayName, avatarURL: user.displayAvatarURL() || user.defaultAvatarURL });
- } catch (error) {
- await handleError(channel.guild.id, error, 'sendWebhookMessage');
- }
- };
- // unmuteUser, unbanUser, unvoiceMuteUser functions remain unchanged
- const unmuteUser = async (guildId, userId, guild, reason = 'המיוט הסתיים', isManual = false, admin = null) => {
- const db = databases.get(guildId);
- const muteRoleId = guildData.mute_role_id.get(guildId);
- const member = await guild.members.fetch(userId).catch(() => null);
- if (!db || !member || !muteRoleId) return;
- await member.roles.remove(muteRoleId).catch(error => console.error(`Failed to remove mute role for ${userId}:`, error));
- const mutedUser = await db.get('SELECT roles FROM muted_users WHERE user_id = ?', userId);
- if (mutedUser?.roles) {
- const savedRoles = JSON.parse(mutedUser.roles).filter(roleId => guild.roles.cache.has(roleId));
- if (savedRoles.length) await member.roles.add(savedRoles).catch(error => console.error(`Failed to restore roles for ${userId}:`, error));
- }
- await db.run('DELETE FROM muted_users WHERE user_id = ?', userId);
- clearTimeout(muteTimers.get(`${guildId}_${userId}`));
- muteTimers.delete(`${guildId}_${userId}`);
- if (isManual) {
- const embed = new EmbedBuilder()
- .setTitle('המיוט שלך הוסר')
- .setDescription(`המיוט שלך בשרת **${guild.name}** הוסר.`)
- .addFields(
- { name: 'סיבה', value: reason || 'לא צוינה', inline: true },
- { name: 'הוסר על ידי', value: admin ? `<@${admin.id}>` : 'מנהל', inline: true },
- { name: 'תאריך ושעה', value: new Date().toLocaleString('he-IL'), inline: false }
- )
- .setColor('#00ff00')
- .setTimestamp()
- .setFooter({ text: 'בוט עזר', iconURL: client.user.displayAvatarURL() });
- await member.send({ embeds: [embed] }).catch(error => {
- console.error(`Failed to send DM to ${userId}:`, error);
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- logChannel.send(`לא ניתן לשלוח הודעה פרטית ל-<@${userId}> על הסרת המיוט. סיבה: ${error.message}`);
- }
- }
- });
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) await logChannel.send(`<@${admin.id}> הוריד את המיוט ל-<@${userId}>`);
- }
- }
- };
- const unbanUser = async (guildId, userId, guild, reason = 'הבאן הסתיים', isManual = false, admin = null) => {
- const db = databases.get(guildId);
- const banRoleId = guildData.ban_role_id.get(guildId);
- const member = await guild.members.fetch(userId).catch(() => null);
- if (!db || !member || !banRoleId) return;
- await member.roles.remove(banRoleId).catch(error => console.error(`Failed to remove ban role for ${userId}:`, error));
- const bannedUser = await db.get('SELECT roles FROM banned_users WHERE user_id = ?', userId);
- if (bannedUser?.roles) {
- const savedRoles = JSON.parse(bannedUser.roles).filter(roleId => guild.roles.cache.has(roleId));
- if (savedRoles.length) await member.roles.add(savedRoles).catch(error => console.error(`Failed to restore roles for ${userId}:`, error));
- }
- await db.run('DELETE FROM banned_users WHERE user_id = ?', userId);
- clearTimeout(banTimers.get(`${guildId}_${userId}`));
- banTimers.delete(`${guildId}_${userId}`);
- const embed = new EmbedBuilder()
- .setTitle('הבאן שלך הוסר')
- .setDescription(`הבאן שלך בשרת **${guild.name}** הוסר.`)
- .addFields(
- { name: 'סיבה', value: reason || 'לא צוינה', inline: true },
- { name: 'הוסר על ידי', value: isManual && admin ? `<@${admin.id}>` : 'אוטומטית', inline: true },
- { name: 'תאריך ושעה', value: new Date().toLocaleString('he-IL'), inline: false }
- )
- .setColor('#00ff00')
- .setTimestamp()
- .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
- await member.send({ embeds: [embed] }).catch(error => {
- console.error(`Failed to send DM to ${userId}:`, error);
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- logChannel.send(`לא ניתן לשלוח הודעה פרטית ל-<@${userId}> על הסרת הבאן. סיבה: ${error.message}`);
- }
- }
- });
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- const logMessage = isManual
- ? `<@${admin.id}> הוריד את הבאן ל-<@${userId}> (סיבה: ${reason})`
- : `הבאן של <@${userId}> הוסר אוטומטית (סיבה: ${reason})`;
- await logChannel.send(logMessage);
- }
- }
- };
- const unvoiceMuteUser = async (guildId, userId, guild, reason = 'הוויס מיוט הסתיים', isManual = false, admin = null) => {
- const db = databases.get(guildId);
- const member = await guild.members.fetch(userId).catch(() => null);
- if (!db || !member) return;
- const existingMute = await db.get('SELECT source, muted_by, reason AS mute_reason FROM voice_muted_users WHERE user_id = ?', userId);
- if (!existingMute) return;
- const muteKey = `voice_${guildId}_${userId}`;
- if (voiceMuteFlags.has(`unmute_${muteKey}`)) return;
- voiceMuteFlags.set(`unmute_${muteKey}`, true);
- try {
- if (!isManual) voiceMuteFlags.set(`timer_${guildId}_${userId}`, true);
- if (member.voice?.channel) {
- await member.voice.setMute(false, reason).catch(error => handleError(guildId, error, 'unvoiceMuteUser'));
- }
- await db.run('DELETE FROM voice_muted_users WHERE user_id = ?', userId);
- clearTimeout(muteTimers.get(muteKey));
- muteTimers.delete(muteKey);
- const embed = new EmbedBuilder()
- .setTitle('הוויס מיוט שלך הוסר')
- .setDescription(`הוויס מיוט שלך בשרת **${guild.name}** הוסר.`)
- .addFields(
- { name: 'סיבה', value: reason || 'לא צוינה', inline: true },
- { name: 'הוסר על ידי', value: isManual && admin ? `<@${admin.id}>` : 'אוטומטית', inline: true },
- { name: 'תאריך ושעה', value: new Date().toLocaleString('he-IL'), inline: false }
- )
- .setColor('#00ff00')
- .setTimestamp()
- .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
- await member.send({ embeds: [embed] }).catch(error => {
- console.error(`Failed to send DM to ${userId}:`, error);
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- logChannel.send(`לא ניתן לשלוח הודעה פרטית ל-<@${userId}> על הסרת הוויס מיוט. סיבה: ${error.message}`);
- }
- }
- });
- if (isManual) {
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- const mutedByText = existingMute.muted_by === 'מנהל דרך ממשק דיסקורד' ? existingMute.muted_by : `<@${existingMute.muted_by}>`;
- const removedByText = admin ? `<@${admin.id}>` : 'אוטומטית';
- const sourceText = existingMute.source === 'command' ? 'פקודה' : 'ממשק דיסקורד'; // תיקון: הגדרת sourceText
- await logChannel.send(
- `הוויס מיוט של <@${userId}> (${sourceText}, ניתן על ידי ${mutedByText}, סיבה: ${existingMute.mute_reason || 'לא צוינה'}) הוסר ${removedByText}. סיבה: ${reason}`
- ).catch(error => handleError(guildId, error, 'unvoiceMuteUser'));
- }
- }
- }
- } finally {
- voiceMuteFlags.delete(`unmute_${muteKey}`);
- if (!isManual) voiceMuteFlags.delete(`timer_${guildId}_${userId}`);
- }
- };
- const commands = [
- // Existing commands unchanged until 'setlogchannel'
- {
- builder: new SlashCommandBuilder()
- .setName('h')
- .setDescription('פתיחת קריאת עזרה'),
- handler: async (interaction, guildId, member, options) => {
- try {
- // Validate guildId
- if (!/^\d+$/.test(guildId)) {
- throw new Error(`Invalid guildId: ${guildId}`);
- }
- // Initialize guildData maps if not already initialized
- if (!guildData.help_requests.has(guildId)) {
- guildData.help_requests.set(guildId, new Map());
- }
- if (!guildData.last_help_request.has(guildId)) {
- guildData.last_help_request.set(guildId, new Map());
- }
- // Check cooldown
- const lastRequestTime = guildData.last_help_request.get(guildId).get(interaction.user.id) || 0;
- const cooldownTime = guildData.cooldown_time.get(guildId) || 5; // Default to 5 seconds
- const currentTime = Date.now() / 1000; // Current time in seconds
- const timeSinceLastRequest = currentTime - lastRequestTime;
- if (timeSinceLastRequest < cooldownTime) {
- const timeLeft = Math.ceil(cooldownTime - timeSinceLastRequest);
- return interaction.reply({ content: `אנא המתן ${timeLeft} שניות לפני פתיחת קריאת עזרה נוספת!`, ephemeral: true });
- }
- // Determine current voice channel (if any)
- const voiceChannel = member.voice.channel ? `<#${member.voice.channelId}>` : 'ללא שיחה';
- // Create help request
- guildData.help_requests.get(guildId).set(interaction.user.id, interaction.channelId);
- guildData.last_help_request.get(guildId).set(interaction.user.id, currentTime);
- // Create embed
- const embed = new EmbedBuilder()
- .setTitle('קריאת עזרה')
- .setDescription(`המשתמש <@${interaction.user.id}> צריך עזרה! ${guildData.admin_role_mentions.get(guildId)?.map(role => `${role}`).join(' ') || '@Helper'}\nשיחה נוכחית: ${voiceChannel}`)
- .setColor('#ff0000')
- .setTimestamp()
- .setFooter({ text: 'בוט עזר', iconURL: interaction.client.user.displayAvatarURL() });
- // Create button
- const button = new ButtonBuilder()
- .setCustomId(`handle_${interaction.user.id}_${guildId}`)
- .setLabel('טפל')
- .setStyle(ButtonStyle.Primary);
- const row = new ActionRowBuilder().addComponents(button);
- // Send message in the same channel
- await interaction.reply({ embeds: [embed], components: [row] });
- // Save data to database
- const db = databases.get(guildId) || await initializeDatabase(guildId);
- await db.run(
- 'INSERT OR REPLACE INTO help_requests (user_id, channel_id, created_at) VALUES (?, ?, ?)',
- [interaction.user.id, interaction.channelId, currentTime]
- );
- await saveData(guildId);
- } catch (error) {
- console.error(`Error in /h command for guild ${guildId}:`, error);
- await interaction.reply({ content: 'שגיאה בפתיחת קריאת עזרה.', ephemeral: true }).catch(() => {});
- await handleError(guildId, error, '/h command');
- }
- },
- },
- {
- builder: new SlashCommandBuilder().setName('tophelps').setDescription('מציג את רשימת המנהלים המובילים לפי מספר קריאות'),
- handler: async (context, guildId, member) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
- const adminCounts = guildData.admin_help_counts.get(guildId) || new Map();
- if (!adminCounts.size) return context.editReply({ content: 'אין נתונים על קריאות!' });
- const embed = new EmbedBuilder()
- .setTitle('🏆 מנהלי העזרה המובילים')
- .setDescription('רשימת המנהלים המובילים:')
- .setColor('#ffd700');
- [...adminCounts.entries()]
- .sort((a, b) => b[1] - a[1])
- .slice(0, 10)
- .forEach(([adminId, count], i) => {
- const member = context.guild.members.cache.get(adminId);
- if (member) embed.addFields({ name: `${i < 3 ? ['🥇', '🥈', '🥉'][i] : `#${i + 1}`} ${member.displayName}`, value: `קריאות: **${count}**`, inline: true });
- });
- await context.editReply({ embeds: [embed.setFooter({ text: 'תודה למנהלים!' })] });
- } catch (error) {
- await handleError(guildId, error, 'command_tophelps');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /tophelps.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /tophelps.').catch(() => {}));
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('resethelps')
- .setDescription('מאפס את טבלת העזרה')
- .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator),
- handler: async (context, guildId, member) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!hasAdministratorPermission({ member })) return context.editReply({ content: 'אין לך הרשאת Administrator!' });
- const adminCounts = guildData.admin_help_counts.get(guildId) || new Map();
- if (guildData.log_channel_id.get(guildId) && adminCounts.size) {
- const logChannel = client.channels.cache.get(guildData.log_channel_id.get(guildId));
- if (logChannel) {
- const embed = new EmbedBuilder()
- .setTitle('טבלת עזרה לפני איפוס')
- .setDescription('רשימת המנהלים לפני האיפוס:')
- .setColor('#ff9900')
- .setTimestamp();
- [...adminCounts.entries()]
- .sort((a, b) => b[1] - a[1])
- .forEach(([adminId, count], i) => {
- const member = context.guild.members.cache.get(adminId);
- if (member) embed.addFields({ name: `${i + 1}. ${member.displayName}`, value: `קריאות: **${count}**`, inline: true });
- });
- await logChannel.send({ embeds: [embed.setFooter({ text: `איפוס בוצע על ידי ${context.user.tag}` })] });
- }
- }
- guildData.admin_help_counts.set(guildId, new Map());
- await saveData(guildId);
- await context.editReply({ content: 'טבלת העזרה אופסה!' });
- } catch (error) {
- await handleError(guildId, error, 'command_resethelps');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /resethelps.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /resethelps.').catch(() => {}));
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('setcooldown')
- .setDescription('הגדרת זמן המתנה בין קריאות')
- .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
- .addIntegerOption(option => option.setName('seconds').setDescription('שניות').setRequired(true)),
- handler: async (context, guildId, member, options) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!hasAdministratorPermission({ member })) return context.editReply({ content: 'אין לך הרשאת Administrator!' });
- const seconds = options.getInteger('seconds');
- if (seconds < 0) return context.editReply({ content: 'זמן המתנה חייב להיות חיובי!' });
- guildData.cooldown_time.set(guildId, seconds);
- await saveData(guildId);
- await context.editReply({ content: `זמן ההמתנה עודכן ל-${seconds} שניות.` });
- } catch (error) {
- await handleError(guildId, error, 'command_setcooldown');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /setcooldown.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /setcooldown.').catch(() => {}));
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('setrolehelp')
- .setDescription('הגדרת עד 3 תפקידים לעזרה')
- .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
- .addRoleOption(option => option.setName('role1').setDescription('תפקיד ראשון').setRequired(true))
- .addRoleOption(option => option.setName('role2').setDescription('תפקיד שני').setRequired(false))
- .addRoleOption(option => option.setName('role3').setDescription('תפקיד שלישי').setRequired(false)),
- handler: async (context, guildId, member, options) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!hasAdministratorPermission({ member })) return context.editReply({ content: 'אין לך הרשאת Administrator!' });
- const roles = [
- options.getRole('role1')?.toString(),
- options.getRole('role2')?.toString(),
- options.getRole('role3')?.toString(),
- ].filter(Boolean);
- if (!roles.length) return context.editReply({ content: 'ציין לפחות תפקיד אחד!' });
- guildData.admin_role_mentions.set(guildId, roles);
- await saveData(guildId);
- await context.editReply({ content: `תפקידי המנהלים עודכנו ל: ${roles.join(' ')}` });
- } catch (error) {
- await handleError(guildId, error, 'command_setrolehelp');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /setrolehelp.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /setrolehelp.').catch(() => {}));
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('cc')
- .setDescription('מחיקת הודעות של משתמש')
- .addUserOption(option => option.setName('user').setDescription('משתמש').setRequired(true))
- .addIntegerOption(option => option.setName('count').setDescription('מספר הודעות').setRequired(true)),
- handler: async (context, guildId, member, options) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
- const targetUser = options.getMember('user');
- const count = options.getInteger('count');
- if (count <= 0 || count > 100) return context.editReply({ content: 'מספר ההודעות חייב להיות בין 1 ל-100!' });
- const messages = await context.channel.messages.fetch({ limit: 100 });
- const filteredMessages = messages.filter(msg => msg.author.id === targetUser.id).first(count);
- if (!filteredMessages.length) return context.editReply({ content: `לא נמצאו הודעות של ${targetUser}!` });
- const logContent = `הודעות שנמחקו עבור ${targetUser.displayName} (${targetUser.id}):\n` +
- filteredMessages.map(msg => `[${msg.createdAt.toISOString().slice(0, 19).replace('T', ' ')}] ${msg.content}\n${msg.attachments.size ? `קבצים: ${msg.attachments.map(att => att.url).join(', ')}\n` : ''}`).join('');
- await context.channel.bulkDelete(filteredMessages);
- await context.editReply({ content: `נמחקו ${filteredMessages.length} הודעות של ${targetUser}!` });
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send({
- content: `${context.user} מחק ${filteredMessages.length} הודעות של ${targetUser} בערוץ ${context.channel}`,
- files: [{ attachment: Buffer.from(logContent, 'utf8'), name: `deleted_messages_${targetUser.id}_${new Date().toISOString().replace(/[-:.]/g, '')}.txt` }],
- });
- }
- }
- } catch (error) {
- await handleError(guildId, error, 'command_cc');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /cc.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /cc.').catch(() => {}));
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('setlogchannel')
- .setDescription('הגדרת ערוץ לוגים')
- .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
- .addChannelOption(option => option.setName('channel').setDescription('ערוץ').setRequired(true)),
- handler: async (context, guildId, member, options) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!hasAdministratorPermission({ member })) return context.editReply({ content: 'אין לך הרשאת Administrator!' });
- const channel = options.getChannel('channel');
- if (channel.type !== ChannelType.GuildText) return context.editReply({ content: 'בחר ערוץ טקסט!' });
- guildData.log_channel_id.set(guildId, channel.id);
- await saveData(guildId);
- await context.editReply({ content: `ערוץ לוגים עודכן ל-${channel}` });
- } catch (error) {
- await handleError(guildId, error, 'command_setlogchannel');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /setlogchannel.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /setlogchannel.').catch(() => {}));
- }
- },
- },
- // New command: setappealchannel
- {
- builder: new SlashCommandBuilder()
- .setName('setappealchannel')
- .setDescription('הגדרת ערוץ לבקשות ערעור על באן')
- .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
- .addChannelOption(option => option.setName('channel').setDescription('ערוץ').setRequired(true)),
- handler: async (context, guildId, member, options) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!hasAdministratorPermission({ member })) return context.editReply({ content: 'אין לך הרשאת Administrator!' });
- const channel = options.getChannel('channel');
- if (channel.type !== ChannelType.GuildText) return context.editReply({ content: 'בחר ערוץ טקסט!' });
- guildData.appeal_channel_id.set(guildId, channel.id);
- await saveData(guildId);
- await context.editReply({ content: `ערוץ בקשות הערעור הוגדר ל-${channel}.` });
- } catch (error) {
- await handleError(guildId, error, 'command_setappealchannel');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /setappealchannel.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /setappealchannel.').catch(() => {}));
- }
- },
- },
- // Other existing commands (mute, unmute, setmuterole, mutelists, voicemute, unvoicemute, voicemutelists, setroleban, ban, unban, banslist, checkban) remain unchanged
- {
- builder: new SlashCommandBuilder()
- .setName('mute')
- .setDescription('מיוט משתמש')
- .addUserOption(option => option.setName('user').setDescription('משתמש').setRequired(true))
- .addStringOption(option => option.setName('duration').setDescription('משך המיוט (1h, 30m, 1d)').setRequired(true))
- .addStringOption(option => option.setName('reason').setDescription('סיבה').setRequired(true)),
- handler: async (context, guildId, member, options) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
- const targetUser = options.getMember('user');
- const duration = options.getString('duration');
- const reason = options.getString('reason');
- if (!targetUser) return context.editReply({ content: 'בחר משתמש תקין!' });
- if (targetUser.roles.highest.position >= member.roles.highest.position) {
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) await logChannel.send(`<@${context.user.id}> ניסה לתת מיוט ל-<@${targetUser.id}> אך נכשל כי תפקידו גבוה/שווה.`);
- }
- return context.editReply({ content: 'אינך יכול לתת מיוט למשתמש עם תפקיד גבוה/שווה!' });
- }
- const muteRoleId = guildData.mute_role_id.get(guildId);
- if (!muteRoleId) return context.editReply({ content: 'תפקיד המיוט לא הוגדר! השתמש ב-/setmuterole.' });
- const muteRole = context.guild.roles.cache.get(muteRoleId);
- if (!muteRole) return context.editReply({ content: 'תפקיד המיוט אינו קיים!' });
- const botMember = context.guild.members.me;
- if (!botMember.permissions.has(PermissionsBitField.Flags.ManageRoles) || muteRole.position >= botMember.roles.highest.position) {
- return context.editReply({ content: 'אין לי הרשאה לנהל תפקידים או שתפקיד המיוט גבוה מדי!' });
- }
- const durationSeconds = parseDuration(duration);
- if (!durationSeconds) return context.editReply({ content: 'משך זמן לא תקין!' });
- const db = databases.get(guildId) || await initializeDatabase(guildId);
- await ensureColumns(db, guildId);
- if (await db.get('SELECT * FROM muted_users WHERE user_id = ?', targetUser.id)) {
- return context.editReply({ content: 'המשתמש כבר ממווט!' });
- }
- const currentRoles = targetUser.roles.cache.map(role => role.id).filter(id => id !== targetUser.guild.id);
- await db.run(
- 'INSERT INTO muted_users (user_id, end_time, roles, muted_by, duration, reason) VALUES (?, ?, ?, ?, ?, ?)',
- targetUser.id, durationSeconds === Infinity ? null : Date.now() / 1000 + durationSeconds, JSON.stringify(currentRoles), context.user.id, duration, reason
- );
- await targetUser.roles.set([muteRoleId]).catch(error => { throw new Error(`Failed to set mute role: ${error.message}`); });
- if (durationSeconds !== Infinity) {
- muteTimers.set(`${guildId}_${targetUser.id}`, setTimeout(() => unmuteUser(guildId, targetUser.id, context.guild), durationSeconds * 1000));
- }
- const endDate = durationSeconds === Infinity ? 'תמידי' : new Date((Date.now() / 1000 + durationSeconds) * 1000).toLocaleString('he-IL');
- const embed = new EmbedBuilder()
- .setTitle('קיבלת מיוט')
- .setDescription(`קיבלת מיוט בשרת **${context.guild.name}**.`)
- .addFields(
- { name: 'משך', value: formatDuration(duration), inline: true },
- { name: 'יסתיים ב', value: endDate, inline: true },
- { name: 'סיבה', value: reason || 'לא צוינה', inline: false },
- { name: 'נתן את המיוט', value: `<@${context.user.id}>`, inline: true }
- )
- .setColor('#ff0000')
- .setTimestamp()
- .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
- await targetUser.send({ embeds: [embed] }).catch(error => {
- console.error(`Failed to send DM to ${targetUser.id}:`, error);
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- logChannel.send(`לא ניתן לשלוח הודעה פרטית ל-<@${targetUser.id}> על המיוט. סיבה: ${error.message}`);
- }
- }
- });
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) await logChannel.send(`<@${context.user.id}> נתן מיוט ל-<@${targetUser.id}> ל-${formatDuration(duration)} (סיבה: ${reason})`);
- }
- await context.editReply({ content: `המשתמש <@${targetUser.id}> קיבל מיוט ל-${formatDuration(duration)}.` });
- } catch (error) {
- await handleError(guildId, error, 'command_mute');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /mute.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /mute.').catch(() => {}));
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('unmute')
- .setDescription('שחרור מיוט')
- .addUserOption(option => option.setName('user').setDescription('משתמש').setRequired(true)),
- handler: async (context, guildId, member, options) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
- const targetUser = options.getMember('user');
- if (!targetUser) return context.editReply({ content: 'בחר משתמש תקין!' });
- if (targetUser.roles.highest.position >= member.roles.highest.position) {
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) await logChannel.send(`<@${context.user.id}> ניסה להסיר מיוט מ-<@${targetUser.id}> אך נכשל כי תפקידו גבוה/שווה.`);
- }
- return context.editReply({ content: 'אינך יכול להסיר מיוט ממשתמש עם תפקיד גבוה/שווה!' });
- }
- const db = databases.get(guildId) || await initializeDatabase(guildId);
- await ensureColumns(db, guildId);
- if (!await db.get('SELECT * FROM muted_users WHERE user_id = ?', targetUser.id)) {
- return context.editReply({ content: 'המשתמש לא ממווט!' });
- }
- await unmuteUser(guildId, targetUser.id, context.guild, `שחרור ידני על ידי <@${context.user.id}>`, true, context.user);
- await context.editReply({ content: `המיוט של <@${targetUser.id}> שוחרר!` });
- } catch (error) {
- await handleError(guildId, error, 'command_unmute');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /unmute.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /unmute.').catch(() => {}));
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('setmuterole')
- .setDescription('הגדרת תפקיד המיוט')
- .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
- .addRoleOption(option => option.setName('role').setDescription('תפקיד').setRequired(true)),
- handler: async (context, guildId, member, options) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!hasAdministratorPermission({ member })) return context.editReply({ content: 'אין לך הרשאת Administrator!' });
- const role = options.getRole('role');
- if (!role) return context.editReply({ content: 'בחר תפקיד תקין!' });
- const botMember = context.guild.members.me;
- if (!botMember.permissions.has(PermissionsBitField.Flags.ManageRoles) || role.position >= botMember.roles.highest.position) {
- return context.editReply({ content: 'אין לי הרשאה לנהל תפקידים או שתפקיד המיוט גבוה מדי!' });
- }
- guildData.mute_role_id.set(guildId, role.id);
- await saveData(guildId);
- await context.editReply({ content: `תפקיד המיוט הוגדר ל-${role}.` });
- } catch (error) {
- await handleError(guildId, error, 'command_setmuterole');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /setmuterole.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /setmuterole.').catch(() => {}));
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('mutelists')
- .setDescription('רשימת משתמשים מושתקים'),
- handler: async (context, guildId, member) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
- const db = databases.get(guildId) || await initializeDatabase(guildId);
- await ensureColumns(db, guildId);
- const mutedUsers = await db.all('SELECT * FROM muted_users');
- if (!mutedUsers.length) return context.editReply({ content: 'אין משתמשים מושתקים!' });
- let response = 'משתמש | מיוט על ידי | משך המיוט | זמן שנותר | סיבה\n' + '-'.repeat(80) + '\n';
- for (const muted of mutedUsers) {
- const timeLeft = muted.end_time ? muted.end_time - Date.now() / 1000 : Infinity;
- const member = context.guild.members.cache.get(muted.user_id);
- const mutedBy = context.guild.members.cache.get(muted.muted_by) || { id: muted.muted_by };
- response += `${(member ? `<@${muted.user_id}>` : `<@${muted.user_id}>`).padEnd(20)} | ${mutedBy ? `<@${muted.muted_by}>` : 'לא ידוע'.padEnd(20)} | ${formatDuration(muted.duration || 'לא ידוע').padEnd(15)} | ${formatTimeLeft(timeLeft).padEnd(20)} | ${muted.reason || 'לא צוינה'}\n`;
- }
- await context.editReply({ content: response });
- } catch (error) {
- await handleError(guildId, error, 'command_mutelists');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /mutelists.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /mutelists.').catch(() => {}));
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('voicemute')
- .setDescription('וויס מיוט למשתמש')
- .addUserOption(option => option.setName('user').setDescription('משתמש').setRequired(true))
- .addStringOption(option => option.setName('duration').setDescription('משך (1h, 30m, 1d)').setRequired(true))
- .addStringOption(option => option.setName('reason').setDescription('סיבה').setRequired(false)),
- handler: async (context, guildId, member, options) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
- const targetUser = options.getMember('user');
- const duration = options.getString('duration');
- const reason = options.getString('reason') || 'לא צוינה';
- if (!targetUser) return context.editReply({ content: 'בחר משתמש תקין!' });
- if (targetUser.roles.highest.position >= member.roles.highest.position) {
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) await logChannel.send(`<@${context.user.id}> ניסה לתת וויס מיוט ל-<@${targetUser.id}> אך נכשל כי תפקידו גבוה/שווה.`);
- }
- return context.editReply({ content: 'אינך יכול לתת וויס מיוט למשתמש עם תפקיד גבוה/שווה!' });
- }
- const botMember = context.guild.members.me;
- if (!botMember.permissions.has(PermissionsBitField.Flags.MuteMembers)) return context.editReply({ content: 'אין לי הרשאה לנהל וויס מיוט!' });
- const durationSeconds = parseDuration(duration);
- if (!durationSeconds) return context.editReply({ content: 'משך זמן לא תקין!' });
- const db = databases.get(guildId) || await initializeDatabase(guildId);
- await ensureColumns(db, guildId);
- const existingMute = await db.get('SELECT * FROM voice_muted_users WHERE user_id = ?', targetUser.id);
- if (existingMute) return context.editReply({ content: 'המשתמש כבר מושתק קולית!' });
- const muteKey = `voice_${guildId}_${targetUser.id}`;
- clearTimeout(muteTimers.get(muteKey));
- muteTimers.delete(muteKey);
- await db.run(
- 'INSERT INTO voice_muted_users (user_id, end_time, muted_by, duration, reason, source) VALUES (?, ?, ?, ?, ?, ?)',
- targetUser.id,
- durationSeconds === Infinity ? null : Date.now() / 1000 + durationSeconds,
- context.user.id,
- duration,
- reason,
- 'command'
- );
- let voiceMuted = false;
- if (targetUser.voice?.channel) {
- voiceMuteFlags.set(`${guildId}_${targetUser.id}`, true);
- await targetUser.voice.setMute(true, reason).catch(error => {
- throw new Error(`Failed to set voice mute: ${error.message}`);
- });
- voiceMuted = true;
- voiceMuteFlags.delete(`${guildId}_${targetUser.id}`);
- }
- if (durationSeconds !== Infinity) {
- muteTimers.set(muteKey, setTimeout(() => unvoiceMuteUser(guildId, targetUser.id, context.guild), durationSeconds * 1000));
- }
- const endDate = durationSeconds === Infinity ? 'תמידי' : new Date((Date.now() / 1000 + durationSeconds) * 1000).toLocaleString('he-IL');
- const embed = new EmbedBuilder()
- .setTitle('קיבלת וויס מיוט')
- .setDescription(`קיבלת וויס מיוט בשרת **${context.guild.name}**.${!voiceMuted ? ' המיוט יחול כאשר תצטרף לערוץ קולי.' : ''}`)
- .addFields(
- { name: 'משך', value: formatDuration(duration), inline: true },
- { name: 'יסתיים ב', value: endDate, inline: true },
- { name: 'סיבה', value: reason, inline: false },
- { name: 'נתן את הוויס מיוט', value: `<@${context.user.id}>`, inline: true }
- )
- .setColor('#ff0000')
- .setTimestamp()
- .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
- await targetUser.send({ embeds: [embed] }).catch(error => {
- console.error(`Failed to send DM to ${targetUser.id}:`, error);
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- logChannel.send(`לא ניתן לשלוח הודעה פרטית ל-<@${targetUser.id}> על הוויס מיוט. סיבה: ${error.message}`);
- }
- }
- });
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(`<@${context.user.id}> נתן וויס מיוט ל-<@${targetUser.id}> ל-${formatDuration(duration)} (סיבה: ${reason})${!voiceMuted ? ' (יוחל כאשר המשתמש יצטרף לערוץ קולי)' : ''}`);
- }
- }
- await context.editReply({ content: `המשתמש <@${targetUser.id}> קיבל וויס מיוט ל-${formatDuration(duration)}.${!voiceMuted ? ' המיוט יחול כאשר יצטרף לערוץ קולי.' : ''}` });
- } catch (error) {
- voiceMuteFlags.delete(`${guildId}_${options.getUser('user').id}`);
- await handleError(guildId, error, 'command_voicemute');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /voicemute.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /voicemute.').catch(() => {}));
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('unvoicemute')
- .setDescription('שחרור וויס מיוט')
- .addUserOption(option => option.setName('user').setDescription('משתמש').setRequired(true)),
- handler: async (context, guildId, member, options) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
- const targetUser = options.getMember('user');
- if (!targetUser) return context.editReply({ content: 'בחר משתמש תקין!' });
- if (targetUser.roles.highest.position >= member.roles.highest.position) {
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) await logChannel.send(`<@${context.user.id}> ניסה להסיר וויס מיוט מ-<@${targetUser.id}> אך נכשל כי תפקידו גבוה/שווה.`);
- }
- return context.editReply({ content: 'אינך יכול להסיר וויס מיוט ממשתמש עם תפקיד גבוה/שווה!' });
- }
- const db = databases.get(guildId) || await initializeDatabase(guildId);
- await ensureColumns(db);
- if (!await db.get('SELECT * FROM voice_muted_users WHERE user_id = ?', targetUser.id)) {
- return context.editReply({ content: 'המשתמש לא מושתק קולית!' });
- }
- await unvoiceMuteUser(guildId, targetUser.id, context.guild, `שחרור ידני על ידי <@${context.user.id}>`, true, context.user);
- await context.editReply({ content: `הוויס מיוט של <@${targetUser.id}> שוחרר!` });
- } catch (error) {
- await handleError(guildId, error, 'command_unvoicemute');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /unvoicemute.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /unvoicemute.').catch(() => {}));
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('voicemutelists')
- .setDescription('רשימת משתמשים בוויס מיוט'),
- handler: async (context, guildId, member) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
- const db = databases.get(guildId) || await initializeDatabase(guildId);
- await ensureColumns(db);
- const voiceMutedUsers = await db.all('SELECT * FROM voice_muted_users');
- if (!voiceMutedUsers.length) return context.editReply({ content: 'אין משתמשים בוויס מיוט!' });
- let response = 'משתמש | וויס מיוט על ידי | משך המיוט | זמן שנותר | סיבה | מקור\n' + '-'.repeat(100) + '\n';
- for (const muted of voiceMutedUsers) {
- const timeLeft = muted.end_time ? muted.end_time - Date.now() / 1000 : Infinity;
- const member = context.guild.members.cache.get(muted.user_id);
- const mutedBy = context.guild.members.cache.get(muted.muted_by) || { id: muted.user_id };
- const sourceText = muted.source === 'command' ? 'פקודה' : 'ממשק דיסקורד';
- response += `${(member ? `<@${muted.user_id}>` : `<@${muted.user_id}>`).padEnd(20)} | ${mutedBy ? `<@${muted.muted_by}>` : 'לא ידוע'.padEnd(20)} | ${formatDuration(muted.duration || 'לא ידוע').padEnd(15)} | ${formatTimeLeft(timeLeft).padEnd(20)} | ${(muted.reason || 'לא צוינה').padEnd(20)} | ${sourceText}\n`;
- }
- await context.editReply({ content: response });
- } catch (error) {
- await handleError(guildId, error, 'command_voicemutelists');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /voicemutelists.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /voicemutelists.').catch(() => {}));
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('setroleban')
- .setDescription('הגדרת תפקיד הבאן')
- .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
- .addRoleOption(option => option.setName('role').setDescription('תפקיד').setRequired(true)),
- handler: async (context, guildId, member, options) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!hasAdministratorPermission(context)) return context.editReply({ content: 'אין לך הרשאת Administrator!' });
- const role = options.getRole('role');
- if (!role) return context.editReply({ content: 'בחר תפקיד תקין!' });
- const botMember = context.guild.members.me;
- if (!botMember.permissions.has(PermissionsBitField.Flags.ManageRoles) || role.position >= botMember.roles.highest.position) {
- return context.editReply({ content: 'אין לי הרשאה לנהל תפקידים או שתפקיד הבאן גבוה מדי!' });
- }
- guildData.ban_role_id.set(guildId, role.id);
- await saveData(guildId);
- await context.editReply({ content: `תפקיד הבאן הוגדר ל-${role}.` });
- } catch (error) {
- await handleError(guildId, error, 'command_setroleban');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /setroleban.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /setroleban.').catch(() => {}));
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('ban')
- .setDescription('באן')
- .addUserOption(option => option.setName('user').setDescription('משתמש').setRequired(true))
- .addStringOption(option => option.setName('duration').setDescription('משך הבאן (1h, 30m, 1d, 0 לתמידי)').setRequired(true))
- .addStringOption(option => option.setName('reason').setDescription('סיבה').setRequired(true)),
- handler: async (context, guildId, member, options) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
- const targetUser = options.getMember('user');
- const duration = options.getString('duration');
- const reason = options.getString('reason');
- if (!targetUser) return context.editReply({ content: 'בחר משתמש תקין!' });
- if (targetUser.roles.highest.position >= member.roles.highest.position) {
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) await logChannel.send(`<@${context.user.id}> ניסה לתת באן ל-<@${targetUser.id}> אך נכשל כי תפקידו גבוה/שווה.`);
- }
- return context.editReply({ content: 'אינך יכול לתת באן למשתמש עם תפקיד גבוה/שווה!' });
- }
- const banRoleId = guildData.ban_role_id.get(guildId);
- if (!banRoleId) return context.editReply({ content: 'תפקיד הבאן לא הוגדר! השתמש ב-/setroleban.' });
- const banRole = context.guild.roles.cache.get(banRoleId);
- if (!banRole) return context.editReply({ content: 'תפקיד הבאן אינו קיים!' });
- const botMember = context.guild.members.me;
- if (!botMember.permissions.has(PermissionsBitField.Flags.ManageRoles) || banRole.position >= botMember.roles.highest.position) {
- return context.editReply({ content: 'אין לי הרשאה לנהל תפקידים או שתפקיד הבאן גבוה מדי!' });
- }
- const durationSeconds = parseDuration(duration);
- if (!durationSeconds) return context.editReply({ content: 'משך זמן לא תקין!' });
- const db = databases.get(guildId) || await initializeDatabase(guildId);
- await ensureColumns(db, guildId);
- if (await db.get('SELECT * FROM banned_users WHERE user_id = ?', targetUser.id)) {
- return context.editReply({ content: 'המשתמש כבר בבאן!' });
- }
- const currentRoles = targetUser.roles.cache.map(role => role.id).filter(id => id !== targetUser.guild.id);
- await db.run(
- 'INSERT INTO banned_users (user_id, end_time, roles, banned_by, duration, reason) VALUES (?, ?, ?, ?, ?, ?)',
- targetUser.id, durationSeconds === Infinity ? null : Date.now() / 1000 + durationSeconds, JSON.stringify(currentRoles), context.user.id, duration, reason
- );
- await targetUser.roles.set([banRoleId]).catch(error => { throw new Error(`Failed to set ban role: ${error.message}`); });
- if (durationSeconds !== Infinity) {
- banTimers.set(`${guildId}_${targetUser.id}`, setTimeout(() => unbanUser(guildId, targetUser.id, context.guild), durationSeconds * 1000));
- }
- const endDate = durationSeconds === Infinity ? 'תמידי' : new Date((Date.now() / 1000 + durationSeconds) * 1000).toLocaleString('he-IL');
- const embed = new EmbedBuilder()
- .setTitle('קיבלת באן')
- .setDescription(`קיבלת באן בשרת **${context.guild.name}**. לשליחת ערעור, שלח הודעה פרטית לבוט עם המילה "ערעור" והסבר מפורט.`)
- .addFields(
- { name: 'משך', value: formatDuration(duration), inline: true },
- { name: 'יסתיים ב', value: endDate, inline: true },
- { name: 'סיבה', value: reason || 'לא צוינה', inline: false },
- { name: 'נתן את הבאן', value: `<@${context.user.id}>`, inline: true }
- )
- .setColor('#ff0000')
- .setTimestamp()
- .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
- await targetUser.send({ embeds: [embed] }).catch(error => {
- console.error(`Failed to send DM to ${targetUser.id}:`, error);
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- logChannel.send(`לא ניתן לשלוח הודעה פרטית ל-<@${targetUser.id}> על הבאן. סיבה: ${error.message}`);
- }
- }
- });
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) await logChannel.send(`<@${context.user.id}> נתן באן ל-<@${targetUser.id}> ל-${formatDuration(duration)} (סיבה: ${reason}).`);
- }
- await context.editReply({ content: `המשתמש <@${targetUser.id}> קיבל באן ל-${formatDuration(duration)}.` });
- } catch (error) {
- await handleError(guildId, error, 'command_ban');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /ban.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /ban.').catch(() => {}));
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('unban')
- .setDescription('שחרור באן')
- .addUserOption(option => option.setName('user').setDescription('משתמש').setRequired(true)),
- handler: async (context, guildId, member, options) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
- const targetUser = options.getMember('user');
- if (!targetUser) return context.editReply({ content: 'בחר משתמש תקין!' });
- if (targetUser.roles.highest.position >= member.roles.highest.position) {
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) await logChannel.send(`<@${context.user.id}> ניסה להסיר באן מ-<@${targetUser.id}> אך נכשל כי תפקידו גבוה/שווה.`);
- }
- return context.editReply({ content: 'אינך יכול להסיר באן ממשתמש עם תפקיד גבוה/שווה!' });
- }
- const db = databases.get(guildId) || await initializeDatabase(guildId);
- await ensureColumns(db, guildId);
- if (!await db.get('SELECT * FROM banned_users WHERE user_id = ?', targetUser.id)) {
- return context.editReply({ content: 'המשתמש לא בבאן!' });
- }
- await unbanUser(guildId, targetUser.id, context.guild, `שחרור ידני על ידי <@${context.user.id}>`, true, context.user);
- await context.editReply({ content: `הבאן של <@${targetUser.id}> שוחרר!` });
- } catch (error) {
- await handleError(guildId, error, 'command_unban');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /unban.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /unban.').catch(() => {}));
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('banslist')
- .setDescription('רשימת משתמשים בבאן'),
- handler: async (context, guildId, member) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
- const db = databases.get(guildId) || await initializeDatabase(guildId);
- await ensureColumns(db, guildId);
- const bannedUsers = await db.all('SELECT * FROM banned_users');
- if (!bannedUsers.length) return context.editReply({ content: 'אין משתמשים בבאן!' });
- let response = 'משתמש | באן על ידי | משך הבאן | זמן שנותר | סיבה\n' + '-'.repeat(80) + '\n';
- for (const banned of bannedUsers) {
- const timeLeft = banned.end_time ? banned.end_time - Date.now() / 1000 : Infinity;
- const member = context.guild.members.cache.get(banned.user_id);
- const bannedBy = context.guild.members.cache.get(banned.banned_by) || { id: banned.banned_by };
- response += `${(member ? `<@${banned.user_id}>` : `<@${banned.user_id}>`).padEnd(20)} | ${bannedBy ? `<@${banned.banned_by}>` : 'לא ידוע'.padEnd(20)} | ${formatDuration(banned.duration || 'לא ידוע').padEnd(15)} | ${formatTimeLeft(timeLeft).padEnd(20)} | ${banned.reason || 'לא צוינה'}\n`;
- }
- await context.editReply({ content: response });
- } catch (error) {
- await handleError(guildId, error, 'command_banslist');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /banslist.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /banslist.').catch(() => {}));
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('checkban')
- .setDescription('בודק מי נתן באן למשתמש מסוים')
- .addStringOption(option =>
- option.setName('user_id')
- .setDescription('ה-ID של המשתמש לבדיקה')
- .setRequired(true)
- ),
- handler: async (context, guildId, member, options) => {
- await context.deferReply({ ephemeral: true });
- const userId = options.getString('user_id');
- const db = databases.get(guildId);
- if (!db) return context.editReply({ content: 'מאגר הנתונים לא זמין.', ephemeral: true });
- try {
- if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
- const bannedUser = await db.get('SELECT banned_by, reason, duration, end_time FROM banned_users WHERE user_id = ?', userId);
- if (!bannedUser) return context.editReply({ content: 'המשתמש לא נמצא ברשימת החסומים.', ephemeral: true });
- const bannedBy = await context.client.users.fetch(bannedUser.banned_by).catch(() => null);
- const timeLeft = bannedUser.end_time ? (bannedUser.end_time - Date.now() / 1000) : Infinity;
- const formattedTimeLeft = timeLeft === Infinity ? 'תמידי' : formatTimeLeft(timeLeft);
- const formattedDuration = bannedUser.duration === '0' ? 'תמידי' : formatDuration(bannedUser.duration);
- await context.editReply({
- content: `**משתמש:** <@${userId}>\n**מי נתן את הבאן:** ${bannedBy ? `<@${bannedBy.id}>` : 'לא ידוע'}\n**סיבה:** ${bannedUser.reason || 'לא צוינה'}\n**משך:** ${formattedDuration}\n**זמן שנותר:** ${formattedTimeLeft}`,
- ephemeral: true
- });
- } catch (error) {
- await handleError(guildId, error, 'command_checkban');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /checkban.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /checkban.').catch(() => {}));
- }
- }
- },
- {
- builder: new SlashCommandBuilder()
- .setName('toggleautomutelinks')
- .setDescription('הפעלה/כיבוי של מיוט אוטומטי לשליחת קישורי דיסקורד')
- .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
- .addBooleanOption(option =>
- option.setName('enabled').setDescription('הפעל או כבה את המיוט האוטומטי').setRequired(true)
- ),
- handler: async (context, guildId, member, options) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!hasAdministratorPermission({ member })) {
- await context.editReply({ content: 'אין לך הרשאת Administrator!' });
- return;
- }
- const enabled = options.getBoolean('enabled');
- guildData.auto_mute_links.set(guildId, enabled);
- await saveData(guildId);
- await context.editReply({
- content: `מיוט אוטומטי לשליחת קישורי דיסקורד ${enabled ? 'הופעל' : 'כובה'} בהצלחה.`,
- });
- // שליחת לוג לערוץ הלוגים רק על שינוי מוצלח
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = context.client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(`<@${member.user.id}> ${enabled ? 'הפעיל' : 'כיבה'} את המיוט האוטומטי לשליחת קישורי דיסקורד בשרת.`);
- }
- }
- } catch (error) {
- await handleError(guildId, error, 'command_toggleautomutelinks');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /toggleautomutelinks.' }).catch(() => {
- context.channel.send('שגיאה בביצוע הפקודה /toggleautomutelinks.').catch(() => {});
- });
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('setautomuteduration')
- .setDescription('הגדרת משך המיוט לשליחת קישורי דיסקורד')
- .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
- .addStringOption(option =>
- option.setName('duration').setDescription('משך המיוט (לדוגמה: 1h, 30m, 1d)').setRequired(true)
- ),
- handler: async (context, guildId, member, options) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!hasAdministratorPermission({ member })) {
- await context.editReply({ content: 'אין לך הרשאת Administrator!' });
- return;
- }
- const duration = options.getString('duration');
- const durationSeconds = parseDuration(duration);
- if (!durationSeconds) {
- await context.editReply({ content: 'משך זמן לא תקין!' });
- return;
- }
- guildData.auto_mute_duration.set(guildId, duration);
- await saveData(guildId);
- await context.editReply({
- content: `משך המיוט האוטומטי עודכן ל-${formatDuration(duration)}.`,
- });
- // שליחת לוג לערוץ הלוגים רק על שינוי מוצלח
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = context.client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(`<@${member.user.id}> עדכן את משך המיוט האוטומטי לשליחת קישורי דיסקורד ל-${formatDuration(duration)} בשרת.`);
- }
- }
- } catch (error) {
- await handleError(guildId, error, 'command_setautomuteduration');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /setautomuteduration.' }).catch(() => {
- context.channel.send('שגיאה בביצוע הפקודה /setautomuteduration.').catch(() => {});
- });
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('setallowedlinkroles')
- .setDescription('הגדרת תפקידים שמורשים לשלוח קישורי דיסקורד')
- .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
- .addRoleOption(option => option.setName('role1').setDescription('תפקיד ראשון').setRequired(false))
- .addRoleOption(option => option.setName('role2').setDescription('תפקיד שני').setRequired(false))
- .addRoleOption(option => option.setName('role3').setDescription('תפקיד שלישי').setRequired(false)),
- handler: async (context, guildId, member, options) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!hasAdministratorPermission({ member })) {
- await context.editReply({ content: 'אין לך הרשאת Administrator!' });
- return;
- }
- const roles = [
- options.getRole('role1')?.id,
- options.getRole('role2')?.id,
- options.getRole('role3')?.id,
- ].filter(Boolean);
- if (!roles.length) {
- guildData.allowed_link_roles.set(guildId, []);
- await saveData(guildId);
- await context.editReply({ content: 'רשימת התפקידים המותרים לשליחת קישורי דיסקורד אופסה בהצלחה.' });
- // שליחת לוג לערוץ הלוגים על איפוס תפקידים
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = context.client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(`<@${member.user.id}> איפס את רשימת התפקידים המותרים לשליחת קישורי דיסקורד בשרת.`);
- }
- }
- return;
- }
- guildData.allowed_link_roles.set(guildId, roles);
- await saveData(guildId);
- await context.editReply({
- content: `תפקידים המורשים לשלוח קישורי דיסקורד עודכנו ל: ${roles
- .map(id => `<@&${id}>`)
- .join(' ')}`,
- });
- // שליחת לוג לערוץ הלוגים על עדכון תפקידים
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = context.client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(`<@${member.user.id}> עדכן את התפקידים המותרים לשליחת קישורי דיסקורד ל-${roles.map(id => `<@&${id}>`).join(', ')} בשרת.`);
- }
- }
- } catch (error) {
- await handleError(guildId, error, 'command_setallowedlinkroles');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /setallowedlinkroles.' }).catch(() => {
- context.channel.send('שגיאה בביצוע הפקודה /setallowedlinkroles.').catch(() => {});
- });
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('antilinkinfo')
- .setDescription('מציג מידע על מערכת האנטי-לינק של השרת'),
- handler: async (context, guildId, member) => {
- await context.deferReply({ ephemeral: true });
- try {
- // בדיקת הרשאה למנהלים בלבד
- if (!checkAdminRole(member, guildId)) {
- return context.editReply({ content: 'אין לך הרשאה!' });
- }
- // קבלת נתונים ממפות guildData
- const isAutoMuteEnabled = guildData.auto_mute_links.get(guildId) || false;
- const muteDuration = guildData.auto_mute_duration.get(guildId) || '1h';
- const allowedRoles = guildData.allowed_link_roles.get(guildId) || [];
- // יצירת תיאור התפקידים המורשים
- let allowedRolesText = 'אין תפקידים מורשים';
- if (allowedRoles.length > 0) {
- allowedRolesText = allowedRoles.map(id => `<@&${id}>`).join(', ');
- }
- // יצירת embed לתצוגת המידע
- const embed = new EmbedBuilder()
- .setTitle('מידע על מערכת האנטי-לינק')
- .setDescription('הגדרות מערכת האנטי-לינק של השרת:')
- .addFields(
- { name: 'סטטוס', value: isAutoMuteEnabled ? '🟢 מופעלת' : '🔴 כבויה', inline: true },
- { name: 'משך המיוט', value: formatDuration(muteDuration), inline: true },
- { name: 'תפקידים מורשים', value: allowedRolesText, inline: false }
- )
- .setColor(isAutoMuteEnabled ? '#00ff00' : '#ff0000')
- .setTimestamp()
- .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
- // שליחת התגובה
- await context.editReply({ embeds: [embed] });
- // רישום ללוג אם מוגדר ערוץ לוגים
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(`<@${member.user.id}> בדק את מצב מערכת האנטי-לינק בשרת.`);
- }
- }
- } catch (error) {
- await handleError(guildId, error, 'command_antilinkinfo');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /antilinkinfo.' }).catch(() => {
- context.channel.send('שגיאה בביצוע הפקודה /antilinkinfo.').catch(() => {});
- });
- }
- },
- builder: new SlashCommandBuilder()
- .setName('antispaminfo')
- .setDescription('מציג מידע על מערכת האנטי-ספאם של השרת'),
- handler: async (context, guildId, member) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!checkAdminRole(member, guildId)) {
- return context.editReply({ content: 'אין לך הרשאה!' });
- }
- const messageLimit = guildData.spam_message_limit.get(guildId) || 5;
- const timeWindow = guildData.spam_time_window.get(guildId) || 10;
- const muteDuration = guildData.spam_mute_duration.get(guildId) || '1h';
- const allowedRoles = guildData.allowed_spam_roles.get(guildId) || [];
- let allowedRolesText = 'אין תפקידים מורשים';
- if (allowedRoles.length > 0) {
- allowedRolesText = allowedRoles.map(id => `<@&${id}>`).join(', ');
- }
- const embed = new EmbedBuilder()
- .setTitle('מידע על מערכת האנטי-ספאם')
- .setDescription('הגדרות מערכת האנטי-ספאם של השרת:')
- .addFields(
- { name: 'מספר הודעות מקסימלי', value: `${messageLimit} הודעות`, inline: true },
- { name: 'חלון זמן', value: `${timeWindow} שניות`, inline: true },
- { name: 'משך המיוט', value: formatDuration(muteDuration), inline: true },
- { name: 'תפקידים מורשים', value: allowedRolesText, inline: false }
- )
- .setColor('#00ff00')
- .setTimestamp()
- .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
- await context.editReply({ embeds: [embed] });
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(`<@${member.user.id}> בדק את מצב מערכת האנטי-ספאם בשרת.`);
- }
- }
- } catch (error) {
- await handleError(guildId, error, 'command_antispaminfo');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /antispaminfo.' }).catch(() => {
- context.channel.send('שגיאה בביצוע הפקודה /antispaminfo.').catch(() => {});
- });
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('setantispam')
- .setDescription('הגדרת מערכת אנטי-ספאם (מספר הודעות, חלון זמן, משך מיוט)')
- .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
- .addIntegerOption(option =>
- option.setName('message_limit').setDescription('מספר הודעות מקסימלי').setRequired(true).setMinValue(1)
- )
- .addIntegerOption(option =>
- option.setName('time_window').setDescription('חלון זמן בשניות').setRequired(true).setMinValue(1)
- )
- .addStringOption(option =>
- option.setName('mute_duration').setDescription('משך המיוט (לדוגמה: 1h, 30m, 1d)').setRequired(true)
- ),
- handler: async (context, guildId, member, options) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!hasAdministratorPermission({ member })) {
- return context.editReply({ content: 'אין לך הרשאת Administrator!' });
- }
- const messageLimit = options.getInteger('message_limit');
- const timeWindow = options.getInteger('time_window');
- const muteDuration = options.getString('mute_duration');
- const durationSeconds = parseDuration(muteDuration);
- if (!durationSeconds) {
- return context.editReply({ content: 'משך המיוט לא תקין! השתמש בפורמט כמו 1h, 30m, 1d.' });
- }
- guildData.spam_message_limit.set(guildId, messageLimit);
- guildData.spam_time_window.set(guildId, timeWindow);
- guildData.spam_mute_duration.set(guildId, muteDuration);
- await saveData(guildId);
- await context.editReply({
- content: `מערכת האנטי-ספאם עודכנה: ${messageLimit} הודעות ב-${timeWindow} שניות יגרמו למיוט של ${formatDuration(muteDuration)}.`,
- });
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(
- `<@${member.user.id}> עדכן את מערכת האנטי-ספאם: ${messageLimit} הודעות ב-${timeWindow} שניות, מיוט ל-${formatDuration(muteDuration)}.`
- );
- }
- }
- } catch (error) {
- await handleError(guildId, error, 'command_setantispam');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /setantispam.' }).catch(() => {
- context.channel.send('שגיאה בביצוע הפקודה /setantispam.').catch(() => {});
- });
- }
- },
- },
- {
- builder: new SlashCommandBuilder()
- .setName('setallowspam')
- .setDescription('הגדרת תפקידים שמורשים לעקוף את מערכת האנטי-ספאם')
- .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
- .addRoleOption(option => option.setName('role1').setDescription('תפקיד ראשון').setRequired(false))
- .addRoleOption(option => option.setName('role2').setDescription('תפקיד שני').setRequired(false))
- .addRoleOption(option => option.setName('role3').setDescription('תפקיד שלישי').setRequired(false)),
- handler: async (context, guildId, member, options) => {
- await context.deferReply({ ephemeral: true });
- try {
- if (!hasAdministratorPermission({ member })) {
- return context.editReply({ content: 'אין לך הרשאת Administrator!' });
- }
- const roles = [
- options.getRole('role1')?.id,
- options.getRole('role2')?.id,
- options.getRole('role3')?.id,
- ].filter(Boolean);
- if (!roles.length) {
- guildData.allowed_spam_roles.set(guildId, []);
- await saveData(guildId);
- await context.editReply({ content: 'רשימת התפקידים המותרים לעקוף אנטי-ספאם אופסה בהצלחה.' });
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(`<@${member.user.id}> איפס את רשימת התפקידים המותרים לעקוף אנטי-ספאם בשרת.`);
- }
- }
- return;
- }
- guildData.allowed_spam_roles.set(guildId, roles);
- await saveData(guildId);
- await context.editReply({
- content: `תפקידים המורשים לעקוף אנטי-ספאם עודכנו ל: ${roles.map(id => `<@&${id}>`).join(' ')}`,
- });
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(
- `<@${member.user.id}> עדכן את התפקידים המותרים לעקוף אנטי-ספאם ל-${roles.map(id => `<@&${id}>`).join(', ')} בשרת.`
- );
- }
- }
- } catch (error) {
- await handleError(guildId, error, 'command_setallowspam');
- await context.editReply({ content: 'שגיאה בביצוע הפקודה /setallowspam.' }).catch(() => {
- context.channel.send('שגיאה בביצוע הפקודה /setallowspam.').catch(() => {});
- });
- }
- },
- }
- ];
- const loadData = async guildId => {
- try {
- const db = databases.get(guildId) || await initializeDatabase(guildId);
- let guildRecord = await db.get('SELECT * FROM guilds WHERE guild_id = ?', guildId);
- if (!guildRecord) {
- guildRecord = {
- guild_id: guildId,
- admin_role_mentions: JSON.stringify(['Helper']),
- log_channel_id: null,
- cooldown_time: 5,
- mute_role_id: null,
- ban_role_id: null,
- appeal_channel_id: null,
- auto_mute_links: 0,
- auto_mute_duration: '1h',
- allowed_link_roles: '[]',
- spam_message_limit: 5, // Default: 5 messages
- spam_time_window: 10, // Default: 10 seconds
- spam_mute_duration: '1h', // Default: 1 hour
- allowed_spam_roles: '[]', // Default: No roles
- };
- await db.run(
- 'INSERT INTO guilds (guild_id, admin_role_mentions, log_channel_id, cooldown_time, mute_role_id, ban_role_id, appeal_channel_id, auto_mute_links, auto_mute_duration, allowed_link_roles, spam_message_limit, spam_time_window, spam_mute_duration, allowed_spam_roles) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
- [
- guildId,
- guildRecord.admin_role_mentions,
- guildRecord.log_channel_id,
- guildRecord.cooldown_time,
- guildRecord.mute_role_id,
- guildRecord.ban_role_id,
- guildRecord.appeal_channel_id,
- guildRecord.auto_mute_links,
- guildRecord.auto_mute_duration,
- guildRecord.allowed_link_roles,
- guildRecord.spam_message_limit,
- guildRecord.spam_time_window,
- guildRecord.spam_mute_duration,
- guildRecord.allowed_spam_roles,
- ]
- );
- }
- guildData.admin_role_mentions.set(guildId, JSON.parse(guildRecord.admin_role_mentions));
- guildData.log_channel_id.set(guildId, guildRecord.log_channel_id);
- guildData.cooldown_time.set(guildId, guildRecord.cooldown_time);
- guildData.mute_role_id.set(guildId, guildRecord.mute_role_id);
- guildData.ban_role_id.set(guildId, guildRecord.ban_role_id);
- guildData.appeal_channel_id.set(guildId, guildRecord.appeal_channel_id);
- guildData.auto_mute_links.set(guildId, guildRecord.auto_mute_links === 1);
- guildData.auto_mute_duration.set(guildId, guildRecord.auto_mute_duration || '1h');
- guildData.allowed_link_roles.set(guildId, JSON.parse(guildRecord.allowed_link_roles || '[]'));
- guildData.spam_message_limit.set(guildId, guildRecord.spam_message_limit || 5);
- guildData.spam_time_window.set(guildId, guildRecord.spam_time_window || 10);
- guildData.spam_mute_duration.set(guildId, guildRecord.spam_mute_duration || '1h');
- guildData.allowed_spam_roles.set(guildId, JSON.parse(guildRecord.allowed_spam_roles || '[]'));
- guildData.user_message_timestamps.set(guildId, new Map());
- guildData.help_requests.set(guildId, new Map());
- guildData.last_help_request.set(guildId, new Map());
- for (const req of await db.all('SELECT * FROM help_requests')) {
- guildData.help_requests.get(guildId).set(req.user_id, req.channel_id);
- guildData.last_help_request.get(guildId).set(req.user_id, req.created_at);
- }
- guildData.taken_requests.set(guildId, new Map());
- for (const req of await db.all('SELECT * FROM taken_requests')) {
- guildData.taken_requests.get(guildId).set(req.user_id, req.admin_id);
- }
- guildData.admin_help_counts.set(guildId, new Map());
- for (const admin of await db.all('SELECT * FROM admin_help_counts')) {
- guildData.admin_help_counts.get(guildId).set(admin.admin_id, admin.count);
- }
- for (const muted of await db.all('SELECT * FROM muted_users')) {
- if (muted.end_time && muted.end_time > Date.now() / 1000) {
- const timeLeft = (muted.end_time - Date.now() / 1000) * 1000;
- muteTimers.set(`${guildId}_${muted.user_id}`, setTimeout(() => unmuteUser(guildId, muted.user_id, client.guilds.cache.get(guildId)), timeLeft));
- }
- }
- for (const muted of await db.all('SELECT * FROM voice_muted_users')) {
- if (muted.end_time && muted.end_time > Date.now() / 1000) {
- const timeLeft = (muted.end_time - Date.now() / 1000) * 1000;
- const muteKey = `voice_${guildId}_${muted.user_id}`;
- muteTimers.set(muteKey, setTimeout(() => unvoiceMuteUser(guildId, muted.user_id, client.guilds.cache.get(guildId)), timeLeft));
- }
- }
- for (const banned of await db.all('SELECT * FROM banned_users')) {
- if (banned.end_time && banned.end_time > Date.now() / 1000) {
- const timeLeft = (banned.end_time - Date.now() / 1000) * 1000;
- banTimers.set(`${guildId}_${banned.user_id}`, setTimeout(() => unbanUser(guildId, banned.user_id, client.guilds.cache.get(guildId)), timeLeft));
- }
- }
- } catch (error) {
- await handleError(guildId, error, 'loadData');
- }
- };
- const saveData = async guildId => {
- try {
- const db = databases.get(guildId);
- if (!db) return;
- await db.run(
- 'UPDATE guilds SET admin_role_mentions = ?, log_channel_id = ?, cooldown_time = ?, mute_role_id = ?, ban_role_id = ?, appeal_channel_id = ?, auto_mute_links = ?, auto_mute_duration = ?, allowed_link_roles = ?, spam_message_limit = ?, spam_time_window = ?, spam_mute_duration = ?, allowed_spam_roles = ? WHERE guild_id = ?',
- [
- JSON.stringify(guildData.admin_role_mentions.get(guildId) || ['Helper']),
- guildData.log_channel_id.get(guildId),
- guildData.cooldown_time.get(guildId) || 5,
- guildData.mute_role_id.get(guildId),
- guildData.ban_role_id.get(guildId),
- guildData.appeal_channel_id.get(guildId),
- guildData.auto_mute_links.get(guildId) ? 1 : 0,
- guildData.auto_mute_duration.get(guildId) || '1h',
- JSON.stringify(guildData.allowed_link_roles.get(guildId) || []),
- guildData.spam_message_limit.get(guildId) || 5,
- guildData.spam_time_window.get(guildId) || 10,
- guildData.spam_mute_duration.get(guildId) || '1h',
- JSON.stringify(guildData.allowed_spam_roles.get(guildId) || []),
- guildId,
- ]
- );
- await db.run('DELETE FROM help_requests WHERE 1=1');
- for (const [userId, channelId] of guildData.help_requests.get(guildId) || new Map()) {
- const createdAt = guildData.last_help_request.get(guildId)?.get(userId) || Date.now() / 1000;
- await db.run('INSERT INTO help_requests (user_id, channel_id, created_at) VALUES (?, ?, ?)', userId, channelId, createdAt);
- }
- await db.run('DELETE FROM taken_requests WHERE 1=1');
- for (const [userId, adminId] of guildData.taken_requests.get(guildId) || new Map()) {
- await db.run('INSERT INTO taken_requests (user_id, admin_id) VALUES (?, ?)', userId, adminId);
- }
- await db.run('DELETE FROM admin_help_counts WHERE 1=1');
- for (const [adminId, count] of guildData.admin_help_counts.get(guildId) || new Map()) {
- await db.run('INSERT INTO admin_help_counts (admin_id, count) VALUES (?, ?)', adminId, count);
- }
- } catch (error) {
- await handleError(guildId, error, 'saveData');
- }
- };
- // Register slash commands
- client.once('ready', async () => {
- console.log(`מחובר כ-${client.user.tag}`);
- try {
- const rest = new REST({ version: '10' }).setToken(TOKEN);
- const commandsData = commands.map(cmd => cmd.builder.toJSON());
- await rest.put(Routes.applicationCommands(client.user.id), { body: commandsData });
- console.log('פקודות נרשמו בהצלחה!');
- for (const guild of client.guilds.cache.values()) {
- await loadData(guild.id);
- }
- } catch (error) {
- console.error('שגיאה ברישום הפקודות:', error);
- }
- });
- // Handle slash commands
- client.on('interactionCreate', async interaction => {
- if (!interaction.isChatInputCommand() && !interaction.isButton()) return;
- const guildId = interaction.guildId;
- if (!guildId) {
- return interaction.reply({ content: 'פקודה זו זמינה רק בשרתים!', ephemeral: true }).catch(() => {});
- }
- try {
- // Validate guildId
- if (!/^\d+$/.test(guildId)) {
- throw new Error(`Invalid guildId: ${guildId}`);
- }
- const db = databases.get(guildId) || await initializeDatabase(guildId);
- await ensureColumns(db, guildId);
- if (interaction.isChatInputCommand()) {
- const command = commands.find(cmd => cmd.builder.name === interaction.commandName);
- if (command) {
- await command.handler(interaction, guildId, interaction.member, interaction.options);
- }
- } else if (interaction.isButton()) {
- const [action, userId, targetGuildId] = interaction.customId.split('_');
- if (targetGuildId !== guildId) {
- return interaction.reply({ content: 'גילדה לא תואמת!', ephemeral: true }).catch(() => {});
- }
- if (action === 'handle') {
- if (!checkAdminRole(interaction.member, guildId)) {
- return interaction.reply({ content: 'אין לך הרשאה!', ephemeral: true });
- }
- await interaction.deferReply({ ephemeral: true });
- // Determine the channel where the user is
- const channelId = guildData.help_requests.get(guildId)?.get(userId) || interaction.channelId;
- const channel = interaction.client.channels.cache.get(channelId);
- const channelName = channel ? `<#${channelId}>` : 'ללא שיחה';
- // Update guild data
- if (!guildData.taken_requests.has(guildId)) {
- guildData.taken_requests.set(guildId, new Map());
- }
- if (!guildData.admin_help_counts.has(guildId)) {
- guildData.admin_help_counts.set(guildId, new Map());
- }
- guildData.taken_requests.get(guildId).set(userId, interaction.user.id);
- guildData.admin_help_counts.get(guildId).set(interaction.user.id, (guildData.admin_help_counts.get(guildId).get(interaction.user.id) || 0) + 1);
- // Save data to database
- await db.run(
- 'INSERT OR REPLACE INTO taken_requests (user_id, admin_id) VALUES (?, ?)',
- [userId, interaction.user.id]
- );
- await db.run(
- 'INSERT OR REPLACE INTO admin_help_counts (admin_id, count) VALUES (?, ?)',
- [interaction.user.id, guildData.admin_help_counts.get(guildId).get(interaction.user.id)]
- );
- await saveData(guildId);
- // Remove the help request
- guildData.help_requests.get(guildId)?.delete(userId);
- await db.run('DELETE FROM help_requests WHERE user_id = ?', userId);
- // Send message to log channel
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = interaction.client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(`${interaction.user} לקח את הקריאה של <@${userId}>.`);
- } else {
- console.error(`Log channel ${logChannelId} not found for guild ${guildId}`);
- }
- } else {
- console.error(`No log channel set for guild ${guildId}`);
- }
- // Edit the original help request message to remove the button
- if (interaction.message) {
- await interaction.message.edit({ components: [] }).catch(err => console.error(`Failed to edit message: ${err}`));
- }
- await sendWebhookMessage(interaction.channel, interaction.user, 'הקריאה בטיפול');
- await interaction.editReply({ content: `לקחת את קריאת העזרה של <@${userId}>!` });
- // Clear taken_requests after handling
- guildData.taken_requests.get(guildId).delete(userId);
- await db.run('DELETE FROM taken_requests WHERE user_id = ?', userId);
- } else if (action === 'approve' || action === 'reject') {
- if (!checkAdminRole(interaction.member, guildId)) {
- return interaction.reply({ content: 'אין לך הרשאה!', ephemeral: true });
- }
- const appeal = await db.get('SELECT * FROM ban_appeals WHERE user_id = ? AND guild_id = ? AND status = ?', userId, guildId, 'pending');
- if (!appeal) {
- return interaction.editReply({ content: 'בקשת הערעור כבר לא קיימת או טופלה!', ephemeral: true });
- }
- await interaction.deferReply({ ephemeral: true });
- const status = action === 'approve' ? 'approved' : 'rejected';
- await db.run('UPDATE ban_appeals SET status = ? WHERE user_id = ? AND guild_id = ?', status, userId, guildId);
- const user = await client.users.fetch(userId).catch(() => null);
- const guild = client.guilds.cache.get(guildId);
- const member = await guild.members.fetch(userId).catch(() => null);
- if (action === 'approve' && member) {
- await unbanUser(guildId, userId, guild, `ערעור התקבל על ידי <@${interaction.user.id}>`, true, interaction.user);
- }
- const embed = new EmbedBuilder()
- .setTitle(`בקשת ערעור ${action === 'approve' ? 'אושרה' : 'נדחתה'}`)
- .setDescription(`בקשת הערעור שלך בשרת ${action === 'approve' ? 'אושרה' : 'נדחתה'}.`)
- .addFields(
- { name: 'טופל על ידי', value: `<@${interaction.user.id}>`, inline: true },
- { name: 'תאריך ושעה', value: new Date().toLocaleString('he-IL'), inline: true }
- )
- .setColor(action === 'approve' ? '#00ff00' : '#ff0000')
- .setTimestamp()
- .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
- if (user) {
- await user.send({ embeds: [embed] }).catch(error => {
- console.error(`Failed to send DM to ${userId}:`, error);
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- logChannel.send(`לא ניתן לשלוח הודעה פרטית ל-<@${userId}> על תוצאת הערעור. סיבה: ${error.message}`);
- }
- }
- });
- }
- const appealChannelId = guildData.appeal_channel_id.get(guildId);
- if (appealChannelId) {
- const appealChannel = client.channels.cache.get(appealChannelId);
- if (appealChannel) {
- const appealMessage = await appealChannel.messages.fetch(interaction.message.id).catch(() => null);
- if (appealMessage) {
- const updatedEmbed = new EmbedBuilder()
- .setTitle('בקשת ערעור')
- .setDescription(`**משתמש:** <@${userId}>\n**סיבת הבאן:** ${appeal.reason || 'לא צוינה'}\n**ערעור:** ${appeal.appeal_text}\n**סטטוס:** ${status === 'approved' ? 'אושר' : 'נדחה'}\n**טופל על ידי:** <@${interaction.user.id}>`)
- .setColor(status === 'approved' ? '#00ff00' : '#ff0000')
- .setTimestamp()
- .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
- await appealMessage.edit({ embeds: [updatedEmbed], components: [] });
- }
- }
- }
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(`<@${interaction.user.id}> ${action === 'approve' ? 'אישר' : 'דחה'} את בקשת הערעור של <@${userId}> בשרת **${guild.name}**.`);
- }
- }
- await interaction.editReply({ content: `בקשת הערעור של <@${userId}> ${action === 'approve' ? 'אושרה' : 'נדחתה'} בהצלחה.` });
- }
- }
- } catch (error) {
- console.error(`Error in interactionCreate for guild ${guildId}:`, error);
- await handleError(guildId, error, 'interactionCreate');
- if (!interaction.replied && !interaction.deferred) {
- await interaction.reply({ content: 'שגיאה בעיבוד הפקודה או הלחצן.', ephemeral: true }).catch(() => {});
- }
- }
- });
- // Handle messages for help requests and ban appeals
- client.on('messageCreate', async message => {
- if (message.author.bot) return;
- const guildId = message.guildId;
- const isDM = !guildId && message.channel.type === ChannelType.DM;
- if (!isDM && guildData.auto_mute_links.get(guildId)) {
- try {
- // רגקס מעודכן שתומך בקישורים עם או בלי https:// או http://
- const inviteRegex = /(?:https?:\/\/)?(?:www\.)?(?:discord\.(?:gg|com)|discordapp\.com)\/(?:invite[\/\\])?[a-zA-Z0-9\-_\\]+/gi;
- if (inviteRegex.test(message.content)) {
- const invites = message.content.match(inviteRegex) || [];
- // סינון קישורים שמכילים discord.gg, discord.com או discordapp.com
- const validInvites = invites.filter(invite =>
- invite.includes('discord.gg') ||
- invite.includes('discord.com') ||
- invite.includes('discordapp.com')
- );
- if (validInvites.length === 0) return;
- const guild = message.guild;
- const member = await guild.members.fetch(message.author.id).catch(() => null);
- if (!member) return;
- const allowedRoles = guildData.allowed_link_roles.get(guildId) || [];
- const hasAllowedRole = member.roles.cache.some(role => allowedRoles.includes(role.id));
- if (hasAllowedRole) return;
- const guildInvites = await guild.invites.fetch().catch(() => null);
- let isOwnInvite = false;
- if (guildInvites) {
- for (const invite of validInvites) {
- const foundInvite = guildInvites.find(i =>
- i.url === invite ||
- `https://discord.gg/${i.code}` === invite ||
- `https://discord.com/invite/${i.code}` === invite ||
- `https://discordapp.com/invite/${i.code}` === invite ||
- `discordapp.com/invite/${i.code}` === invite
- );
- if (foundInvite) {
- isOwnInvite = true;
- break;
- }
- }
- }
- if (isOwnInvite) return;
- const muteRoleId = guildData.mute_role_id.get(guildId);
- if (!muteRoleId) {
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send('⚠️ תפקיד המיוט לא הוגדר! אנא הגדר תפקיד מיוט עם `/setmuterole`.');
- }
- }
- return;
- }
- const muteRole = guild.roles.cache.get(muteRoleId);
- if (!muteRole) {
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send('⚠️ תפקיד המיוט לא נמצא בשרת! אנא הגדר תפקיד תקף עם `/setmuterole`.');
- }
- }
- return;
- }
- const botMember = guild.members.me;
- const hasManageRoles = botMember.permissions.has(PermissionsBitField.Flags.ManageRoles);
- const roleHierarchyValid = muteRole.position < botMember.roles.highest.position;
- if (!hasManageRoles || !roleHierarchyValid) {
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(`⚠️ אין לי הרשאה לנהל תפקידים או שתפקיד המיוט גבוה מדי בהיררכיה! אנא בדוק את ההרשאות שלי ואת מיקום תפקיד המיוט.`);
- }
- }
- return;
- }
- const db = databases.get(guildId) || await initializeDatabase(guildId);
- await ensureColumns(db, guildId);
- if (await db.get('SELECT * FROM muted_users WHERE user_id = ?', member.id)) return;
- const duration = guildData.auto_mute_duration.get(guildId) || '1h';
- const durationSeconds = parseDuration(duration);
- if (!durationSeconds) {
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(`⚠️ משך המיוט לא תקין: ${duration}. אנא הגדר משך תקף עם `/setautomuteduration`.`);
- }
- }
- return;
- }
- // מחיקת ההודעה לפני המיוט
- await message.delete().catch(err => {
- throw new Error(`שגיאה במחיקת הודעה: ${err.message}`);
- });
- const reason = 'שליחת קישור דיסקורד של שרת אחר';
- const currentRoles = member.roles.cache.map(role => role.id).filter(id => id !== guild.id);
- await db.run(
- 'INSERT INTO muted_users (user_id, end_time, roles, muted_by, duration, reason) VALUES (?, ?, ?, ?, ?, ?)',
- member.id,
- durationSeconds === Infinity ? null : Date.now() / 1000 + durationSeconds,
- JSON.stringify(currentRoles),
- client.user.id,
- duration,
- reason
- );
- await member.roles.set([muteRoleId]).catch(error => {
- throw new Error(`שגיאה בהגדרת תפקיד המיוט: ${error.message}`);
- });
- if (durationSeconds !== Infinity) {
- muteTimers.set(`${guildId}_${member.id}`, setTimeout(() => unmuteUser(guildId, member.id, guild), durationSeconds * 1000));
- }
- const endDate = durationSeconds === Infinity ? 'תמידי' : new Date((Date.now() / 1000 + durationSeconds) * 1000).toLocaleString('he-IL');
- const userMessage = message.content.replace(inviteRegex, '[קישור הוסר]') || 'אין תוכן נוסף';
- const embed = new EmbedBuilder()
- .setTitle('קיבלת מיוט')
- .setDescription(`קיבלת מיוט בשרת **${guild.name}** בגלל שליחת קישור דיסקורד של שרת אחר.`)
- .addFields(
- { name: 'משך', value: formatDuration(duration), inline: true },
- { name: 'יסתיים ב', value: endDate, inline: true },
- { name: 'סיבה', value: reason, inline: false },
- { name: 'נתן את המיוט', value: client.user.toString(), inline: true }
- )
- .setColor('#ff0000')
- .setTimestamp()
- .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
- await member.send({ embeds: [embed] }).catch(error => {
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- logChannel.send(`⚠️ לא ניתן לשלוח הודעה פרטית ל-<@${member.id}> על המיוט. סיבה: ${error.message}`);
- }
- }
- });
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- const logEmbed = new EmbedBuilder()
- .setTitle('מיוט אוטומטי')
- .setDescription(`משתמש קיבל מיוט בגלל שליחת קישור דיסקורד של שרת אחר.`)
- .addFields(
- { name: 'משתמש', value: `<@${member.id}>`, inline: true },
- { name: 'משך', value: formatDuration(duration), inline: true },
- { name: 'סיבה', value: reason, inline: false },
- { name: 'הודעה', value: userMessage, inline: false },
- { name: 'קישור', value: validInvites.join(', ') || 'אין קישור', inline: false }
- )
- .setColor('#ff0000')
- .setTimestamp()
- .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
- await logChannel.send({ embeds: [logEmbed] });
- }
- }
- }
- } catch (error) {
- await handleError(guildId, error, 'auto_mute_links');
- }
- }
- // נעילה זמנית למניעת עיבוד מקביל של ספאם
- const processingSpam = new Set();
- if (!isDM) {
- try {
- const messageLimit = guildData.spam_message_limit.get(guildId) || 5;
- const timeWindow = guildData.spam_time_window.get(guildId) || 10;
- const muteDuration = guildData.spam_mute_duration.get(guildId) || '10m';
- const allowedRoles = guildData.allowed_spam_roles.get(guildId) || [];
- console.log(`Anti-spam check for guild ${guildId}: messageLimit=${messageLimit}, timeWindow=${timeWindow}, muteDuration=${muteDuration}`);
- if (messageLimit === 0) {
- console.log(`Anti-spam disabled for guild ${guildId} (messageLimit=0)`);
- return;
- }
- const guild = message.guild;
- const member = await guild.members.fetch(message.author.id).catch(() => null);
- if (!member) {
- console.log(`Member ${message.author.id} not found in guild ${guildId}`);
- return;
- }
- const hasAllowedRole = member.roles.cache.some(role => allowedRoles.includes(role.id));
- if (hasAllowedRole) {
- console.log(`User ${member.id} has allowed role, skipping anti-spam`);
- return;
- }
- const spamKey = `${guildId}_${member.id}`;
- if (processingSpam.has(spamKey) || muteTimers.has(spamKey)) {
- console.log(`User ${member.id} is already being processed for spam or has an active mute timer in guild ${guildId}`);
- return;
- }
- if (!guildData.user_message_timestamps.get(guildId)) {
- guildData.user_message_timestamps.set(guildId, new Map());
- }
- const userTimestamps = guildData.user_message_timestamps.get(guildId);
- if (!userTimestamps.get(message.author.id)) {
- userTimestamps.set(message.author.id, []);
- }
- const timestamps = userTimestamps.get(message.author.id);
- timestamps.push(Date.now() / 1000);
- const cutoff = (Date.now() / 1000) - timeWindow;
- while (timestamps.length && timestamps[0] < cutoff) {
- timestamps.shift();
- }
- console.log(`User ${member.id} has ${timestamps.length}/${messageLimit} messages in ${timeWindow}s`);
- if (timestamps.length > messageLimit) {
- console.log(`Spam detected for user ${member.id} in guild ${guildId}`);
- processingSpam.add(spamKey);
- try {
- const muteRoleId = guildData.mute_role_id.get(guildId);
- if (!muteRoleId) {
- console.log(`No mute role defined for guild ${guildId}`);
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send('⚠️ תפקיד המיוט לא הוגדר! אנא הגדר תפקיד מיוט עם `/setmuterole`.');
- }
- }
- userTimestamps.set(member.id, []); // נקה טיימסטמפים
- return;
- }
- const muteRole = guild.roles.cache.get(muteRoleId);
- if (!muteRole) {
- console.log(`Mute role ${muteRoleId} not found in guild ${guildId}`);
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send('⚠️ תפקיד המיוט לא נמצא בשרת! אנא הגדר תפקיד תקף עם `/setmuterole`.');
- }
- }
- userTimestamps.set(member.id, []); // נקה טיימסטמפים
- return;
- }
- const botMember = guild.members.me;
- const hasManageRoles = botMember.permissions.has(PermissionsBitField.Flags.ManageRoles);
- const roleHierarchyValid = muteRole.position < botMember.roles.highest.position;
- if (!hasManageRoles || !roleHierarchyValid) {
- console.log(`Permission or hierarchy issue in guild ${guildId}: ManageRoles=${hasManageRoles}, RoleHierarchyValid=${roleHierarchyValid}`);
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(`⚠️ אין לי הרשאה לנהל תפקידים או שתפקיד המיוט גבוה מדי בהיררכיה!`);
- }
- }
- userTimestamps.set(member.id, []); // נקה טיימסטמפים
- return;
- }
- const db = databases.get(guildId) || await initializeDatabase(guildId);
- // בדיקה וניקוי רשומות מיוט ישנות
- await member.fetch(true); // רענן את התפקידים לפני הבדיקה
- const isActuallyMuted = member.roles.cache.has(muteRoleId);
- const dbMuted = db.get('SELECT * FROM muted_users WHERE user_id = ?', member.id);
- if (dbMuted) {
- const currentTime = Date.now() / 1000;
- if (dbMuted.end_time && dbMuted.end_time < currentTime) {
- console.log(`User ${member.id} has expired mute record in guild ${guildId}, removing...`);
- db.run('DELETE FROM muted_users WHERE user_id = ?', member.id);
- } else if (isActuallyMuted) {
- console.log(`User ${member.id} is already muted in guild ${guildId}`);
- userTimestamps.set(member.id, []); // נקה טיימסטמפים
- return;
- }
- }
- const durationSeconds = parseDuration(muteDuration);
- console.log(`Parsed mute duration for guild ${guildId}: ${muteDuration} -> ${durationSeconds} seconds`);
- if (!durationSeconds) {
- console.log(`Invalid mute duration ${muteDuration} for guild ${guildId}`);
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(`⚠️ משך המיוט לא תקין: ${muteDuration}. אנא הגדר משך תקף עם /setantispam.`);
- }
- }
- userTimestamps.set(member.id, []); // נקה טיימסטמפים
- return;
- }
- // הגדר muteTimers מיד לאחר זיהוי הספאם
- if (durationSeconds !== Infinity) {
- muteTimers.set(
- spamKey,
- setTimeout(() => unmuteUser(guildId, member.id, guild), durationSeconds * 1000)
- );
- }
- // שמור מספר ההודעות שזוהו כספאם ונקה טיימסטמפים
- const spamMessageCount = timestamps.length;
- userTimestamps.set(member.id, []);
- // איסוף כל ההודעות מאז ההודעה הראשונה בחלון
- const channel = message.channel;
- const firstTimestamp = timestamps[0] ? timestamps[0] * 1000 : (Date.now() / 1000 - timeWindow) * 1000; // זמן ההודעה הראשונה או חלון של 10 שניות
- const collectedMessages = await channel.awaitMessages({
- filter: msg => msg.author.id === member.id && msg.createdTimestamp >= firstTimestamp,
- max: messageLimit + 10, // אסוף עד 10 הודעות נוספות
- time: 10000, // המתן 10 שניות להודעות נוספות
- errors: ['time']
- }).catch(() => new Map());
- // איסוף הודעות בפגינציה (עד 100 הודעות בכל קריאה)
- let recentMessages = new Map();
- let lastMessageId = null;
- do {
- const fetchOptions = { limit: 100 };
- if (lastMessageId) fetchOptions.before = lastMessageId;
- const messages = await channel.messages.fetch(fetchOptions).catch(err => {
- console.error(`Failed to fetch messages: ${err.message}`);
- return new Map();
- });
- recentMessages = new Map([...recentMessages, ...messages]);
- lastMessageId = messages.size === 100 ? messages.lastKey() : null;
- } while (lastMessageId && recentMessages.size < 200); // הגבל ל-200 הודעות כדי למנוע לולאה אינסופית
- const userMessages = new Map([
- ...recentMessages.filter(msg => msg.author.id === member.id && msg.createdTimestamp >= firstTimestamp),
- ...collectedMessages
- ]);
- const messagesToDelete = Array.from(userMessages.values())
- .sort((a, b) => a.createdTimestamp - b.createdTimestamp); // מיון כרונולוגי
- const logContent = `הודעות שנמחקו עבור ${member.displayName} (${member.id}):\n` +
- messagesToDelete
- .map(msg => `[${msg.createdAt.toISOString().slice(0, 19).replace('T', ' ')}] ${msg.content || '[ריק]'}\n${msg.attachments.size ? `קבצים: ${msg.attachments.map(att => att.url).join(', ')}\n` : ''}`)
- .join('');
- // מחיקת ההודעות
- if (messagesToDelete.length > 0) {
- await channel.bulkDelete(messagesToDelete, true).catch(err => console.error(`Failed to bulk delete messages: ${err.message}`));
- }
- const reason = `ספאם: שליחת ${spamMessageCount} הודעות תוך ${timeWindow} שניות`; // השתמש במספר ההודעות שזוהו כספאם
- db.run(
- 'INSERT OR REPLACE INTO muted_users (user_id, end_time, roles, muted_by, duration, reason) VALUES (?, ?, ?, ?, ?, ?)',
- [
- member.id,
- durationSeconds === Infinity ? null : Date.now() / 1000 + durationSeconds,
- JSON.stringify(member.roles.cache.map(role => role.id).filter(id => id !== guild.id)),
- client.user.id,
- muteDuration,
- reason,
- ]
- );
- try {
- await member.roles.set([muteRoleId]);
- console.log(`Muted user ${member.id} in guild ${guildId} with role ${muteRoleId}`);
- } catch (error) {
- console.error(`Failed to mute user ${member.id} in guild ${guildId}: ${error.message}`);
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(`⚠️ שגיאה בהשתקת משתמש <@${member.id}>: ${error.message}`);
- }
- }
- muteTimers.delete(spamKey); // הסר טיימר אם המיוט נכשל
- return;
- }
- const endDate = durationSeconds === Infinity ? 'תמידי' : new Date((Date.now() / 1000 + durationSeconds) * 1000).toLocaleString('he-IL');
- const embed = new EmbedBuilder()
- .setTitle('קיבלת מיוט')
- .setDescription(`קיבלת מיוט בשרת **${guild.name}** בגלל ספאם.`)
- .addFields(
- { name: 'משך', value: formatDuration(muteDuration), inline: true },
- { name: 'יסתיים ב', value: endDate, inline: true },
- { name: 'סיבה', value: reason, inline: false },
- { name: 'נתן את המיוט', value: client.user.toString(), inline: true }
- )
- .setColor('#ff0000')
- .setTimestamp()
- .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
- await member.send({ embeds: [embed] }).catch(error => {
- console.log(`Failed to DM user ${member.id}: ${error.message}`);
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- logChannel.send(`⚠️ לא ניתן לשלוח הודעה פרטית ל-<@${member.id}> על המיוט. סיבה: ${error.message}`);
- }
- }
- });
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- const logEmbed = new EmbedBuilder()
- .setTitle('מיוט אוטומטי - ספאם')
- .setDescription(`משתמש קיבל מיוט בגלל ספאם.`)
- .addFields(
- { name: 'משתמש', value: `<@${member.id}>`, inline: true },
- { name: 'משך', value: formatDuration(muteDuration), inline: true },
- { name: 'סיבה', value: reason, inline: false },
- { name: 'הודעות שנמחקו', value: messagesToDelete.length ? logContent.slice(0, 1000) : 'אין הודעות שנמחקו', inline: false }
- )
- .setColor('#ff0000')
- .setTimestamp()
- .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
- await logChannel.send({
- embeds: [logEmbed],
- files: messagesToDelete.length ? [{
- attachment: Buffer.from(logContent, 'utf8'),
- name: `deleted_messages_${member.id}_${new Date().toISOString().replace(/[-:.]/g, '')}.txt`
- }] : []
- });
- }
- }
- } finally {
- processingSpam.delete(spamKey); // הסר את הנעילה
- }
- }
- userTimestamps.set(message.author.id, timestamps);
- } catch (error) {
- console.error(`Anti-spam error in guild ${guildId}: ${error.message}`);
- await handleError(guildId, error, 'anti_spam');
- }
- }
- // Handle !h in guild channels
- if (!isDM && message.content === '!h') {
- const guild = message.guild;
- if (!guild) return;
- const member = await guild.members.fetch(message.author.id).catch(() => null);
- if (!member) return;
- const command = commands.find(cmd => cmd.builder.name === 'h');
- if (command) {
- try {
- const fakeInteraction = {
- guildId: guild.id,
- user: message.author,
- member: member,
- channel: message.channel,
- channelId: message.channel.id,
- client: client,
- guild: guild,
- reply: async (options) => {
- if (options.embeds && options.components) {
- const sentMessage = await message.channel.send({
- embeds: options.embeds,
- components: options.components,
- });
- return sentMessage;
- }
- return { message: null };
- },
- deferReply: async () => {},
- editReply: async () => {}
- };
- await command.handler(fakeInteraction, guild.id, member, {});
- } catch (error) {
- await handleError(guild.id, error, 'message_h');
- }
- }
- return;
- }
- // Handle /h in guild channels
- if (!isDM && message.content === '/h') {
- const guild = message.guild;
- if (!guild) return;
- const member = await guild.members.fetch(message.author.id).catch(() => null);
- if (!member) return;
- const command = commands.find(cmd => cmd.builder.name === 'h');
- if (command) {
- try {
- const fakeInteraction = {
- guildId: guild.id,
- user: message.author,
- member: member,
- channel: message.channel,
- channelId: message.channel.id,
- client: client,
- guild: guild,
- reply: async (options) => {
- const sentMessage = await message.channel.send({
- content: options.content,
- embeds: options.embeds,
- components: options.components,
- });
- return sentMessage;
- },
- deferReply: async () => {},
- editReply: async (options) => {
- await message.channel.send({
- content: options.content,
- embeds: options.embeds,
- components: options.components,
- });
- }
- };
- await command.handler(fakeInteraction, guild.id, member, {});
- } catch (error) {
- await handleError(guild.id, error, 'message_h');
- await message.reply('שגיאה בביצוע הפקודה /h.').catch(() => {});
- }
- }
- return;
- }
- // Handle ban appeal requests in DM
- if (isDM && message.content.startsWith('ערעור')) {
- try {
- const appealText = message.content.slice(5).trim();
- if (!appealText) {
- return message.reply('אנא ציין את פרטי הערעור לאחר המילה "ערעור".');
- }
- const bannedGuilds = [];
- for (const guild of client.guilds.cache.values()) {
- const db = databases.get(guild.id) || await initializeDatabase(guild.id);
- await ensureColumns(db, guild.id);
- const bannedUser = await db.get('SELECT * FROM banned_users WHERE user_id = ?', message.author.id);
- if (bannedUser) {
- bannedGuilds.push({ guild, db, bannedUser });
- }
- }
- if (!bannedGuilds.length) {
- return message.reply('לא נמצאו שרתים שבהם אתה חסום.');
- }
- if (bannedGuilds.length > 1) {
- return message.reply('אתה חסום במספר שרתים. אנא ציין את שם השרת שאליו הערעור מתייחס.');
- }
- const { guild, db, bannedUser } = bannedGuilds[0];
- const appealChannelId = guildData.appeal_channel_id.get(guild.id);
- if (!appealChannelId) {
- return message.reply('השרת לא הגדיר ערוץ לבקשות ערעור. אנא פנה למנהלי השרת.');
- }
- const existingAppeal = await db.get('SELECT * FROM ban_appeals WHERE user_id = ? AND guild_id = ?', message.author.id, guild.id);
- if (existingAppeal) {
- if (existingAppeal.status === 'pending') {
- return message.reply('כבר יש לך בקשת ערעור ממתינה בשרת זה.');
- }
- const timeSinceLastAppeal = Date.now() / 1000 - existingAppeal.timestamp;
- if (timeSinceLastAppeal < 5) {
- const timeLeft = formatTimeLeft(5 - timeSinceLastAppeal);
- return message.reply(`עליך להמתין ${timeLeft} לפני שליחת ערעור נוסף בשרת זה.`);
- }
- }
- await db.run(
- 'INSERT OR REPLACE INTO ban_appeals (user_id, guild_id, appeal_text, timestamp, status) VALUES (?, ?, ?, ?, ?)',
- message.author.id, guild.id, appealText, Date.now() / 1000, 'pending'
- );
- const appealChannel = client.channels.cache.get(appealChannelId);
- if (appealChannel) {
- const embed = new EmbedBuilder()
- .setTitle('בקשת ערעור')
- .setDescription(`**משתמש:** <@${message.author.id}>\n**סיבת הבאן:** ${bannedUser.reason || 'לא צוינה'}\n**ערעור:** ${appealText}\n**סטטוס:** ממתין`)
- .setColor('#ffa500')
- .setTimestamp()
- .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
- const approveButton = new ButtonBuilder()
- .setCustomId(`approve_${message.author.id}_${guild.id}`)
- .setLabel('אשר')
- .setStyle(ButtonStyle.Success);
- const rejectButton = new ButtonBuilder()
- .setCustomId(`reject_${message.author.id}_${guild.id}`)
- .setLabel('דחה')
- .setStyle(ButtonStyle.Danger);
- const row = new ActionRowBuilder().addComponents(approveButton, rejectButton);
- await appealChannel.send({ embeds: [embed], components: [row] });
- }
- const logChannelId = guildData.log_channel_id.get(guild.id);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(`בקשת ערעור חדשה מ-<@${message.author.id}>.`);
- }
- }
- await message.reply(`בקשת הערעור שלך נשלחה בהצלחה לשרת **${guild.name}**. תקבל הודעה כאשר המנהלים יבדקו אותה.`);
- } catch (error) {
- await handleError(null, error, 'dm_appeal');
- await message.reply('שגיאה בעיבוד בקשת הערעור. אנא נסה שוב מאוחר יותר.').catch(() => {});
- }
- }
- });
- // Handle voice state updates (unchanged)
- client.on('voiceStateUpdate', async (oldState, newState) => {
- const guildId = newState.guild.id;
- const userId = newState.member.id;
- const db = databases.get(guildId) || await initializeDatabase(guildId);
- await ensureColumns(db, guildId);
- const existingMute = await db.get('SELECT * FROM voice_muted_users WHERE user_id = ?', userId);
- if (!existingMute) return;
- try {
- const muteKey = `voice_${guildId}_${userId}`;
- if (newState.channel && !voiceMuteFlags.has(`${guildId}_${userId}`) && !voiceMuteFlags.has(`timer_${guildId}_${userId}`)) {
- voiceMuteFlags.set(`${guildId}_${userId}`, true);
- await newState.setMute(true, existingMute.reason || 'המשך וויס מיוט קיים').catch(error => {
- handleError(guildId, error, 'voiceStateUpdate_mute');
- });
- voiceMuteFlags.delete(`${guildId}_${userId}`);
- } else if (!newState.channel && existingMute.source === 'interface') {
- await db.run('DELETE FROM voice_muted_users WHERE user_id = ?', userId);
- clearTimeout(muteTimers.get(muteKey));
- muteTimers.delete(muteKey);
- }
- } catch (error) {
- voiceMuteFlags.delete(`${guildId}_${userId}`);
- await handleError(guildId, error, 'voiceStateUpdate');
- }
- });
- // Handle guild member updates for voice mute (unchanged)
- client.on('guildMemberUpdate', async (oldMember, newMember) => {
- const guildId = newMember.guild.id;
- const db = databases.get(guildId) || await initializeDatabase(guildId);
- await ensureColumns(db, guildId);
- try {
- const existingMute = await db.get('SELECT * FROM voice_muted_users WHERE user_id = ?', newMember.id);
- if (newMember.serverMute && !existingMute && newMember.voice.channel) {
- await db.run(
- 'INSERT INTO voice_muted_users (user_id, end_time, muted_by, duration, reason, source) VALUES (?, ?, ?, ?, ?, ?)',
- newMember.id, null, 'מנהל דרך ממשק דיסקורד', '0', 'מיוט דרך ממשק דיסקורד', 'interface'
- );
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(`<@${newMember.id}> קיבל וויס מיוט דרך ממשק דיסקורד.`);
- }
- }
- } else if (!newMember.serverMute && existingMute && existingMute.source === 'interface' && !voiceMuteFlags.has(`unmute_voice_${guildId}_${newMember.id}`)) {
- await db.run('DELETE FROM voice_muted_users WHERE user_id = ?', newMember.id);
- const muteKey = `voice_${guildId}_${newMember.id}`;
- clearTimeout(muteTimers.get(muteKey));
- muteTimers.delete(muteKey);
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(`הוויס מיוט של <@${newMember.id}> הוסר דרך ממשק דיסקורד.`);
- }
- }
- }
- } catch (error) {
- await handleError(guildId, error, 'guildMemberUpdate');
- }
- });
- // Handle guild member add to restore mute/voice mute/ban roles
- client.on('guildMemberAdd', async member => {
- const guildId = member.guild.id;
- const userId = member.id;
- try {
- // Validate guildId
- if (!/^\d+$/.test(guildId)) {
- throw new Error(`Invalid guildId: ${guildId}`);
- }
- // Initialize database if not already initialized
- const db = databases.get(guildId) || await initializeDatabase(guildId);
- await ensureColumns(db, guildId);
- // Check if user is muted
- const mutedUser = await db.get('SELECT * FROM muted_users WHERE user_id = ?', userId);
- if (mutedUser) {
- const muteRoleId = guildData.mute_role_id.get(guildId);
- if (muteRoleId) {
- const muteRole = member.guild.roles.cache.get(muteRoleId);
- if (muteRole) {
- // Ensure bot has permission to manage roles and role hierarchy is valid
- const botMember = member.guild.members.me;
- if (
- botMember.permissions.has(PermissionsBitField.Flags.ManageRoles) &&
- muteRole.position < botMember.roles.highest.position
- ) {
- await member.roles.set([muteRoleId]).catch(error => {
- throw new Error(`Failed to set mute role on join: ${error.message}`);
- });
- // Restart mute timer if applicable
- if (mutedUser.end_time && mutedUser.end_time > Date.now() / 1000) {
- const timeLeft = (mutedUser.end_time - Date.now() / 1000) * 1000;
- clearTimeout(muteTimers.get(`${guildId}_${userId}`));
- muteTimers.set(
- `${guildId}_${userId}`,
- setTimeout(() => unmuteUser(guildId, userId, member.guild), timeLeft)
- );
- }
- // Log restoration of mute role
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(
- `תפקיד המיוט הוחזר ל-<@${userId}> עם הצטרפותו לשרת. זמן שנותר: ${formatTimeLeft(
- mutedUser.end_time ? mutedUser.end_time - Date.now() / 1000 : Infinity
- )}`
- );
- }
- }
- } else {
- console.error(`Bot lacks permissions or mute role is too high for guild ${guildId}`);
- }
- }
- }
- }
- // Check if user is voice muted
- const voiceMutedUser = await db.get('SELECT * FROM voice_muted_users WHERE user_id = ?', userId);
- if (voiceMutedUser) {
- const muteKey = `voice_${guildId}_${userId}`;
- // If user is in a voice channel, apply voice mute
- if (member.voice.channel) {
- voiceMuteFlags.set(`${guildId}_${userId}`, true);
- await member.voice.setMute(true, voiceMutedUser.reason || 'המשך וויס מיוט קיים').catch(error => {
- throw new Error(`Failed to set voice mute on join: ${error.message}`);
- });
- voiceMuteFlags.delete(`${guildId}_${userId}`);
- }
- // Restart voice mute timer if applicable
- if (voiceMutedUser.end_time && voiceMutedUser.end_time > Date.now() / 1000) {
- const timeLeft = (voiceMutedUser.end_time - Date.now() / 1000) * 1000;
- clearTimeout(muteTimers.get(muteKey));
- muteTimers.set(
- muteKey,
- setTimeout(() => unvoiceMuteUser(guildId, userId, member.guild), timeLeft)
- );
- }
- // Log restoration of voice mute
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(
- `וויס מיוט הוחזר ל-<@${userId}> עם הצטרפותו לשרת. זמן שנותר: ${formatTimeLeft(
- voiceMutedUser.end_time ? voiceMutedUser.end_time - Date.now() / 1000 : Infinity
- )}`
- );
- }
- }
- }
- // Check if user is banned
- const bannedUser = await db.get('SELECT * FROM banned_users WHERE user_id = ?', userId);
- if (bannedUser) {
- const banRoleId = guildData.ban_role_id.get(guildId);
- if (banRoleId) {
- const banRole = member.guild.roles.cache.get(banRoleId);
- if (banRole) {
- // Ensure bot has permission to manage roles and role hierarchy is valid
- const botMember = member.guild.members.me;
- if (
- botMember.permissions.has(PermissionsBitField.Flags.ManageRoles) &&
- banRole.position < botMember.roles.highest.position
- ) {
- await member.roles.set([banRoleId]).catch(error => {
- throw new Error(`Failed to set ban role on join: ${error.message}`);
- });
- // Restart ban timer if applicable
- if (bannedUser.end_time && bannedUser.end_time > Date.now() / 1000) {
- const timeLeft = (bannedUser.end_time - Date.now() / 1000) * 1000;
- clearTimeout(banTimers.get(`${guildId}_${userId}`));
- banTimers.set(
- `${guildId}_${userId}`,
- setTimeout(() => unbanUser(guildId, userId, member.guild), timeLeft)
- );
- }
- // Log restoration of ban role
- const logChannelId = guildData.log_channel_id.get(guildId);
- if (logChannelId) {
- const logChannel = client.channels.cache.get(logChannelId);
- if (logChannel) {
- await logChannel.send(
- `תפקיד הבאן הוחזר ל-<@${userId}> עם הצטרפותו לשרת. זמן שנותר: ${formatTimeLeft(
- bannedUser.end_time ? bannedUser.end_time - Date.now() / 1000 : Infinity
- )}`
- );
- }
- }
- } else {
- console.error(`Bot lacks permissions or ban role is too high for guild ${guildId}`);
- }
- }
- }
- }
- } catch (error) {
- await handleError(guildId, error, 'guildMemberAdd');
- console.error(`Error handling guildMemberAdd for user ${userId} in guild ${guildId}:`, error);
- }
- });
- // Initialize bot
- client.login(TOKEN).catch(error => {
- console.error('שגיאה בהתחברות:', error);
- process.exit(1);
- });
Advertisement
Add Comment
Please, Sign In to add comment