SaTla

Untitled

Jul 29th, 2025
63
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. const { Client, GatewayIntentBits, EmbedBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, PermissionsBitField, SlashCommandBuilder, REST, Routes, ChannelType } = require('discord.js');
  2. const dotenv = require('dotenv');
  3. const sqlite3 = require('sqlite3');
  4. const { open } = require('sqlite');
  5.  
  6. dotenv.config();
  7. const TOKEN = process.env.DISCORD_HELPER_TOKEN;
  8. if (!TOKEN) throw new Error('שגיאה: לא נמצא טוקן. אנא צור קובץ .env עם DISCORD_HELPER_TOKEN');
  9.  
  10. const client = new Client({
  11.   intents: [
  12.     GatewayIntentBits.Guilds,
  13.     GatewayIntentBits.GuildMembers,
  14.     GatewayIntentBits.GuildMessages,
  15.     GatewayIntentBits.MessageContent,
  16.     GatewayIntentBits.GuildVoiceStates,
  17.     GatewayIntentBits.DirectMessages, // Added for DM handling
  18.   ],
  19. });
  20.  
  21. const databases = new Map();
  22. const muteTimers = new Map();
  23. const banTimers = new Map();
  24. const voiceMuteFlags = new Map();
  25. const guildData = {
  26.   admin_role_mentions: new Map(),
  27.   log_channel_id: new Map(),
  28.   help_requests: new Map(),
  29.   taken_requests: new Map(),
  30.   last_help_request: new Map(),
  31.   cooldown_time: new Map(),
  32.   admin_help_counts: new Map(),
  33.   mute_role_id: new Map(),
  34.   ban_role_id: new Map(),
  35.   appeal_channel_id: new Map(),
  36.   auto_mute_links: new Map(), // New: Enable/disable auto-mute for links
  37.   auto_mute_duration: new Map(), // New: Mute duration for links
  38.   allowed_link_roles: new Map(), // New: Roles allowed to send links
  39.   spam_message_limit: new Map(), // Number of messages before triggering anti-spam
  40.   spam_time_window: new Map(), // Time window in seconds for spam detection
  41.   spam_mute_duration: new Map(), // Mute duration for spam violations
  42.   allowed_spam_roles: new Map(), // Roles exempt from anti-spam
  43.   user_message_timestamps: new Map(), // Tracks message timestamps per user per guild
  44. };
  45.  
  46. // פונקציות עזר (unchanged, kept for completeness)
  47. const parseDuration = duration => {
  48.   if (duration === '0') return Infinity;
  49.   const match = /^(\d+)([smhd])$/.exec(duration);
  50.   if (!match) return null;
  51.   const value = parseInt(match[1]);
  52.   const unit = { s: 1, m: 60, h: 3600, d: 86400 }[match[2]];
  53.   return value * unit;
  54. };
  55.  
  56. const formatDuration = duration => {
  57.   if (duration === '0') return 'תמידי';
  58.   const match = /^(\d+)([smhd])$/.exec(duration);
  59.   if (!match) return duration;
  60.   const value = parseInt(match[1]);
  61.   const unit = { s: 'שניות', m: 'דקות', h: 'שעות', d: 'ימים' }[match[2]];
  62.   return `${value} ${unit}`;
  63. };
  64.  
  65. const formatTimeLeft = seconds => {
  66.   if (seconds === Infinity) return 'תמידי';
  67.   if (seconds <= 0) return 'פג תוקף';
  68.   const parts = [];
  69.   const days = Math.floor(seconds / 86400);
  70.   const hours = Math.floor((seconds % 86400) / 3600);
  71.   const minutes = Math.floor((seconds % 3600) / 60);
  72.   const secs = Math.floor(seconds % 60);
  73.   if (days) parts.push(`${days} ימים`);
  74.   if (hours) parts.push(`${hours} שעות`);
  75.   if (minutes) parts.push(`${minutes} דקות`);
  76.   if (secs && !days && !hours) parts.push(`${secs} שניות`);
  77.   return parts.join(', ') || 'פחות מדקה';
  78. };
  79.  
  80. const initializeDatabase = async guildId => {
  81.   try {
  82.     if (!/^\d+$/.test(guildId)) {
  83.       throw new Error(`Invalid guildId: ${guildId}`);
  84.     }
  85.  
  86.     const db = await open({
  87.       filename: `./helper_${guildId}.db`,
  88.       driver: sqlite3.Database,
  89.     });
  90.  
  91.     await db.exec(`
  92.       CREATE TABLE IF NOT EXISTS guilds (
  93.         guild_id TEXT PRIMARY KEY,
  94.         admin_role_mentions TEXT,
  95.         log_channel_id TEXT,
  96.         cooldown_time INTEGER DEFAULT 5,
  97.         mute_role_id TEXT,
  98.         ban_role_id TEXT,
  99.         appeal_channel_id TEXT,
  100.         auto_mute_links BOOLEAN DEFAULT 0,
  101.         auto_mute_duration TEXT DEFAULT '1h',
  102.         allowed_link_roles TEXT DEFAULT '[]',
  103.         spam_message_limit INTEGER DEFAULT 5, -- New: Max messages before spam detection
  104.         spam_time_window INTEGER DEFAULT 10, -- New: Time window in seconds
  105.         spam_mute_duration TEXT DEFAULT '1h', -- New: Mute duration for spam
  106.         allowed_spam_roles TEXT DEFAULT '[]' -- New: Roles exempt from spam
  107.       );
  108.       -- Other table definitions remain unchanged --
  109.     `);
  110.  
  111.     databases.set(guildId, db);
  112.     await ensureColumns(db, guildId);
  113.     return db;
  114.   } catch (error) {
  115.     console.error(`Error in initializeDatabase for guild ${guildId}:`, error);
  116.     throw error;
  117.   }
  118. };
  119.  
  120. const ensureColumns = async (db, guildId) => {
  121.   try {
  122.     const addColumnIfMissing = async (table, column, type) => {
  123.       const columns = await db.all(`PRAGMA table_info(${table})`);
  124.       if (!columns.some(c => c.name === column)) {
  125.         const query = `ALTER TABLE ${table} ADD COLUMN ${column} ${type}`;
  126.         await db.exec(query);
  127.       }
  128.     };
  129.  
  130.     await addColumnIfMissing('guilds', 'mute_role_id', 'TEXT');
  131.     await addColumnIfMissing('guilds', 'ban_role_id', 'TEXT');
  132.     await addColumnIfMissing('guilds', 'appeal_channel_id', 'TEXT');
  133.     await addColumnIfMissing('guilds', 'auto_mute_links', 'BOOLEAN DEFAULT 0');
  134.     await addColumnIfMissing('guilds', 'auto_mute_duration', 'TEXT DEFAULT "1h"');
  135.     await addColumnIfMissing('guilds', 'allowed_link_roles', 'TEXT DEFAULT "[]"');
  136.     await addColumnIfMissing('guilds', 'spam_message_limit', 'INTEGER DEFAULT 5');
  137.     await addColumnIfMissing('guilds', 'spam_time_window', 'INTEGER DEFAULT 10');
  138.     await addColumnIfMissing('guilds', 'spam_mute_duration', 'TEXT DEFAULT "1h"');
  139.     await addColumnIfMissing('guilds', 'allowed_spam_roles', 'TEXT DEFAULT "[]"');
  140.     for (const col of [
  141.       ['muted_users', 'muted_by', 'TEXT'],
  142.       ['muted_users', 'duration', 'TEXT'],
  143.       ['muted_users', 'reason', 'TEXT'],
  144.       ['voice_muted_users', 'muted_by', 'TEXT'],
  145.       ['voice_muted_users', 'duration', 'TEXT'],
  146.       ['voice_muted_users', 'reason', 'TEXT'],
  147.       ['voice_muted_users', 'source', 'TEXT'],
  148.       ['banned_users', 'banned_by', 'TEXT'],
  149.       ['banned_users', 'duration', 'TEXT'],
  150.       ['banned_users', 'reason', 'TEXT'],
  151.     ]) {
  152.       await addColumnIfMissing(...col);
  153.     }
  154.   } catch (error) {
  155.     console.error(`Error ensuring columns for guild ${guildId}:`, error);
  156.     throw error;
  157.   }
  158. };
  159.  
  160. const handleError = async (guildId, error, context) => {
  161.   console.error(`[${context}] שגיאה:`, error);
  162.   try {
  163.     if (guildId && guildData.log_channel_id.get(guildId)) {
  164.       const logChannel = client.channels.cache.get(guildData.log_channel_id.get(guildId));
  165.       if (logChannel) {
  166.         await logChannel.send(`שגיאה ב-${context}: ${error.message}`).catch(err => console.error(`Failed to send error to log channel: ${err}`));
  167.       }
  168.     }
  169.     if (error.code === 10062 && guildId) {
  170.       const channel = client.channels.cache.get(guildId);
  171.       if (channel) {
  172.         await channel.send('שגיאה: האינטראקציה פגה. אנא נסה שוב.').catch(err => console.error(`Failed to send interaction expiry message: ${err}`));
  173.       }
  174.     }
  175.   } catch (err) {
  176.     console.error(`Error in handleError for ${context}:`, err);
  177.   }
  178. };
  179.  
  180. const checkAdminRole = (member, guildId) =>
  181.   member.roles.cache.some(role => guildData.admin_role_mentions.get(guildId)?.includes(role.toString()));
  182.  
  183. const hasAdministratorPermission = interactionOrMember =>
  184.   interactionOrMember.member.permissions.has(PermissionsBitField.Flags.Administrator);
  185.  
  186. const getUserVoiceChannel = member => member.voice?.channel?.toString() || 'ללא שיחה';
  187.  
  188. const sendWebhookMessage = async (channel, user, message) => {
  189.   try {
  190.     const webhooks = await channel.fetchWebhooks();
  191.     let webhook = webhooks.find(w => w.name === 'HelpBotWebhook') || (await channel.createWebhook({ name: 'HelpBotWebhook' }));
  192.     await webhook.send({ content: message, username: user.displayName, avatarURL: user.displayAvatarURL() || user.defaultAvatarURL });
  193.   } catch (error) {
  194.     await handleError(channel.guild.id, error, 'sendWebhookMessage');
  195.   }
  196. };
  197.  
  198. // unmuteUser, unbanUser, unvoiceMuteUser functions remain unchanged
  199. const unmuteUser = async (guildId, userId, guild, reason = 'המיוט הסתיים', isManual = false, admin = null) => {
  200.   const db = databases.get(guildId);
  201.   const muteRoleId = guildData.mute_role_id.get(guildId);
  202.   const member = await guild.members.fetch(userId).catch(() => null);
  203.   if (!db || !member || !muteRoleId) return;
  204.  
  205.   await member.roles.remove(muteRoleId).catch(error => console.error(`Failed to remove mute role for ${userId}:`, error));
  206.   const mutedUser = await db.get('SELECT roles FROM muted_users WHERE user_id = ?', userId);
  207.   if (mutedUser?.roles) {
  208.     const savedRoles = JSON.parse(mutedUser.roles).filter(roleId => guild.roles.cache.has(roleId));
  209.     if (savedRoles.length) await member.roles.add(savedRoles).catch(error => console.error(`Failed to restore roles for ${userId}:`, error));
  210.   }
  211.   await db.run('DELETE FROM muted_users WHERE user_id = ?', userId);
  212.   clearTimeout(muteTimers.get(`${guildId}_${userId}`));
  213.   muteTimers.delete(`${guildId}_${userId}`);
  214.  
  215.   if (isManual) {
  216.     const embed = new EmbedBuilder()
  217.       .setTitle('המיוט שלך הוסר')
  218.       .setDescription(`המיוט שלך בשרת **${guild.name}** הוסר.`)
  219.       .addFields(
  220.         { name: 'סיבה', value: reason || 'לא צוינה', inline: true },
  221.         { name: 'הוסר על ידי', value: admin ? `<@${admin.id}>` : 'מנהל', inline: true },
  222.         { name: 'תאריך ושעה', value: new Date().toLocaleString('he-IL'), inline: false }
  223.       )
  224.       .setColor('#00ff00')
  225.       .setTimestamp()
  226.       .setFooter({ text: 'בוט עזר', iconURL: client.user.displayAvatarURL() });
  227.  
  228.     await member.send({ embeds: [embed] }).catch(error => {
  229.       console.error(`Failed to send DM to ${userId}:`, error);
  230.       const logChannelId = guildData.log_channel_id.get(guildId);
  231.       if (logChannelId) {
  232.         const logChannel = client.channels.cache.get(logChannelId);
  233.         if (logChannel) {
  234.           logChannel.send(`לא ניתן לשלוח הודעה פרטית ל-<@${userId}> על הסרת המיוט. סיבה: ${error.message}`);
  235.         }
  236.       }
  237.     });
  238.  
  239.     const logChannelId = guildData.log_channel_id.get(guildId);
  240.     if (logChannelId) {
  241.       const logChannel = client.channels.cache.get(logChannelId);
  242.       if (logChannel) await logChannel.send(`<@${admin.id}> הוריד את המיוט ל-<@${userId}>`);
  243.     }
  244.   }
  245. };
  246.  
  247. const unbanUser = async (guildId, userId, guild, reason = 'הבאן הסתיים', isManual = false, admin = null) => {
  248.   const db = databases.get(guildId);
  249.   const banRoleId = guildData.ban_role_id.get(guildId);
  250.   const member = await guild.members.fetch(userId).catch(() => null);
  251.   if (!db || !member || !banRoleId) return;
  252.  
  253.   await member.roles.remove(banRoleId).catch(error => console.error(`Failed to remove ban role for ${userId}:`, error));
  254.   const bannedUser = await db.get('SELECT roles FROM banned_users WHERE user_id = ?', userId);
  255.   if (bannedUser?.roles) {
  256.     const savedRoles = JSON.parse(bannedUser.roles).filter(roleId => guild.roles.cache.has(roleId));
  257.     if (savedRoles.length) await member.roles.add(savedRoles).catch(error => console.error(`Failed to restore roles for ${userId}:`, error));
  258.   }
  259.   await db.run('DELETE FROM banned_users WHERE user_id = ?', userId);
  260.   clearTimeout(banTimers.get(`${guildId}_${userId}`));
  261.   banTimers.delete(`${guildId}_${userId}`);
  262.  
  263.   const embed = new EmbedBuilder()
  264.     .setTitle('הבאן שלך הוסר')
  265.     .setDescription(`הבאן שלך בשרת **${guild.name}** הוסר.`)
  266.     .addFields(
  267.       { name: 'סיבה', value: reason || 'לא צוינה', inline: true },
  268.       { name: 'הוסר על ידי', value: isManual && admin ? `<@${admin.id}>` : 'אוטומטית', inline: true },
  269.       { name: 'תאריך ושעה', value: new Date().toLocaleString('he-IL'), inline: false }
  270.     )
  271.     .setColor('#00ff00')
  272.     .setTimestamp()
  273.     .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
  274.  
  275.   await member.send({ embeds: [embed] }).catch(error => {
  276.     console.error(`Failed to send DM to ${userId}:`, error);
  277.     const logChannelId = guildData.log_channel_id.get(guildId);
  278.     if (logChannelId) {
  279.       const logChannel = client.channels.cache.get(logChannelId);
  280.       if (logChannel) {
  281.         logChannel.send(`לא ניתן לשלוח הודעה פרטית ל-<@${userId}> על הסרת הבאן. סיבה: ${error.message}`);
  282.       }
  283.     }
  284.   });
  285.  
  286.   const logChannelId = guildData.log_channel_id.get(guildId);
  287.   if (logChannelId) {
  288.     const logChannel = client.channels.cache.get(logChannelId);
  289.     if (logChannel) {
  290.       const logMessage = isManual
  291.         ? `<@${admin.id}> הוריד את הבאן ל-<@${userId}> (סיבה: ${reason})`
  292.         : `הבאן של <@${userId}> הוסר אוטומטית (סיבה: ${reason})`;
  293.       await logChannel.send(logMessage);
  294.     }
  295.   }
  296. };
  297.  
  298. const unvoiceMuteUser = async (guildId, userId, guild, reason = 'הוויס מיוט הסתיים', isManual = false, admin = null) => {
  299.   const db = databases.get(guildId);
  300.   const member = await guild.members.fetch(userId).catch(() => null);
  301.   if (!db || !member) return;
  302.  
  303.   const existingMute = await db.get('SELECT source, muted_by, reason AS mute_reason FROM voice_muted_users WHERE user_id = ?', userId);
  304.   if (!existingMute) return;
  305.  
  306.   const muteKey = `voice_${guildId}_${userId}`;
  307.   if (voiceMuteFlags.has(`unmute_${muteKey}`)) return;
  308.   voiceMuteFlags.set(`unmute_${muteKey}`, true);
  309.  
  310.   try {
  311.     if (!isManual) voiceMuteFlags.set(`timer_${guildId}_${userId}`, true);
  312.     if (member.voice?.channel) {
  313.       await member.voice.setMute(false, reason).catch(error => handleError(guildId, error, 'unvoiceMuteUser'));
  314.     }
  315.     await db.run('DELETE FROM voice_muted_users WHERE user_id = ?', userId);
  316.     clearTimeout(muteTimers.get(muteKey));
  317.     muteTimers.delete(muteKey);
  318.  
  319.     const embed = new EmbedBuilder()
  320.       .setTitle('הוויס מיוט שלך הוסר')
  321.       .setDescription(`הוויס מיוט שלך בשרת **${guild.name}** הוסר.`)
  322.       .addFields(
  323.         { name: 'סיבה', value: reason || 'לא צוינה', inline: true },
  324.         { name: 'הוסר על ידי', value: isManual && admin ? `<@${admin.id}>` : 'אוטומטית', inline: true },
  325.         { name: 'תאריך ושעה', value: new Date().toLocaleString('he-IL'), inline: false }
  326.       )
  327.       .setColor('#00ff00')
  328.       .setTimestamp()
  329.       .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
  330.  
  331.     await member.send({ embeds: [embed] }).catch(error => {
  332.       console.error(`Failed to send DM to ${userId}:`, error);
  333.       const logChannelId = guildData.log_channel_id.get(guildId);
  334.       if (logChannelId) {
  335.         const logChannel = client.channels.cache.get(logChannelId);
  336.         if (logChannel) {
  337.           logChannel.send(`לא ניתן לשלוח הודעה פרטית ל-<@${userId}> על הסרת הוויס מיוט. סיבה: ${error.message}`);
  338.         }
  339.       }
  340.     });
  341.  
  342.     if (isManual) {
  343.       const logChannelId = guildData.log_channel_id.get(guildId);
  344.       if (logChannelId) {
  345.         const logChannel = client.channels.cache.get(logChannelId);
  346.         if (logChannel) {
  347.           const mutedByText = existingMute.muted_by === 'מנהל דרך ממשק דיסקורד' ? existingMute.muted_by : `<@${existingMute.muted_by}>`;
  348.           const removedByText = admin ? `<@${admin.id}>` : 'אוטומטית';
  349.           const sourceText = existingMute.source === 'command' ? 'פקודה' : 'ממשק דיסקורד'; // תיקון: הגדרת sourceText
  350.           await logChannel.send(
  351.             `הוויס מיוט של <@${userId}> (${sourceText}, ניתן על ידי ${mutedByText}, סיבה: ${existingMute.mute_reason || 'לא צוינה'}) הוסר ${removedByText}. סיבה: ${reason}`
  352.           ).catch(error => handleError(guildId, error, 'unvoiceMuteUser'));
  353.         }
  354.       }
  355.     }
  356.   } finally {
  357.     voiceMuteFlags.delete(`unmute_${muteKey}`);
  358.     if (!isManual) voiceMuteFlags.delete(`timer_${guildId}_${userId}`);
  359.   }
  360. };
  361.  
  362. const commands = [
  363.   // Existing commands unchanged until 'setlogchannel'
  364.   {
  365.     builder: new SlashCommandBuilder()
  366.     .setName('h')
  367.     .setDescription('פתיחת קריאת עזרה'),
  368.   handler: async (interaction, guildId, member, options) => {
  369.     try {
  370.  
  371.       // Validate guildId
  372.       if (!/^\d+$/.test(guildId)) {
  373.         throw new Error(`Invalid guildId: ${guildId}`);
  374.       }
  375.  
  376.       // Initialize guildData maps if not already initialized
  377.       if (!guildData.help_requests.has(guildId)) {
  378.         guildData.help_requests.set(guildId, new Map());
  379.       }
  380.       if (!guildData.last_help_request.has(guildId)) {
  381.         guildData.last_help_request.set(guildId, new Map());
  382.       }
  383.  
  384.       // Check cooldown
  385.       const lastRequestTime = guildData.last_help_request.get(guildId).get(interaction.user.id) || 0;
  386.       const cooldownTime = guildData.cooldown_time.get(guildId) || 5; // Default to 5 seconds
  387.       const currentTime = Date.now() / 1000; // Current time in seconds
  388.       const timeSinceLastRequest = currentTime - lastRequestTime;
  389.  
  390.       if (timeSinceLastRequest < cooldownTime) {
  391.         const timeLeft = Math.ceil(cooldownTime - timeSinceLastRequest);
  392.         return interaction.reply({ content: `אנא המתן ${timeLeft} שניות לפני פתיחת קריאת עזרה נוספת!`, ephemeral: true });
  393.       }
  394.  
  395.       // Determine current voice channel (if any)
  396.       const voiceChannel = member.voice.channel ? `<#${member.voice.channelId}>` : 'ללא שיחה';
  397.  
  398.       // Create help request
  399.       guildData.help_requests.get(guildId).set(interaction.user.id, interaction.channelId);
  400.       guildData.last_help_request.get(guildId).set(interaction.user.id, currentTime);
  401.  
  402.       // Create embed
  403.       const embed = new EmbedBuilder()
  404.         .setTitle('קריאת עזרה')
  405.         .setDescription(`המשתמש <@${interaction.user.id}> צריך עזרה! ${guildData.admin_role_mentions.get(guildId)?.map(role => `${role}`).join(' ') || '@Helper'}\nשיחה נוכחית: ${voiceChannel}`)
  406.         .setColor('#ff0000')
  407.         .setTimestamp()
  408.         .setFooter({ text: 'בוט עזר', iconURL: interaction.client.user.displayAvatarURL() });
  409.  
  410.       // Create button
  411.       const button = new ButtonBuilder()
  412.         .setCustomId(`handle_${interaction.user.id}_${guildId}`)
  413.         .setLabel('טפל')
  414.         .setStyle(ButtonStyle.Primary);
  415.  
  416.       const row = new ActionRowBuilder().addComponents(button);
  417.  
  418.       // Send message in the same channel
  419.       await interaction.reply({ embeds: [embed], components: [row] });
  420.  
  421.       // Save data to database
  422.       const db = databases.get(guildId) || await initializeDatabase(guildId);
  423.       await db.run(
  424.         'INSERT OR REPLACE INTO help_requests (user_id, channel_id, created_at) VALUES (?, ?, ?)',
  425.         [interaction.user.id, interaction.channelId, currentTime]
  426.       );
  427.       await saveData(guildId);
  428.     } catch (error) {
  429.       console.error(`Error in /h command for guild ${guildId}:`, error);
  430.       await interaction.reply({ content: 'שגיאה בפתיחת קריאת עזרה.', ephemeral: true }).catch(() => {});
  431.       await handleError(guildId, error, '/h command');
  432.     }
  433.   },
  434. },
  435.   {
  436.     builder: new SlashCommandBuilder().setName('tophelps').setDescription('מציג את רשימת המנהלים המובילים לפי מספר קריאות'),
  437.     handler: async (context, guildId, member) => {
  438.       await context.deferReply({ ephemeral: true });
  439.       try {
  440.         if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
  441.         const adminCounts = guildData.admin_help_counts.get(guildId) || new Map();
  442.         if (!adminCounts.size) return context.editReply({ content: 'אין נתונים על קריאות!' });
  443.  
  444.         const embed = new EmbedBuilder()
  445.           .setTitle('🏆 מנהלי העזרה המובילים')
  446.           .setDescription('רשימת המנהלים המובילים:')
  447.           .setColor('#ffd700');
  448.         [...adminCounts.entries()]
  449.           .sort((a, b) => b[1] - a[1])
  450.           .slice(0, 10)
  451.           .forEach(([adminId, count], i) => {
  452.             const member = context.guild.members.cache.get(adminId);
  453.             if (member) embed.addFields({ name: `${i < 3 ? ['🥇', '🥈', '🥉'][i] : `#${i + 1}`} ${member.displayName}`, value: `קריאות: **${count}**`, inline: true });
  454.           });
  455.         await context.editReply({ embeds: [embed.setFooter({ text: 'תודה למנהלים!' })] });
  456.       } catch (error) {
  457.         await handleError(guildId, error, 'command_tophelps');
  458.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /tophelps.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /tophelps.').catch(() => {}));
  459.       }
  460.     },
  461.   },
  462.   {
  463.     builder: new SlashCommandBuilder()
  464.       .setName('resethelps')
  465.       .setDescription('מאפס את טבלת העזרה')
  466.       .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator),
  467.     handler: async (context, guildId, member) => {
  468.       await context.deferReply({ ephemeral: true });
  469.       try {
  470.         if (!hasAdministratorPermission({ member })) return context.editReply({ content: 'אין לך הרשאת Administrator!' });
  471.         const adminCounts = guildData.admin_help_counts.get(guildId) || new Map();
  472.         if (guildData.log_channel_id.get(guildId) && adminCounts.size) {
  473.           const logChannel = client.channels.cache.get(guildData.log_channel_id.get(guildId));
  474.           if (logChannel) {
  475.             const embed = new EmbedBuilder()
  476.               .setTitle('טבלת עזרה לפני איפוס')
  477.               .setDescription('רשימת המנהלים לפני האיפוס:')
  478.               .setColor('#ff9900')
  479.               .setTimestamp();
  480.             [...adminCounts.entries()]
  481.               .sort((a, b) => b[1] - a[1])
  482.               .forEach(([adminId, count], i) => {
  483.                 const member = context.guild.members.cache.get(adminId);
  484.                 if (member) embed.addFields({ name: `${i + 1}. ${member.displayName}`, value: `קריאות: **${count}**`, inline: true });
  485.               });
  486.             await logChannel.send({ embeds: [embed.setFooter({ text: `איפוס בוצע על ידי ${context.user.tag}` })] });
  487.           }
  488.         }
  489.         guildData.admin_help_counts.set(guildId, new Map());
  490.         await saveData(guildId);
  491.         await context.editReply({ content: 'טבלת העזרה אופסה!' });
  492.       } catch (error) {
  493.         await handleError(guildId, error, 'command_resethelps');
  494.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /resethelps.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /resethelps.').catch(() => {}));
  495.       }
  496.     },
  497.   },
  498.   {
  499.     builder: new SlashCommandBuilder()
  500.       .setName('setcooldown')
  501.       .setDescription('הגדרת זמן המתנה בין קריאות')
  502.       .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
  503.       .addIntegerOption(option => option.setName('seconds').setDescription('שניות').setRequired(true)),
  504.     handler: async (context, guildId, member, options) => {
  505.       await context.deferReply({ ephemeral: true });
  506.       try {
  507.         if (!hasAdministratorPermission({ member })) return context.editReply({ content: 'אין לך הרשאת Administrator!' });
  508.         const seconds = options.getInteger('seconds');
  509.         if (seconds < 0) return context.editReply({ content: 'זמן המתנה חייב להיות חיובי!' });
  510.         guildData.cooldown_time.set(guildId, seconds);
  511.         await saveData(guildId);
  512.         await context.editReply({ content: `זמן ההמתנה עודכן ל-${seconds} שניות.` });
  513.       } catch (error) {
  514.         await handleError(guildId, error, 'command_setcooldown');
  515.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /setcooldown.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /setcooldown.').catch(() => {}));
  516.       }
  517.     },
  518.   },
  519.   {
  520.     builder: new SlashCommandBuilder()
  521.       .setName('setrolehelp')
  522.       .setDescription('הגדרת עד 3 תפקידים לעזרה')
  523.       .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
  524.       .addRoleOption(option => option.setName('role1').setDescription('תפקיד ראשון').setRequired(true))
  525.       .addRoleOption(option => option.setName('role2').setDescription('תפקיד שני').setRequired(false))
  526.       .addRoleOption(option => option.setName('role3').setDescription('תפקיד שלישי').setRequired(false)),
  527.     handler: async (context, guildId, member, options) => {
  528.       await context.deferReply({ ephemeral: true });
  529.       try {
  530.         if (!hasAdministratorPermission({ member })) return context.editReply({ content: 'אין לך הרשאת Administrator!' });
  531.         const roles = [
  532.           options.getRole('role1')?.toString(),
  533.           options.getRole('role2')?.toString(),
  534.           options.getRole('role3')?.toString(),
  535.         ].filter(Boolean);
  536.         if (!roles.length) return context.editReply({ content: 'ציין לפחות תפקיד אחד!' });
  537.         guildData.admin_role_mentions.set(guildId, roles);
  538.         await saveData(guildId);
  539.         await context.editReply({ content: `תפקידי המנהלים עודכנו ל: ${roles.join(' ')}` });
  540.       } catch (error) {
  541.         await handleError(guildId, error, 'command_setrolehelp');
  542.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /setrolehelp.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /setrolehelp.').catch(() => {}));
  543.       }
  544.     },
  545.   },
  546.   {
  547.     builder: new SlashCommandBuilder()
  548.       .setName('cc')
  549.       .setDescription('מחיקת הודעות של משתמש')
  550.       .addUserOption(option => option.setName('user').setDescription('משתמש').setRequired(true))
  551.       .addIntegerOption(option => option.setName('count').setDescription('מספר הודעות').setRequired(true)),
  552.     handler: async (context, guildId, member, options) => {
  553.       await context.deferReply({ ephemeral: true });
  554.       try {
  555.         if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
  556.         const targetUser = options.getMember('user');
  557.         const count = options.getInteger('count');
  558.         if (count <= 0 || count > 100) return context.editReply({ content: 'מספר ההודעות חייב להיות בין 1 ל-100!' });
  559.         const messages = await context.channel.messages.fetch({ limit: 100 });
  560.         const filteredMessages = messages.filter(msg => msg.author.id === targetUser.id).first(count);
  561.         if (!filteredMessages.length) return context.editReply({ content: `לא נמצאו הודעות של ${targetUser}!` });
  562.  
  563.         const logContent = `הודעות שנמחקו עבור ${targetUser.displayName} (${targetUser.id}):\n` +
  564.           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('');
  565.         await context.channel.bulkDelete(filteredMessages);
  566.         await context.editReply({ content: `נמחקו ${filteredMessages.length} הודעות של ${targetUser}!` });
  567.  
  568.         const logChannelId = guildData.log_channel_id.get(guildId);
  569.         if (logChannelId) {
  570.           const logChannel = client.channels.cache.get(logChannelId);
  571.           if (logChannel) {
  572.             await logChannel.send({
  573.               content: `${context.user} מחק ${filteredMessages.length} הודעות של ${targetUser} בערוץ ${context.channel}`,
  574.               files: [{ attachment: Buffer.from(logContent, 'utf8'), name: `deleted_messages_${targetUser.id}_${new Date().toISOString().replace(/[-:.]/g, '')}.txt` }],
  575.             });
  576.           }
  577.         }
  578.       } catch (error) {
  579.         await handleError(guildId, error, 'command_cc');
  580.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /cc.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /cc.').catch(() => {}));
  581.       }
  582.     },
  583.   },
  584.   {
  585.     builder: new SlashCommandBuilder()
  586.       .setName('setlogchannel')
  587.       .setDescription('הגדרת ערוץ לוגים')
  588.       .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
  589.       .addChannelOption(option => option.setName('channel').setDescription('ערוץ').setRequired(true)),
  590.     handler: async (context, guildId, member, options) => {
  591.       await context.deferReply({ ephemeral: true });
  592.       try {
  593.         if (!hasAdministratorPermission({ member })) return context.editReply({ content: 'אין לך הרשאת Administrator!' });
  594.         const channel = options.getChannel('channel');
  595.         if (channel.type !== ChannelType.GuildText) return context.editReply({ content: 'בחר ערוץ טקסט!' });
  596.         guildData.log_channel_id.set(guildId, channel.id);
  597.         await saveData(guildId);
  598.         await context.editReply({ content: `ערוץ לוגים עודכן ל-${channel}` });
  599.       } catch (error) {
  600.         await handleError(guildId, error, 'command_setlogchannel');
  601.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /setlogchannel.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /setlogchannel.').catch(() => {}));
  602.       }
  603.     },
  604.   },
  605.   // New command: setappealchannel
  606.   {
  607.     builder: new SlashCommandBuilder()
  608.       .setName('setappealchannel')
  609.       .setDescription('הגדרת ערוץ לבקשות ערעור על באן')
  610.       .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
  611.       .addChannelOption(option => option.setName('channel').setDescription('ערוץ').setRequired(true)),
  612.     handler: async (context, guildId, member, options) => {
  613.       await context.deferReply({ ephemeral: true });
  614.       try {
  615.         if (!hasAdministratorPermission({ member })) return context.editReply({ content: 'אין לך הרשאת Administrator!' });
  616.         const channel = options.getChannel('channel');
  617.         if (channel.type !== ChannelType.GuildText) return context.editReply({ content: 'בחר ערוץ טקסט!' });
  618.         guildData.appeal_channel_id.set(guildId, channel.id);
  619.         await saveData(guildId);
  620.         await context.editReply({ content: `ערוץ בקשות הערעור הוגדר ל-${channel}.` });
  621.       } catch (error) {
  622.         await handleError(guildId, error, 'command_setappealchannel');
  623.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /setappealchannel.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /setappealchannel.').catch(() => {}));
  624.       }
  625.     },
  626.   },
  627.   // Other existing commands (mute, unmute, setmuterole, mutelists, voicemute, unvoicemute, voicemutelists, setroleban, ban, unban, banslist, checkban) remain unchanged
  628.   {
  629.     builder: new SlashCommandBuilder()
  630.       .setName('mute')
  631.       .setDescription('מיוט משתמש')
  632.       .addUserOption(option => option.setName('user').setDescription('משתמש').setRequired(true))
  633.       .addStringOption(option => option.setName('duration').setDescription('משך המיוט (1h, 30m, 1d)').setRequired(true))
  634.       .addStringOption(option => option.setName('reason').setDescription('סיבה').setRequired(true)),
  635.     handler: async (context, guildId, member, options) => {
  636.       await context.deferReply({ ephemeral: true });
  637.       try {
  638.         if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
  639.         const targetUser = options.getMember('user');
  640.         const duration = options.getString('duration');
  641.         const reason = options.getString('reason');
  642.         if (!targetUser) return context.editReply({ content: 'בחר משתמש תקין!' });
  643.         if (targetUser.roles.highest.position >= member.roles.highest.position) {
  644.           const logChannelId = guildData.log_channel_id.get(guildId);
  645.           if (logChannelId) {
  646.             const logChannel = client.channels.cache.get(logChannelId);
  647.             if (logChannel) await logChannel.send(`<@${context.user.id}> ניסה לתת מיוט ל-<@${targetUser.id}> אך נכשל כי תפקידו גבוה/שווה.`);
  648.           }
  649.           return context.editReply({ content: 'אינך יכול לתת מיוט למשתמש עם תפקיד גבוה/שווה!' });
  650.         }
  651.  
  652.         const muteRoleId = guildData.mute_role_id.get(guildId);
  653.         if (!muteRoleId) return context.editReply({ content: 'תפקיד המיוט לא הוגדר! השתמש ב-/setmuterole.' });
  654.         const muteRole = context.guild.roles.cache.get(muteRoleId);
  655.         if (!muteRole) return context.editReply({ content: 'תפקיד המיוט אינו קיים!' });
  656.  
  657.         const botMember = context.guild.members.me;
  658.         if (!botMember.permissions.has(PermissionsBitField.Flags.ManageRoles) || muteRole.position >= botMember.roles.highest.position) {
  659.           return context.editReply({ content: 'אין לי הרשאה לנהל תפקידים או שתפקיד המיוט גבוה מדי!' });
  660.         }
  661.  
  662.         const durationSeconds = parseDuration(duration);
  663.         if (!durationSeconds) return context.editReply({ content: 'משך זמן לא תקין!' });
  664.  
  665.         const db = databases.get(guildId) || await initializeDatabase(guildId);
  666.         await ensureColumns(db, guildId);
  667.         if (await db.get('SELECT * FROM muted_users WHERE user_id = ?', targetUser.id)) {
  668.           return context.editReply({ content: 'המשתמש כבר ממווט!' });
  669.         }
  670.  
  671.         const currentRoles = targetUser.roles.cache.map(role => role.id).filter(id => id !== targetUser.guild.id);
  672.         await db.run(
  673.           'INSERT INTO muted_users (user_id, end_time, roles, muted_by, duration, reason) VALUES (?, ?, ?, ?, ?, ?)',
  674.           targetUser.id, durationSeconds === Infinity ? null : Date.now() / 1000 + durationSeconds, JSON.stringify(currentRoles), context.user.id, duration, reason
  675.         );
  676.         await targetUser.roles.set([muteRoleId]).catch(error => { throw new Error(`Failed to set mute role: ${error.message}`); });
  677.  
  678.         if (durationSeconds !== Infinity) {
  679.           muteTimers.set(`${guildId}_${targetUser.id}`, setTimeout(() => unmuteUser(guildId, targetUser.id, context.guild), durationSeconds * 1000));
  680.         }
  681.         const endDate = durationSeconds === Infinity ? 'תמידי' : new Date((Date.now() / 1000 + durationSeconds) * 1000).toLocaleString('he-IL');
  682.  
  683.         const embed = new EmbedBuilder()
  684.           .setTitle('קיבלת מיוט')
  685.           .setDescription(`קיבלת מיוט בשרת **${context.guild.name}**.`)
  686.           .addFields(
  687.             { name: 'משך', value: formatDuration(duration), inline: true },
  688.             { name: 'יסתיים ב', value: endDate, inline: true },
  689.             { name: 'סיבה', value: reason || 'לא צוינה', inline: false },
  690.             { name: 'נתן את המיוט', value: `<@${context.user.id}>`, inline: true }
  691.           )
  692.           .setColor('#ff0000')
  693.           .setTimestamp()
  694.           .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
  695.  
  696.         await targetUser.send({ embeds: [embed] }).catch(error => {
  697.           console.error(`Failed to send DM to ${targetUser.id}:`, error);
  698.           const logChannelId = guildData.log_channel_id.get(guildId);
  699.           if (logChannelId) {
  700.             const logChannel = client.channels.cache.get(logChannelId);
  701.             if (logChannel) {
  702.               logChannel.send(`לא ניתן לשלוח הודעה פרטית ל-<@${targetUser.id}> על המיוט. סיבה: ${error.message}`);
  703.             }
  704.           }
  705.         });
  706.  
  707.         const logChannelId = guildData.log_channel_id.get(guildId);
  708.         if (logChannelId) {
  709.           const logChannel = client.channels.cache.get(logChannelId);
  710.           if (logChannel) await logChannel.send(`<@${context.user.id}> נתן מיוט ל-<@${targetUser.id}> ל-${formatDuration(duration)} (סיבה: ${reason})`);
  711.         }
  712.         await context.editReply({ content: `המשתמש <@${targetUser.id}> קיבל מיוט ל-${formatDuration(duration)}.` });
  713.       } catch (error) {
  714.         await handleError(guildId, error, 'command_mute');
  715.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /mute.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /mute.').catch(() => {}));
  716.       }
  717.     },
  718.   },
  719.   {
  720.     builder: new SlashCommandBuilder()
  721.       .setName('unmute')
  722.       .setDescription('שחרור מיוט')
  723.       .addUserOption(option => option.setName('user').setDescription('משתמש').setRequired(true)),
  724.     handler: async (context, guildId, member, options) => {
  725.       await context.deferReply({ ephemeral: true });
  726.       try {
  727.         if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
  728.         const targetUser = options.getMember('user');
  729.         if (!targetUser) return context.editReply({ content: 'בחר משתמש תקין!' });
  730.         if (targetUser.roles.highest.position >= member.roles.highest.position) {
  731.           const logChannelId = guildData.log_channel_id.get(guildId);
  732.           if (logChannelId) {
  733.             const logChannel = client.channels.cache.get(logChannelId);
  734.             if (logChannel) await logChannel.send(`<@${context.user.id}> ניסה להסיר מיוט מ-<@${targetUser.id}> אך נכשל כי תפקידו גבוה/שווה.`);
  735.           }
  736.           return context.editReply({ content: 'אינך יכול להסיר מיוט ממשתמש עם תפקיד גבוה/שווה!' });
  737.         }
  738.  
  739.         const db = databases.get(guildId) || await initializeDatabase(guildId);
  740.         await ensureColumns(db, guildId);
  741.         if (!await db.get('SELECT * FROM muted_users WHERE user_id = ?', targetUser.id)) {
  742.           return context.editReply({ content: 'המשתמש לא ממווט!' });
  743.         }
  744.         await unmuteUser(guildId, targetUser.id, context.guild, `שחרור ידני על ידי <@${context.user.id}>`, true, context.user);
  745.         await context.editReply({ content: `המיוט של <@${targetUser.id}> שוחרר!` });
  746.       } catch (error) {
  747.         await handleError(guildId, error, 'command_unmute');
  748.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /unmute.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /unmute.').catch(() => {}));
  749.       }
  750.     },
  751.   },
  752.   {
  753.     builder: new SlashCommandBuilder()
  754.       .setName('setmuterole')
  755.       .setDescription('הגדרת תפקיד המיוט')
  756.       .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
  757.       .addRoleOption(option => option.setName('role').setDescription('תפקיד').setRequired(true)),
  758.     handler: async (context, guildId, member, options) => {
  759.       await context.deferReply({ ephemeral: true });
  760.       try {
  761.         if (!hasAdministratorPermission({ member })) return context.editReply({ content: 'אין לך הרשאת Administrator!' });
  762.         const role = options.getRole('role');
  763.         if (!role) return context.editReply({ content: 'בחר תפקיד תקין!' });
  764.         const botMember = context.guild.members.me;
  765.         if (!botMember.permissions.has(PermissionsBitField.Flags.ManageRoles) || role.position >= botMember.roles.highest.position) {
  766.           return context.editReply({ content: 'אין לי הרשאה לנהל תפקידים או שתפקיד המיוט גבוה מדי!' });
  767.         }
  768.         guildData.mute_role_id.set(guildId, role.id);
  769.         await saveData(guildId);
  770.         await context.editReply({ content: `תפקיד המיוט הוגדר ל-${role}.` });
  771.       } catch (error) {
  772.         await handleError(guildId, error, 'command_setmuterole');
  773.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /setmuterole.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /setmuterole.').catch(() => {}));
  774.       }
  775.     },
  776.   },
  777.   {
  778.     builder: new SlashCommandBuilder()
  779.       .setName('mutelists')
  780.       .setDescription('רשימת משתמשים מושתקים'),
  781.     handler: async (context, guildId, member) => {
  782.       await context.deferReply({ ephemeral: true });
  783.       try {
  784.         if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
  785.         const db = databases.get(guildId) || await initializeDatabase(guildId);
  786.         await ensureColumns(db, guildId);
  787.         const mutedUsers = await db.all('SELECT * FROM muted_users');
  788.         if (!mutedUsers.length) return context.editReply({ content: 'אין משתמשים מושתקים!' });
  789.  
  790.         let response = 'משתמש | מיוט על ידי | משך המיוט | זמן שנותר | סיבה\n' + '-'.repeat(80) + '\n';
  791.         for (const muted of mutedUsers) {
  792.           const timeLeft = muted.end_time ? muted.end_time - Date.now() / 1000 : Infinity;
  793.           const member = context.guild.members.cache.get(muted.user_id);
  794.           const mutedBy = context.guild.members.cache.get(muted.muted_by) || { id: muted.muted_by };
  795.           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`;
  796.         }
  797.         await context.editReply({ content: response });
  798.       } catch (error) {
  799.         await handleError(guildId, error, 'command_mutelists');
  800.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /mutelists.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /mutelists.').catch(() => {}));
  801.       }
  802.     },
  803.   },
  804.   {
  805.     builder: new SlashCommandBuilder()
  806.       .setName('voicemute')
  807.       .setDescription('וויס מיוט למשתמש')
  808.       .addUserOption(option => option.setName('user').setDescription('משתמש').setRequired(true))
  809.       .addStringOption(option => option.setName('duration').setDescription('משך (1h, 30m, 1d)').setRequired(true))
  810.       .addStringOption(option => option.setName('reason').setDescription('סיבה').setRequired(false)),
  811.     handler: async (context, guildId, member, options) => {
  812.       await context.deferReply({ ephemeral: true });
  813.       try {
  814.         if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
  815.         const targetUser = options.getMember('user');
  816.         const duration = options.getString('duration');
  817.         const reason = options.getString('reason') || 'לא צוינה';
  818.         if (!targetUser) return context.editReply({ content: 'בחר משתמש תקין!' });
  819.         if (targetUser.roles.highest.position >= member.roles.highest.position) {
  820.           const logChannelId = guildData.log_channel_id.get(guildId);
  821.           if (logChannelId) {
  822.             const logChannel = client.channels.cache.get(logChannelId);
  823.             if (logChannel) await logChannel.send(`<@${context.user.id}> ניסה לתת וויס מיוט ל-<@${targetUser.id}> אך נכשל כי תפקידו גבוה/שווה.`);
  824.           }
  825.           return context.editReply({ content: 'אינך יכול לתת וויס מיוט למשתמש עם תפקיד גבוה/שווה!' });
  826.         }
  827.  
  828.         const botMember = context.guild.members.me;
  829.         if (!botMember.permissions.has(PermissionsBitField.Flags.MuteMembers)) return context.editReply({ content: 'אין לי הרשאה לנהל וויס מיוט!' });
  830.         const durationSeconds = parseDuration(duration);
  831.         if (!durationSeconds) return context.editReply({ content: 'משך זמן לא תקין!' });
  832.  
  833.         const db = databases.get(guildId) || await initializeDatabase(guildId);
  834.         await ensureColumns(db, guildId);
  835.        
  836.         const existingMute = await db.get('SELECT * FROM voice_muted_users WHERE user_id = ?', targetUser.id);
  837.         if (existingMute) return context.editReply({ content: 'המשתמש כבר מושתק קולית!' });
  838.  
  839.         const muteKey = `voice_${guildId}_${targetUser.id}`;
  840.         clearTimeout(muteTimers.get(muteKey));
  841.         muteTimers.delete(muteKey);
  842.  
  843.         await db.run(
  844.           'INSERT INTO voice_muted_users (user_id, end_time, muted_by, duration, reason, source) VALUES (?, ?, ?, ?, ?, ?)',
  845.           targetUser.id,
  846.           durationSeconds === Infinity ? null : Date.now() / 1000 + durationSeconds,
  847.           context.user.id,
  848.           duration,
  849.           reason,
  850.           'command'
  851.         );
  852.  
  853.         let voiceMuted = false;
  854.         if (targetUser.voice?.channel) {
  855.           voiceMuteFlags.set(`${guildId}_${targetUser.id}`, true);
  856.           await targetUser.voice.setMute(true, reason).catch(error => {
  857.             throw new Error(`Failed to set voice mute: ${error.message}`);
  858.           });
  859.           voiceMuted = true;
  860.           voiceMuteFlags.delete(`${guildId}_${targetUser.id}`);
  861.         }
  862.  
  863.         if (durationSeconds !== Infinity) {
  864.           muteTimers.set(muteKey, setTimeout(() => unvoiceMuteUser(guildId, targetUser.id, context.guild), durationSeconds * 1000));
  865.         }
  866.         const endDate = durationSeconds === Infinity ? 'תמידי' : new Date((Date.now() / 1000 + durationSeconds) * 1000).toLocaleString('he-IL');
  867.  
  868.         const embed = new EmbedBuilder()
  869.           .setTitle('קיבלת וויס מיוט')
  870.           .setDescription(`קיבלת וויס מיוט בשרת **${context.guild.name}**.${!voiceMuted ? ' המיוט יחול כאשר תצטרף לערוץ קולי.' : ''}`)
  871.           .addFields(
  872.             { name: 'משך', value: formatDuration(duration), inline: true },
  873.             { name: 'יסתיים ב', value: endDate, inline: true },
  874.             { name: 'סיבה', value: reason, inline: false },
  875.             { name: 'נתן את הוויס מיוט', value: `<@${context.user.id}>`, inline: true }
  876.           )
  877.           .setColor('#ff0000')
  878.           .setTimestamp()
  879.           .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
  880.  
  881.         await targetUser.send({ embeds: [embed] }).catch(error => {
  882.           console.error(`Failed to send DM to ${targetUser.id}:`, error);
  883.           const logChannelId = guildData.log_channel_id.get(guildId);
  884.           if (logChannelId) {
  885.             const logChannel = client.channels.cache.get(logChannelId);
  886.             if (logChannel) {
  887.               logChannel.send(`לא ניתן לשלוח הודעה פרטית ל-<@${targetUser.id}> על הוויס מיוט. סיבה: ${error.message}`);
  888.             }
  889.           }
  890.         });
  891.  
  892.         const logChannelId = guildData.log_channel_id.get(guildId);
  893.         if (logChannelId) {
  894.           const logChannel = client.channels.cache.get(logChannelId);
  895.           if (logChannel) {
  896.             await logChannel.send(`<@${context.user.id}> נתן וויס מיוט ל-<@${targetUser.id}> ל-${formatDuration(duration)} (סיבה: ${reason})${!voiceMuted ? ' (יוחל כאשר המשתמש יצטרף לערוץ קולי)' : ''}`);
  897.           }
  898.         }
  899.  
  900.         await context.editReply({ content: `המשתמש <@${targetUser.id}> קיבל וויס מיוט ל-${formatDuration(duration)}.${!voiceMuted ? ' המיוט יחול כאשר יצטרף לערוץ קולי.' : ''}` });
  901.       } catch (error) {
  902.         voiceMuteFlags.delete(`${guildId}_${options.getUser('user').id}`);
  903.         await handleError(guildId, error, 'command_voicemute');
  904.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /voicemute.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /voicemute.').catch(() => {}));
  905.       }
  906.     },
  907.   },
  908.   {
  909.     builder: new SlashCommandBuilder()
  910.       .setName('unvoicemute')
  911.       .setDescription('שחרור וויס מיוט')
  912.       .addUserOption(option => option.setName('user').setDescription('משתמש').setRequired(true)),
  913.     handler: async (context, guildId, member, options) => {
  914.       await context.deferReply({ ephemeral: true });
  915.       try {
  916.         if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
  917.         const targetUser = options.getMember('user');
  918.         if (!targetUser) return context.editReply({ content: 'בחר משתמש תקין!' });
  919.         if (targetUser.roles.highest.position >= member.roles.highest.position) {
  920.           const logChannelId = guildData.log_channel_id.get(guildId);
  921.           if (logChannelId) {
  922.             const logChannel = client.channels.cache.get(logChannelId);
  923.             if (logChannel) await logChannel.send(`<@${context.user.id}> ניסה להסיר וויס מיוט מ-<@${targetUser.id}> אך נכשל כי תפקידו גבוה/שווה.`);
  924.           }
  925.           return context.editReply({ content: 'אינך יכול להסיר וויס מיוט ממשתמש עם תפקיד גבוה/שווה!' });
  926.         }
  927.  
  928.         const db = databases.get(guildId) || await initializeDatabase(guildId);
  929.         await ensureColumns(db);
  930.         if (!await db.get('SELECT * FROM voice_muted_users WHERE user_id = ?', targetUser.id)) {
  931.           return context.editReply({ content: 'המשתמש לא מושתק קולית!' });
  932.         }
  933.         await unvoiceMuteUser(guildId, targetUser.id, context.guild, `שחרור ידני על ידי <@${context.user.id}>`, true, context.user);
  934.         await context.editReply({ content: `הוויס מיוט של <@${targetUser.id}> שוחרר!` });
  935.       } catch (error) {
  936.         await handleError(guildId, error, 'command_unvoicemute');
  937.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /unvoicemute.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /unvoicemute.').catch(() => {}));
  938.       }
  939.     },
  940.   },
  941.   {
  942.     builder: new SlashCommandBuilder()
  943.       .setName('voicemutelists')
  944.       .setDescription('רשימת משתמשים בוויס מיוט'),
  945.     handler: async (context, guildId, member) => {
  946.       await context.deferReply({ ephemeral: true });
  947.       try {
  948.         if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
  949.         const db = databases.get(guildId) || await initializeDatabase(guildId);
  950.         await ensureColumns(db);
  951.         const voiceMutedUsers = await db.all('SELECT * FROM voice_muted_users');
  952.         if (!voiceMutedUsers.length) return context.editReply({ content: 'אין משתמשים בוויס מיוט!' });
  953.  
  954.         let response = 'משתמש | וויס מיוט על ידי | משך המיוט | זמן שנותר | סיבה | מקור\n' + '-'.repeat(100) + '\n';
  955.         for (const muted of voiceMutedUsers) {
  956.           const timeLeft = muted.end_time ? muted.end_time - Date.now() / 1000 : Infinity;
  957.           const member = context.guild.members.cache.get(muted.user_id);
  958.           const mutedBy = context.guild.members.cache.get(muted.muted_by) || { id: muted.user_id };
  959.           const sourceText = muted.source === 'command' ? 'פקודה' : 'ממשק דיסקורד';
  960.           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`;
  961.         }
  962.         await context.editReply({ content: response });
  963.       } catch (error) {
  964.         await handleError(guildId, error, 'command_voicemutelists');
  965.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /voicemutelists.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /voicemutelists.').catch(() => {}));
  966.       }
  967.     },
  968.   },
  969.   {
  970.     builder: new SlashCommandBuilder()
  971.       .setName('setroleban')
  972.       .setDescription('הגדרת תפקיד הבאן')
  973.       .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
  974.       .addRoleOption(option => option.setName('role').setDescription('תפקיד').setRequired(true)),
  975.     handler: async (context, guildId, member, options) => {
  976.       await context.deferReply({ ephemeral: true });
  977.       try {
  978.         if (!hasAdministratorPermission(context)) return context.editReply({ content: 'אין לך הרשאת Administrator!' });
  979.         const role = options.getRole('role');
  980.         if (!role) return context.editReply({ content: 'בחר תפקיד תקין!' });
  981.         const botMember = context.guild.members.me;
  982.         if (!botMember.permissions.has(PermissionsBitField.Flags.ManageRoles) || role.position >= botMember.roles.highest.position) {
  983.           return context.editReply({ content: 'אין לי הרשאה לנהל תפקידים או שתפקיד הבאן גבוה מדי!' });
  984.         }
  985.         guildData.ban_role_id.set(guildId, role.id);
  986.         await saveData(guildId);
  987.         await context.editReply({ content: `תפקיד הבאן הוגדר ל-${role}.` });
  988.       } catch (error) {
  989.         await handleError(guildId, error, 'command_setroleban');
  990.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /setroleban.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /setroleban.').catch(() => {}));
  991.       }
  992.     },
  993.   },
  994.   {
  995.     builder: new SlashCommandBuilder()
  996.       .setName('ban')
  997.       .setDescription('באן')
  998.       .addUserOption(option => option.setName('user').setDescription('משתמש').setRequired(true))
  999.       .addStringOption(option => option.setName('duration').setDescription('משך הבאן (1h, 30m, 1d, 0 לתמידי)').setRequired(true))
  1000.       .addStringOption(option => option.setName('reason').setDescription('סיבה').setRequired(true)),
  1001.     handler: async (context, guildId, member, options) => {
  1002.       await context.deferReply({ ephemeral: true });
  1003.       try {
  1004.         if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
  1005.         const targetUser = options.getMember('user');
  1006.         const duration = options.getString('duration');
  1007.         const reason = options.getString('reason');
  1008.         if (!targetUser) return context.editReply({ content: 'בחר משתמש תקין!' });
  1009.         if (targetUser.roles.highest.position >= member.roles.highest.position) {
  1010.           const logChannelId = guildData.log_channel_id.get(guildId);
  1011.           if (logChannelId) {
  1012.             const logChannel = client.channels.cache.get(logChannelId);
  1013.             if (logChannel) await logChannel.send(`<@${context.user.id}> ניסה לתת באן ל-<@${targetUser.id}> אך נכשל כי תפקידו גבוה/שווה.`);
  1014.           }
  1015.           return context.editReply({ content: 'אינך יכול לתת באן למשתמש עם תפקיד גבוה/שווה!' });
  1016.         }
  1017.  
  1018.         const banRoleId = guildData.ban_role_id.get(guildId);
  1019.         if (!banRoleId) return context.editReply({ content: 'תפקיד הבאן לא הוגדר! השתמש ב-/setroleban.' });
  1020.         const banRole = context.guild.roles.cache.get(banRoleId);
  1021.         if (!banRole) return context.editReply({ content: 'תפקיד הבאן אינו קיים!' });
  1022.  
  1023.         const botMember = context.guild.members.me;
  1024.         if (!botMember.permissions.has(PermissionsBitField.Flags.ManageRoles) || banRole.position >= botMember.roles.highest.position) {
  1025.           return context.editReply({ content: 'אין לי הרשאה לנהל תפקידים או שתפקיד הבאן גבוה מדי!' });
  1026.         }
  1027.  
  1028.         const durationSeconds = parseDuration(duration);
  1029.         if (!durationSeconds) return context.editReply({ content: 'משך זמן לא תקין!' });
  1030.  
  1031.         const db = databases.get(guildId) || await initializeDatabase(guildId);
  1032.         await ensureColumns(db, guildId);
  1033.         if (await db.get('SELECT * FROM banned_users WHERE user_id = ?', targetUser.id)) {
  1034.           return context.editReply({ content: 'המשתמש כבר בבאן!' });
  1035.         }
  1036.  
  1037.         const currentRoles = targetUser.roles.cache.map(role => role.id).filter(id => id !== targetUser.guild.id);
  1038.         await db.run(
  1039.           'INSERT INTO banned_users (user_id, end_time, roles, banned_by, duration, reason) VALUES (?, ?, ?, ?, ?, ?)',
  1040.           targetUser.id, durationSeconds === Infinity ? null : Date.now() / 1000 + durationSeconds, JSON.stringify(currentRoles), context.user.id, duration, reason
  1041.         );
  1042.         await targetUser.roles.set([banRoleId]).catch(error => { throw new Error(`Failed to set ban role: ${error.message}`); });
  1043.  
  1044.         if (durationSeconds !== Infinity) {
  1045.           banTimers.set(`${guildId}_${targetUser.id}`, setTimeout(() => unbanUser(guildId, targetUser.id, context.guild), durationSeconds * 1000));
  1046.         }
  1047.         const endDate = durationSeconds === Infinity ? 'תמידי' : new Date((Date.now() / 1000 + durationSeconds) * 1000).toLocaleString('he-IL');
  1048.  
  1049.         const embed = new EmbedBuilder()
  1050.           .setTitle('קיבלת באן')
  1051.           .setDescription(`קיבלת באן בשרת **${context.guild.name}**. לשליחת ערעור, שלח הודעה פרטית לבוט עם המילה "ערעור" והסבר מפורט.`)
  1052.           .addFields(
  1053.             { name: 'משך', value: formatDuration(duration), inline: true },
  1054.             { name: 'יסתיים ב', value: endDate, inline: true },
  1055.             { name: 'סיבה', value: reason || 'לא צוינה', inline: false },
  1056.             { name: 'נתן את הבאן', value: `<@${context.user.id}>`, inline: true }
  1057.           )
  1058.           .setColor('#ff0000')
  1059.           .setTimestamp()
  1060.           .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
  1061.  
  1062.         await targetUser.send({ embeds: [embed] }).catch(error => {
  1063.           console.error(`Failed to send DM to ${targetUser.id}:`, error);
  1064.           const logChannelId = guildData.log_channel_id.get(guildId);
  1065.           if (logChannelId) {
  1066.             const logChannel = client.channels.cache.get(logChannelId);
  1067.             if (logChannel) {
  1068.               logChannel.send(`לא ניתן לשלוח הודעה פרטית ל-<@${targetUser.id}> על הבאן. סיבה: ${error.message}`);
  1069.             }
  1070.           }
  1071.         });
  1072.  
  1073.         const logChannelId = guildData.log_channel_id.get(guildId);
  1074.         if (logChannelId) {
  1075.           const logChannel = client.channels.cache.get(logChannelId);
  1076.           if (logChannel) await logChannel.send(`<@${context.user.id}> נתן באן ל-<@${targetUser.id}> ל-${formatDuration(duration)} (סיבה: ${reason}).`);
  1077.         }
  1078.         await context.editReply({ content: `המשתמש <@${targetUser.id}> קיבל באן ל-${formatDuration(duration)}.` });
  1079.       } catch (error) {
  1080.         await handleError(guildId, error, 'command_ban');
  1081.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /ban.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /ban.').catch(() => {}));
  1082.       }
  1083.     },
  1084.   },
  1085.   {
  1086.     builder: new SlashCommandBuilder()
  1087.       .setName('unban')
  1088.       .setDescription('שחרור באן')
  1089.       .addUserOption(option => option.setName('user').setDescription('משתמש').setRequired(true)),
  1090.     handler: async (context, guildId, member, options) => {
  1091.       await context.deferReply({ ephemeral: true });
  1092.       try {
  1093.         if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
  1094.         const targetUser = options.getMember('user');
  1095.         if (!targetUser) return context.editReply({ content: 'בחר משתמש תקין!' });
  1096.         if (targetUser.roles.highest.position >= member.roles.highest.position) {
  1097.           const logChannelId = guildData.log_channel_id.get(guildId);
  1098.           if (logChannelId) {
  1099.             const logChannel = client.channels.cache.get(logChannelId);
  1100.             if (logChannel) await logChannel.send(`<@${context.user.id}> ניסה להסיר באן מ-<@${targetUser.id}> אך נכשל כי תפקידו גבוה/שווה.`);
  1101.           }
  1102.           return context.editReply({ content: 'אינך יכול להסיר באן ממשתמש עם תפקיד גבוה/שווה!' });
  1103.         }
  1104.  
  1105.         const db = databases.get(guildId) || await initializeDatabase(guildId);
  1106.         await ensureColumns(db, guildId);
  1107.         if (!await db.get('SELECT * FROM banned_users WHERE user_id = ?', targetUser.id)) {
  1108.           return context.editReply({ content: 'המשתמש לא בבאן!' });
  1109.         }
  1110.         await unbanUser(guildId, targetUser.id, context.guild, `שחרור ידני על ידי <@${context.user.id}>`, true, context.user);
  1111.         await context.editReply({ content: `הבאן של <@${targetUser.id}> שוחרר!` });
  1112.       } catch (error) {
  1113.         await handleError(guildId, error, 'command_unban');
  1114.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /unban.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /unban.').catch(() => {}));
  1115.       }
  1116.     },
  1117.   },
  1118.   {
  1119.     builder: new SlashCommandBuilder()
  1120.       .setName('banslist')
  1121.       .setDescription('רשימת משתמשים בבאן'),
  1122.     handler: async (context, guildId, member) => {
  1123.       await context.deferReply({ ephemeral: true });
  1124.       try {
  1125.         if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
  1126.         const db = databases.get(guildId) || await initializeDatabase(guildId);
  1127.         await ensureColumns(db, guildId);
  1128.         const bannedUsers = await db.all('SELECT * FROM banned_users');
  1129.         if (!bannedUsers.length) return context.editReply({ content: 'אין משתמשים בבאן!' });
  1130.  
  1131.         let response = 'משתמש | באן על ידי | משך הבאן | זמן שנותר | סיבה\n' + '-'.repeat(80) + '\n';
  1132.         for (const banned of bannedUsers) {
  1133.           const timeLeft = banned.end_time ? banned.end_time - Date.now() / 1000 : Infinity;
  1134.           const member = context.guild.members.cache.get(banned.user_id);
  1135.           const bannedBy = context.guild.members.cache.get(banned.banned_by) || { id: banned.banned_by };
  1136.           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`;
  1137.         }
  1138.         await context.editReply({ content: response });
  1139.       } catch (error) {
  1140.         await handleError(guildId, error, 'command_banslist');
  1141.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /banslist.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /banslist.').catch(() => {}));
  1142.       }
  1143.     },
  1144.   },
  1145.   {
  1146.     builder: new SlashCommandBuilder()
  1147.       .setName('checkban')
  1148.       .setDescription('בודק מי נתן באן למשתמש מסוים')
  1149.       .addStringOption(option =>
  1150.         option.setName('user_id')
  1151.           .setDescription('ה-ID של המשתמש לבדיקה')
  1152.           .setRequired(true)
  1153.       ),
  1154.     handler: async (context, guildId, member, options) => {
  1155.       await context.deferReply({ ephemeral: true });
  1156.       const userId = options.getString('user_id');
  1157.       const db = databases.get(guildId);
  1158.  
  1159.       if (!db) return context.editReply({ content: 'מאגר הנתונים לא זמין.', ephemeral: true });
  1160.  
  1161.       try {
  1162.         if (!checkAdminRole(member, guildId)) return context.editReply({ content: 'אין לך הרשאה!' });
  1163.         const bannedUser = await db.get('SELECT banned_by, reason, duration, end_time FROM banned_users WHERE user_id = ?', userId);
  1164.         if (!bannedUser) return context.editReply({ content: 'המשתמש לא נמצא ברשימת החסומים.', ephemeral: true });
  1165.  
  1166.         const bannedBy = await context.client.users.fetch(bannedUser.banned_by).catch(() => null);
  1167.         const timeLeft = bannedUser.end_time ? (bannedUser.end_time - Date.now() / 1000) : Infinity;
  1168.         const formattedTimeLeft = timeLeft === Infinity ? 'תמידי' : formatTimeLeft(timeLeft);
  1169.         const formattedDuration = bannedUser.duration === '0' ? 'תמידי' : formatDuration(bannedUser.duration);
  1170.  
  1171.         await context.editReply({
  1172.           content: `**משתמש:** <@${userId}>\n**מי נתן את הבאן:** ${bannedBy ? `<@${bannedBy.id}>` : 'לא ידוע'}\n**סיבה:** ${bannedUser.reason || 'לא צוינה'}\n**משך:** ${formattedDuration}\n**זמן שנותר:** ${formattedTimeLeft}`,
  1173.           ephemeral: true
  1174.         });
  1175.       } catch (error) {
  1176.         await handleError(guildId, error, 'command_checkban');
  1177.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /checkban.' }).catch(() => context.channel.send('שגיאה בביצוע הפקודה /checkban.').catch(() => {}));
  1178.       }
  1179.     }
  1180.   },
  1181.     {
  1182.     builder: new SlashCommandBuilder()
  1183.       .setName('toggleautomutelinks')
  1184.       .setDescription('הפעלה/כיבוי של מיוט אוטומטי לשליחת קישורי דיסקורד')
  1185.       .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
  1186.       .addBooleanOption(option =>
  1187.         option.setName('enabled').setDescription('הפעל או כבה את המיוט האוטומטי').setRequired(true)
  1188.       ),
  1189.     handler: async (context, guildId, member, options) => {
  1190.       await context.deferReply({ ephemeral: true });
  1191.       try {
  1192.         if (!hasAdministratorPermission({ member })) {
  1193.           await context.editReply({ content: 'אין לך הרשאת Administrator!' });
  1194.           return;
  1195.         }
  1196.  
  1197.         const enabled = options.getBoolean('enabled');
  1198.         guildData.auto_mute_links.set(guildId, enabled);
  1199.         await saveData(guildId);
  1200.         await context.editReply({
  1201.           content: `מיוט אוטומטי לשליחת קישורי דיסקורד ${enabled ? 'הופעל' : 'כובה'} בהצלחה.`,
  1202.         });
  1203.  
  1204.         // שליחת לוג לערוץ הלוגים רק על שינוי מוצלח
  1205.         const logChannelId = guildData.log_channel_id.get(guildId);
  1206.         if (logChannelId) {
  1207.           const logChannel = context.client.channels.cache.get(logChannelId);
  1208.           if (logChannel) {
  1209.             await logChannel.send(`<@${member.user.id}> ${enabled ? 'הפעיל' : 'כיבה'} את המיוט האוטומטי לשליחת קישורי דיסקורד בשרת.`);
  1210.           }
  1211.         }
  1212.       } catch (error) {
  1213.         await handleError(guildId, error, 'command_toggleautomutelinks');
  1214.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /toggleautomutelinks.' }).catch(() => {
  1215.           context.channel.send('שגיאה בביצוע הפקודה /toggleautomutelinks.').catch(() => {});
  1216.         });
  1217.       }
  1218.     },
  1219.   },
  1220.   {
  1221.     builder: new SlashCommandBuilder()
  1222.       .setName('setautomuteduration')
  1223.       .setDescription('הגדרת משך המיוט לשליחת קישורי דיסקורד')
  1224.       .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
  1225.       .addStringOption(option =>
  1226.         option.setName('duration').setDescription('משך המיוט (לדוגמה: 1h, 30m, 1d)').setRequired(true)
  1227.       ),
  1228.     handler: async (context, guildId, member, options) => {
  1229.       await context.deferReply({ ephemeral: true });
  1230.       try {
  1231.         if (!hasAdministratorPermission({ member })) {
  1232.           await context.editReply({ content: 'אין לך הרשאת Administrator!' });
  1233.           return;
  1234.         }
  1235.  
  1236.         const duration = options.getString('duration');
  1237.         const durationSeconds = parseDuration(duration);
  1238.         if (!durationSeconds) {
  1239.           await context.editReply({ content: 'משך זמן לא תקין!' });
  1240.           return;
  1241.         }
  1242.  
  1243.         guildData.auto_mute_duration.set(guildId, duration);
  1244.         await saveData(guildId);
  1245.         await context.editReply({
  1246.           content: `משך המיוט האוטומטי עודכן ל-${formatDuration(duration)}.`,
  1247.         });
  1248.  
  1249.         // שליחת לוג לערוץ הלוגים רק על שינוי מוצלח
  1250.         const logChannelId = guildData.log_channel_id.get(guildId);
  1251.         if (logChannelId) {
  1252.           const logChannel = context.client.channels.cache.get(logChannelId);
  1253.           if (logChannel) {
  1254.             await logChannel.send(`<@${member.user.id}> עדכן את משך המיוט האוטומטי לשליחת קישורי דיסקורד ל-${formatDuration(duration)} בשרת.`);
  1255.           }
  1256.         }
  1257.       } catch (error) {
  1258.         await handleError(guildId, error, 'command_setautomuteduration');
  1259.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /setautomuteduration.' }).catch(() => {
  1260.           context.channel.send('שגיאה בביצוע הפקודה /setautomuteduration.').catch(() => {});
  1261.         });
  1262.       }
  1263.     },
  1264.   },
  1265.   {
  1266.     builder: new SlashCommandBuilder()
  1267.       .setName('setallowedlinkroles')
  1268.       .setDescription('הגדרת תפקידים שמורשים לשלוח קישורי דיסקורד')
  1269.       .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
  1270.       .addRoleOption(option => option.setName('role1').setDescription('תפקיד ראשון').setRequired(false))
  1271.       .addRoleOption(option => option.setName('role2').setDescription('תפקיד שני').setRequired(false))
  1272.       .addRoleOption(option => option.setName('role3').setDescription('תפקיד שלישי').setRequired(false)),
  1273.     handler: async (context, guildId, member, options) => {
  1274.       await context.deferReply({ ephemeral: true });
  1275.       try {
  1276.         if (!hasAdministratorPermission({ member })) {
  1277.           await context.editReply({ content: 'אין לך הרשאת Administrator!' });
  1278.           return;
  1279.         }
  1280.  
  1281.         const roles = [
  1282.           options.getRole('role1')?.id,
  1283.           options.getRole('role2')?.id,
  1284.           options.getRole('role3')?.id,
  1285.         ].filter(Boolean);
  1286.  
  1287.         if (!roles.length) {
  1288.           guildData.allowed_link_roles.set(guildId, []);
  1289.           await saveData(guildId);
  1290.           await context.editReply({ content: 'רשימת התפקידים המותרים לשליחת קישורי דיסקורד אופסה בהצלחה.' });
  1291.  
  1292.           // שליחת לוג לערוץ הלוגים על איפוס תפקידים
  1293.           const logChannelId = guildData.log_channel_id.get(guildId);
  1294.           if (logChannelId) {
  1295.             const logChannel = context.client.channels.cache.get(logChannelId);
  1296.             if (logChannel) {
  1297.               await logChannel.send(`<@${member.user.id}> איפס את רשימת התפקידים המותרים לשליחת קישורי דיסקורד בשרת.`);
  1298.             }
  1299.           }
  1300.           return;
  1301.         }
  1302.  
  1303.         guildData.allowed_link_roles.set(guildId, roles);
  1304.         await saveData(guildId);
  1305.         await context.editReply({
  1306.           content: `תפקידים המורשים לשלוח קישורי דיסקורד עודכנו ל: ${roles
  1307.             .map(id => `<@&${id}>`)
  1308.             .join(' ')}`,
  1309.         });
  1310.  
  1311.         // שליחת לוג לערוץ הלוגים על עדכון תפקידים
  1312.         const logChannelId = guildData.log_channel_id.get(guildId);
  1313.         if (logChannelId) {
  1314.           const logChannel = context.client.channels.cache.get(logChannelId);
  1315.           if (logChannel) {
  1316.             await logChannel.send(`<@${member.user.id}> עדכן את התפקידים המותרים לשליחת קישורי דיסקורד ל-${roles.map(id => `<@&${id}>`).join(', ')} בשרת.`);
  1317.           }
  1318.         }
  1319.       } catch (error) {
  1320.         await handleError(guildId, error, 'command_setallowedlinkroles');
  1321.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /setallowedlinkroles.' }).catch(() => {
  1322.           context.channel.send('שגיאה בביצוע הפקודה /setallowedlinkroles.').catch(() => {});
  1323.         });
  1324.       }
  1325.     },
  1326.   },
  1327.   {
  1328.   builder: new SlashCommandBuilder()
  1329.     .setName('antilinkinfo')
  1330.     .setDescription('מציג מידע על מערכת האנטי-לינק של השרת'),
  1331.   handler: async (context, guildId, member) => {
  1332.     await context.deferReply({ ephemeral: true });
  1333.     try {
  1334.       // בדיקת הרשאה למנהלים בלבד
  1335.       if (!checkAdminRole(member, guildId)) {
  1336.         return context.editReply({ content: 'אין לך הרשאה!' });
  1337.       }
  1338.  
  1339.       // קבלת נתונים ממפות guildData
  1340.       const isAutoMuteEnabled = guildData.auto_mute_links.get(guildId) || false;
  1341.       const muteDuration = guildData.auto_mute_duration.get(guildId) || '1h';
  1342.       const allowedRoles = guildData.allowed_link_roles.get(guildId) || [];
  1343.  
  1344.       // יצירת תיאור התפקידים המורשים
  1345.       let allowedRolesText = 'אין תפקידים מורשים';
  1346.       if (allowedRoles.length > 0) {
  1347.         allowedRolesText = allowedRoles.map(id => `<@&${id}>`).join(', ');
  1348.       }
  1349.  
  1350.       // יצירת embed לתצוגת המידע
  1351.       const embed = new EmbedBuilder()
  1352.         .setTitle('מידע על מערכת האנטי-לינק')
  1353.         .setDescription('הגדרות מערכת האנטי-לינק של השרת:')
  1354.         .addFields(
  1355.           { name: 'סטטוס', value: isAutoMuteEnabled ? '🟢 מופעלת' : '🔴 כבויה', inline: true },
  1356.           { name: 'משך המיוט', value: formatDuration(muteDuration), inline: true },
  1357.           { name: 'תפקידים מורשים', value: allowedRolesText, inline: false }
  1358.         )
  1359.         .setColor(isAutoMuteEnabled ? '#00ff00' : '#ff0000')
  1360.         .setTimestamp()
  1361.         .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
  1362.  
  1363.       // שליחת התגובה
  1364.       await context.editReply({ embeds: [embed] });
  1365.  
  1366.       // רישום ללוג אם מוגדר ערוץ לוגים
  1367.       const logChannelId = guildData.log_channel_id.get(guildId);
  1368.       if (logChannelId) {
  1369.         const logChannel = client.channels.cache.get(logChannelId);
  1370.         if (logChannel) {
  1371.           await logChannel.send(`<@${member.user.id}> בדק את מצב מערכת האנטי-לינק בשרת.`);
  1372.         }
  1373.       }
  1374.     } catch (error) {
  1375.       await handleError(guildId, error, 'command_antilinkinfo');
  1376.       await context.editReply({ content: 'שגיאה בביצוע הפקודה /antilinkinfo.' }).catch(() => {
  1377.         context.channel.send('שגיאה בביצוע הפקודה /antilinkinfo.').catch(() => {});
  1378.       });
  1379.     }
  1380.   },
  1381.     builder: new SlashCommandBuilder()
  1382.       .setName('antispaminfo')
  1383.       .setDescription('מציג מידע על מערכת האנטי-ספאם של השרת'),
  1384.     handler: async (context, guildId, member) => {
  1385.       await context.deferReply({ ephemeral: true });
  1386.       try {
  1387.         if (!checkAdminRole(member, guildId)) {
  1388.           return context.editReply({ content: 'אין לך הרשאה!' });
  1389.         }
  1390.  
  1391.         const messageLimit = guildData.spam_message_limit.get(guildId) || 5;
  1392.         const timeWindow = guildData.spam_time_window.get(guildId) || 10;
  1393.         const muteDuration = guildData.spam_mute_duration.get(guildId) || '1h';
  1394.         const allowedRoles = guildData.allowed_spam_roles.get(guildId) || [];
  1395.  
  1396.         let allowedRolesText = 'אין תפקידים מורשים';
  1397.         if (allowedRoles.length > 0) {
  1398.           allowedRolesText = allowedRoles.map(id => `<@&${id}>`).join(', ');
  1399.         }
  1400.  
  1401.         const embed = new EmbedBuilder()
  1402.           .setTitle('מידע על מערכת האנטי-ספאם')
  1403.           .setDescription('הגדרות מערכת האנטי-ספאם של השרת:')
  1404.           .addFields(
  1405.             { name: 'מספר הודעות מקסימלי', value: `${messageLimit} הודעות`, inline: true },
  1406.             { name: 'חלון זמן', value: `${timeWindow} שניות`, inline: true },
  1407.             { name: 'משך המיוט', value: formatDuration(muteDuration), inline: true },
  1408.             { name: 'תפקידים מורשים', value: allowedRolesText, inline: false }
  1409.           )
  1410.           .setColor('#00ff00')
  1411.           .setTimestamp()
  1412.           .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
  1413.  
  1414.         await context.editReply({ embeds: [embed] });
  1415.  
  1416.         const logChannelId = guildData.log_channel_id.get(guildId);
  1417.         if (logChannelId) {
  1418.           const logChannel = client.channels.cache.get(logChannelId);
  1419.           if (logChannel) {
  1420.             await logChannel.send(`<@${member.user.id}> בדק את מצב מערכת האנטי-ספאם בשרת.`);
  1421.           }
  1422.         }
  1423.       } catch (error) {
  1424.         await handleError(guildId, error, 'command_antispaminfo');
  1425.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /antispaminfo.' }).catch(() => {
  1426.           context.channel.send('שגיאה בביצוע הפקודה /antispaminfo.').catch(() => {});
  1427.         });
  1428.       }
  1429.     },
  1430.   },
  1431.   {
  1432.     builder: new SlashCommandBuilder()
  1433.       .setName('setantispam')
  1434.       .setDescription('הגדרת מערכת אנטי-ספאם (מספר הודעות, חלון זמן, משך מיוט)')
  1435.       .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
  1436.       .addIntegerOption(option =>
  1437.         option.setName('message_limit').setDescription('מספר הודעות מקסימלי').setRequired(true).setMinValue(1)
  1438.       )
  1439.       .addIntegerOption(option =>
  1440.         option.setName('time_window').setDescription('חלון זמן בשניות').setRequired(true).setMinValue(1)
  1441.       )
  1442.       .addStringOption(option =>
  1443.         option.setName('mute_duration').setDescription('משך המיוט (לדוגמה: 1h, 30m, 1d)').setRequired(true)
  1444.       ),
  1445.     handler: async (context, guildId, member, options) => {
  1446.       await context.deferReply({ ephemeral: true });
  1447.       try {
  1448.         if (!hasAdministratorPermission({ member })) {
  1449.           return context.editReply({ content: 'אין לך הרשאת Administrator!' });
  1450.         }
  1451.  
  1452.         const messageLimit = options.getInteger('message_limit');
  1453.         const timeWindow = options.getInteger('time_window');
  1454.         const muteDuration = options.getString('mute_duration');
  1455.  
  1456.         const durationSeconds = parseDuration(muteDuration);
  1457.         if (!durationSeconds) {
  1458.           return context.editReply({ content: 'משך המיוט לא תקין! השתמש בפורמט כמו 1h, 30m, 1d.' });
  1459.         }
  1460.  
  1461.         guildData.spam_message_limit.set(guildId, messageLimit);
  1462.         guildData.spam_time_window.set(guildId, timeWindow);
  1463.         guildData.spam_mute_duration.set(guildId, muteDuration);
  1464.         await saveData(guildId);
  1465.  
  1466.         await context.editReply({
  1467.           content: `מערכת האנטי-ספאם עודכנה: ${messageLimit} הודעות ב-${timeWindow} שניות יגרמו למיוט של ${formatDuration(muteDuration)}.`,
  1468.         });
  1469.  
  1470.         const logChannelId = guildData.log_channel_id.get(guildId);
  1471.         if (logChannelId) {
  1472.           const logChannel = client.channels.cache.get(logChannelId);
  1473.           if (logChannel) {
  1474.             await logChannel.send(
  1475.               `<@${member.user.id}> עדכן את מערכת האנטי-ספאם: ${messageLimit} הודעות ב-${timeWindow} שניות, מיוט ל-${formatDuration(muteDuration)}.`
  1476.             );
  1477.           }
  1478.         }
  1479.       } catch (error) {
  1480.         await handleError(guildId, error, 'command_setantispam');
  1481.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /setantispam.' }).catch(() => {
  1482.           context.channel.send('שגיאה בביצוע הפקודה /setantispam.').catch(() => {});
  1483.         });
  1484.       }
  1485.     },
  1486.   },
  1487.   {
  1488.     builder: new SlashCommandBuilder()
  1489.       .setName('setallowspam')
  1490.       .setDescription('הגדרת תפקידים שמורשים לעקוף את מערכת האנטי-ספאם')
  1491.       .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
  1492.       .addRoleOption(option => option.setName('role1').setDescription('תפקיד ראשון').setRequired(false))
  1493.       .addRoleOption(option => option.setName('role2').setDescription('תפקיד שני').setRequired(false))
  1494.       .addRoleOption(option => option.setName('role3').setDescription('תפקיד שלישי').setRequired(false)),
  1495.     handler: async (context, guildId, member, options) => {
  1496.       await context.deferReply({ ephemeral: true });
  1497.       try {
  1498.         if (!hasAdministratorPermission({ member })) {
  1499.           return context.editReply({ content: 'אין לך הרשאת Administrator!' });
  1500.         }
  1501.  
  1502.         const roles = [
  1503.           options.getRole('role1')?.id,
  1504.           options.getRole('role2')?.id,
  1505.           options.getRole('role3')?.id,
  1506.         ].filter(Boolean);
  1507.  
  1508.         if (!roles.length) {
  1509.           guildData.allowed_spam_roles.set(guildId, []);
  1510.           await saveData(guildId);
  1511.           await context.editReply({ content: 'רשימת התפקידים המותרים לעקוף אנטי-ספאם אופסה בהצלחה.' });
  1512.  
  1513.           const logChannelId = guildData.log_channel_id.get(guildId);
  1514.           if (logChannelId) {
  1515.             const logChannel = client.channels.cache.get(logChannelId);
  1516.             if (logChannel) {
  1517.               await logChannel.send(`<@${member.user.id}> איפס את רשימת התפקידים המותרים לעקוף אנטי-ספאם בשרת.`);
  1518.             }
  1519.           }
  1520.           return;
  1521.         }
  1522.  
  1523.         guildData.allowed_spam_roles.set(guildId, roles);
  1524.         await saveData(guildId);
  1525.         await context.editReply({
  1526.           content: `תפקידים המורשים לעקוף אנטי-ספאם עודכנו ל: ${roles.map(id => `<@&${id}>`).join(' ')}`,
  1527.         });
  1528.  
  1529.         const logChannelId = guildData.log_channel_id.get(guildId);
  1530.         if (logChannelId) {
  1531.           const logChannel = client.channels.cache.get(logChannelId);
  1532.           if (logChannel) {
  1533.             await logChannel.send(
  1534.               `<@${member.user.id}> עדכן את התפקידים המותרים לעקוף אנטי-ספאם ל-${roles.map(id => `<@&${id}>`).join(', ')} בשרת.`
  1535.             );
  1536.           }
  1537.         }
  1538.       } catch (error) {
  1539.         await handleError(guildId, error, 'command_setallowspam');
  1540.         await context.editReply({ content: 'שגיאה בביצוע הפקודה /setallowspam.' }).catch(() => {
  1541.           context.channel.send('שגיאה בביצוע הפקודה /setallowspam.').catch(() => {});
  1542.         });
  1543.       }
  1544.     },
  1545.   }
  1546. ];
  1547.  
  1548. const loadData = async guildId => {
  1549.   try {
  1550.     const db = databases.get(guildId) || await initializeDatabase(guildId);
  1551.     let guildRecord = await db.get('SELECT * FROM guilds WHERE guild_id = ?', guildId);
  1552.     if (!guildRecord) {
  1553.       guildRecord = {
  1554.         guild_id: guildId,
  1555.         admin_role_mentions: JSON.stringify(['Helper']),
  1556.         log_channel_id: null,
  1557.         cooldown_time: 5,
  1558.         mute_role_id: null,
  1559.         ban_role_id: null,
  1560.         appeal_channel_id: null,
  1561.         auto_mute_links: 0,
  1562.         auto_mute_duration: '1h',
  1563.         allowed_link_roles: '[]',
  1564.         spam_message_limit: 5, // Default: 5 messages
  1565.         spam_time_window: 10, // Default: 10 seconds
  1566.         spam_mute_duration: '1h', // Default: 1 hour
  1567.         allowed_spam_roles: '[]', // Default: No roles
  1568.       };
  1569.       await db.run(
  1570.         '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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
  1571.         [
  1572.           guildId,
  1573.           guildRecord.admin_role_mentions,
  1574.           guildRecord.log_channel_id,
  1575.           guildRecord.cooldown_time,
  1576.           guildRecord.mute_role_id,
  1577.           guildRecord.ban_role_id,
  1578.           guildRecord.appeal_channel_id,
  1579.           guildRecord.auto_mute_links,
  1580.           guildRecord.auto_mute_duration,
  1581.           guildRecord.allowed_link_roles,
  1582.           guildRecord.spam_message_limit,
  1583.           guildRecord.spam_time_window,
  1584.           guildRecord.spam_mute_duration,
  1585.           guildRecord.allowed_spam_roles,
  1586.         ]
  1587.       );
  1588.     }
  1589.  
  1590.     guildData.admin_role_mentions.set(guildId, JSON.parse(guildRecord.admin_role_mentions));
  1591.     guildData.log_channel_id.set(guildId, guildRecord.log_channel_id);
  1592.     guildData.cooldown_time.set(guildId, guildRecord.cooldown_time);
  1593.     guildData.mute_role_id.set(guildId, guildRecord.mute_role_id);
  1594.     guildData.ban_role_id.set(guildId, guildRecord.ban_role_id);
  1595.     guildData.appeal_channel_id.set(guildId, guildRecord.appeal_channel_id);
  1596.     guildData.auto_mute_links.set(guildId, guildRecord.auto_mute_links === 1);
  1597.     guildData.auto_mute_duration.set(guildId, guildRecord.auto_mute_duration || '1h');
  1598.     guildData.allowed_link_roles.set(guildId, JSON.parse(guildRecord.allowed_link_roles || '[]'));
  1599.     guildData.spam_message_limit.set(guildId, guildRecord.spam_message_limit || 5);
  1600.     guildData.spam_time_window.set(guildId, guildRecord.spam_time_window || 10);
  1601.     guildData.spam_mute_duration.set(guildId, guildRecord.spam_mute_duration || '1h');
  1602.     guildData.allowed_spam_roles.set(guildId, JSON.parse(guildRecord.allowed_spam_roles || '[]'));
  1603.     guildData.user_message_timestamps.set(guildId, new Map());
  1604.  
  1605.     guildData.help_requests.set(guildId, new Map());
  1606.     guildData.last_help_request.set(guildId, new Map());
  1607.     for (const req of await db.all('SELECT * FROM help_requests')) {
  1608.       guildData.help_requests.get(guildId).set(req.user_id, req.channel_id);
  1609.       guildData.last_help_request.get(guildId).set(req.user_id, req.created_at);
  1610.     }
  1611.  
  1612.     guildData.taken_requests.set(guildId, new Map());
  1613.     for (const req of await db.all('SELECT * FROM taken_requests')) {
  1614.       guildData.taken_requests.get(guildId).set(req.user_id, req.admin_id);
  1615.     }
  1616.  
  1617.     guildData.admin_help_counts.set(guildId, new Map());
  1618.     for (const admin of await db.all('SELECT * FROM admin_help_counts')) {
  1619.       guildData.admin_help_counts.get(guildId).set(admin.admin_id, admin.count);
  1620.     }
  1621.  
  1622.     for (const muted of await db.all('SELECT * FROM muted_users')) {
  1623.       if (muted.end_time && muted.end_time > Date.now() / 1000) {
  1624.         const timeLeft = (muted.end_time - Date.now() / 1000) * 1000;
  1625.         muteTimers.set(`${guildId}_${muted.user_id}`, setTimeout(() => unmuteUser(guildId, muted.user_id, client.guilds.cache.get(guildId)), timeLeft));
  1626.       }
  1627.     }
  1628.  
  1629.     for (const muted of await db.all('SELECT * FROM voice_muted_users')) {
  1630.       if (muted.end_time && muted.end_time > Date.now() / 1000) {
  1631.         const timeLeft = (muted.end_time - Date.now() / 1000) * 1000;
  1632.         const muteKey = `voice_${guildId}_${muted.user_id}`;
  1633.         muteTimers.set(muteKey, setTimeout(() => unvoiceMuteUser(guildId, muted.user_id, client.guilds.cache.get(guildId)), timeLeft));
  1634.       }
  1635.     }
  1636.  
  1637.     for (const banned of await db.all('SELECT * FROM banned_users')) {
  1638.       if (banned.end_time && banned.end_time > Date.now() / 1000) {
  1639.         const timeLeft = (banned.end_time - Date.now() / 1000) * 1000;
  1640.         banTimers.set(`${guildId}_${banned.user_id}`, setTimeout(() => unbanUser(guildId, banned.user_id, client.guilds.cache.get(guildId)), timeLeft));
  1641.       }
  1642.     }
  1643.   } catch (error) {
  1644.     await handleError(guildId, error, 'loadData');
  1645.   }
  1646. };
  1647.  
  1648. const saveData = async guildId => {
  1649.   try {
  1650.     const db = databases.get(guildId);
  1651.     if (!db) return;
  1652.  
  1653.     await db.run(
  1654.       '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 = ?',
  1655.       [
  1656.         JSON.stringify(guildData.admin_role_mentions.get(guildId) || ['Helper']),
  1657.         guildData.log_channel_id.get(guildId),
  1658.         guildData.cooldown_time.get(guildId) || 5,
  1659.         guildData.mute_role_id.get(guildId),
  1660.         guildData.ban_role_id.get(guildId),
  1661.         guildData.appeal_channel_id.get(guildId),
  1662.         guildData.auto_mute_links.get(guildId) ? 1 : 0,
  1663.         guildData.auto_mute_duration.get(guildId) || '1h',
  1664.         JSON.stringify(guildData.allowed_link_roles.get(guildId) || []),
  1665.         guildData.spam_message_limit.get(guildId) || 5,
  1666.         guildData.spam_time_window.get(guildId) || 10,
  1667.         guildData.spam_mute_duration.get(guildId) || '1h',
  1668.         JSON.stringify(guildData.allowed_spam_roles.get(guildId) || []),
  1669.         guildId,
  1670.       ]
  1671.     );
  1672.  
  1673.     await db.run('DELETE FROM help_requests WHERE 1=1');
  1674.     for (const [userId, channelId] of guildData.help_requests.get(guildId) || new Map()) {
  1675.       const createdAt = guildData.last_help_request.get(guildId)?.get(userId) || Date.now() / 1000;
  1676.       await db.run('INSERT INTO help_requests (user_id, channel_id, created_at) VALUES (?, ?, ?)', userId, channelId, createdAt);
  1677.     }
  1678.  
  1679.     await db.run('DELETE FROM taken_requests WHERE 1=1');
  1680.     for (const [userId, adminId] of guildData.taken_requests.get(guildId) || new Map()) {
  1681.       await db.run('INSERT INTO taken_requests (user_id, admin_id) VALUES (?, ?)', userId, adminId);
  1682.     }
  1683.  
  1684.     await db.run('DELETE FROM admin_help_counts WHERE 1=1');
  1685.     for (const [adminId, count] of guildData.admin_help_counts.get(guildId) || new Map()) {
  1686.       await db.run('INSERT INTO admin_help_counts (admin_id, count) VALUES (?, ?)', adminId, count);
  1687.     }
  1688.   } catch (error) {
  1689.     await handleError(guildId, error, 'saveData');
  1690.   }
  1691. };
  1692.  
  1693. // Register slash commands
  1694. client.once('ready', async () => {
  1695.   console.log(`מחובר כ-${client.user.tag}`);
  1696.   try {
  1697.     const rest = new REST({ version: '10' }).setToken(TOKEN);
  1698.     const commandsData = commands.map(cmd => cmd.builder.toJSON());
  1699.     await rest.put(Routes.applicationCommands(client.user.id), { body: commandsData });
  1700.     console.log('פקודות נרשמו בהצלחה!');
  1701.  
  1702.     for (const guild of client.guilds.cache.values()) {
  1703.       await loadData(guild.id);
  1704.     }
  1705.   } catch (error) {
  1706.     console.error('שגיאה ברישום הפקודות:', error);
  1707.   }
  1708. });
  1709.  
  1710. // Handle slash commands
  1711. client.on('interactionCreate', async interaction => {
  1712.   if (!interaction.isChatInputCommand() && !interaction.isButton()) return;
  1713.  
  1714.   const guildId = interaction.guildId;
  1715.   if (!guildId) {
  1716.     return interaction.reply({ content: 'פקודה זו זמינה רק בשרתים!', ephemeral: true }).catch(() => {});
  1717.   }
  1718.  
  1719.   try {
  1720.     // Validate guildId
  1721.     if (!/^\d+$/.test(guildId)) {
  1722.       throw new Error(`Invalid guildId: ${guildId}`);
  1723.     }
  1724.  
  1725.     const db = databases.get(guildId) || await initializeDatabase(guildId);
  1726.     await ensureColumns(db, guildId);
  1727.  
  1728.     if (interaction.isChatInputCommand()) {
  1729.       const command = commands.find(cmd => cmd.builder.name === interaction.commandName);
  1730.       if (command) {
  1731.         await command.handler(interaction, guildId, interaction.member, interaction.options);
  1732.       }
  1733.     } else if (interaction.isButton()) {
  1734.       const [action, userId, targetGuildId] = interaction.customId.split('_');
  1735.  
  1736.       if (targetGuildId !== guildId) {
  1737.         return interaction.reply({ content: 'גילדה לא תואמת!', ephemeral: true }).catch(() => {});
  1738.       }
  1739.  
  1740.       if (action === 'handle') {
  1741.  
  1742.         if (!checkAdminRole(interaction.member, guildId)) {
  1743.           return interaction.reply({ content: 'אין לך הרשאה!', ephemeral: true });
  1744.         }
  1745.  
  1746.         await interaction.deferReply({ ephemeral: true });
  1747.  
  1748.         // Determine the channel where the user is
  1749.         const channelId = guildData.help_requests.get(guildId)?.get(userId) || interaction.channelId;
  1750.         const channel = interaction.client.channels.cache.get(channelId);
  1751.         const channelName = channel ? `<#${channelId}>` : 'ללא שיחה';
  1752.  
  1753.         // Update guild data
  1754.         if (!guildData.taken_requests.has(guildId)) {
  1755.           guildData.taken_requests.set(guildId, new Map());
  1756.         }
  1757.         if (!guildData.admin_help_counts.has(guildId)) {
  1758.           guildData.admin_help_counts.set(guildId, new Map());
  1759.         }
  1760.         guildData.taken_requests.get(guildId).set(userId, interaction.user.id);
  1761.         guildData.admin_help_counts.get(guildId).set(interaction.user.id, (guildData.admin_help_counts.get(guildId).get(interaction.user.id) || 0) + 1);
  1762.  
  1763.         // Save data to database
  1764.         await db.run(
  1765.           'INSERT OR REPLACE INTO taken_requests (user_id, admin_id) VALUES (?, ?)',
  1766.           [userId, interaction.user.id]
  1767.         );
  1768.         await db.run(
  1769.           'INSERT OR REPLACE INTO admin_help_counts (admin_id, count) VALUES (?, ?)',
  1770.           [interaction.user.id, guildData.admin_help_counts.get(guildId).get(interaction.user.id)]
  1771.         );
  1772.         await saveData(guildId);
  1773.  
  1774.         // Remove the help request
  1775.         guildData.help_requests.get(guildId)?.delete(userId);
  1776.         await db.run('DELETE FROM help_requests WHERE user_id = ?', userId);
  1777.  
  1778.         // Send message to log channel
  1779.         const logChannelId = guildData.log_channel_id.get(guildId);
  1780.         if (logChannelId) {
  1781.           const logChannel = interaction.client.channels.cache.get(logChannelId);
  1782.           if (logChannel) {
  1783.                 await logChannel.send(`${interaction.user} לקח את הקריאה של <@${userId}>.`);
  1784.           } else {
  1785.             console.error(`Log channel ${logChannelId} not found for guild ${guildId}`);
  1786.           }
  1787.         } else {
  1788.           console.error(`No log channel set for guild ${guildId}`);
  1789.         }
  1790.  
  1791.         // Edit the original help request message to remove the button
  1792.         if (interaction.message) {
  1793.           await interaction.message.edit({ components: [] }).catch(err => console.error(`Failed to edit message: ${err}`));
  1794.         }
  1795.         await sendWebhookMessage(interaction.channel, interaction.user, 'הקריאה בטיפול');
  1796.         await interaction.editReply({ content: `לקחת את קריאת העזרה של <@${userId}>!` });
  1797.  
  1798.         // Clear taken_requests after handling
  1799.         guildData.taken_requests.get(guildId).delete(userId);
  1800.         await db.run('DELETE FROM taken_requests WHERE user_id = ?', userId);
  1801.       } else if (action === 'approve' || action === 'reject') {
  1802.         if (!checkAdminRole(interaction.member, guildId)) {
  1803.           return interaction.reply({ content: 'אין לך הרשאה!', ephemeral: true });
  1804.         }
  1805.  
  1806.         const appeal = await db.get('SELECT * FROM ban_appeals WHERE user_id = ? AND guild_id = ? AND status = ?', userId, guildId, 'pending');
  1807.         if (!appeal) {
  1808.           return interaction.editReply({ content: 'בקשת הערעור כבר לא קיימת או טופלה!', ephemeral: true });
  1809.         }
  1810.  
  1811.         await interaction.deferReply({ ephemeral: true });
  1812.         const status = action === 'approve' ? 'approved' : 'rejected';
  1813.         await db.run('UPDATE ban_appeals SET status = ? WHERE user_id = ? AND guild_id = ?', status, userId, guildId);
  1814.  
  1815.         const user = await client.users.fetch(userId).catch(() => null);
  1816.         const guild = client.guilds.cache.get(guildId);
  1817.         const member = await guild.members.fetch(userId).catch(() => null);
  1818.  
  1819.         if (action === 'approve' && member) {
  1820.           await unbanUser(guildId, userId, guild, `ערעור התקבל על ידי <@${interaction.user.id}>`, true, interaction.user);
  1821.         }
  1822.  
  1823.         const embed = new EmbedBuilder()
  1824.           .setTitle(`בקשת ערעור ${action === 'approve' ? 'אושרה' : 'נדחתה'}`)
  1825.           .setDescription(`בקשת הערעור שלך בשרת ${action === 'approve' ? 'אושרה' : 'נדחתה'}.`)
  1826.           .addFields(
  1827.             { name: 'טופל על ידי', value: `<@${interaction.user.id}>`, inline: true },
  1828.             { name: 'תאריך ושעה', value: new Date().toLocaleString('he-IL'), inline: true }
  1829.           )
  1830.           .setColor(action === 'approve' ? '#00ff00' : '#ff0000')
  1831.           .setTimestamp()
  1832.           .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
  1833.  
  1834.         if (user) {
  1835.           await user.send({ embeds: [embed] }).catch(error => {
  1836.             console.error(`Failed to send DM to ${userId}:`, error);
  1837.             const logChannelId = guildData.log_channel_id.get(guildId);
  1838.             if (logChannelId) {
  1839.               const logChannel = client.channels.cache.get(logChannelId);
  1840.               if (logChannel) {
  1841.                 logChannel.send(`לא ניתן לשלוח הודעה פרטית ל-<@${userId}> על תוצאת הערעור. סיבה: ${error.message}`);
  1842.               }
  1843.             }
  1844.           });
  1845.         }
  1846.  
  1847.         const appealChannelId = guildData.appeal_channel_id.get(guildId);
  1848.         if (appealChannelId) {
  1849.           const appealChannel = client.channels.cache.get(appealChannelId);
  1850.           if (appealChannel) {
  1851.             const appealMessage = await appealChannel.messages.fetch(interaction.message.id).catch(() => null);
  1852.             if (appealMessage) {
  1853.               const updatedEmbed = new EmbedBuilder()
  1854.                 .setTitle('בקשת ערעור')
  1855.                 .setDescription(`**משתמש:** <@${userId}>\n**סיבת הבאן:** ${appeal.reason || 'לא צוינה'}\n**ערעור:** ${appeal.appeal_text}\n**סטטוס:** ${status === 'approved' ? 'אושר' : 'נדחה'}\n**טופל על ידי:** <@${interaction.user.id}>`)
  1856.                 .setColor(status === 'approved' ? '#00ff00' : '#ff0000')
  1857.                 .setTimestamp()
  1858.                 .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
  1859.               await appealMessage.edit({ embeds: [updatedEmbed], components: [] });
  1860.             }
  1861.           }
  1862.         }
  1863.  
  1864.         const logChannelId = guildData.log_channel_id.get(guildId);
  1865.         if (logChannelId) {
  1866.           const logChannel = client.channels.cache.get(logChannelId);
  1867.           if (logChannel) {
  1868.             await logChannel.send(`<@${interaction.user.id}> ${action === 'approve' ? 'אישר' : 'דחה'} את בקשת הערעור של <@${userId}> בשרת **${guild.name}**.`);
  1869.           }
  1870.         }
  1871.  
  1872.         await interaction.editReply({ content: `בקשת הערעור של <@${userId}> ${action === 'approve' ? 'אושרה' : 'נדחתה'} בהצלחה.` });
  1873.       }
  1874.     }
  1875.   } catch (error) {
  1876.     console.error(`Error in interactionCreate for guild ${guildId}:`, error);
  1877.     await handleError(guildId, error, 'interactionCreate');
  1878.     if (!interaction.replied && !interaction.deferred) {
  1879.       await interaction.reply({ content: 'שגיאה בעיבוד הפקודה או הלחצן.', ephemeral: true }).catch(() => {});
  1880.     }
  1881.   }
  1882. });
  1883.  
  1884. // Handle messages for help requests and ban appeals
  1885. client.on('messageCreate', async message => {
  1886.   if (message.author.bot) return;
  1887.  
  1888.   const guildId = message.guildId;
  1889.   const isDM = !guildId && message.channel.type === ChannelType.DM;
  1890.  
  1891.   if (!isDM && guildData.auto_mute_links.get(guildId)) {
  1892.     try {
  1893.       // רגקס מעודכן שתומך בקישורים עם או בלי https:// או http://
  1894.       const inviteRegex = /(?:https?:\/\/)?(?:www\.)?(?:discord\.(?:gg|com)|discordapp\.com)\/(?:invite[\/\\])?[a-zA-Z0-9\-_\\]+/gi;
  1895.  
  1896.       if (inviteRegex.test(message.content)) {
  1897.         const invites = message.content.match(inviteRegex) || [];
  1898.         // סינון קישורים שמכילים discord.gg, discord.com או discordapp.com
  1899.         const validInvites = invites.filter(invite =>
  1900.           invite.includes('discord.gg') ||
  1901.           invite.includes('discord.com') ||
  1902.           invite.includes('discordapp.com')
  1903.         );
  1904.  
  1905.         if (validInvites.length === 0) return;
  1906.  
  1907.         const guild = message.guild;
  1908.         const member = await guild.members.fetch(message.author.id).catch(() => null);
  1909.         if (!member) return;
  1910.  
  1911.         const allowedRoles = guildData.allowed_link_roles.get(guildId) || [];
  1912.         const hasAllowedRole = member.roles.cache.some(role => allowedRoles.includes(role.id));
  1913.         if (hasAllowedRole) return;
  1914.  
  1915.         const guildInvites = await guild.invites.fetch().catch(() => null);
  1916.         let isOwnInvite = false;
  1917.         if (guildInvites) {
  1918.           for (const invite of validInvites) {
  1919.             const foundInvite = guildInvites.find(i =>
  1920.               i.url === invite ||
  1921.               `https://discord.gg/${i.code}` === invite ||
  1922.               `https://discord.com/invite/${i.code}` === invite ||
  1923.               `https://discordapp.com/invite/${i.code}` === invite ||
  1924.               `discordapp.com/invite/${i.code}` === invite
  1925.             );
  1926.             if (foundInvite) {
  1927.               isOwnInvite = true;
  1928.               break;
  1929.             }
  1930.           }
  1931.         }
  1932.         if (isOwnInvite) return;
  1933.  
  1934.         const muteRoleId = guildData.mute_role_id.get(guildId);
  1935.         if (!muteRoleId) {
  1936.           const logChannelId = guildData.log_channel_id.get(guildId);
  1937.           if (logChannelId) {
  1938.             const logChannel = client.channels.cache.get(logChannelId);
  1939.             if (logChannel) {
  1940.               await logChannel.send('⚠️ תפקיד המיוט לא הוגדר! אנא הגדר תפקיד מיוט עם `/setmuterole`.');
  1941.             }
  1942.           }
  1943.           return;
  1944.         }
  1945.  
  1946.         const muteRole = guild.roles.cache.get(muteRoleId);
  1947.         if (!muteRole) {
  1948.           const logChannelId = guildData.log_channel_id.get(guildId);
  1949.           if (logChannelId) {
  1950.             const logChannel = client.channels.cache.get(logChannelId);
  1951.             if (logChannel) {
  1952.               await logChannel.send('⚠️ תפקיד המיוט לא נמצא בשרת! אנא הגדר תפקיד תקף עם `/setmuterole`.');
  1953.             }
  1954.           }
  1955.           return;
  1956.         }
  1957.  
  1958.         const botMember = guild.members.me;
  1959.         const hasManageRoles = botMember.permissions.has(PermissionsBitField.Flags.ManageRoles);
  1960.         const roleHierarchyValid = muteRole.position < botMember.roles.highest.position;
  1961.         if (!hasManageRoles || !roleHierarchyValid) {
  1962.           const logChannelId = guildData.log_channel_id.get(guildId);
  1963.           if (logChannelId) {
  1964.             const logChannel = client.channels.cache.get(logChannelId);
  1965.             if (logChannel) {
  1966.               await logChannel.send(`⚠️ אין לי הרשאה לנהל תפקידים או שתפקיד המיוט גבוה מדי בהיררכיה! אנא בדוק את ההרשאות שלי ואת מיקום תפקיד המיוט.`);
  1967.             }
  1968.           }
  1969.           return;
  1970.         }
  1971.  
  1972.         const db = databases.get(guildId) || await initializeDatabase(guildId);
  1973.         await ensureColumns(db, guildId);
  1974.         if (await db.get('SELECT * FROM muted_users WHERE user_id = ?', member.id)) return;
  1975.  
  1976.         const duration = guildData.auto_mute_duration.get(guildId) || '1h';
  1977.         const durationSeconds = parseDuration(duration);
  1978.         if (!durationSeconds) {
  1979.           const logChannelId = guildData.log_channel_id.get(guildId);
  1980.           if (logChannelId) {
  1981.             const logChannel = client.channels.cache.get(logChannelId);
  1982.             if (logChannel) {
  1983.               await logChannel.send(`⚠️ משך המיוט לא תקין: ${duration}. אנא הגדר משך תקף עם `/setautomuteduration`.`);
  1984.             }
  1985.           }
  1986.           return;
  1987.         }
  1988.  
  1989.         // מחיקת ההודעה לפני המיוט
  1990.         await message.delete().catch(err => {
  1991.           throw new Error(`שגיאה במחיקת הודעה: ${err.message}`);
  1992.         });
  1993.  
  1994.         const reason = 'שליחת קישור דיסקורד של שרת אחר';
  1995.         const currentRoles = member.roles.cache.map(role => role.id).filter(id => id !== guild.id);
  1996.         await db.run(
  1997.           'INSERT INTO muted_users (user_id, end_time, roles, muted_by, duration, reason) VALUES (?, ?, ?, ?, ?, ?)',
  1998.           member.id,
  1999.           durationSeconds === Infinity ? null : Date.now() / 1000 + durationSeconds,
  2000.           JSON.stringify(currentRoles),
  2001.           client.user.id,
  2002.           duration,
  2003.           reason
  2004.         );
  2005.  
  2006.         await member.roles.set([muteRoleId]).catch(error => {
  2007.           throw new Error(`שגיאה בהגדרת תפקיד המיוט: ${error.message}`);
  2008.         });
  2009.  
  2010.         if (durationSeconds !== Infinity) {
  2011.           muteTimers.set(`${guildId}_${member.id}`, setTimeout(() => unmuteUser(guildId, member.id, guild), durationSeconds * 1000));
  2012.         }
  2013.         const endDate = durationSeconds === Infinity ? 'תמידי' : new Date((Date.now() / 1000 + durationSeconds) * 1000).toLocaleString('he-IL');
  2014.  
  2015.         const userMessage = message.content.replace(inviteRegex, '[קישור הוסר]') || 'אין תוכן נוסף';
  2016.         const embed = new EmbedBuilder()
  2017.           .setTitle('קיבלת מיוט')
  2018.           .setDescription(`קיבלת מיוט בשרת **${guild.name}** בגלל שליחת קישור דיסקורד של שרת אחר.`)
  2019.           .addFields(
  2020.             { name: 'משך', value: formatDuration(duration), inline: true },
  2021.             { name: 'יסתיים ב', value: endDate, inline: true },
  2022.             { name: 'סיבה', value: reason, inline: false },
  2023.             { name: 'נתן את המיוט', value: client.user.toString(), inline: true }
  2024.           )
  2025.           .setColor('#ff0000')
  2026.           .setTimestamp()
  2027.           .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
  2028.  
  2029.         await member.send({ embeds: [embed] }).catch(error => {
  2030.           const logChannelId = guildData.log_channel_id.get(guildId);
  2031.           if (logChannelId) {
  2032.             const logChannel = client.channels.cache.get(logChannelId);
  2033.             if (logChannel) {
  2034.               logChannel.send(`⚠️ לא ניתן לשלוח הודעה פרטית ל-<@${member.id}> על המיוט. סיבה: ${error.message}`);
  2035.             }
  2036.           }
  2037.         });
  2038.  
  2039.         const logChannelId = guildData.log_channel_id.get(guildId);
  2040.         if (logChannelId) {
  2041.           const logChannel = client.channels.cache.get(logChannelId);
  2042.           if (logChannel) {
  2043.             const logEmbed = new EmbedBuilder()
  2044.               .setTitle('מיוט אוטומטי')
  2045.               .setDescription(`משתמש קיבל מיוט בגלל שליחת קישור דיסקורד של שרת אחר.`)
  2046.               .addFields(
  2047.                 { name: 'משתמש', value: `<@${member.id}>`, inline: true },
  2048.                 { name: 'משך', value: formatDuration(duration), inline: true },
  2049.                 { name: 'סיבה', value: reason, inline: false },
  2050.                 { name: 'הודעה', value: userMessage, inline: false },
  2051.                 { name: 'קישור', value: validInvites.join(', ') || 'אין קישור', inline: false }
  2052.               )
  2053.               .setColor('#ff0000')
  2054.               .setTimestamp()
  2055.               .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
  2056.             await logChannel.send({ embeds: [logEmbed] });
  2057.           }
  2058.         }
  2059.       }
  2060.     } catch (error) {
  2061.       await handleError(guildId, error, 'auto_mute_links');
  2062.     }
  2063.   }
  2064.  
  2065. // נעילה זמנית למניעת עיבוד מקביל של ספאם
  2066. const processingSpam = new Set();
  2067.  
  2068. if (!isDM) {
  2069.   try {
  2070.     const messageLimit = guildData.spam_message_limit.get(guildId) || 5;
  2071.     const timeWindow = guildData.spam_time_window.get(guildId) || 10;
  2072.     const muteDuration = guildData.spam_mute_duration.get(guildId) || '10m';
  2073.     const allowedRoles = guildData.allowed_spam_roles.get(guildId) || [];
  2074.  
  2075.     console.log(`Anti-spam check for guild ${guildId}: messageLimit=${messageLimit}, timeWindow=${timeWindow}, muteDuration=${muteDuration}`);
  2076.  
  2077.     if (messageLimit === 0) {
  2078.       console.log(`Anti-spam disabled for guild ${guildId} (messageLimit=0)`);
  2079.       return;
  2080.     }
  2081.  
  2082.     const guild = message.guild;
  2083.     const member = await guild.members.fetch(message.author.id).catch(() => null);
  2084.     if (!member) {
  2085.       console.log(`Member ${message.author.id} not found in guild ${guildId}`);
  2086.       return;
  2087.     }
  2088.  
  2089.     const hasAllowedRole = member.roles.cache.some(role => allowedRoles.includes(role.id));
  2090.     if (hasAllowedRole) {
  2091.       console.log(`User ${member.id} has allowed role, skipping anti-spam`);
  2092.       return;
  2093.     }
  2094.  
  2095.     const spamKey = `${guildId}_${member.id}`;
  2096.     if (processingSpam.has(spamKey) || muteTimers.has(spamKey)) {
  2097.       console.log(`User ${member.id} is already being processed for spam or has an active mute timer in guild ${guildId}`);
  2098.       return;
  2099.     }
  2100.  
  2101.     if (!guildData.user_message_timestamps.get(guildId)) {
  2102.       guildData.user_message_timestamps.set(guildId, new Map());
  2103.     }
  2104.  
  2105.     const userTimestamps = guildData.user_message_timestamps.get(guildId);
  2106.     if (!userTimestamps.get(message.author.id)) {
  2107.       userTimestamps.set(message.author.id, []);
  2108.     }
  2109.  
  2110.     const timestamps = userTimestamps.get(message.author.id);
  2111.     timestamps.push(Date.now() / 1000);
  2112.  
  2113.     const cutoff = (Date.now() / 1000) - timeWindow;
  2114.     while (timestamps.length && timestamps[0] < cutoff) {
  2115.       timestamps.shift();
  2116.     }
  2117.  
  2118.     console.log(`User ${member.id} has ${timestamps.length}/${messageLimit} messages in ${timeWindow}s`);
  2119.  
  2120.     if (timestamps.length > messageLimit) {
  2121.       console.log(`Spam detected for user ${member.id} in guild ${guildId}`);
  2122.       processingSpam.add(spamKey);
  2123.  
  2124.       try {
  2125.         const muteRoleId = guildData.mute_role_id.get(guildId);
  2126.         if (!muteRoleId) {
  2127.           console.log(`No mute role defined for guild ${guildId}`);
  2128.           const logChannelId = guildData.log_channel_id.get(guildId);
  2129.           if (logChannelId) {
  2130.             const logChannel = client.channels.cache.get(logChannelId);
  2131.             if (logChannel) {
  2132.               await logChannel.send('⚠️ תפקיד המיוט לא הוגדר! אנא הגדר תפקיד מיוט עם `/setmuterole`.');
  2133.             }
  2134.           }
  2135.           userTimestamps.set(member.id, []); // נקה טיימסטמפים
  2136.           return;
  2137.         }
  2138.  
  2139.         const muteRole = guild.roles.cache.get(muteRoleId);
  2140.         if (!muteRole) {
  2141.           console.log(`Mute role ${muteRoleId} not found in guild ${guildId}`);
  2142.           const logChannelId = guildData.log_channel_id.get(guildId);
  2143.           if (logChannelId) {
  2144.             const logChannel = client.channels.cache.get(logChannelId);
  2145.             if (logChannel) {
  2146.               await logChannel.send('⚠️ תפקיד המיוט לא נמצא בשרת! אנא הגדר תפקיד תקף עם `/setmuterole`.');
  2147.             }
  2148.           }
  2149.           userTimestamps.set(member.id, []); // נקה טיימסטמפים
  2150.           return;
  2151.         }
  2152.  
  2153.         const botMember = guild.members.me;
  2154.         const hasManageRoles = botMember.permissions.has(PermissionsBitField.Flags.ManageRoles);
  2155.         const roleHierarchyValid = muteRole.position < botMember.roles.highest.position;
  2156.         if (!hasManageRoles || !roleHierarchyValid) {
  2157.           console.log(`Permission or hierarchy issue in guild ${guildId}: ManageRoles=${hasManageRoles}, RoleHierarchyValid=${roleHierarchyValid}`);
  2158.           const logChannelId = guildData.log_channel_id.get(guildId);
  2159.           if (logChannelId) {
  2160.             const logChannel = client.channels.cache.get(logChannelId);
  2161.             if (logChannel) {
  2162.               await logChannel.send(`⚠️ אין לי הרשאה לנהל תפקידים או שתפקיד המיוט גבוה מדי בהיררכיה!`);
  2163.             }
  2164.           }
  2165.           userTimestamps.set(member.id, []); // נקה טיימסטמפים
  2166.           return;
  2167.         }
  2168.  
  2169.         const db = databases.get(guildId) || await initializeDatabase(guildId);
  2170.  
  2171.         // בדיקה וניקוי רשומות מיוט ישנות
  2172.         await member.fetch(true); // רענן את התפקידים לפני הבדיקה
  2173.         const isActuallyMuted = member.roles.cache.has(muteRoleId);
  2174.         const dbMuted = db.get('SELECT * FROM muted_users WHERE user_id = ?', member.id);
  2175.         if (dbMuted) {
  2176.           const currentTime = Date.now() / 1000;
  2177.           if (dbMuted.end_time && dbMuted.end_time < currentTime) {
  2178.             console.log(`User ${member.id} has expired mute record in guild ${guildId}, removing...`);
  2179.             db.run('DELETE FROM muted_users WHERE user_id = ?', member.id);
  2180.           } else if (isActuallyMuted) {
  2181.             console.log(`User ${member.id} is already muted in guild ${guildId}`);
  2182.             userTimestamps.set(member.id, []); // נקה טיימסטמפים
  2183.             return;
  2184.           }
  2185.         }
  2186.  
  2187.         const durationSeconds = parseDuration(muteDuration);
  2188.         console.log(`Parsed mute duration for guild ${guildId}: ${muteDuration} -> ${durationSeconds} seconds`);
  2189.         if (!durationSeconds) {
  2190.           console.log(`Invalid mute duration ${muteDuration} for guild ${guildId}`);
  2191.           const logChannelId = guildData.log_channel_id.get(guildId);
  2192.           if (logChannelId) {
  2193.             const logChannel = client.channels.cache.get(logChannelId);
  2194.             if (logChannel) {
  2195.               await logChannel.send(`⚠️ משך המיוט לא תקין: ${muteDuration}. אנא הגדר משך תקף עם /setantispam.`);
  2196.             }
  2197.           }
  2198.           userTimestamps.set(member.id, []); // נקה טיימסטמפים
  2199.           return;
  2200.         }
  2201.  
  2202.         // הגדר muteTimers מיד לאחר זיהוי הספאם
  2203.         if (durationSeconds !== Infinity) {
  2204.           muteTimers.set(
  2205.             spamKey,
  2206.             setTimeout(() => unmuteUser(guildId, member.id, guild), durationSeconds * 1000)
  2207.           );
  2208.         }
  2209.  
  2210.         // שמור מספר ההודעות שזוהו כספאם ונקה טיימסטמפים
  2211.         const spamMessageCount = timestamps.length;
  2212.         userTimestamps.set(member.id, []);
  2213.  
  2214.         // איסוף כל ההודעות מאז ההודעה הראשונה בחלון
  2215.         const channel = message.channel;
  2216.         const firstTimestamp = timestamps[0] ? timestamps[0] * 1000 : (Date.now() / 1000 - timeWindow) * 1000; // זמן ההודעה הראשונה או חלון של 10 שניות
  2217.         const collectedMessages = await channel.awaitMessages({
  2218.           filter: msg => msg.author.id === member.id && msg.createdTimestamp >= firstTimestamp,
  2219.           max: messageLimit + 10, // אסוף עד 10 הודעות נוספות
  2220.           time: 10000, // המתן 10 שניות להודעות נוספות
  2221.           errors: ['time']
  2222.         }).catch(() => new Map());
  2223.  
  2224.         // איסוף הודעות בפגינציה (עד 100 הודעות בכל קריאה)
  2225.         let recentMessages = new Map();
  2226.         let lastMessageId = null;
  2227.         do {
  2228.           const fetchOptions = { limit: 100 };
  2229.           if (lastMessageId) fetchOptions.before = lastMessageId;
  2230.           const messages = await channel.messages.fetch(fetchOptions).catch(err => {
  2231.             console.error(`Failed to fetch messages: ${err.message}`);
  2232.             return new Map();
  2233.           });
  2234.           recentMessages = new Map([...recentMessages, ...messages]);
  2235.           lastMessageId = messages.size === 100 ? messages.lastKey() : null;
  2236.         } while (lastMessageId && recentMessages.size < 200); // הגבל ל-200 הודעות כדי למנוע לולאה אינסופית
  2237.  
  2238.         const userMessages = new Map([
  2239.           ...recentMessages.filter(msg => msg.author.id === member.id && msg.createdTimestamp >= firstTimestamp),
  2240.           ...collectedMessages
  2241.         ]);
  2242.         const messagesToDelete = Array.from(userMessages.values())
  2243.           .sort((a, b) => a.createdTimestamp - b.createdTimestamp); // מיון כרונולוגי
  2244.         const logContent = `הודעות שנמחקו עבור ${member.displayName} (${member.id}):\n` +
  2245.           messagesToDelete
  2246.             .map(msg => `[${msg.createdAt.toISOString().slice(0, 19).replace('T', ' ')}] ${msg.content || '[ריק]'}\n${msg.attachments.size ? `קבצים: ${msg.attachments.map(att => att.url).join(', ')}\n` : ''}`)
  2247.             .join('');
  2248.  
  2249.         // מחיקת ההודעות
  2250.         if (messagesToDelete.length > 0) {
  2251.           await channel.bulkDelete(messagesToDelete, true).catch(err => console.error(`Failed to bulk delete messages: ${err.message}`));
  2252.         }
  2253.  
  2254.         const reason = `ספאם: שליחת ${spamMessageCount} הודעות תוך ${timeWindow} שניות`; // השתמש במספר ההודעות שזוהו כספאם
  2255.         db.run(
  2256.           'INSERT OR REPLACE INTO muted_users (user_id, end_time, roles, muted_by, duration, reason) VALUES (?, ?, ?, ?, ?, ?)',
  2257.           [
  2258.             member.id,
  2259.             durationSeconds === Infinity ? null : Date.now() / 1000 + durationSeconds,
  2260.             JSON.stringify(member.roles.cache.map(role => role.id).filter(id => id !== guild.id)),
  2261.             client.user.id,
  2262.             muteDuration,
  2263.             reason,
  2264.           ]
  2265.         );
  2266.  
  2267.         try {
  2268.           await member.roles.set([muteRoleId]);
  2269.           console.log(`Muted user ${member.id} in guild ${guildId} with role ${muteRoleId}`);
  2270.         } catch (error) {
  2271.           console.error(`Failed to mute user ${member.id} in guild ${guildId}: ${error.message}`);
  2272.           const logChannelId = guildData.log_channel_id.get(guildId);
  2273.           if (logChannelId) {
  2274.             const logChannel = client.channels.cache.get(logChannelId);
  2275.             if (logChannel) {
  2276.               await logChannel.send(`⚠️ שגיאה בהשתקת משתמש <@${member.id}>: ${error.message}`);
  2277.             }
  2278.           }
  2279.           muteTimers.delete(spamKey); // הסר טיימר אם המיוט נכשל
  2280.           return;
  2281.         }
  2282.  
  2283.         const endDate = durationSeconds === Infinity ? 'תמידי' : new Date((Date.now() / 1000 + durationSeconds) * 1000).toLocaleString('he-IL');
  2284.  
  2285.         const embed = new EmbedBuilder()
  2286.           .setTitle('קיבלת מיוט')
  2287.           .setDescription(`קיבלת מיוט בשרת **${guild.name}** בגלל ספאם.`)
  2288.           .addFields(
  2289.             { name: 'משך', value: formatDuration(muteDuration), inline: true },
  2290.             { name: 'יסתיים ב', value: endDate, inline: true },
  2291.             { name: 'סיבה', value: reason, inline: false },
  2292.             { name: 'נתן את המיוט', value: client.user.toString(), inline: true }
  2293.           )
  2294.           .setColor('#ff0000')
  2295.           .setTimestamp()
  2296.           .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
  2297.  
  2298.         await member.send({ embeds: [embed] }).catch(error => {
  2299.           console.log(`Failed to DM user ${member.id}: ${error.message}`);
  2300.           const logChannelId = guildData.log_channel_id.get(guildId);
  2301.           if (logChannelId) {
  2302.             const logChannel = client.channels.cache.get(logChannelId);
  2303.             if (logChannel) {
  2304.               logChannel.send(`⚠️ לא ניתן לשלוח הודעה פרטית ל-<@${member.id}> על המיוט. סיבה: ${error.message}`);
  2305.             }
  2306.           }
  2307.         });
  2308.  
  2309.         const logChannelId = guildData.log_channel_id.get(guildId);
  2310.         if (logChannelId) {
  2311.           const logChannel = client.channels.cache.get(logChannelId);
  2312.           if (logChannel) {
  2313.             const logEmbed = new EmbedBuilder()
  2314.               .setTitle('מיוט אוטומטי - ספאם')
  2315.               .setDescription(`משתמש קיבל מיוט בגלל ספאם.`)
  2316.               .addFields(
  2317.                 { name: 'משתמש', value: `<@${member.id}>`, inline: true },
  2318.                 { name: 'משך', value: formatDuration(muteDuration), inline: true },
  2319.                 { name: 'סיבה', value: reason, inline: false },
  2320.                 { name: 'הודעות שנמחקו', value: messagesToDelete.length ? logContent.slice(0, 1000) : 'אין הודעות שנמחקו', inline: false }
  2321.               )
  2322.               .setColor('#ff0000')
  2323.               .setTimestamp()
  2324.               .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
  2325.  
  2326.             await logChannel.send({
  2327.               embeds: [logEmbed],
  2328.               files: messagesToDelete.length ? [{
  2329.                 attachment: Buffer.from(logContent, 'utf8'),
  2330.                 name: `deleted_messages_${member.id}_${new Date().toISOString().replace(/[-:.]/g, '')}.txt`
  2331.               }] : []
  2332.             });
  2333.           }
  2334.         }
  2335.       } finally {
  2336.         processingSpam.delete(spamKey); // הסר את הנעילה
  2337.       }
  2338.     }
  2339.  
  2340.     userTimestamps.set(message.author.id, timestamps);
  2341.   } catch (error) {
  2342.     console.error(`Anti-spam error in guild ${guildId}: ${error.message}`);
  2343.     await handleError(guildId, error, 'anti_spam');
  2344.   }
  2345. }
  2346.  
  2347.   // Handle !h in guild channels
  2348.   if (!isDM && message.content === '!h') {
  2349.     const guild = message.guild;
  2350.     if (!guild) return;
  2351.     const member = await guild.members.fetch(message.author.id).catch(() => null);
  2352.     if (!member) return;
  2353.     const command = commands.find(cmd => cmd.builder.name === 'h');
  2354.     if (command) {
  2355.       try {
  2356.         const fakeInteraction = {
  2357.           guildId: guild.id,
  2358.           user: message.author,
  2359.           member: member,
  2360.           channel: message.channel,
  2361.           channelId: message.channel.id,
  2362.           client: client,
  2363.           guild: guild,
  2364.           reply: async (options) => {
  2365.             if (options.embeds && options.components) {
  2366.               const sentMessage = await message.channel.send({
  2367.                 embeds: options.embeds,
  2368.                 components: options.components,
  2369.               });
  2370.               return sentMessage;
  2371.             }
  2372.             return { message: null };
  2373.           },
  2374.           deferReply: async () => {},
  2375.           editReply: async () => {}
  2376.         };
  2377.         await command.handler(fakeInteraction, guild.id, member, {});
  2378.       } catch (error) {
  2379.         await handleError(guild.id, error, 'message_h');
  2380.       }
  2381.     }
  2382.     return;
  2383.   }
  2384.  
  2385.   // Handle /h in guild channels
  2386.   if (!isDM && message.content === '/h') {
  2387.     const guild = message.guild;
  2388.     if (!guild) return;
  2389.     const member = await guild.members.fetch(message.author.id).catch(() => null);
  2390.     if (!member) return;
  2391.     const command = commands.find(cmd => cmd.builder.name === 'h');
  2392.     if (command) {
  2393.       try {
  2394.         const fakeInteraction = {
  2395.           guildId: guild.id,
  2396.           user: message.author,
  2397.           member: member,
  2398.           channel: message.channel,
  2399.           channelId: message.channel.id,
  2400.           client: client,
  2401.           guild: guild,
  2402.           reply: async (options) => {
  2403.             const sentMessage = await message.channel.send({
  2404.               content: options.content,
  2405.               embeds: options.embeds,
  2406.               components: options.components,
  2407.             });
  2408.             return sentMessage;
  2409.           },
  2410.           deferReply: async () => {},
  2411.           editReply: async (options) => {
  2412.             await message.channel.send({
  2413.               content: options.content,
  2414.               embeds: options.embeds,
  2415.               components: options.components,
  2416.             });
  2417.           }
  2418.         };
  2419.         await command.handler(fakeInteraction, guild.id, member, {});
  2420.       } catch (error) {
  2421.         await handleError(guild.id, error, 'message_h');
  2422.         await message.reply('שגיאה בביצוע הפקודה /h.').catch(() => {});
  2423.       }
  2424.     }
  2425.     return;
  2426.   }
  2427.  
  2428.   // Handle ban appeal requests in DM
  2429.   if (isDM && message.content.startsWith('ערעור')) {
  2430.     try {
  2431.       const appealText = message.content.slice(5).trim();
  2432.       if (!appealText) {
  2433.         return message.reply('אנא ציין את פרטי הערעור לאחר המילה "ערעור".');
  2434.       }
  2435.  
  2436.       const bannedGuilds = [];
  2437.       for (const guild of client.guilds.cache.values()) {
  2438.         const db = databases.get(guild.id) || await initializeDatabase(guild.id);
  2439.         await ensureColumns(db, guild.id);
  2440.         const bannedUser = await db.get('SELECT * FROM banned_users WHERE user_id = ?', message.author.id);
  2441.         if (bannedUser) {
  2442.           bannedGuilds.push({ guild, db, bannedUser });
  2443.         }
  2444.       }
  2445.  
  2446.       if (!bannedGuilds.length) {
  2447.         return message.reply('לא נמצאו שרתים שבהם אתה חסום.');
  2448.       }
  2449.  
  2450.       if (bannedGuilds.length > 1) {
  2451.         return message.reply('אתה חסום במספר שרתים. אנא ציין את שם השרת שאליו הערעור מתייחס.');
  2452.       }
  2453.  
  2454.       const { guild, db, bannedUser } = bannedGuilds[0];
  2455.       const appealChannelId = guildData.appeal_channel_id.get(guild.id);
  2456.       if (!appealChannelId) {
  2457.         return message.reply('השרת לא הגדיר ערוץ לבקשות ערעור. אנא פנה למנהלי השרת.');
  2458.       }
  2459.  
  2460.       const existingAppeal = await db.get('SELECT * FROM ban_appeals WHERE user_id = ? AND guild_id = ?', message.author.id, guild.id);
  2461.       if (existingAppeal) {
  2462.         if (existingAppeal.status === 'pending') {
  2463.           return message.reply('כבר יש לך בקשת ערעור ממתינה בשרת זה.');
  2464.         }
  2465.         const timeSinceLastAppeal = Date.now() / 1000 - existingAppeal.timestamp;
  2466.         if (timeSinceLastAppeal < 5) {
  2467.           const timeLeft = formatTimeLeft(5 - timeSinceLastAppeal);
  2468.           return message.reply(`עליך להמתין ${timeLeft} לפני שליחת ערעור נוסף בשרת זה.`);
  2469.         }
  2470.       }
  2471.  
  2472.       await db.run(
  2473.         'INSERT OR REPLACE INTO ban_appeals (user_id, guild_id, appeal_text, timestamp, status) VALUES (?, ?, ?, ?, ?)',
  2474.         message.author.id, guild.id, appealText, Date.now() / 1000, 'pending'
  2475.       );
  2476.  
  2477.       const appealChannel = client.channels.cache.get(appealChannelId);
  2478.       if (appealChannel) {
  2479.         const embed = new EmbedBuilder()
  2480.           .setTitle('בקשת ערעור')
  2481.           .setDescription(`**משתמש:** <@${message.author.id}>\n**סיבת הבאן:** ${bannedUser.reason || 'לא צוינה'}\n**ערעור:** ${appealText}\n**סטטוס:** ממתין`)
  2482.           .setColor('#ffa500')
  2483.           .setTimestamp()
  2484.           .setFooter({ text: 'בוט עזרה', iconURL: client.user.displayAvatarURL() });
  2485.  
  2486.         const approveButton = new ButtonBuilder()
  2487.           .setCustomId(`approve_${message.author.id}_${guild.id}`)
  2488.           .setLabel('אשר')
  2489.           .setStyle(ButtonStyle.Success);
  2490.         const rejectButton = new ButtonBuilder()
  2491.           .setCustomId(`reject_${message.author.id}_${guild.id}`)
  2492.           .setLabel('דחה')
  2493.           .setStyle(ButtonStyle.Danger);
  2494.         const row = new ActionRowBuilder().addComponents(approveButton, rejectButton);
  2495.  
  2496.         await appealChannel.send({ embeds: [embed], components: [row] });
  2497.       }
  2498.  
  2499.       const logChannelId = guildData.log_channel_id.get(guild.id);
  2500.       if (logChannelId) {
  2501.         const logChannel = client.channels.cache.get(logChannelId);
  2502.         if (logChannel) {
  2503.           await logChannel.send(`בקשת ערעור חדשה מ-<@${message.author.id}>.`);
  2504.         }
  2505.       }
  2506.  
  2507.       await message.reply(`בקשת הערעור שלך נשלחה בהצלחה לשרת **${guild.name}**. תקבל הודעה כאשר המנהלים יבדקו אותה.`);
  2508.     } catch (error) {
  2509.       await handleError(null, error, 'dm_appeal');
  2510.       await message.reply('שגיאה בעיבוד בקשת הערעור. אנא נסה שוב מאוחר יותר.').catch(() => {});
  2511.     }
  2512.   }
  2513. });
  2514.  
  2515.  
  2516. // Handle voice state updates (unchanged)
  2517. client.on('voiceStateUpdate', async (oldState, newState) => {
  2518.   const guildId = newState.guild.id;
  2519.   const userId = newState.member.id;
  2520.   const db = databases.get(guildId) || await initializeDatabase(guildId);
  2521.   await ensureColumns(db, guildId);
  2522.  
  2523.   const existingMute = await db.get('SELECT * FROM voice_muted_users WHERE user_id = ?', userId);
  2524.   if (!existingMute) return;
  2525.  
  2526.   try {
  2527.     const muteKey = `voice_${guildId}_${userId}`;
  2528.     if (newState.channel && !voiceMuteFlags.has(`${guildId}_${userId}`) && !voiceMuteFlags.has(`timer_${guildId}_${userId}`)) {
  2529.       voiceMuteFlags.set(`${guildId}_${userId}`, true);
  2530.       await newState.setMute(true, existingMute.reason || 'המשך וויס מיוט קיים').catch(error => {
  2531.         handleError(guildId, error, 'voiceStateUpdate_mute');
  2532.       });
  2533.       voiceMuteFlags.delete(`${guildId}_${userId}`);
  2534.     } else if (!newState.channel && existingMute.source === 'interface') {
  2535.       await db.run('DELETE FROM voice_muted_users WHERE user_id = ?', userId);
  2536.       clearTimeout(muteTimers.get(muteKey));
  2537.       muteTimers.delete(muteKey);
  2538.     }
  2539.   } catch (error) {
  2540.     voiceMuteFlags.delete(`${guildId}_${userId}`);
  2541.     await handleError(guildId, error, 'voiceStateUpdate');
  2542.   }
  2543. });
  2544.  
  2545. // Handle guild member updates for voice mute (unchanged)
  2546. client.on('guildMemberUpdate', async (oldMember, newMember) => {
  2547.   const guildId = newMember.guild.id;
  2548.   const db = databases.get(guildId) || await initializeDatabase(guildId);
  2549.   await ensureColumns(db, guildId);
  2550.  
  2551.   try {
  2552.     const existingMute = await db.get('SELECT * FROM voice_muted_users WHERE user_id = ?', newMember.id);
  2553.     if (newMember.serverMute && !existingMute && newMember.voice.channel) {
  2554.       await db.run(
  2555.         'INSERT INTO voice_muted_users (user_id, end_time, muted_by, duration, reason, source) VALUES (?, ?, ?, ?, ?, ?)',
  2556.         newMember.id, null, 'מנהל דרך ממשק דיסקורד', '0', 'מיוט דרך ממשק דיסקורד', 'interface'
  2557.       );
  2558.       const logChannelId = guildData.log_channel_id.get(guildId);
  2559.       if (logChannelId) {
  2560.         const logChannel = client.channels.cache.get(logChannelId);
  2561.         if (logChannel) {
  2562.           await logChannel.send(`<@${newMember.id}> קיבל וויס מיוט דרך ממשק דיסקורד.`);
  2563.         }
  2564.       }
  2565.     } else if (!newMember.serverMute && existingMute && existingMute.source === 'interface' && !voiceMuteFlags.has(`unmute_voice_${guildId}_${newMember.id}`)) {
  2566.       await db.run('DELETE FROM voice_muted_users WHERE user_id = ?', newMember.id);
  2567.       const muteKey = `voice_${guildId}_${newMember.id}`;
  2568.       clearTimeout(muteTimers.get(muteKey));
  2569.       muteTimers.delete(muteKey);
  2570.       const logChannelId = guildData.log_channel_id.get(guildId);
  2571.       if (logChannelId) {
  2572.         const logChannel = client.channels.cache.get(logChannelId);
  2573.         if (logChannel) {
  2574.           await logChannel.send(`הוויס מיוט של <@${newMember.id}> הוסר דרך ממשק דיסקורד.`);
  2575.         }
  2576.       }
  2577.     }
  2578.   } catch (error) {
  2579.     await handleError(guildId, error, 'guildMemberUpdate');
  2580.   }
  2581. });
  2582.  
  2583. // Handle guild member add to restore mute/voice mute/ban roles
  2584. client.on('guildMemberAdd', async member => {
  2585.   const guildId = member.guild.id;
  2586.   const userId = member.id;
  2587.  
  2588.   try {
  2589.     // Validate guildId
  2590.     if (!/^\d+$/.test(guildId)) {
  2591.       throw new Error(`Invalid guildId: ${guildId}`);
  2592.     }
  2593.  
  2594.     // Initialize database if not already initialized
  2595.     const db = databases.get(guildId) || await initializeDatabase(guildId);
  2596.     await ensureColumns(db, guildId);
  2597.  
  2598.     // Check if user is muted
  2599.     const mutedUser = await db.get('SELECT * FROM muted_users WHERE user_id = ?', userId);
  2600.     if (mutedUser) {
  2601.       const muteRoleId = guildData.mute_role_id.get(guildId);
  2602.       if (muteRoleId) {
  2603.         const muteRole = member.guild.roles.cache.get(muteRoleId);
  2604.         if (muteRole) {
  2605.           // Ensure bot has permission to manage roles and role hierarchy is valid
  2606.           const botMember = member.guild.members.me;
  2607.           if (
  2608.             botMember.permissions.has(PermissionsBitField.Flags.ManageRoles) &&
  2609.             muteRole.position < botMember.roles.highest.position
  2610.           ) {
  2611.             await member.roles.set([muteRoleId]).catch(error => {
  2612.               throw new Error(`Failed to set mute role on join: ${error.message}`);
  2613.             });
  2614.  
  2615.             // Restart mute timer if applicable
  2616.             if (mutedUser.end_time && mutedUser.end_time > Date.now() / 1000) {
  2617.               const timeLeft = (mutedUser.end_time - Date.now() / 1000) * 1000;
  2618.               clearTimeout(muteTimers.get(`${guildId}_${userId}`));
  2619.               muteTimers.set(
  2620.                 `${guildId}_${userId}`,
  2621.                 setTimeout(() => unmuteUser(guildId, userId, member.guild), timeLeft)
  2622.               );
  2623.             }
  2624.  
  2625.             // Log restoration of mute role
  2626.             const logChannelId = guildData.log_channel_id.get(guildId);
  2627.             if (logChannelId) {
  2628.               const logChannel = client.channels.cache.get(logChannelId);
  2629.               if (logChannel) {
  2630.                 await logChannel.send(
  2631.                   `תפקיד המיוט הוחזר ל-<@${userId}> עם הצטרפותו לשרת. זמן שנותר: ${formatTimeLeft(
  2632.                     mutedUser.end_time ? mutedUser.end_time - Date.now() / 1000 : Infinity
  2633.                   )}`
  2634.                 );
  2635.               }
  2636.             }
  2637.           } else {
  2638.             console.error(`Bot lacks permissions or mute role is too high for guild ${guildId}`);
  2639.           }
  2640.         }
  2641.       }
  2642.     }
  2643.  
  2644.     // Check if user is voice muted
  2645.     const voiceMutedUser = await db.get('SELECT * FROM voice_muted_users WHERE user_id = ?', userId);
  2646.     if (voiceMutedUser) {
  2647.       const muteKey = `voice_${guildId}_${userId}`;
  2648.       // If user is in a voice channel, apply voice mute
  2649.       if (member.voice.channel) {
  2650.         voiceMuteFlags.set(`${guildId}_${userId}`, true);
  2651.         await member.voice.setMute(true, voiceMutedUser.reason || 'המשך וויס מיוט קיים').catch(error => {
  2652.           throw new Error(`Failed to set voice mute on join: ${error.message}`);
  2653.         });
  2654.         voiceMuteFlags.delete(`${guildId}_${userId}`);
  2655.       }
  2656.  
  2657.       // Restart voice mute timer if applicable
  2658.       if (voiceMutedUser.end_time && voiceMutedUser.end_time > Date.now() / 1000) {
  2659.         const timeLeft = (voiceMutedUser.end_time - Date.now() / 1000) * 1000;
  2660.         clearTimeout(muteTimers.get(muteKey));
  2661.         muteTimers.set(
  2662.           muteKey,
  2663.           setTimeout(() => unvoiceMuteUser(guildId, userId, member.guild), timeLeft)
  2664.         );
  2665.       }
  2666.  
  2667.       // Log restoration of voice mute
  2668.       const logChannelId = guildData.log_channel_id.get(guildId);
  2669.       if (logChannelId) {
  2670.         const logChannel = client.channels.cache.get(logChannelId);
  2671.         if (logChannel) {
  2672.           await logChannel.send(
  2673.             `וויס מיוט הוחזר ל-<@${userId}> עם הצטרפותו לשרת. זמן שנותר: ${formatTimeLeft(
  2674.               voiceMutedUser.end_time ? voiceMutedUser.end_time - Date.now() / 1000 : Infinity
  2675.             )}`
  2676.           );
  2677.         }
  2678.       }
  2679.     }
  2680.  
  2681.     // Check if user is banned
  2682.     const bannedUser = await db.get('SELECT * FROM banned_users WHERE user_id = ?', userId);
  2683.     if (bannedUser) {
  2684.       const banRoleId = guildData.ban_role_id.get(guildId);
  2685.       if (banRoleId) {
  2686.         const banRole = member.guild.roles.cache.get(banRoleId);
  2687.         if (banRole) {
  2688.           // Ensure bot has permission to manage roles and role hierarchy is valid
  2689.           const botMember = member.guild.members.me;
  2690.           if (
  2691.             botMember.permissions.has(PermissionsBitField.Flags.ManageRoles) &&
  2692.             banRole.position < botMember.roles.highest.position
  2693.           ) {
  2694.             await member.roles.set([banRoleId]).catch(error => {
  2695.               throw new Error(`Failed to set ban role on join: ${error.message}`);
  2696.             });
  2697.  
  2698.             // Restart ban timer if applicable
  2699.             if (bannedUser.end_time && bannedUser.end_time > Date.now() / 1000) {
  2700.               const timeLeft = (bannedUser.end_time - Date.now() / 1000) * 1000;
  2701.               clearTimeout(banTimers.get(`${guildId}_${userId}`));
  2702.               banTimers.set(
  2703.                 `${guildId}_${userId}`,
  2704.                 setTimeout(() => unbanUser(guildId, userId, member.guild), timeLeft)
  2705.               );
  2706.             }
  2707.  
  2708.             // Log restoration of ban role
  2709.             const logChannelId = guildData.log_channel_id.get(guildId);
  2710.             if (logChannelId) {
  2711.               const logChannel = client.channels.cache.get(logChannelId);
  2712.               if (logChannel) {
  2713.                 await logChannel.send(
  2714.                   `תפקיד הבאן הוחזר ל-<@${userId}> עם הצטרפותו לשרת. זמן שנותר: ${formatTimeLeft(
  2715.                     bannedUser.end_time ? bannedUser.end_time - Date.now() / 1000 : Infinity
  2716.                   )}`
  2717.                 );
  2718.               }
  2719.             }
  2720.           } else {
  2721.             console.error(`Bot lacks permissions or ban role is too high for guild ${guildId}`);
  2722.           }
  2723.         }
  2724.       }
  2725.     }
  2726.   } catch (error) {
  2727.     await handleError(guildId, error, 'guildMemberAdd');
  2728.     console.error(`Error handling guildMemberAdd for user ${userId} in guild ${guildId}:`, error);
  2729.   }
  2730. });
  2731.  
  2732.  
  2733. // Initialize bot
  2734. client.login(TOKEN).catch(error => {
  2735.   console.error('שגיאה בהתחברות:', error);
  2736.   process.exit(1);
  2737. });
Advertisement
Add Comment
Please, Sign In to add comment