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.
- package/LICENSE +21 -0
- package/README.md +105 -0
- package/SETUP.md +242 -0
- package/app.cjs +337 -0
- package/bin/levix.js +7 -0
- package/deploy/install.sh +182 -0
- package/deploy/levix.service +56 -0
- package/deploy/nginx/levix.leviro.net.conf +79 -0
- package/package.json +78 -0
- package/public/brand/apple-touch-icon.png +0 -0
- package/public/brand/banner.jpg +0 -0
- package/public/brand/banner.webp +0 -0
- package/public/brand/icon-16.png +0 -0
- package/public/brand/icon-192.png +0 -0
- package/public/brand/icon-32.png +0 -0
- package/public/brand/icon-512.png +0 -0
- package/public/brand/icon-512.webp +0 -0
- package/public/brand/mark.png +0 -0
- package/public/brand/mark.svg +16 -0
- package/public/brand/mark.webp +0 -0
- package/public/brand/wordmark.png +0 -0
- package/public/brand/wordmark.webp +0 -0
- package/public/dashboard.css +881 -0
- package/public/dashboard.js +1057 -0
- package/public/logo.svg +16 -0
- package/public/qrcode.min.js +1 -0
- package/public/socket.io.min.js +7 -0
- package/scheduler.cjs +197 -0
- package/src/auth/auth-storage.cjs +74 -0
- package/src/auth/use-database-auth-state.js +130 -0
- package/src/bootstrap/core.js +74 -0
- package/src/bootstrap/events.cjs +43 -0
- package/src/bootstrap/panel.js +97 -0
- package/src/cli.js +153 -0
- package/src/commands/autoschedule.cjs +84 -0
- package/src/commands/blacklist.cjs +116 -0
- package/src/commands/block.cjs +70 -0
- package/src/commands/calc.cjs +387 -0
- package/src/commands/debt.cjs +175 -0
- package/src/commands/deleteschedule.cjs +36 -0
- package/src/commands/gemini.cjs +727 -0
- package/src/commands/group/add.cjs +136 -0
- package/src/commands/group/all.cjs +43 -0
- package/src/commands/group/antiSpam.cjs +96 -0
- package/src/commands/group/antilink.cjs +167 -0
- package/src/commands/group/approveall.cjs +54 -0
- package/src/commands/group/clearwarns.cjs +41 -0
- package/src/commands/group/deletenote.cjs +33 -0
- package/src/commands/group/demote.cjs +62 -0
- package/src/commands/group/kick.cjs +85 -0
- package/src/commands/group/media.cjs +120 -0
- package/src/commands/group/members.cjs +59 -0
- package/src/commands/group/note.cjs +32 -0
- package/src/commands/group/promote.cjs +60 -0
- package/src/commands/group/removeall.cjs +42 -0
- package/src/commands/group/rules.cjs +31 -0
- package/src/commands/group/save.cjs +36 -0
- package/src/commands/group/setname.cjs +41 -0
- package/src/commands/group/setpp.cjs +51 -0
- package/src/commands/group/setrules.cjs +46 -0
- package/src/commands/group/setwarn.cjs +58 -0
- package/src/commands/group/tagadmins.cjs +50 -0
- package/src/commands/group/warn.cjs +105 -0
- package/src/commands/group/warns.cjs +66 -0
- package/src/commands/group/welcome.cjs +109 -0
- package/src/commands/group.cjs +160 -0
- package/src/commands/help.cjs +230 -0
- package/src/commands/listschedules.cjs +47 -0
- package/src/commands/loop.cjs +163 -0
- package/src/commands/memory.cjs +201 -0
- package/src/commands/mod.cjs +155 -0
- package/src/commands/notes.cjs +28 -0
- package/src/commands/perm.cjs +236 -0
- package/src/commands/ping.cjs +17 -0
- package/src/commands/poll.cjs +80 -0
- package/src/commands/prayer.cjs +64 -0
- package/src/commands/qr.cjs +39 -0
- package/src/commands/rand.cjs +130 -0
- package/src/commands/restart.cjs +25 -0
- package/src/commands/schedule.cjs +86 -0
- package/src/commands/score.cjs +130 -0
- package/src/commands/setprefix.cjs +40 -0
- package/src/commands/shortlink.cjs +74 -0
- package/src/commands/shutdown.cjs +45 -0
- package/src/commands/status.cjs +75 -0
- package/src/commands/stt.cjs +150 -0
- package/src/commands/todo.cjs +88 -0
- package/src/commands/tts.cjs +170 -0
- package/src/commands/unblock.cjs +57 -0
- package/src/commands/weather.cjs +79 -0
- package/src/config/ai-persona.md +17 -0
- package/src/config/baileys.config.js +51 -0
- package/src/config/brand.cjs +36 -0
- package/src/config/brand.esm.js +17 -0
- package/src/config/constants.js +16 -0
- package/src/config/defaults.cjs +91 -0
- package/src/config/lock.cjs +110 -0
- package/src/config/paths.cjs +90 -0
- package/src/config/runtime-config.cjs +219 -0
- package/src/config/secrets.cjs +130 -0
- package/src/config/settings.cjs +386 -0
- package/src/core/connection.js +199 -0
- package/src/core/events.js +61 -0
- package/src/core/socket.js +64 -0
- package/src/db/db.cjs +282 -0
- package/src/db/store.cjs +837 -0
- package/src/db/store.esm.js +17 -0
- package/src/domain/caddy.js +120 -0
- package/src/domain/command.js +380 -0
- package/src/domain/detect.js +173 -0
- package/src/domain/nginx.js +138 -0
- package/src/domain/system.js +148 -0
- package/src/handlers/command.handler.js +414 -0
- package/src/handlers/group.handler.js +73 -0
- package/src/handlers/message.handler.js +149 -0
- package/src/index.js +217 -0
- package/src/middleware/antispam.middleware.js +68 -0
- package/src/middleware/blacklist.middleware.js +29 -0
- package/src/middleware/forward-tracking.middleware.js +49 -0
- package/src/middleware/permissions.middleware.js +114 -0
- package/src/routes/dashboard.api.esm.js +779 -0
- package/src/services/aiAgent.cjs +317 -0
- package/src/services/aiTools.cjs +635 -0
- package/src/utils/datetime.cjs +61 -0
- package/src/utils/lid-helper.esm.js +143 -0
- package/src/utils/logger.cjs +72 -0
- package/src/utils/memory.cjs +385 -0
- package/src/utils/normalizeJid.cjs +65 -0
- package/src/utils/normalizeJid.esm.js +37 -0
- package/src/utils/openBrowser.cjs +129 -0
- package/src/utils/permissions.cjs +154 -0
- package/src/utils/permissions.esm.js +375 -0
- package/src/utils/recentMessageCache.esm.js +70 -0
- package/src/utils/requestOrigin.cjs +102 -0
- package/src/utils/sendBotMessage.cjs +43 -0
- package/src/utils/sendBotMessage.esm.js +139 -0
- package/src/utils/statusMessage.cjs +179 -0
- package/src/utils/storage-hub.cjs +49 -0
- package/src/utils/storage-hub.esm.js +24 -0
- package/src/utils/storage.cjs +80 -0
- package/src/utils/storage.esm.js +88 -0
- package/src/utils/textDecode.cjs +197 -0
- package/src/utils/thumbnail.cjs +591 -0
- package/src/utils/typing.esm.js +102 -0
- package/views/dashboard.ejs +329 -0
- package/views/login.ejs +105 -0
- package/views/qr.ejs +108 -0
- package/views/setup.ejs +122 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// file: /commands/tagadmins.js
|
|
2
|
+
const logger = require("../../utils/logger.cjs");
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
name: "tagadmins",
|
|
6
|
+
description: "Mentions all admins in the group.",
|
|
7
|
+
usage: "tagadmins [رسالة]",
|
|
8
|
+
chat: "group",
|
|
9
|
+
|
|
10
|
+
async execute(sock, msg, args, body, groupMetadata) {
|
|
11
|
+
const groupId = msg.key.remoteJid;
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
// Filter the participants to get only admins and superadmins
|
|
15
|
+
const admins = groupMetadata.participants.filter(
|
|
16
|
+
(p) => p.admin === "admin" || p.admin === "superadmin"
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
if (admins.length === 0) {
|
|
20
|
+
return await sock.sendMessage(groupId, {
|
|
21
|
+
text: "لا يوجد مشرفون في هذا الجروب.",
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Get the custom message provided by the user, or use a default one
|
|
26
|
+
const customMessage = args.join(" ") || "يرجى حضور المشرفين";
|
|
27
|
+
|
|
28
|
+
// Get an array of JIDs for the mentions property
|
|
29
|
+
const mentions = admins.map((a) => a.id);
|
|
30
|
+
|
|
31
|
+
let text = `*🚨 تنبيه للمشرفين 🚨*\n\n*الرسالة:* ${customMessage}\n\n`;
|
|
32
|
+
|
|
33
|
+
// Add a tag for each admin
|
|
34
|
+
for (const admin of admins) {
|
|
35
|
+
text += `» @${admin.id.split("@")[0]}\n`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
await sock.sendMessage(groupId, {
|
|
39
|
+
text: text,
|
|
40
|
+
mentions: mentions,
|
|
41
|
+
});
|
|
42
|
+
} catch (error) {
|
|
43
|
+
logger.error(
|
|
44
|
+
{ err: error, command: "tagadmins" },
|
|
45
|
+
"Error in !tagadmins command"
|
|
46
|
+
);
|
|
47
|
+
await sock.sendMessage(groupId, { text: "حدث خطأ." });
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// file: /commands/warn.js (Corrected Logic Flow)
|
|
2
|
+
const { getGroupSettings } = require("../../utils/storage.cjs"); // <-- 1. Import getGroupSettings
|
|
3
|
+
const {
|
|
4
|
+
getUserWarnings,
|
|
5
|
+
saveUserWarnings,
|
|
6
|
+
clearUserWarnings,
|
|
7
|
+
} = require("../../utils/storage.cjs");
|
|
8
|
+
const logger = require("../../utils/logger.cjs");
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
name: "warn",
|
|
12
|
+
description: "Warns a user and takes action if the limit is reached.",
|
|
13
|
+
usage: "warn @عضو [السبب]",
|
|
14
|
+
chat: "group",
|
|
15
|
+
userAdminRequired: true,
|
|
16
|
+
|
|
17
|
+
async execute(sock, msg, args) {
|
|
18
|
+
try {
|
|
19
|
+
const groupId = msg.key.remoteJid;
|
|
20
|
+
const senderId = msg.key.participant;
|
|
21
|
+
const mentionedJid =
|
|
22
|
+
msg.message?.extendedTextMessage?.contextInfo?.mentionedJid?.[0];
|
|
23
|
+
const reason = args.slice(1).join(" ");
|
|
24
|
+
|
|
25
|
+
// --- 1. Validate Inputs ---
|
|
26
|
+
if (!mentionedJid) {
|
|
27
|
+
return await sock.sendMessage(groupId, {
|
|
28
|
+
text: "يجب عمل منشن للعضو الذي تريد تحذيره.",
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
if (!reason) {
|
|
32
|
+
return await sock.sendMessage(groupId, {
|
|
33
|
+
text: "يجب كتابة سبب التحذير.",
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// --- 2. Add the New Warning ---
|
|
38
|
+
const userWarnings = getUserWarnings(groupId, mentionedJid);
|
|
39
|
+
userWarnings.push({
|
|
40
|
+
reason: reason,
|
|
41
|
+
by: senderId,
|
|
42
|
+
date: new Date().toISOString(),
|
|
43
|
+
});
|
|
44
|
+
saveUserWarnings(groupId, mentionedJid, userWarnings);
|
|
45
|
+
|
|
46
|
+
const newWarnCount = userWarnings.length;
|
|
47
|
+
|
|
48
|
+
// --- 3. Send Confirmation & Check for Action ---
|
|
49
|
+
let replyText =
|
|
50
|
+
`✅ تم توجيه تحذير إلى @${mentionedJid.split("@")[0]}.\n` +
|
|
51
|
+
`*السبب:* ${reason}`;
|
|
52
|
+
|
|
53
|
+
await sock.sendMessage(groupId, {
|
|
54
|
+
text: replyText,
|
|
55
|
+
mentions: [mentionedJid],
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const warnConfig = getGroupSettings(groupId)?.warn_system;
|
|
59
|
+
|
|
60
|
+
if (
|
|
61
|
+
warnConfig &&
|
|
62
|
+
warnConfig.action === "KICK" &&
|
|
63
|
+
newWarnCount >= warnConfig.max_warnings
|
|
64
|
+
) {
|
|
65
|
+
await sock.sendMessage(groupId, {
|
|
66
|
+
text: `🚫 لقد وصل العضو @${
|
|
67
|
+
mentionedJid.split("@")[0]
|
|
68
|
+
} إلى الحد الأقصى للتحذيرات (${newWarnCount}/${
|
|
69
|
+
warnConfig.max_warnings
|
|
70
|
+
}). سيتم حذفه.`,
|
|
71
|
+
mentions: [mentionedJid],
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
// Kick the user and then clear their warnings
|
|
76
|
+
await sock.groupParticipantsUpdate(groupId, [mentionedJid], "remove");
|
|
77
|
+
clearUserWarnings(groupId, mentionedJid);
|
|
78
|
+
logger.info(
|
|
79
|
+
`[Warn Kick] Kicked and cleared warnings for ${mentionedJid} from ${groupId}`
|
|
80
|
+
);
|
|
81
|
+
} catch (kickError) {
|
|
82
|
+
logger.error(
|
|
83
|
+
{ err: kickError },
|
|
84
|
+
"Failed to kick user after max warnings"
|
|
85
|
+
);
|
|
86
|
+
await sock.sendMessage(groupId, {
|
|
87
|
+
text: "حاولت حذف العضو ولكني لا أملك صلاحية كافية لذلك.",
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
// If no action is taken, just inform about the new count
|
|
92
|
+
await sock.sendMessage(groupId, {
|
|
93
|
+
text: `*إجمالي التحذيرات الآن:* ${newWarnCount}${
|
|
94
|
+
warnConfig ? `/${warnConfig.max_warnings}` : ""
|
|
95
|
+
}`,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
} catch (error) {
|
|
99
|
+
logger.error({ err: error }, "An error occurred in the !warn command.");
|
|
100
|
+
await sock.sendMessage(msg.key.remoteJid, {
|
|
101
|
+
text: "حدث خطأ عام في أمر التحذير.",
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// file: /commands/warns.js
|
|
2
|
+
const { getUserWarnings } = require("../../utils/storage.cjs");
|
|
3
|
+
const logger = require("../../utils/logger.cjs");
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
name: "warns",
|
|
7
|
+
description: "Displays the warnings for a specific user.",
|
|
8
|
+
usage: "warns @عضو",
|
|
9
|
+
chat: "group",
|
|
10
|
+
userAdminRequired: true,
|
|
11
|
+
|
|
12
|
+
async execute(sock, msg) {
|
|
13
|
+
try {
|
|
14
|
+
const groupId = msg.key.remoteJid;
|
|
15
|
+
const mentionedJid =
|
|
16
|
+
msg.message?.extendedTextMessage?.contextInfo?.mentionedJid?.[0];
|
|
17
|
+
|
|
18
|
+
if (!mentionedJid) {
|
|
19
|
+
return await sock.sendMessage(groupId, {
|
|
20
|
+
text: "يجب عمل منشن للعضو الذي تريد عرض تحذيراته.",
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Fetch warnings directly from the database
|
|
25
|
+
const userWarnings = getUserWarnings(groupId, mentionedJid);
|
|
26
|
+
|
|
27
|
+
if (userWarnings.length === 0) {
|
|
28
|
+
return await sock.sendMessage(groupId, {
|
|
29
|
+
text: `✅ لا توجد أي تحذيرات للعضو @${mentionedJid.split("@")[0]}.`,
|
|
30
|
+
mentions: [mentionedJid],
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let reply = `*سجل تحذيرات @${
|
|
35
|
+
mentionedJid.split("@")[0]
|
|
36
|
+
}:*\n*إجمالي التحذيرات: ${userWarnings.length}*\n\n`;
|
|
37
|
+
|
|
38
|
+
const mentionedAdmins = [];
|
|
39
|
+
userWarnings.forEach((warning, index) => {
|
|
40
|
+
const adminJid = warning.by;
|
|
41
|
+
mentionedAdmins.push(adminJid);
|
|
42
|
+
const warningDate = new Date(warning.date).toLocaleString("ar-EG", {
|
|
43
|
+
timeZone: "Africa/Cairo",
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
reply +=
|
|
47
|
+
`*${index + 1}. التحذير:*\n` +
|
|
48
|
+
`*السبب:* ${warning.reason}\n` +
|
|
49
|
+
`*بواسطة المشرف:* @${adminJid.split("@")[0]}\n` +
|
|
50
|
+
`*التاريخ:* ${warningDate}\n\n`;
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const mentions = [mentionedJid, ...mentionedAdmins];
|
|
54
|
+
|
|
55
|
+
await sock.sendMessage(groupId, {
|
|
56
|
+
text: reply,
|
|
57
|
+
mentions: mentions,
|
|
58
|
+
});
|
|
59
|
+
} catch (error) {
|
|
60
|
+
logger.error({ err: error }, "Error in !warns command");
|
|
61
|
+
await sock.sendMessage(msg.key.remoteJid, {
|
|
62
|
+
text: "حدث خطأ أثناء عرض التحذيرات.",
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// file: /commands/welcome.js
|
|
2
|
+
const {
|
|
3
|
+
getGroupSettings,
|
|
4
|
+
saveGroupSettings,
|
|
5
|
+
} = require("../../utils/storage.cjs");
|
|
6
|
+
const logger = require("../../utils/logger.cjs");
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
name: "welcome",
|
|
10
|
+
description: "Manages the group's welcome message system.",
|
|
11
|
+
usage: "welcome [status]\nwelcome <on|off>\nwelcome add <نص الترحيب>\nwelcome list\nwelcome delete <رقم>",
|
|
12
|
+
chat: "group",
|
|
13
|
+
userAdminRequired: true,
|
|
14
|
+
|
|
15
|
+
async execute(sock, msg, args) {
|
|
16
|
+
const groupId = msg.key.remoteJid;
|
|
17
|
+
const subCommand = args[0] ? args[0].toLowerCase() : "status"; // Default to showing status
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const settings = getGroupSettings(groupId);
|
|
21
|
+
|
|
22
|
+
// Initialize the welcome system settings if they don't exist
|
|
23
|
+
if (!settings.welcome_system) {
|
|
24
|
+
settings.welcome_system = { enabled: false, messages: [] };
|
|
25
|
+
}
|
|
26
|
+
const welcomeConfig = settings.welcome_system;
|
|
27
|
+
|
|
28
|
+
switch (subCommand) {
|
|
29
|
+
case "on":
|
|
30
|
+
welcomeConfig.enabled = true;
|
|
31
|
+
await sock.sendMessage(groupId, {
|
|
32
|
+
text: "✅ تم تفعيل نظام الترحيب التلقائي.",
|
|
33
|
+
});
|
|
34
|
+
break;
|
|
35
|
+
|
|
36
|
+
case "off":
|
|
37
|
+
welcomeConfig.enabled = false;
|
|
38
|
+
await sock.sendMessage(groupId, {
|
|
39
|
+
text: "☑️ تم تعطيل نظام الترحيب التلقائي.",
|
|
40
|
+
});
|
|
41
|
+
break;
|
|
42
|
+
|
|
43
|
+
case "add":
|
|
44
|
+
const messageToAdd = args.slice(1).join(" ");
|
|
45
|
+
if (!messageToAdd || !messageToAdd.includes("${user}")) {
|
|
46
|
+
return await sock.sendMessage(groupId, {
|
|
47
|
+
text: "يرجى كتابة رسالة ترحيب تحتوي على المتغير ${user} لعمل منشن للعضو الجديد.",
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
welcomeConfig.messages.push(messageToAdd);
|
|
51
|
+
await sock.sendMessage(groupId, {
|
|
52
|
+
text: "✅ تم إضافة رسالة الترحيب بنجاح.",
|
|
53
|
+
});
|
|
54
|
+
break;
|
|
55
|
+
|
|
56
|
+
case "list":
|
|
57
|
+
if (welcomeConfig.messages.length === 0) {
|
|
58
|
+
return await sock.sendMessage(groupId, {
|
|
59
|
+
text: "لا توجد رسائل ترحيب محفوظة.",
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
let listReply = "*قائمة رسائل الترحيب المحفوظة:*\n\n";
|
|
63
|
+
welcomeConfig.messages.forEach((message, index) => {
|
|
64
|
+
listReply += `#${index + 1}: ${message}\n`;
|
|
65
|
+
});
|
|
66
|
+
await sock.sendMessage(groupId, { text: listReply });
|
|
67
|
+
break;
|
|
68
|
+
|
|
69
|
+
case "delete":
|
|
70
|
+
const indexToDelete = parseInt(args[1]?.replace("#", ""), 10) - 1;
|
|
71
|
+
if (
|
|
72
|
+
isNaN(indexToDelete) ||
|
|
73
|
+
indexToDelete < 0 ||
|
|
74
|
+
indexToDelete >= welcomeConfig.messages.length
|
|
75
|
+
) {
|
|
76
|
+
return await sock.sendMessage(groupId, {
|
|
77
|
+
text: "رقم الرسالة غير صالح. استخدم `!welcome list` لمعرفة الأرقام.",
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const deletedMessage = welcomeConfig.messages.splice(
|
|
81
|
+
indexToDelete,
|
|
82
|
+
1
|
|
83
|
+
);
|
|
84
|
+
await sock.sendMessage(groupId, {
|
|
85
|
+
text: `☑️ تم حذف الرسالة بنجاح:\n*${deletedMessage[0]}*`,
|
|
86
|
+
});
|
|
87
|
+
break;
|
|
88
|
+
|
|
89
|
+
case "status":
|
|
90
|
+
default:
|
|
91
|
+
const statusReply =
|
|
92
|
+
`*상태 نظام الترحيب:*\n\n` +
|
|
93
|
+
`الحالة: ${welcomeConfig.enabled ? "مفعل ✅" : "معطل ☑️"}\n` +
|
|
94
|
+
`عدد الرسائل المحفوظة: ${welcomeConfig.messages.length}`;
|
|
95
|
+
await sock.sendMessage(groupId, { text: statusReply });
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Save the updated settings back to the database
|
|
100
|
+
saveGroupSettings(groupId, settings);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
logger.error(
|
|
103
|
+
{ err: error, command: "welcome" },
|
|
104
|
+
"Error in !welcome command"
|
|
105
|
+
);
|
|
106
|
+
await sock.sendMessage(groupId, { text: "حدث خطأ." });
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// file: /commands/group.js (Permission-aware sub-command router)
|
|
2
|
+
//
|
|
3
|
+
// Audit fix: this used to read owner status from `config.owners` (always
|
|
4
|
+
// empty) and admin status with a raw `p.id === senderId`
|
|
5
|
+
// comparison that breaks under v7's LID/PN split. Both checks now go
|
|
6
|
+
// through the centralized permissions module.
|
|
7
|
+
|
|
8
|
+
const fs = require("fs");
|
|
9
|
+
const path = require("path");
|
|
10
|
+
const logger = require("../utils/logger.cjs");
|
|
11
|
+
const runtimeConfig = require("../config/runtime-config.cjs");
|
|
12
|
+
const {
|
|
13
|
+
isOwnerJidSync,
|
|
14
|
+
isAdminInGroupSync,
|
|
15
|
+
isBotAdminInGroupSync,
|
|
16
|
+
} = require("../utils/permissions.cjs");
|
|
17
|
+
const normalizeJid = require("../utils/normalizeJid.cjs");
|
|
18
|
+
|
|
19
|
+
// --- Sub-command Loader ---
|
|
20
|
+
//
|
|
21
|
+
// Same story as the top-level command loader: read the directory normally, and
|
|
22
|
+
// take a generated manifest instead when there is no directory to read (a
|
|
23
|
+
// packaged executable). See scripts/build-sea.mjs.
|
|
24
|
+
const subCommands = new Map();
|
|
25
|
+
|
|
26
|
+
function register(command, label) {
|
|
27
|
+
try {
|
|
28
|
+
if (!command?.name) return;
|
|
29
|
+
subCommands.set(command.name, command);
|
|
30
|
+
if (Array.isArray(command.aliases)) {
|
|
31
|
+
command.aliases.forEach((alias) => subCommands.set(alias, command));
|
|
32
|
+
}
|
|
33
|
+
} catch (error) {
|
|
34
|
+
logger.error({ err: error, file: label }, `Failed to load group sub-command from ${label}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let manifest = null;
|
|
39
|
+
try {
|
|
40
|
+
const loaded = require("./group/_manifest.cjs");
|
|
41
|
+
if (Array.isArray(loaded)) manifest = loaded;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (error?.code !== "MODULE_NOT_FOUND") {
|
|
44
|
+
logger.error({ err: error }, "[group] manifest failed to load");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (manifest) {
|
|
49
|
+
for (const entry of manifest) register(entry.module, entry.label);
|
|
50
|
+
} else {
|
|
51
|
+
const subCommandsPath = path.join(__dirname, "group");
|
|
52
|
+
for (const file of fs.readdirSync(subCommandsPath)) {
|
|
53
|
+
if (!file.endsWith(".cjs") || file.startsWith("_")) continue;
|
|
54
|
+
try {
|
|
55
|
+
register(require(path.join(subCommandsPath, file)), file);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
logger.error(
|
|
58
|
+
{ err: error, file },
|
|
59
|
+
`Failed to load group sub-command from ${file}`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = {
|
|
66
|
+
name: "group",
|
|
67
|
+
description: "Main command for all group management actions.",
|
|
68
|
+
usage: "group <أمر فرعي> [المعاملات]",
|
|
69
|
+
chat: "group",
|
|
70
|
+
|
|
71
|
+
async execute(sock, msg, args, body, groupMetadata) {
|
|
72
|
+
const subCommandName = args.shift()?.toLowerCase();
|
|
73
|
+
|
|
74
|
+
if (!subCommandName) {
|
|
75
|
+
return await sock.sendMessage(msg.key.remoteJid, {
|
|
76
|
+
text: "يرجى تحديد أمر فرعي بعد `!group`.",
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const subCommand = subCommands.get(subCommandName);
|
|
81
|
+
if (!subCommand) {
|
|
82
|
+
return await sock.sendMessage(msg.key.remoteJid, {
|
|
83
|
+
text: `الأمر الفرعي \`${subCommandName}\` غير معروف.`,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Switched off from the dashboard: `!group kick` has to refuse it too, not
|
|
88
|
+
// just the direct `!kick`.
|
|
89
|
+
if (runtimeConfig.isDisabled(subCommand.name)) {
|
|
90
|
+
return await sock.sendMessage(msg.key.remoteJid, {
|
|
91
|
+
text: "⛔ الأمر ده متوقف حاليًا.",
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const senderId = normalizeJid(msg.key.participant || msg.key.remoteJid);
|
|
96
|
+
// Centralized owner check — covers the bot itself, LID<->PN,
|
|
97
|
+
// and the user_metadata table.
|
|
98
|
+
const isOwner = msg.key.fromMe || isOwnerJidSync(senderId);
|
|
99
|
+
const isSenderAdmin = isAdminInGroupSync(groupMetadata, senderId);
|
|
100
|
+
|
|
101
|
+
// "group:kick" — the flat key runtime-config uses for sub-commands, so a
|
|
102
|
+
// level changed from the dashboard applies here too.
|
|
103
|
+
const permissionLevel = runtimeConfig.getPermission(
|
|
104
|
+
`group:${subCommand.name}`
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
let hasPermission = false;
|
|
108
|
+
switch (permissionLevel) {
|
|
109
|
+
case "MEMBERS":
|
|
110
|
+
case "ALL":
|
|
111
|
+
hasPermission = true;
|
|
112
|
+
break;
|
|
113
|
+
case "OWNER_ONLY":
|
|
114
|
+
hasPermission = isOwner;
|
|
115
|
+
break;
|
|
116
|
+
case "ADMINS_ONLY":
|
|
117
|
+
hasPermission = isSenderAdmin || isOwner;
|
|
118
|
+
break;
|
|
119
|
+
case "ADMINS_OWNER":
|
|
120
|
+
hasPermission = isOwner || isSenderAdmin;
|
|
121
|
+
break;
|
|
122
|
+
default:
|
|
123
|
+
logger.warn(
|
|
124
|
+
`[group] Unknown permission level "${permissionLevel}" for sub-command "${subCommand.name}"`
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!hasPermission) {
|
|
129
|
+
return await sock.sendMessage(msg.key.remoteJid, {
|
|
130
|
+
text: "🚫 ليس لديك الصلاحية لاستخدام هذا الأمر.",
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const isBotAdmin = isBotAdminInGroupSync(groupMetadata, sock);
|
|
135
|
+
if (subCommand.userAdminRequired && !isSenderAdmin && !isOwner) {
|
|
136
|
+
return await sock.sendMessage(msg.key.remoteJid, {
|
|
137
|
+
text: "⚠️ هذا الأمر يتطلب أن تكون مشرفًا.",
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
if (subCommand.botAdminRequired && !isBotAdmin) {
|
|
141
|
+
return await sock.sendMessage(msg.key.remoteJid, {
|
|
142
|
+
text: "⚠️ يجب أن أكون مشرفًا لتنفيذ هذا الأمر.",
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
await subCommand.execute(sock, msg, args, body, groupMetadata);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
logger.error(
|
|
150
|
+
{ err: error, subCommand: subCommandName },
|
|
151
|
+
"Error executing a group sub-command"
|
|
152
|
+
);
|
|
153
|
+
await sock.sendMessage(msg.key.remoteJid, {
|
|
154
|
+
text: `❌ *فشل تنفيذ الأمر الفرعي* (\`${error.name || "Error"}\`): ${
|
|
155
|
+
error.message || "غير معروف"
|
|
156
|
+
}`,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
};
|