levix-bot 2.0.0

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 (148) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +105 -0
  3. package/SETUP.md +242 -0
  4. package/app.cjs +337 -0
  5. package/bin/levix.js +7 -0
  6. package/deploy/install.sh +182 -0
  7. package/deploy/levix.service +56 -0
  8. package/deploy/nginx/levix.leviro.net.conf +79 -0
  9. package/package.json +78 -0
  10. package/public/brand/apple-touch-icon.png +0 -0
  11. package/public/brand/banner.jpg +0 -0
  12. package/public/brand/banner.webp +0 -0
  13. package/public/brand/icon-16.png +0 -0
  14. package/public/brand/icon-192.png +0 -0
  15. package/public/brand/icon-32.png +0 -0
  16. package/public/brand/icon-512.png +0 -0
  17. package/public/brand/icon-512.webp +0 -0
  18. package/public/brand/mark.png +0 -0
  19. package/public/brand/mark.svg +16 -0
  20. package/public/brand/mark.webp +0 -0
  21. package/public/brand/wordmark.png +0 -0
  22. package/public/brand/wordmark.webp +0 -0
  23. package/public/dashboard.css +881 -0
  24. package/public/dashboard.js +1057 -0
  25. package/public/logo.svg +16 -0
  26. package/public/qrcode.min.js +1 -0
  27. package/public/socket.io.min.js +7 -0
  28. package/scheduler.cjs +197 -0
  29. package/src/auth/auth-storage.cjs +74 -0
  30. package/src/auth/use-database-auth-state.js +130 -0
  31. package/src/bootstrap/core.js +74 -0
  32. package/src/bootstrap/events.cjs +43 -0
  33. package/src/bootstrap/panel.js +97 -0
  34. package/src/cli.js +153 -0
  35. package/src/commands/autoschedule.cjs +84 -0
  36. package/src/commands/blacklist.cjs +116 -0
  37. package/src/commands/block.cjs +70 -0
  38. package/src/commands/calc.cjs +387 -0
  39. package/src/commands/debt.cjs +175 -0
  40. package/src/commands/deleteschedule.cjs +36 -0
  41. package/src/commands/gemini.cjs +727 -0
  42. package/src/commands/group/add.cjs +136 -0
  43. package/src/commands/group/all.cjs +43 -0
  44. package/src/commands/group/antiSpam.cjs +96 -0
  45. package/src/commands/group/antilink.cjs +167 -0
  46. package/src/commands/group/approveall.cjs +54 -0
  47. package/src/commands/group/clearwarns.cjs +41 -0
  48. package/src/commands/group/deletenote.cjs +33 -0
  49. package/src/commands/group/demote.cjs +62 -0
  50. package/src/commands/group/kick.cjs +85 -0
  51. package/src/commands/group/media.cjs +120 -0
  52. package/src/commands/group/members.cjs +59 -0
  53. package/src/commands/group/note.cjs +32 -0
  54. package/src/commands/group/promote.cjs +60 -0
  55. package/src/commands/group/removeall.cjs +42 -0
  56. package/src/commands/group/rules.cjs +31 -0
  57. package/src/commands/group/save.cjs +36 -0
  58. package/src/commands/group/setname.cjs +41 -0
  59. package/src/commands/group/setpp.cjs +51 -0
  60. package/src/commands/group/setrules.cjs +46 -0
  61. package/src/commands/group/setwarn.cjs +58 -0
  62. package/src/commands/group/tagadmins.cjs +50 -0
  63. package/src/commands/group/warn.cjs +105 -0
  64. package/src/commands/group/warns.cjs +66 -0
  65. package/src/commands/group/welcome.cjs +109 -0
  66. package/src/commands/group.cjs +160 -0
  67. package/src/commands/help.cjs +230 -0
  68. package/src/commands/listschedules.cjs +47 -0
  69. package/src/commands/loop.cjs +163 -0
  70. package/src/commands/memory.cjs +201 -0
  71. package/src/commands/mod.cjs +155 -0
  72. package/src/commands/notes.cjs +28 -0
  73. package/src/commands/perm.cjs +236 -0
  74. package/src/commands/ping.cjs +17 -0
  75. package/src/commands/poll.cjs +80 -0
  76. package/src/commands/prayer.cjs +64 -0
  77. package/src/commands/qr.cjs +39 -0
  78. package/src/commands/rand.cjs +130 -0
  79. package/src/commands/restart.cjs +25 -0
  80. package/src/commands/schedule.cjs +86 -0
  81. package/src/commands/score.cjs +130 -0
  82. package/src/commands/setprefix.cjs +40 -0
  83. package/src/commands/shortlink.cjs +74 -0
  84. package/src/commands/shutdown.cjs +45 -0
  85. package/src/commands/status.cjs +75 -0
  86. package/src/commands/stt.cjs +150 -0
  87. package/src/commands/todo.cjs +88 -0
  88. package/src/commands/tts.cjs +170 -0
  89. package/src/commands/unblock.cjs +57 -0
  90. package/src/commands/weather.cjs +79 -0
  91. package/src/config/ai-persona.md +17 -0
  92. package/src/config/baileys.config.js +51 -0
  93. package/src/config/brand.cjs +36 -0
  94. package/src/config/brand.esm.js +17 -0
  95. package/src/config/constants.js +16 -0
  96. package/src/config/defaults.cjs +91 -0
  97. package/src/config/lock.cjs +110 -0
  98. package/src/config/paths.cjs +90 -0
  99. package/src/config/runtime-config.cjs +219 -0
  100. package/src/config/secrets.cjs +130 -0
  101. package/src/config/settings.cjs +386 -0
  102. package/src/core/connection.js +199 -0
  103. package/src/core/events.js +61 -0
  104. package/src/core/socket.js +64 -0
  105. package/src/db/db.cjs +282 -0
  106. package/src/db/store.cjs +837 -0
  107. package/src/db/store.esm.js +17 -0
  108. package/src/domain/caddy.js +120 -0
  109. package/src/domain/command.js +380 -0
  110. package/src/domain/detect.js +173 -0
  111. package/src/domain/nginx.js +138 -0
  112. package/src/domain/system.js +148 -0
  113. package/src/handlers/command.handler.js +414 -0
  114. package/src/handlers/group.handler.js +73 -0
  115. package/src/handlers/message.handler.js +149 -0
  116. package/src/index.js +217 -0
  117. package/src/middleware/antispam.middleware.js +68 -0
  118. package/src/middleware/blacklist.middleware.js +29 -0
  119. package/src/middleware/forward-tracking.middleware.js +49 -0
  120. package/src/middleware/permissions.middleware.js +114 -0
  121. package/src/routes/dashboard.api.esm.js +779 -0
  122. package/src/services/aiAgent.cjs +317 -0
  123. package/src/services/aiTools.cjs +635 -0
  124. package/src/utils/datetime.cjs +61 -0
  125. package/src/utils/lid-helper.esm.js +143 -0
  126. package/src/utils/logger.cjs +72 -0
  127. package/src/utils/memory.cjs +385 -0
  128. package/src/utils/normalizeJid.cjs +65 -0
  129. package/src/utils/normalizeJid.esm.js +37 -0
  130. package/src/utils/openBrowser.cjs +129 -0
  131. package/src/utils/permissions.cjs +154 -0
  132. package/src/utils/permissions.esm.js +375 -0
  133. package/src/utils/recentMessageCache.esm.js +70 -0
  134. package/src/utils/requestOrigin.cjs +102 -0
  135. package/src/utils/sendBotMessage.cjs +43 -0
  136. package/src/utils/sendBotMessage.esm.js +139 -0
  137. package/src/utils/statusMessage.cjs +179 -0
  138. package/src/utils/storage-hub.cjs +49 -0
  139. package/src/utils/storage-hub.esm.js +24 -0
  140. package/src/utils/storage.cjs +80 -0
  141. package/src/utils/storage.esm.js +88 -0
  142. package/src/utils/textDecode.cjs +197 -0
  143. package/src/utils/thumbnail.cjs +591 -0
  144. package/src/utils/typing.esm.js +102 -0
  145. package/views/dashboard.ejs +329 -0
  146. package/views/login.ejs +105 -0
  147. package/views/qr.ejs +108 -0
  148. package/views/setup.ejs +122 -0
@@ -0,0 +1,85 @@
1
+ // file: /commands/kick.js
2
+ const logger = require("../../utils/logger.cjs");
3
+ const { isOwnerJidSync } = require("../../utils/permissions.cjs");
4
+
5
+ module.exports = {
6
+ name: "kick",
7
+ description: "Removes a member from the group.",
8
+ usage: "kick @عضو [السبب]",
9
+ chat: "group",
10
+ userAdminRequired: true,
11
+ botAdminRequired: true,
12
+
13
+ async execute(sock, msg, args, body, groupMetadata) {
14
+ const groupId = msg.key.remoteJid;
15
+ let targetJid;
16
+
17
+ // --- Target Identification Logic ---
18
+ // 1. Check for mentions
19
+ if (
20
+ msg.message?.extendedTextMessage?.contextInfo?.mentionedJid?.length > 0
21
+ ) {
22
+ targetJid = msg.message.extendedTextMessage.contextInfo.mentionedJid[0];
23
+ }
24
+ // 2. Check if it's a reply to another message
25
+ else if (msg.message?.extendedTextMessage?.contextInfo?.quotedMessage) {
26
+ targetJid = msg.message.extendedTextMessage.contextInfo.participant;
27
+ } else {
28
+ return await sock.sendMessage(groupId, {
29
+ text: "يجب عمل منشن للعضو أو الرد على رسالته لطرده.",
30
+ });
31
+ }
32
+
33
+ // --- Safety Checks ---
34
+ // Can't kick the bot itself
35
+ if (targetJid === sock.user.id) {
36
+ return await sock.sendMessage(groupId, { text: "لا يمكنني طرد نفسي." });
37
+ }
38
+
39
+ // Can't kick one of the owners
40
+ if (isOwnerJidSync(targetJid)) {
41
+ return await sock.sendMessage(groupId, {
42
+ text: "لا يمكن طرد مالك البوت.",
43
+ });
44
+ }
45
+
46
+ // 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
+ ) {
54
+ return await sock.sendMessage(groupId, {
55
+ text: "لا يمكن للمشرف طرد مشرف آخر.",
56
+ });
57
+ }
58
+
59
+ // --- Execution ---
60
+ try {
61
+ const reason = args.slice(1).join(" ") || "بدون سبب";
62
+ const senderName = msg.pushName;
63
+
64
+ const kickMessage =
65
+ `*تم بواسطة:* ${senderName}\n` +
66
+ `*الإجراء:* طرد\n` +
67
+ `*العضو:* @${targetJid.split("@")[0]}\n` +
68
+ `*السبب:* ${reason}`;
69
+
70
+ // Announce the kick before performing it
71
+ await sock.sendMessage(groupId, {
72
+ text: kickMessage,
73
+ mentions: [targetJid],
74
+ });
75
+
76
+ // Perform the kick
77
+ await sock.groupParticipantsUpdate(groupId, [targetJid], "remove");
78
+ } catch (error) {
79
+ logger.error({ err: error, command: "kick" }, "Error in !kick command");
80
+ await sock.sendMessage(groupId, {
81
+ text: "حدث خطأ أثناء محاولة طرد العضو. قد تكون صلاحياتي غير كافية.",
82
+ });
83
+ }
84
+ },
85
+ };
@@ -0,0 +1,120 @@
1
+ // file: /commands/group/media.js
2
+ const { getGroupSettings, saveGroupSettings } = require("../../utils/storage.cjs");
3
+ const logger = require("../../utils/logger.cjs");
4
+ const {
5
+ isOwnerJidSync,
6
+ isAdminInGroupSync,
7
+ } = require("../../utils/permissions.cjs");
8
+
9
+ const VALID_TYPES = ["image", "video", "sticker", "audio"];
10
+
11
+ // --- The Main Command Logic ---
12
+ const command = {
13
+ name: "media",
14
+ description: "Controls which message types are allowed in the group.",
15
+ usage: "media [status]\nmedia <on|off>\nmedia <block|unblock> <image|video|sticker|audio>",
16
+ chat: "group",
17
+ userAdminRequired: true,
18
+
19
+ async execute(sock, msg, args) {
20
+ const groupId = msg.key.remoteJid;
21
+ const subCommand = args[0] ? args[0].toLowerCase() : "status";
22
+ const settings = getGroupSettings(groupId);
23
+
24
+ if (!settings.media_control) {
25
+ settings.media_control = { enabled: false, blocked_types: [] };
26
+ }
27
+ const mediaConfig = settings.media_control;
28
+
29
+ switch (subCommand) {
30
+ case "on":
31
+ mediaConfig.enabled = true;
32
+ await sock.sendMessage(groupId, {
33
+ text: "✅ تم تفعيل نظام مراقبة الوسائط.",
34
+ });
35
+ break;
36
+ case "off":
37
+ mediaConfig.enabled = false;
38
+ await sock.sendMessage(groupId, {
39
+ text: "☑️ تم تعطيل نظام مراقبة الوسائط.",
40
+ });
41
+ break;
42
+ case "block":
43
+ const typeToBlock = args[1] ? args[1].toLowerCase() : "";
44
+ if (!VALID_TYPES.includes(typeToBlock))
45
+ return await sock.sendMessage(groupId, {
46
+ text: `نوع غير صالح. الأنواع المتاحة: ${VALID_TYPES.join(", ")}`,
47
+ });
48
+ if (!mediaConfig.blocked_types.includes(typeToBlock)) {
49
+ mediaConfig.blocked_types.push(typeToBlock);
50
+ }
51
+ await sock.sendMessage(groupId, {
52
+ text: `✅ تم إضافة '${typeToBlock}' للأنواع الممنوعة.`,
53
+ });
54
+ break;
55
+ case "unblock":
56
+ const typeToUnblock = args[1] ? args[1].toLowerCase() : "";
57
+ if (!typeToUnblock)
58
+ return await sock.sendMessage(groupId, {
59
+ text: "يرجى تحديد نوع لإلغاء حظره.",
60
+ });
61
+ mediaConfig.blocked_types = mediaConfig.blocked_types.filter(
62
+ (t) => t !== typeToUnblock
63
+ );
64
+ await sock.sendMessage(groupId, {
65
+ text: `☑️ تم إزالة '${typeToUnblock}' من الأنواع الممنوعة.`,
66
+ });
67
+ break;
68
+ default: // 'status'
69
+ let statusReply = `*حالة نظام مراقبة الوسائط:*\n\n`;
70
+ statusReply += `الحالة: ${
71
+ mediaConfig.enabled ? "مفعل ✅" : "معطل ☑️"
72
+ }\n`;
73
+ statusReply += `الأنواع الممنوعة: ${
74
+ mediaConfig.blocked_types.join(", ") || "لا يوجد"
75
+ }`;
76
+ await sock.sendMessage(groupId, { text: statusReply });
77
+ }
78
+ saveGroupSettings(groupId, settings);
79
+ },
80
+ };
81
+
82
+ // --- The Message Handler Logic ---
83
+ async function handleMediaControl(sock, msg, _legacyConfig, _normalizeJid) {
84
+ const isGroup = msg.key.remoteJid.endsWith("@g.us");
85
+ if (!isGroup) return false;
86
+
87
+ const groupId = msg.key.remoteJid;
88
+ const settings = getGroupSettings(groupId);
89
+ const mediaConfig = settings.media_control;
90
+
91
+ if (!mediaConfig || !mediaConfig.enabled) return false;
92
+
93
+ const senderId = msg.key.participant || msg.key.remoteJid;
94
+
95
+ const groupMetadata = await sock.groupMetadata(groupId);
96
+ const isOwner = isOwnerJidSync(senderId);
97
+ const isSenderAdmin = isAdminInGroupSync(groupMetadata, senderId);
98
+
99
+ if (isOwner || isSenderAdmin) return false;
100
+
101
+ const messageType = Object.keys(msg.message)[0]
102
+ .replace("Message", "")
103
+ .toLowerCase();
104
+
105
+ if (mediaConfig.blocked_types.includes(messageType)) {
106
+ logger.info(
107
+ `[Media Control] Deleting ${messageType} from ${senderId} in ${groupId}.`
108
+ );
109
+ await sock.sendMessage(groupId, { delete: msg.key });
110
+ await sock.sendMessage(groupId, {
111
+ text: `يا @${senderId.split("@")[0]}، إرسال *${messageType}* ممنوع هنا.`,
112
+ mentions: [senderId],
113
+ });
114
+ return true; // Action was taken
115
+ }
116
+ return false; // No action was taken
117
+ }
118
+
119
+ // Export both the command and the handler
120
+ module.exports = { ...command, handleMediaControl };
@@ -0,0 +1,59 @@
1
+ // file: /commands/members.js
2
+ const logger = require("../../utils/logger.cjs");
3
+
4
+ module.exports = {
5
+ name: "members",
6
+ description: "Lists all members in the group with their numbers.",
7
+ usage: "members",
8
+ chat: "group", // This command only works in groups
9
+
10
+ async execute(sock, msg) {
11
+ try {
12
+ // Get the group ID from the message
13
+ const groupId = msg.key.remoteJid;
14
+
15
+ // Send a temporary message to let the user know we're working on it
16
+ await sock.sendMessage(groupId, { text: "جاري جلب قائمة الأعضاء..." });
17
+
18
+ // Fetch the group's metadata
19
+ const metadata = await sock.groupMetadata(groupId);
20
+ const members = metadata.participants;
21
+
22
+ // --- Building the reply message ---
23
+
24
+ // Start with a header
25
+ let replyText = `*قائمة أعضاء الجروب:*\n*العدد الإجمالي: ${members.length}*\n\n`;
26
+
27
+ // Create an array of JIDs for the mentions property
28
+ const mentions = [];
29
+
30
+ // Loop through each member to create a formatted line
31
+ members.forEach((member, index) => {
32
+ // Get the phone number from the JID
33
+ const phone = member.id.split("@")[0];
34
+
35
+ // Check if the member is an admin or superadmin
36
+ const isAdmin =
37
+ member.admin === "admin" || member.admin === "superadmin";
38
+
39
+ // Add the formatted line to our reply text
40
+ // Using @ will create a "mention link" in the WhatsApp message
41
+ replyText += `${index + 1}. @${phone}${isAdmin ? " 👑 *مشرف*" : ""}\n`;
42
+
43
+ // Add the member's JID to the mentions array
44
+ mentions.push(member.id);
45
+ });
46
+
47
+ // Send the final message with the list and mentions
48
+ await sock.sendMessage(groupId, {
49
+ text: replyText,
50
+ mentions: mentions, // This makes the @ tags clickable
51
+ });
52
+ } catch (error) {
53
+ logger.error(error, "[Error] in !members command:");
54
+ await sock.sendMessage(msg.key.remoteJid, {
55
+ text: "حدث خطأ أثناء جلب قائمة الأعضاء.",
56
+ });
57
+ }
58
+ },
59
+ };
@@ -0,0 +1,32 @@
1
+ // file: /commands/note.js
2
+ const { getAllNotes, getNote } = require("../../utils/storage.cjs");
3
+
4
+ module.exports = {
5
+ name: "note",
6
+ description: "Retrieves a saved note.",
7
+ usage: "note #الكلمة-المفتاحية",
8
+ chat: "all",
9
+
10
+ async execute(sock, msg, args) {
11
+ const groupId = msg.key.remoteJid;
12
+ const keywordArg = args[0];
13
+
14
+ if (!keywordArg || !keywordArg.startsWith("#")) {
15
+ return await sock.sendMessage(groupId, {
16
+ text: "صيغة غير صحيحة. استخدم: `!note #keyword`",
17
+ });
18
+ }
19
+
20
+ const keyword = keywordArg.slice(1).toLowerCase();
21
+ const notes = getAllNotes(groupId);
22
+ const noteText = getNote(groupId, keyword);
23
+
24
+ if (noteText) {
25
+ await sock.sendMessage(groupId, { text: noteText });
26
+ } else {
27
+ await sock.sendMessage(groupId, {
28
+ text: `⚠️ لم يتم العثور على ملاحظة بالكلمة المفتاحية: \`${keywordArg}\`\n\nلعرض كل الملاحظات، استخدم: \`!notes\``,
29
+ });
30
+ }
31
+ },
32
+ };
@@ -0,0 +1,60 @@
1
+ // file: /commands/promote.js
2
+ const logger = require("../../utils/logger.cjs");
3
+
4
+ module.exports = {
5
+ name: "promote",
6
+ description: "Promotes a member to an admin.",
7
+ usage: "promote @عضو",
8
+ chat: "group",
9
+ userAdminRequired: true,
10
+ botAdminRequired: true,
11
+
12
+ async execute(sock, msg, args, body, groupMetadata) {
13
+ const groupId = msg.key.remoteJid;
14
+
15
+ // Logic to identify the target user from mention or reply
16
+ const targetJid =
17
+ msg.message?.extendedTextMessage?.contextInfo?.mentionedJid?.[0] ||
18
+ msg.message?.extendedTextMessage?.contextInfo?.participant;
19
+
20
+ if (!targetJid) {
21
+ return await sock.sendMessage(groupId, {
22
+ text: "يجب عمل منشن للعضو أو الرد على رسالته لترقيته.",
23
+ });
24
+ }
25
+
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
+ ) {
34
+ return await sock.sendMessage(groupId, {
35
+ text: `⚠️ العضو @${targetJid.split("@")[0]} مشرف بالفعل.`,
36
+ mentions: [targetJid],
37
+ });
38
+ }
39
+
40
+ try {
41
+ await sock.groupParticipantsUpdate(
42
+ groupId,
43
+ [targetJid],
44
+ "promote" // The action is 'promote'
45
+ );
46
+ await sock.sendMessage(groupId, {
47
+ text: `👑 تم ترقية @${targetJid.split("@")[0]} إلى مشرف بنجاح.`,
48
+ mentions: [targetJid],
49
+ });
50
+ } catch (error) {
51
+ logger.error(
52
+ { err: error, command: "promote" },
53
+ "Error in !promote command"
54
+ );
55
+ await sock.sendMessage(groupId, {
56
+ text: "حدث خطأ أثناء محاولة الترقية.",
57
+ });
58
+ }
59
+ },
60
+ };
@@ -0,0 +1,42 @@
1
+ // file: /commands/removeall.js
2
+ const logger = require("../../utils/logger.cjs");
3
+
4
+ module.exports = {
5
+ name: "removeall",
6
+ description:
7
+ "Removes all non-admin members from the group after confirmation.",
8
+ usage: "removeall",
9
+ chat: "group",
10
+ userAdminRequired: true,
11
+ botAdminRequired: true,
12
+
13
+ async execute(sock, msg, args, body, groupMetadata, confirmationSessions) {
14
+ // This command's only job is to start the confirmation process.
15
+ // The main logic is in index.js
16
+
17
+ const senderId = msg.key.participant || msg.key.remoteJid;
18
+ const groupId = msg.key.remoteJid;
19
+
20
+ // Ask for confirmation
21
+ await sock.sendMessage(groupId, {
22
+ text: "⚠️ هل أنت متأكد من أنك تريد حذف جميع الأعضاء غير المشرفين؟\n\nأرسل `yes` للتأكيد خلال 30 ثانية.",
23
+ });
24
+
25
+ // Store the confirmation request
26
+ confirmationSessions.set(senderId, {
27
+ command: "removeall",
28
+ groupId: groupId,
29
+ timestamp: Date.now(),
30
+ });
31
+
32
+ // Set a timeout to delete the confirmation request after 30 seconds
33
+ setTimeout(() => {
34
+ if (confirmationSessions.has(senderId)) {
35
+ confirmationSessions.delete(senderId);
36
+ logger.info(
37
+ `[Confirmation] Timed out for ${senderId} on command removeall.`
38
+ );
39
+ }
40
+ }, 30000); // 30 seconds
41
+ },
42
+ };
@@ -0,0 +1,31 @@
1
+ // file: /commands/group/rules.js
2
+ const { getGroupSettings } = require("../../utils/storage.cjs");
3
+ const logger = require("../../utils/logger.cjs");
4
+
5
+ module.exports = {
6
+ name: "rules",
7
+ description: "Displays the group rules.",
8
+ usage: "rules",
9
+ chat: "group",
10
+
11
+ async execute(sock, msg) {
12
+ const groupId = msg.key.remoteJid;
13
+
14
+ try {
15
+ const settings = getGroupSettings(groupId);
16
+ const rules = settings.rules;
17
+
18
+ if (rules) {
19
+ const reply = `*📜 قواعد جروب ${msg.pushName}:*\n\n${rules}`;
20
+ await sock.sendMessage(groupId, { text: reply });
21
+ } else {
22
+ await sock.sendMessage(groupId, {
23
+ text: "لم يتم تعيين أي قواعد لهذا الجروب حتى الآن. يمكن للمشرفين تعيينها باستخدام `!setrules`.",
24
+ });
25
+ }
26
+ } catch (error) {
27
+ logger.error(error, "[Error] in !rules command:");
28
+ await sock.sendMessage(groupId, { text: "حدث خطأ أثناء جلب القواعد." });
29
+ }
30
+ },
31
+ };
@@ -0,0 +1,36 @@
1
+ // file: /commands/save.js
2
+ const { saveNote } = require("../../utils/storage.cjs");
3
+ const logger = require("../../utils/logger.cjs");
4
+
5
+ module.exports = {
6
+ name: "save",
7
+ description: "Saves a new note for the group.",
8
+ usage: "save #الكلمة-المفتاحية <نص الملاحظة>",
9
+ chat: "group",
10
+ userAdminRequired: true,
11
+
12
+ async execute(sock, msg, args) {
13
+ const groupId = msg.key.remoteJid;
14
+ const keywordArg = args[0];
15
+ const noteText = args.slice(1).join(" ");
16
+
17
+ if (!keywordArg || !keywordArg.startsWith("#") || !noteText) {
18
+ return await sock.sendMessage(groupId, {
19
+ text: "صيغة غير صحيحة. استخدم:\n`!save #keyword نص الملاحظة`",
20
+ });
21
+ }
22
+
23
+ const keyword = keywordArg.slice(1).toLowerCase();
24
+
25
+ try {
26
+ // Simply call the new storage function
27
+ saveNote(groupId, keyword, noteText);
28
+ await sock.sendMessage(groupId, {
29
+ text: `✅ تم حفظ الملاحظة بنجاح بالكلمة المفتاحية: \`#${keyword}\``,
30
+ });
31
+ } catch (error) {
32
+ logger.error({ err: error, command: "save" }, "Error in !save command");
33
+ await sock.sendMessage(groupId, { text: "حدث خطأ أثناء حفظ الملاحظة." });
34
+ }
35
+ },
36
+ };
@@ -0,0 +1,41 @@
1
+ // file: /commands/group/setname.js
2
+ const logger = require("../../utils/logger.cjs");
3
+
4
+ module.exports = {
5
+ name: "setname",
6
+ description: "Changes the group's subject/name.",
7
+ usage: "setname <الاسم الجديد>",
8
+ chat: "group",
9
+ userAdminRequired: true,
10
+ botAdminRequired: true,
11
+
12
+ async execute(sock, msg, args) {
13
+ const groupId = msg.key.remoteJid;
14
+ const newName = args.join(" ");
15
+
16
+ if (!newName) {
17
+ return await sock.sendMessage(groupId, {
18
+ text: "يرجى كتابة الاسم الجديد للجروب بعد الأمر.",
19
+ });
20
+ }
21
+
22
+ // WhatsApp has a limit of 25 characters for the subject
23
+ if (newName.length > 25) {
24
+ return await sock.sendMessage(groupId, {
25
+ text: "⚠️ اسم الجروب طويل جدًا. الحد الأقصى هو 25 حرفًا.",
26
+ });
27
+ }
28
+
29
+ try {
30
+ await sock.groupUpdateSubject(groupId, newName);
31
+ await sock.sendMessage(groupId, {
32
+ text: `✅ تم تغيير اسم الجروب بنجاح إلى:\n*${newName}*`,
33
+ });
34
+ } catch (error) {
35
+ logger.error({ err: error }, "Error in !group setname command");
36
+ await sock.sendMessage(groupId, {
37
+ text: "حدث خطأ. تأكد من أنني مشرف ولدي صلاحية تغيير اسم الجروب.",
38
+ });
39
+ }
40
+ },
41
+ };
@@ -0,0 +1,51 @@
1
+ // file: /commands/group/setpp.js
2
+ const { downloadMediaMessage } = require("@whiskeysockets/baileys");
3
+ const logger = require("../../utils/logger.cjs");
4
+
5
+ module.exports = {
6
+ name: "setpp", // pp = profile picture
7
+ description: "Changes the group's profile picture.",
8
+ usage: "setpp (رد على صورة)",
9
+ chat: "group",
10
+ userAdminRequired: true,
11
+ botAdminRequired: true,
12
+
13
+ async execute(sock, msg) {
14
+ const groupId = msg.key.remoteJid;
15
+
16
+ // Check if the command is a reply to a message, and if that message is an image
17
+ const quoted = msg.message?.extendedTextMessage?.contextInfo?.quotedMessage;
18
+ if (!quoted || !quoted.imageMessage) {
19
+ return await sock.sendMessage(groupId, {
20
+ text: "لتغيير صورة الجروب، يرجى الرد على الصورة التي تريدها بهذا الأمر.",
21
+ });
22
+ }
23
+
24
+ try {
25
+ await sock.sendMessage(groupId, { text: "🖼️ جاري تغيير صورة الجروب..." });
26
+
27
+ // Download the image from the quoted message
28
+ const imageBuffer = await downloadMediaMessage(
29
+ {
30
+ key: msg.message.extendedTextMessage.contextInfo.stanzaId,
31
+ remoteJid: groupId,
32
+ id: msg.message.extendedTextMessage.contextInfo.participant,
33
+ },
34
+ "buffer",
35
+ {}
36
+ );
37
+
38
+ // Update the group's profile picture using the downloaded image buffer
39
+ await sock.updateProfilePicture(groupId, imageBuffer);
40
+
41
+ await sock.sendMessage(groupId, {
42
+ text: "✅ تم تحديث صورة الجروب بنجاح.",
43
+ });
44
+ } catch (error) {
45
+ logger.error({ err: error }, "Error in !group setpp command");
46
+ await sock.sendMessage(groupId, {
47
+ text: "حدث خطأ. تأكد من أنني مشرف وأن الصورة صالحة.",
48
+ });
49
+ }
50
+ },
51
+ };
@@ -0,0 +1,46 @@
1
+ const {
2
+ getGroupSettings,
3
+ saveGroupSettings,
4
+ } = require("../../utils/storage.cjs");
5
+ const logger = require("../../utils/logger.cjs");
6
+
7
+ module.exports = {
8
+ name: "setrules",
9
+ description: "Sets the rules for the current group.",
10
+ usage: "setrules <القواعد>",
11
+ chat: "group",
12
+ userAdminRequired: true,
13
+
14
+ async execute(sock, msg, args) {
15
+ const groupId = msg.key.remoteJid;
16
+ const rulesText = args.join(" ");
17
+
18
+ if (!rulesText) {
19
+ return await sock.sendMessage(groupId, {
20
+ text: "يرجى كتابة القواعد بعد الأمر.\n*مثال:*\n`!setrules 1. احترام الأعضاء.\n2. ممنوع السبام.`",
21
+ });
22
+ }
23
+
24
+ try {
25
+ // 1. Get the settings object. It will be {} if the group is new.
26
+ const settings = getGroupSettings(groupId);
27
+
28
+ // 2. Add or update the 'rules' property on the object.
29
+ settings.rules = rulesText;
30
+
31
+ // 3. Save the entire updated settings object back to the database.
32
+ saveGroupSettings(groupId, settings);
33
+
34
+ await sock.sendMessage(groupId, {
35
+ text: "✅ تم حفظ قواعد الجروب بنجاح في قاعدة البيانات.",
36
+ });
37
+ } catch (error) {
38
+ // Improved logging for better debugging
39
+ logger.error(
40
+ { err: error, groupId: groupId },
41
+ "Error in !setrules command"
42
+ );
43
+ await sock.sendMessage(groupId, { text: "حدث خطأ أثناء حفظ القواعد." });
44
+ }
45
+ },
46
+ };
@@ -0,0 +1,58 @@
1
+ // file: /commands/group/setwarn.js
2
+ const { getGroupSettings, saveGroupSettings } = require("../../utils/storage.cjs");
3
+
4
+ module.exports = {
5
+ name: "setwarn",
6
+ description: "Configures the automatic warning system.",
7
+ usage: "setwarn max <عدد>\nsetwarn action <KICK|NONE>",
8
+ chat: "group",
9
+ userAdminRequired: true,
10
+
11
+ async execute(sock, msg, args) {
12
+ const groupId = msg.key.remoteJid;
13
+ const subCommand = args[0]?.toLowerCase();
14
+ const value = args[1];
15
+
16
+ const settings = getGroupSettings(groupId);
17
+ if (!settings.warn_system) {
18
+ settings.warn_system = { max_warnings: 3, action: "NONE" };
19
+ }
20
+
21
+ const warnConfig = settings.warn_system;
22
+
23
+ switch (subCommand) {
24
+ case "max":
25
+ const max = parseInt(value, 10);
26
+ if (isNaN(max) || max < 1) {
27
+ return await sock.sendMessage(groupId, {
28
+ text: "يرجى تحديد عدد صحيح وصالح للحد الأقصى للتحذيرات.",
29
+ });
30
+ }
31
+ warnConfig.max_warnings = max;
32
+ await sock.sendMessage(groupId, {
33
+ text: `✅ تم تعيين الحد الأقصى للتحذيرات إلى ${max}.`,
34
+ });
35
+ break;
36
+
37
+ case "action":
38
+ const action = value?.toUpperCase();
39
+ if (action !== "KICK" && action !== "NONE") {
40
+ return await sock.sendMessage(groupId, {
41
+ text: "الإجراء غير صالح. الإجراءات المتاحة: `KICK`, `NONE`",
42
+ });
43
+ }
44
+ warnConfig.action = action;
45
+ await sock.sendMessage(groupId, {
46
+ text: `✅ تم تعيين الإجراء التلقائي إلى ${action}.`,
47
+ });
48
+ break;
49
+
50
+ default:
51
+ return await sock.sendMessage(groupId, {
52
+ text: "صيغة غير صحيحة. استخدم:\n`!setwarn max <number>`\n`!setwarn action <KICK|NONE>`",
53
+ });
54
+ }
55
+
56
+ saveGroupSettings(groupId, settings);
57
+ },
58
+ };