levix-bot 3.3.0-rc.2 → 4.0.0-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/README.md +11 -7
  2. package/app.cjs +42 -45
  3. package/package.json +9 -1
  4. package/public/dashboard/assets/index-C1b7ijfu.js +391 -0
  5. package/public/dashboard/assets/index-pYaivHON.css +1 -0
  6. package/public/dashboard/index.html +19 -0
  7. package/public/dashboard.css +391 -66
  8. package/public/dashboard.js +593 -42
  9. package/scheduler.cjs +9 -14
  10. package/src/auth/use-database-auth-state.js +2 -4
  11. package/src/bootstrap/core.js +2 -5
  12. package/src/bootstrap/panel.js +10 -5
  13. package/src/cli.js +3 -5
  14. package/src/commands/autoschedule.cjs +1 -4
  15. package/src/commands/blacklist.cjs +22 -20
  16. package/src/commands/block.cjs +2 -4
  17. package/src/commands/calc.cjs +9 -16
  18. package/src/commands/digit.cjs +97 -0
  19. package/src/commands/gemini.cjs +147 -161
  20. package/src/commands/group/add.cjs +7 -16
  21. package/src/commands/group/antiSpam.cjs +5 -11
  22. package/src/commands/group/antilink.cjs +34 -28
  23. package/src/commands/group/approveall.cjs +3 -11
  24. package/src/commands/group/clearwarns.cjs +2 -5
  25. package/src/commands/group/demote.cjs +3 -8
  26. package/src/commands/group/kick.cjs +12 -15
  27. package/src/commands/group/media.cjs +17 -24
  28. package/src/commands/group/members.cjs +1 -2
  29. package/src/commands/group/promote.cjs +4 -12
  30. package/src/commands/group/removeall.cjs +2 -5
  31. package/src/commands/group/setpp.cjs +1 -1
  32. package/src/commands/group/setrules.cjs +2 -8
  33. package/src/commands/group/setwarn.cjs +4 -2
  34. package/src/commands/group/tagadmins.cjs +2 -5
  35. package/src/commands/group/warn.cjs +26 -24
  36. package/src/commands/group/warns.cjs +1 -2
  37. package/src/commands/group/welcome.cjs +13 -17
  38. package/src/commands/group.cjs +7 -10
  39. package/src/commands/help.cjs +6 -12
  40. package/src/commands/lang.cjs +104 -0
  41. package/src/commands/listschedules.cjs +2 -6
  42. package/src/commands/loop.cjs +6 -13
  43. package/src/commands/memory.cjs +19 -32
  44. package/src/commands/mod.cjs +97 -35
  45. package/src/commands/perm.cjs +18 -56
  46. package/src/commands/ping.cjs +1 -5
  47. package/src/commands/poll.cjs +1 -1
  48. package/src/commands/rand.cjs +2 -5
  49. package/src/commands/schedule.cjs +4 -8
  50. package/src/commands/score.cjs +8 -2
  51. package/src/commands/setprefix.cjs +2 -2
  52. package/src/commands/shortlink.cjs +7 -15
  53. package/src/commands/shutdown.cjs +1 -1
  54. package/src/commands/sticker.cjs +222 -0
  55. package/src/commands/stt.cjs +136 -68
  56. package/src/commands/todo.cjs +9 -15
  57. package/src/commands/tts.cjs +10 -16
  58. package/src/commands/unblock.cjs +2 -6
  59. package/src/commands/weather.cjs +5 -12
  60. package/src/config/ai-persona.md +5 -3
  61. package/src/config/available-models.json +1156 -0
  62. package/src/config/baileys.config.js +2 -1
  63. package/src/config/brand.esm.js +1 -10
  64. package/src/config/constants.js +1 -1
  65. package/src/config/defaults.cjs +66 -66
  66. package/src/config/lock.cjs +2 -2
  67. package/src/config/modelCapabilities.cjs +172 -0
  68. package/src/config/runtime-config.cjs +8 -13
  69. package/src/config/secrets.cjs +18 -10
  70. package/src/config/settings.cjs +94 -10
  71. package/src/config/vault.cjs +107 -0
  72. package/src/core/connection.js +13 -12
  73. package/src/core/events.js +41 -23
  74. package/src/core/proxy.js +7 -7
  75. package/src/core/session.js +91 -48
  76. package/src/core/socket.js +12 -15
  77. package/src/db/db.cjs +14 -6
  78. package/src/db/store.cjs +221 -87
  79. package/src/domain/caddy.js +1 -1
  80. package/src/domain/command.js +40 -19
  81. package/src/domain/detect.js +1 -1
  82. package/src/domain/system.js +2 -5
  83. package/src/handlers/command.handler.js +22 -43
  84. package/src/handlers/group.handler.js +13 -13
  85. package/src/handlers/message.handler.js +11 -21
  86. package/src/index.js +8 -4
  87. package/src/middleware/antispam.middleware.js +46 -23
  88. package/src/middleware/blacklist.middleware.js +14 -9
  89. package/src/middleware/forward-tracking.middleware.js +3 -2
  90. package/src/middleware/permissions.middleware.js +7 -10
  91. package/src/panel/login-throttle.cjs +43 -0
  92. package/src/panel/session-auth.cjs +17 -0
  93. package/src/routes/dashboard.api.esm.js +394 -72
  94. package/src/services/aiAgent.cjs +136 -49
  95. package/src/services/aiProviders.cjs +193 -47
  96. package/src/services/aiRouter.cjs +137 -0
  97. package/src/services/aiTools.cjs +305 -125
  98. package/src/services/llmDiscovery.cjs +481 -0
  99. package/src/services/modelRegistry.cjs +412 -0
  100. package/src/utils/datetime.cjs +2 -2
  101. package/src/utils/lid-helper.esm.js +7 -7
  102. package/src/utils/logger.cjs +1 -1
  103. package/src/utils/markdownParser.cjs +159 -0
  104. package/src/utils/memory.cjs +43 -13
  105. package/src/utils/messageContent.cjs +40 -0
  106. package/src/utils/normalizeJid.esm.js +7 -7
  107. package/src/utils/numerals.cjs +75 -0
  108. package/src/utils/permissions.esm.js +10 -15
  109. package/src/utils/providerUrl.cjs +281 -0
  110. package/src/utils/recurrence.cjs +7 -11
  111. package/src/utils/sendBotMessage.esm.js +4 -14
  112. package/src/utils/statusMessage.cjs +5 -13
  113. package/src/utils/storage.cjs +10 -0
  114. package/src/utils/storage.esm.js +11 -0
  115. package/src/utils/textDecode.cjs +4 -9
  116. package/src/utils/thumbnail.cjs +8 -14
  117. package/src/utils/typing.esm.js +12 -12
  118. package/views/dashboard.ejs +134 -106
@@ -47,38 +47,32 @@ async function handleAntiSpam(sock, msg) {
47
47
 
48
48
  // 1. فلترة الرسائل القديمة
49
49
  userData.messages = userData.messages.filter(
50
- (m) => now - m.timestamp < SPAM_TIME_WINDOW_SECONDS
50
+ (m) => now - m.timestamp < SPAM_TIME_WINDOW_SECONDS,
51
51
  );
52
52
 
53
53
  // 2. إضافة الرسالة الجديدة
54
54
  userData.messages.push({ content: messageContent, timestamp: now });
55
55
 
56
56
  // 3. التحقق من وجود تكرار
57
- const repeatedMessages = userData.messages.filter(
58
- (m) => m.content === messageContent
59
- );
57
+ const repeatedMessages = userData.messages.filter((m) => m.content === messageContent);
60
58
 
61
59
  if (repeatedMessages.length >= SPAM_MESSAGE_COUNT) {
62
60
  userData.warnCount += 1;
63
61
  userData.messages = []; // مسح الرسائل بعد اكتشاف السبام لمنع التحذيرات المتتالية
64
62
 
65
63
  if (userData.warnCount >= SPAM_WARN_LIMIT) {
66
- logger.info(
67
- `[AntiSpam] Kicking ${senderId} from ${groupId} for repeated spam.`
68
- );
64
+ logger.info(`[AntiSpam] Kicking ${senderId} from ${groupId} for repeated spam.`);
69
65
  // إعادة تعيين العداد بعد الطرد
70
66
  antiSpamCache.delete(cacheKey);
71
67
 
72
68
  await sock.sendMessage(groupId, {
73
- text: `🚫 تم طرد المستخدم @${
74
- senderId.split("@")[0]
75
- } تلقائيًا بسبب الإزعاج.`,
69
+ text: `🚫 تم طرد المستخدم @${senderId.split("@")[0]} تلقائيًا بسبب الإزعاج.`,
76
70
  mentions: [senderId],
77
71
  });
78
72
  await sock.groupParticipantsUpdate(groupId, [senderId], "remove");
79
73
  } else {
80
74
  logger.info(
81
- `[AntiSpam] Warning ${senderId} in ${groupId}. Warn count: ${userData.warnCount}`
75
+ `[AntiSpam] Warning ${senderId} in ${groupId}. Warn count: ${userData.warnCount}`,
82
76
  );
83
77
  await sock.sendMessage(groupId, {
84
78
  text: `⚠️ تحذير للمستخدم @${
@@ -1,16 +1,15 @@
1
1
  // file: /commands/group/antilink.js
2
2
  const { getGroupSettings, saveGroupSettings } = require("../../utils/storage.cjs");
3
3
  const logger = require("../../utils/logger.cjs");
4
- const {
5
- isOwnerJidSync,
6
- isAdminInGroupSync,
7
- } = require("../../utils/permissions.cjs");
4
+ const { isOwnerJidSync, isAdminInGroupSync } = require("../../utils/permissions.cjs");
5
+ const { visibleText } = require("../../utils/messageContent.cjs");
8
6
 
9
7
  // --- The Main Command Logic ---
10
8
  const command = {
11
9
  name: "antilink",
12
10
  description: "Advanced control for the anti-link feature.",
13
- usage: "antilink [status]\nantilink <on|off>\nantilink mode <ALL|WHITELIST|BLACKLIST>\nantilink <allow|disallow> <دومين>",
11
+ usage:
12
+ "antilink [status]\nantilink <on|off>\nantilink mode <ALL|WHITELIST|BLACKLIST>\nantilink <allow|disallow> <دومين>",
14
13
  chat: "group",
15
14
  userAdminRequired: true,
16
15
 
@@ -44,7 +43,7 @@ const command = {
44
43
  text: "☑️ تم تعطيل نظام منع الروابط.",
45
44
  });
46
45
  break;
47
- case "mode":
46
+ case "mode": {
48
47
  const mode = args[1] ? args[1].toUpperCase() : "";
49
48
  if (!["ALL", "WHITELIST", "BLACKLIST"].includes(mode)) {
50
49
  return await sock.sendMessage(groupId, {
@@ -56,7 +55,8 @@ const command = {
56
55
  text: `✅ تم تغيير وضع منع الروابط إلى: ${mode}`,
57
56
  });
58
57
  break;
59
- case "allow":
58
+ }
59
+ case "allow": {
60
60
  const domainToAllow = args[1] ? args[1].toLowerCase() : "";
61
61
  if (!domainToAllow)
62
62
  return await sock.sendMessage(groupId, {
@@ -69,30 +69,31 @@ const command = {
69
69
  text: `✅ تم إضافة '${domainToAllow}' إلى قائمة الدومينات المسموح بها.`,
70
70
  });
71
71
  break;
72
- case "disallow":
72
+ }
73
+ case "disallow": {
73
74
  const domainToDisallow = args[1] ? args[1].toLowerCase() : "";
74
75
  if (!domainToDisallow)
75
76
  return await sock.sendMessage(groupId, {
76
77
  text: "يرجى تحديد دومين لإزالته.",
77
78
  });
78
79
  antilinkConfig.allowed_domains = antilinkConfig.allowed_domains.filter(
79
- (d) => d !== domainToDisallow
80
+ (d) => d !== domainToDisallow,
80
81
  );
81
82
  await sock.sendMessage(groupId, {
82
83
  text: `☑️ تم إزالة '${domainToDisallow}' من قائمة الدومينات المسموح بها.`,
83
84
  });
84
85
  break;
85
- default:
86
+ }
87
+ default: {
86
88
  // Display current status
87
89
  let statusReply = `*حالة نظام منع الروابط:*\n\n`;
88
- statusReply += `الحالة: ${
89
- antilinkConfig.enabled ? "مفعل ✅" : "معطل ☑️"
90
- }\n`;
90
+ statusReply += `الحالة: ${antilinkConfig.enabled ? "مفعل ✅" : "معطل ☑️"}\n`;
91
91
  statusReply += `الوضع: ${antilinkConfig.mode}\n`;
92
92
  statusReply += `الدومينات المسموح بها: ${
93
93
  antilinkConfig.allowed_domains.join(", ") || "لا يوجد"
94
94
  }\n`;
95
95
  await sock.sendMessage(groupId, { text: statusReply });
96
+ }
96
97
  }
97
98
 
98
99
  saveGroupSettings(groupId, settings);
@@ -101,37 +102,42 @@ const command = {
101
102
 
102
103
  // --- The Message Handler Logic ---
103
104
  const linkRegex = new RegExp(
104
- /(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/[a-zA-Z0-9]+\.[^\s]{2,}|[a-zA-Z0-9]+\.[^\s]{2,})/i
105
+ /(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/[a-zA-Z0-9]+\.[^\s]{2,}|[a-zA-Z0-9]+\.[^\s]{2,})/i,
105
106
  );
106
107
 
107
108
  async function handleAntiLink(sock, msg, _legacyConfig, _normalizeJid) {
108
109
  const isGroup = msg.key.remoteJid.endsWith("@g.us");
109
- if (!isGroup) return;
110
+ if (!isGroup) return false;
110
111
 
111
112
  const groupId = msg.key.remoteJid;
112
113
  const settings = getGroupSettings(groupId);
113
114
  const antilinkConfig = settings.antilink;
114
115
 
115
- if (!antilinkConfig || !antilinkConfig.enabled) return;
116
+ if (!antilinkConfig || !antilinkConfig.enabled) return false;
116
117
 
117
118
  const senderId = msg.key.participant || msg.key.remoteJid;
118
119
 
119
120
  const groupMetadata = await sock.groupMetadata(groupId);
120
121
  // Centralized checks — handle LID/PN cross-format and bootstrap roster.
121
- const isOwner = isOwnerJidSync(senderId);
122
- const isSenderAdmin = isAdminInGroupSync(groupMetadata, senderId);
122
+ const senderIds = [senderId, msg.key.participantAlt, msg.key.participantPn].filter(Boolean);
123
+ const isOwner = msg.key.fromMe || senderIds.some(isOwnerJidSync);
124
+ const isSenderAdmin = senderIds.some((id) => isAdminInGroupSync(groupMetadata, id));
123
125
 
124
- if (isOwner || isSenderAdmin) return;
126
+ if (isOwner || isSenderAdmin) return false;
125
127
 
126
- const body =
127
- msg.message.conversation || msg.message.extendedTextMessage?.text || "";
128
- if (!linkRegex.test(body)) return; // No link found
128
+ const body = visibleText(msg.message);
129
+ if (!linkRegex.test(body)) return false; // No link found
129
130
 
130
131
  // --- Link Found, Apply Rules ---
131
132
  const foundLinks = body.match(linkRegex);
132
- const domain = new URL(
133
- foundLinks[0].startsWith("http") ? foundLinks[0] : `http://${foundLinks[0]}`
134
- ).hostname.replace("www.", "");
133
+ let domain;
134
+ try {
135
+ domain = new URL(
136
+ foundLinks[0].startsWith("http") ? foundLinks[0] : `http://${foundLinks[0]}`,
137
+ ).hostname.replace("www.", "");
138
+ } catch {
139
+ return false;
140
+ }
135
141
 
136
142
  let shouldDelete = false;
137
143
 
@@ -152,15 +158,15 @@ async function handleAntiLink(sock, msg, _legacyConfig, _normalizeJid) {
152
158
  }
153
159
 
154
160
  if (shouldDelete) {
155
- logger.info(
156
- `[Anti-Link] Deleting link from ${senderId} in ${groupId}. Domain: ${domain}`
157
- );
161
+ logger.info(`[Anti-Link] Deleting link from ${senderId} in ${groupId}. Domain: ${domain}`);
158
162
  await sock.sendMessage(groupId, { delete: msg.key });
159
163
  await sock.sendMessage(groupId, {
160
164
  text: `ممنوع إرسال الروابط هنا يا @${senderId.split("@")[0]}!`,
161
165
  mentions: [senderId],
162
166
  });
167
+ return true;
163
168
  }
169
+ return false;
164
170
  }
165
171
 
166
172
  // Export both the command and the handler
@@ -1,8 +1,5 @@
1
1
  // file: /commands/group/approveall.js
2
- const {
3
- getGroupSettings,
4
- saveGroupSettings,
5
- } = require("../../utils/storage.cjs"); // Note the path is ../../
2
+ const { getGroupSettings, saveGroupSettings } = require("../../utils/storage.cjs"); // Note the path is ../../
6
3
  const logger = require("../../utils/logger.cjs");
7
4
 
8
5
  module.exports = {
@@ -36,18 +33,13 @@ module.exports = {
36
33
  text: "☑️ تم تعطيل نظام الموافقة التلقائية.",
37
34
  });
38
35
  } else {
39
- const status = settings.join_requests.auto_approve_enabled
40
- ? "مفعل ✅"
41
- : "معطل ☑️";
36
+ const status = settings.join_requests.auto_approve_enabled ? "مفعل ✅" : "معطل ☑️";
42
37
  return await sock.sendMessage(groupId, {
43
38
  text: `حالة الموافقة التلقائية: ${status}.\nاستخدم 'on' أو 'off' للتغيير.`,
44
39
  });
45
40
  }
46
41
  } catch (error) {
47
- logger.error(
48
- { err: error, command: "approveall" },
49
- "Error in !group approveall command"
50
- );
42
+ logger.error({ err: error, command: "approveall" }, "Error in !group approveall command");
51
43
  await sock.sendMessage(groupId, { text: "حدث خطأ." });
52
44
  }
53
45
  },
@@ -12,8 +12,7 @@ module.exports = {
12
12
  async execute(sock, msg) {
13
13
  try {
14
14
  const groupId = msg.key.remoteJid;
15
- const mentionedJid =
16
- msg.message?.extendedTextMessage?.contextInfo?.mentionedJid?.[0];
15
+ const mentionedJid = msg.message?.extendedTextMessage?.contextInfo?.mentionedJid?.[0];
17
16
 
18
17
  if (!mentionedJid) {
19
18
  return await sock.sendMessage(groupId, {
@@ -24,9 +23,7 @@ module.exports = {
24
23
  // Call the new function from storage.js to delete the record from the database
25
24
  clearUserWarnings(groupId, mentionedJid);
26
25
 
27
- const replyText = `✅ تم مسح جميع تحذيرات العضو @${
28
- mentionedJid.split("@")[0]
29
- } بنجاح.`;
26
+ const replyText = `✅ تم مسح جميع تحذيرات العضو @${mentionedJid.split("@")[0]} بنجاح.`;
30
27
  await sock.sendMessage(groupId, {
31
28
  text: replyText,
32
29
  mentions: [mentionedJid],
@@ -24,9 +24,7 @@ module.exports = {
24
24
  }
25
25
 
26
26
  // Safety check: Is the target user NOT an admin?
27
- const targetUser = groupMetadata.participants.find(
28
- (p) => p.id === targetJid
29
- );
27
+ const targetUser = groupMetadata.participants.find((p) => p.id === targetJid);
30
28
  if (!targetUser || !targetUser.admin) {
31
29
  return await sock.sendMessage(groupId, {
32
30
  text: `⚠️ العضو @${targetJid.split("@")[0]} ليس مشرفًا أصلاً.`,
@@ -45,17 +43,14 @@ module.exports = {
45
43
  await sock.groupParticipantsUpdate(
46
44
  groupId,
47
45
  [targetJid],
48
- "demote" // The action is 'demote'
46
+ "demote", // The action is 'demote'
49
47
  );
50
48
  await sock.sendMessage(groupId, {
51
49
  text: `👤 تم عزل @${targetJid.split("@")[0]} من الإشراف.`,
52
50
  mentions: [targetJid],
53
51
  });
54
52
  } catch (error) {
55
- logger.error(
56
- { err: error, command: "demote" },
57
- "Error in !demote command"
58
- );
53
+ logger.error({ err: error, command: "demote" }, "Error in !demote command");
59
54
  await sock.sendMessage(groupId, { text: "حدث خطأ أثناء محاولة العزل." });
60
55
  }
61
56
  },
@@ -1,6 +1,10 @@
1
1
  // file: /commands/kick.js
2
2
  const logger = require("../../utils/logger.cjs");
3
- const { isOwnerJidSync } = require("../../utils/permissions.cjs");
3
+ const {
4
+ hasBotPrivilegesSync,
5
+ isAdminInGroupSync,
6
+ sameUserSync,
7
+ } = require("../../utils/permissions.cjs");
4
8
 
5
9
  module.exports = {
6
10
  name: "kick",
@@ -16,9 +20,7 @@ module.exports = {
16
20
 
17
21
  // --- Target Identification Logic ---
18
22
  // 1. Check for mentions
19
- if (
20
- msg.message?.extendedTextMessage?.contextInfo?.mentionedJid?.length > 0
21
- ) {
23
+ if (msg.message?.extendedTextMessage?.contextInfo?.mentionedJid?.length > 0) {
22
24
  targetJid = msg.message.extendedTextMessage.contextInfo.mentionedJid[0];
23
25
  }
24
26
  // 2. Check if it's a reply to another message
@@ -32,25 +34,20 @@ module.exports = {
32
34
 
33
35
  // --- Safety Checks ---
34
36
  // Can't kick the bot itself
35
- if (targetJid === sock.user.id) {
37
+ if ([sock.user?.id, sock.user?.lid].filter(Boolean).some((id) => sameUserSync(id, targetJid))) {
36
38
  return await sock.sendMessage(groupId, { text: "لا يمكنني طرد نفسي." });
37
39
  }
38
40
 
39
- // Can't kick one of the owners
40
- if (isOwnerJidSync(targetJid)) {
41
+ // Bot owners/admins are privileged across chats and cannot be removed by
42
+ // a group-only admin through an alternate LID/PN representation.
43
+ if (hasBotPrivilegesSync(targetJid)) {
41
44
  return await sock.sendMessage(groupId, {
42
- text: "لا يمكن طرد مالك البوت.",
45
+ text: "لا يمكن طرد مالك أو مشرف البوت.",
43
46
  });
44
47
  }
45
48
 
46
49
  // Check if the target is also an admin
47
- const targetUser = groupMetadata.participants.find(
48
- (p) => p.id === targetJid
49
- );
50
- if (
51
- targetUser &&
52
- (targetUser.admin === "admin" || targetUser.admin === "superadmin")
53
- ) {
50
+ if (isAdminInGroupSync(groupMetadata, targetJid)) {
54
51
  return await sock.sendMessage(groupId, {
55
52
  text: "لا يمكن للمشرف طرد مشرف آخر.",
56
53
  });
@@ -1,10 +1,8 @@
1
1
  // file: /commands/group/media.js
2
2
  const { getGroupSettings, saveGroupSettings } = require("../../utils/storage.cjs");
3
3
  const logger = require("../../utils/logger.cjs");
4
- const {
5
- isOwnerJidSync,
6
- isAdminInGroupSync,
7
- } = require("../../utils/permissions.cjs");
4
+ const { isOwnerJidSync, isAdminInGroupSync } = require("../../utils/permissions.cjs");
5
+ const { mediaType } = require("../../utils/messageContent.cjs");
8
6
 
9
7
  const VALID_TYPES = ["image", "video", "sticker", "audio"];
10
8
 
@@ -39,7 +37,7 @@ const command = {
39
37
  text: "☑️ تم تعطيل نظام مراقبة الوسائط.",
40
38
  });
41
39
  break;
42
- case "block":
40
+ case "block": {
43
41
  const typeToBlock = args[1] ? args[1].toLowerCase() : "";
44
42
  if (!VALID_TYPES.includes(typeToBlock))
45
43
  return await sock.sendMessage(groupId, {
@@ -52,28 +50,26 @@ const command = {
52
50
  text: `✅ تم إضافة '${typeToBlock}' للأنواع الممنوعة.`,
53
51
  });
54
52
  break;
55
- case "unblock":
53
+ }
54
+ case "unblock": {
56
55
  const typeToUnblock = args[1] ? args[1].toLowerCase() : "";
57
56
  if (!typeToUnblock)
58
57
  return await sock.sendMessage(groupId, {
59
58
  text: "يرجى تحديد نوع لإلغاء حظره.",
60
59
  });
61
- mediaConfig.blocked_types = mediaConfig.blocked_types.filter(
62
- (t) => t !== typeToUnblock
63
- );
60
+ mediaConfig.blocked_types = mediaConfig.blocked_types.filter((t) => t !== typeToUnblock);
64
61
  await sock.sendMessage(groupId, {
65
62
  text: `☑️ تم إزالة '${typeToUnblock}' من الأنواع الممنوعة.`,
66
63
  });
67
64
  break;
68
- default: // 'status'
65
+ }
66
+ default: {
67
+ // 'status'
69
68
  let statusReply = `*حالة نظام مراقبة الوسائط:*\n\n`;
70
- statusReply += `الحالة: ${
71
- mediaConfig.enabled ? "مفعل ✅" : "معطل ☑️"
72
- }\n`;
73
- statusReply += `الأنواع الممنوعة: ${
74
- mediaConfig.blocked_types.join(", ") || "لا يوجد"
75
- }`;
69
+ statusReply += `الحالة: ${mediaConfig.enabled ? "مفعل ✅" : "معطل ☑️"}\n`;
70
+ statusReply += `الأنواع الممنوعة: ${mediaConfig.blocked_types.join(", ") || "لا يوجد"}`;
76
71
  await sock.sendMessage(groupId, { text: statusReply });
72
+ }
77
73
  }
78
74
  saveGroupSettings(groupId, settings);
79
75
  },
@@ -93,19 +89,16 @@ async function handleMediaControl(sock, msg, _legacyConfig, _normalizeJid) {
93
89
  const senderId = msg.key.participant || msg.key.remoteJid;
94
90
 
95
91
  const groupMetadata = await sock.groupMetadata(groupId);
96
- const isOwner = isOwnerJidSync(senderId);
97
- const isSenderAdmin = isAdminInGroupSync(groupMetadata, senderId);
92
+ const senderIds = [senderId, msg.key.participantAlt, msg.key.participantPn].filter(Boolean);
93
+ const isOwner = msg.key.fromMe || senderIds.some(isOwnerJidSync);
94
+ const isSenderAdmin = senderIds.some((id) => isAdminInGroupSync(groupMetadata, id));
98
95
 
99
96
  if (isOwner || isSenderAdmin) return false;
100
97
 
101
- const messageType = Object.keys(msg.message)[0]
102
- .replace("Message", "")
103
- .toLowerCase();
98
+ const messageType = mediaType(msg.message);
104
99
 
105
100
  if (mediaConfig.blocked_types.includes(messageType)) {
106
- logger.info(
107
- `[Media Control] Deleting ${messageType} from ${senderId} in ${groupId}.`
108
- );
101
+ logger.info(`[Media Control] Deleting ${messageType} from ${senderId} in ${groupId}.`);
109
102
  await sock.sendMessage(groupId, { delete: msg.key });
110
103
  await sock.sendMessage(groupId, {
111
104
  text: `يا @${senderId.split("@")[0]}، إرسال *${messageType}* ممنوع هنا.`,
@@ -33,8 +33,7 @@ module.exports = {
33
33
  const phone = member.id.split("@")[0];
34
34
 
35
35
  // Check if the member is an admin or superadmin
36
- const isAdmin =
37
- member.admin === "admin" || member.admin === "superadmin";
36
+ const isAdmin = member.admin === "admin" || member.admin === "superadmin";
38
37
 
39
38
  // Add the formatted line to our reply text
40
39
  // Using @ will create a "mention link" in the WhatsApp message
@@ -24,13 +24,8 @@ module.exports = {
24
24
  }
25
25
 
26
26
  // Safety check: Is the target user already an admin?
27
- const targetUser = groupMetadata.participants.find(
28
- (p) => p.id === targetJid
29
- );
30
- if (
31
- targetUser &&
32
- (targetUser.admin === "admin" || targetUser.admin === "superadmin")
33
- ) {
27
+ const targetUser = groupMetadata.participants.find((p) => p.id === targetJid);
28
+ if (targetUser && (targetUser.admin === "admin" || targetUser.admin === "superadmin")) {
34
29
  return await sock.sendMessage(groupId, {
35
30
  text: `⚠️ العضو @${targetJid.split("@")[0]} مشرف بالفعل.`,
36
31
  mentions: [targetJid],
@@ -41,17 +36,14 @@ module.exports = {
41
36
  await sock.groupParticipantsUpdate(
42
37
  groupId,
43
38
  [targetJid],
44
- "promote" // The action is 'promote'
39
+ "promote", // The action is 'promote'
45
40
  );
46
41
  await sock.sendMessage(groupId, {
47
42
  text: `👑 تم ترقية @${targetJid.split("@")[0]} إلى مشرف بنجاح.`,
48
43
  mentions: [targetJid],
49
44
  });
50
45
  } catch (error) {
51
- logger.error(
52
- { err: error, command: "promote" },
53
- "Error in !promote command"
54
- );
46
+ logger.error({ err: error, command: "promote" }, "Error in !promote command");
55
47
  await sock.sendMessage(groupId, {
56
48
  text: "حدث خطأ أثناء محاولة الترقية.",
57
49
  });
@@ -3,8 +3,7 @@ const logger = require("../../utils/logger.cjs");
3
3
 
4
4
  module.exports = {
5
5
  name: "removeall",
6
- description:
7
- "Removes all non-admin members from the group after confirmation.",
6
+ description: "Removes all non-admin members from the group after confirmation.",
8
7
  usage: "removeall",
9
8
  chat: "group",
10
9
  userAdminRequired: true,
@@ -33,9 +32,7 @@ module.exports = {
33
32
  setTimeout(() => {
34
33
  if (confirmationSessions.has(senderId)) {
35
34
  confirmationSessions.delete(senderId);
36
- logger.info(
37
- `[Confirmation] Timed out for ${senderId} on command removeall.`
38
- );
35
+ logger.info(`[Confirmation] Timed out for ${senderId} on command removeall.`);
39
36
  }
40
37
  }, 30000); // 30 seconds
41
38
  },
@@ -32,7 +32,7 @@ module.exports = {
32
32
  id: msg.message.extendedTextMessage.contextInfo.participant,
33
33
  },
34
34
  "buffer",
35
- {}
35
+ {},
36
36
  );
37
37
 
38
38
  // Update the group's profile picture using the downloaded image buffer
@@ -1,7 +1,4 @@
1
- const {
2
- getGroupSettings,
3
- saveGroupSettings,
4
- } = require("../../utils/storage.cjs");
1
+ const { getGroupSettings, saveGroupSettings } = require("../../utils/storage.cjs");
5
2
  const logger = require("../../utils/logger.cjs");
6
3
 
7
4
  module.exports = {
@@ -36,10 +33,7 @@ module.exports = {
36
33
  });
37
34
  } catch (error) {
38
35
  // Improved logging for better debugging
39
- logger.error(
40
- { err: error, groupId: groupId },
41
- "Error in !setrules command"
42
- );
36
+ logger.error({ err: error, groupId: groupId }, "Error in !setrules command");
43
37
  await sock.sendMessage(groupId, { text: "حدث خطأ أثناء حفظ القواعد." });
44
38
  }
45
39
  },
@@ -21,7 +21,7 @@ module.exports = {
21
21
  const warnConfig = settings.warn_system;
22
22
 
23
23
  switch (subCommand) {
24
- case "max":
24
+ case "max": {
25
25
  const max = parseInt(value, 10);
26
26
  if (isNaN(max) || max < 1) {
27
27
  return await sock.sendMessage(groupId, {
@@ -33,8 +33,9 @@ module.exports = {
33
33
  text: `✅ تم تعيين الحد الأقصى للتحذيرات إلى ${max}.`,
34
34
  });
35
35
  break;
36
+ }
36
37
 
37
- case "action":
38
+ case "action": {
38
39
  const action = value?.toUpperCase();
39
40
  if (action !== "KICK" && action !== "NONE") {
40
41
  return await sock.sendMessage(groupId, {
@@ -46,6 +47,7 @@ module.exports = {
46
47
  text: `✅ تم تعيين الإجراء التلقائي إلى ${action}.`,
47
48
  });
48
49
  break;
50
+ }
49
51
 
50
52
  default:
51
53
  return await sock.sendMessage(groupId, {
@@ -13,7 +13,7 @@ module.exports = {
13
13
  try {
14
14
  // Filter the participants to get only admins and superadmins
15
15
  const admins = groupMetadata.participants.filter(
16
- (p) => p.admin === "admin" || p.admin === "superadmin"
16
+ (p) => p.admin === "admin" || p.admin === "superadmin",
17
17
  );
18
18
 
19
19
  if (admins.length === 0) {
@@ -40,10 +40,7 @@ module.exports = {
40
40
  mentions: mentions,
41
41
  });
42
42
  } catch (error) {
43
- logger.error(
44
- { err: error, command: "tagadmins" },
45
- "Error in !tagadmins command"
46
- );
43
+ logger.error({ err: error, command: "tagadmins" }, "Error in !tagadmins command");
47
44
  await sock.sendMessage(groupId, { text: "حدث خطأ." });
48
45
  }
49
46
  },