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,136 @@
|
|
|
1
|
+
// file: /commands/add.js (Upgraded with Invite Fallback)
|
|
2
|
+
const logger = require("../../utils/logger.cjs");
|
|
3
|
+
const normalizeJid = require("../../utils/normalizeJid.esm.js").default;
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
name: "add",
|
|
7
|
+
description:
|
|
8
|
+
"Adds a member to the group or sends an invite if privacy settings block it.",
|
|
9
|
+
usage: "add [admin] <رقم الموبايل>",
|
|
10
|
+
chat: "group",
|
|
11
|
+
userAdminRequired: true,
|
|
12
|
+
botAdminRequired: true,
|
|
13
|
+
|
|
14
|
+
// The full, correct execute function for commands/add.js
|
|
15
|
+
|
|
16
|
+
async execute(sock, msg, args, body, groupMetadata, confirmationSessions) {
|
|
17
|
+
const groupId = msg.key.remoteJid;
|
|
18
|
+
const senderId = msg.key.participant; // The admin who is running the command
|
|
19
|
+
let targetJid;
|
|
20
|
+
let shouldPromote = false;
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
// --- ✅ THIS IS THE MISSING LOGIC TO DEFINE targetJid ---
|
|
24
|
+
// 1. Identify the Target User
|
|
25
|
+
const mentionedJid =
|
|
26
|
+
msg.message?.extendedTextMessage?.contextInfo?.mentionedJid?.[0];
|
|
27
|
+
let contactJid;
|
|
28
|
+
|
|
29
|
+
// Check for contact card in a quoted message
|
|
30
|
+
const quotedVcard =
|
|
31
|
+
msg.message?.extendedTextMessage?.contextInfo?.quotedMessage
|
|
32
|
+
?.contactMessage?.vcard;
|
|
33
|
+
if (quotedVcard) {
|
|
34
|
+
contactJid =
|
|
35
|
+
quotedVcard.match(/waid=([0-9]+)/)?.[1] + "@s.whatsapp.net";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// If not found, check for contact card in the main message
|
|
39
|
+
if (!contactJid || !contactJid.includes("@")) {
|
|
40
|
+
const mainVcard = msg.message?.contactMessage?.vcard;
|
|
41
|
+
if (mainVcard) {
|
|
42
|
+
contactJid =
|
|
43
|
+
mainVcard.match(/waid=([0-9]+)/)?.[1] + "@s.whatsapp.net";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let numberArg = args.join(" ");
|
|
48
|
+
|
|
49
|
+
if (mentionedJid) {
|
|
50
|
+
targetJid = normalizeJid(mentionedJid);
|
|
51
|
+
} else if (contactJid && contactJid.includes("@")) {
|
|
52
|
+
targetJid = normalizeJid(contactJid);
|
|
53
|
+
} else if (numberArg) {
|
|
54
|
+
if (args[0].toLowerCase() === "admin") {
|
|
55
|
+
shouldPromote = true;
|
|
56
|
+
numberArg = args.slice(1).join(" ");
|
|
57
|
+
}
|
|
58
|
+
const cleanNumber = numberArg.replace(/\D/g, "");
|
|
59
|
+
if (!cleanNumber)
|
|
60
|
+
return await sock.sendMessage(groupId, {
|
|
61
|
+
text: "يرجى تقديم رقم هاتف صالح، أو عمل منشن، أو إرسال جهة اتصال.",
|
|
62
|
+
});
|
|
63
|
+
targetJid = `${cleanNumber}@s.whatsapp.net`;
|
|
64
|
+
} else {
|
|
65
|
+
return await sock.sendMessage(groupId, {
|
|
66
|
+
text: "لإضافة عضو، قم بعمل منشن له، أو أرسل رقمه، أو أرسل جهة الاتصال الخاصة به.",
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
// --- End of Target Identification Logic ---
|
|
70
|
+
|
|
71
|
+
// Pre-check if the user is already in the group
|
|
72
|
+
const isAlreadyMember = groupMetadata.participants.some(
|
|
73
|
+
(p) => normalizeJid(p.id) === normalizeJid(targetJid)
|
|
74
|
+
);
|
|
75
|
+
if (isAlreadyMember) {
|
|
76
|
+
return await sock.sendMessage(groupId, {
|
|
77
|
+
text: `⚠️ العضو @${targetJid.split("@")[0]} موجود بالفعل في الجروب.`,
|
|
78
|
+
mentions: [targetJid],
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
await sock.sendMessage(groupId, {
|
|
83
|
+
text: `جاري محاولة إضافة @${targetJid.split("@")[0]}...`,
|
|
84
|
+
mentions: [targetJid],
|
|
85
|
+
});
|
|
86
|
+
const response = await sock.groupParticipantsUpdate(
|
|
87
|
+
groupId,
|
|
88
|
+
[targetJid],
|
|
89
|
+
"add"
|
|
90
|
+
);
|
|
91
|
+
const status = response[0].status;
|
|
92
|
+
|
|
93
|
+
if (status === "200") {
|
|
94
|
+
let successMsg = `✅ تم إضافة @${targetJid.split("@")[0]} بنجاح.`;
|
|
95
|
+
if (shouldPromote) {
|
|
96
|
+
await sock.groupParticipantsUpdate(groupId, [targetJid], "promote");
|
|
97
|
+
successMsg += `\n👑 وتمت ترقيته إلى مشرف.`;
|
|
98
|
+
}
|
|
99
|
+
await sock.sendMessage(groupId, {
|
|
100
|
+
text: successMsg,
|
|
101
|
+
mentions: [targetJid],
|
|
102
|
+
});
|
|
103
|
+
} else if (status === "403") {
|
|
104
|
+
await sock.sendMessage(groupId, {
|
|
105
|
+
text: `⚠️ لا يمكن إضافة @${
|
|
106
|
+
targetJid.split("@")[0]
|
|
107
|
+
} مباشرة بسبب إعدادات الخصوصية لديه.\n\nهل تود إرسال رابط دعوة له في الخاص؟\nأرسل \`yes\` للتأكيد.`,
|
|
108
|
+
mentions: [targetJid],
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
confirmationSessions.set(senderId, {
|
|
112
|
+
command: "send_invite",
|
|
113
|
+
groupId: groupId,
|
|
114
|
+
targetJid: targetJid,
|
|
115
|
+
adminJid: senderId,
|
|
116
|
+
timestamp: Date.now(),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
setTimeout(() => {
|
|
120
|
+
if (confirmationSessions.has(senderId)) {
|
|
121
|
+
confirmationSessions.delete(senderId);
|
|
122
|
+
}
|
|
123
|
+
}, 30000);
|
|
124
|
+
} else {
|
|
125
|
+
await sock.sendMessage(groupId, {
|
|
126
|
+
text: `⚠️ لم أتمكن من إضافة الرقم. (كود الحالة: ${status})`,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
} catch (error) {
|
|
130
|
+
logger.error({ err: error, command: "add" }, "Error in !add command");
|
|
131
|
+
await sock.sendMessage(groupId, {
|
|
132
|
+
text: "حدث خطأ. تأكد من أنني مشرف وأن الرقم صحيح.",
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// file: /commands/all.js
|
|
2
|
+
const logger = require("../../utils/logger.cjs");
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
name: "all",
|
|
6
|
+
description: "Mentions all members of the group.",
|
|
7
|
+
usage: "all [رسالة التنبيه]",
|
|
8
|
+
chat: "group",
|
|
9
|
+
|
|
10
|
+
async execute(sock, msg, args) {
|
|
11
|
+
try {
|
|
12
|
+
// 1. Get the group's JID (JID = WhatsApp ID)
|
|
13
|
+
const groupId = msg.key.remoteJid;
|
|
14
|
+
|
|
15
|
+
// 2. Get the group's metadata (this includes the list of participants)
|
|
16
|
+
// We fetch it fresh to make sure we have the latest list of members.
|
|
17
|
+
const metadata = await sock.groupMetadata(groupId);
|
|
18
|
+
|
|
19
|
+
// 6. Get the list of all participant JIDs
|
|
20
|
+
const participants = metadata.participants.map((p) => p.id);
|
|
21
|
+
|
|
22
|
+
// 7. Prepare the message text and the mentions array
|
|
23
|
+
// You can customize the message text here
|
|
24
|
+
let text = "📢 | تنبيه للجميع";
|
|
25
|
+
if (args && args.length > 0) {
|
|
26
|
+
text = `*📢 | تنبيه للجميع:*\n${args.join(" ")}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 8. Send the message with mentions
|
|
30
|
+
// The `mentions` property is an array of JIDs that should be mentioned.
|
|
31
|
+
await sock.sendMessage(groupId, {
|
|
32
|
+
text: text,
|
|
33
|
+
mentions: participants,
|
|
34
|
+
});
|
|
35
|
+
} catch (error) {
|
|
36
|
+
logger.error(error, "[Error] in !all command:");
|
|
37
|
+
// Send a reply in case of an error
|
|
38
|
+
await sock.sendMessage(msg.key.remoteJid, {
|
|
39
|
+
text: "حدث خطأ أثناء محاولة عمل منشن للجميع.",
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// commands/antiSpam.js
|
|
2
|
+
const logger = require("../../utils/logger.cjs");
|
|
3
|
+
const { isAdminInGroupSync } = require("../../utils/permissions.cjs");
|
|
4
|
+
|
|
5
|
+
// إعدادات يمكن تعديلها
|
|
6
|
+
const SPAM_MESSAGE_COUNT = 3; // عدد الرسائل المتطابقة لتعتبر سبام
|
|
7
|
+
const SPAM_TIME_WINDOW_SECONDS = 8; // خلال كم ثانية
|
|
8
|
+
const SPAM_WARN_LIMIT = 2; // عدد تحذيرات السبام قبل الطرد
|
|
9
|
+
|
|
10
|
+
// ذاكرة مؤقتة لتخزين رسائل المستخدمين وتحذيراتهم
|
|
11
|
+
const antiSpamCache = new Map();
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* دالة لمعالجة الرسائل والتحقق من التكرار (السبام)
|
|
15
|
+
* @param {import('@whiskeysockets/baileys').WASocket} sock - The socket connection
|
|
16
|
+
* @param {import('@whiskeysockets/baileys').WAMessage} msg - The message object
|
|
17
|
+
*/
|
|
18
|
+
async function handleAntiSpam(sock, msg) {
|
|
19
|
+
const isGroup = msg.key.remoteJid.endsWith("@g.us");
|
|
20
|
+
if (!isGroup) return; // النظام يعمل فقط في المجموعات
|
|
21
|
+
|
|
22
|
+
const senderId = msg.key.participant || msg.key.remoteJid;
|
|
23
|
+
const groupId = msg.key.remoteJid;
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
// لا نطبق النظام على المشرفين لتجنب طردهم بالخطأ.
|
|
27
|
+
// v7/LID: نستخدم الفاحص الموحّد الذي يطابق الـ LID والـ PN معًا بدل
|
|
28
|
+
// المقارنة الخام `p.id === senderId` التي كانت تفشل مع اختلاف الصيغة
|
|
29
|
+
// (وكانت ممكن تطرد مشرف بالغلط).
|
|
30
|
+
const groupMetadata = await sock.groupMetadata(groupId);
|
|
31
|
+
const senderAlt = msg.key.participantAlt || msg.key.participantPn;
|
|
32
|
+
if (
|
|
33
|
+
isAdminInGroupSync(groupMetadata, senderId) ||
|
|
34
|
+
(senderAlt && isAdminInGroupSync(groupMetadata, senderAlt))
|
|
35
|
+
) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const messageContent = JSON.stringify(msg.message); // طريقة دقيقة لمقارنة محتوى الرسالة
|
|
40
|
+
const now = Math.floor(Date.now() / 1000);
|
|
41
|
+
const cacheKey = `${senderId}_${groupId}`;
|
|
42
|
+
|
|
43
|
+
if (!antiSpamCache.has(cacheKey)) {
|
|
44
|
+
antiSpamCache.set(cacheKey, { messages: [], warnCount: 0 });
|
|
45
|
+
}
|
|
46
|
+
const userData = antiSpamCache.get(cacheKey);
|
|
47
|
+
|
|
48
|
+
// 1. فلترة الرسائل القديمة
|
|
49
|
+
userData.messages = userData.messages.filter(
|
|
50
|
+
(m) => now - m.timestamp < SPAM_TIME_WINDOW_SECONDS
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// 2. إضافة الرسالة الجديدة
|
|
54
|
+
userData.messages.push({ content: messageContent, timestamp: now });
|
|
55
|
+
|
|
56
|
+
// 3. التحقق من وجود تكرار
|
|
57
|
+
const repeatedMessages = userData.messages.filter(
|
|
58
|
+
(m) => m.content === messageContent
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
if (repeatedMessages.length >= SPAM_MESSAGE_COUNT) {
|
|
62
|
+
userData.warnCount += 1;
|
|
63
|
+
userData.messages = []; // مسح الرسائل بعد اكتشاف السبام لمنع التحذيرات المتتالية
|
|
64
|
+
|
|
65
|
+
if (userData.warnCount >= SPAM_WARN_LIMIT) {
|
|
66
|
+
logger.info(
|
|
67
|
+
`[AntiSpam] Kicking ${senderId} from ${groupId} for repeated spam.`
|
|
68
|
+
);
|
|
69
|
+
// إعادة تعيين العداد بعد الطرد
|
|
70
|
+
antiSpamCache.delete(cacheKey);
|
|
71
|
+
|
|
72
|
+
await sock.sendMessage(groupId, {
|
|
73
|
+
text: `🚫 تم طرد المستخدم @${
|
|
74
|
+
senderId.split("@")[0]
|
|
75
|
+
} تلقائيًا بسبب الإزعاج.`,
|
|
76
|
+
mentions: [senderId],
|
|
77
|
+
});
|
|
78
|
+
await sock.groupParticipantsUpdate(groupId, [senderId], "remove");
|
|
79
|
+
} else {
|
|
80
|
+
logger.info(
|
|
81
|
+
`[AntiSpam] Warning ${senderId} in ${groupId}. Warn count: ${userData.warnCount}`
|
|
82
|
+
);
|
|
83
|
+
await sock.sendMessage(groupId, {
|
|
84
|
+
text: `⚠️ تحذير للمستخدم @${
|
|
85
|
+
senderId.split("@")[0]
|
|
86
|
+
} بسبب تكرار الرسائل.\nالتحذير رقم: ${userData.warnCount}`,
|
|
87
|
+
mentions: [senderId],
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} catch (error) {
|
|
92
|
+
logger.error({ err: error }, "Error in handleAntiSpam");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = { handleAntiSpam };
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// file: /commands/group/antilink.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
|
+
// --- The Main Command Logic ---
|
|
10
|
+
const command = {
|
|
11
|
+
name: "antilink",
|
|
12
|
+
description: "Advanced control for the anti-link feature.",
|
|
13
|
+
usage: "antilink [status]\nantilink <on|off>\nantilink mode <ALL|WHITELIST|BLACKLIST>\nantilink <allow|disallow> <دومين>",
|
|
14
|
+
chat: "group",
|
|
15
|
+
userAdminRequired: true,
|
|
16
|
+
|
|
17
|
+
async execute(sock, msg, args) {
|
|
18
|
+
const groupId = msg.key.remoteJid;
|
|
19
|
+
const subCommand = args[0] ? args[0].toLowerCase() : "status";
|
|
20
|
+
const settings = getGroupSettings(groupId);
|
|
21
|
+
|
|
22
|
+
// Initialize settings for antilink if it doesn't exist
|
|
23
|
+
if (!settings.antilink) {
|
|
24
|
+
settings.antilink = {
|
|
25
|
+
enabled: false,
|
|
26
|
+
mode: "ALL",
|
|
27
|
+
allowed_domains: [],
|
|
28
|
+
blocked_domains: [],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const antilinkConfig = settings.antilink;
|
|
33
|
+
|
|
34
|
+
switch (subCommand) {
|
|
35
|
+
case "on":
|
|
36
|
+
antilinkConfig.enabled = true;
|
|
37
|
+
await sock.sendMessage(groupId, {
|
|
38
|
+
text: "✅ تم تفعيل نظام منع الروابط.",
|
|
39
|
+
});
|
|
40
|
+
break;
|
|
41
|
+
case "off":
|
|
42
|
+
antilinkConfig.enabled = false;
|
|
43
|
+
await sock.sendMessage(groupId, {
|
|
44
|
+
text: "☑️ تم تعطيل نظام منع الروابط.",
|
|
45
|
+
});
|
|
46
|
+
break;
|
|
47
|
+
case "mode":
|
|
48
|
+
const mode = args[1] ? args[1].toUpperCase() : "";
|
|
49
|
+
if (!["ALL", "WHITELIST", "BLACKLIST"].includes(mode)) {
|
|
50
|
+
return await sock.sendMessage(groupId, {
|
|
51
|
+
text: "الوضع غير صالح. الأوضاع المتاحة: `ALL`, `WHITELIST`, `BLACKLIST`",
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
antilinkConfig.mode = mode;
|
|
55
|
+
await sock.sendMessage(groupId, {
|
|
56
|
+
text: `✅ تم تغيير وضع منع الروابط إلى: ${mode}`,
|
|
57
|
+
});
|
|
58
|
+
break;
|
|
59
|
+
case "allow":
|
|
60
|
+
const domainToAllow = args[1] ? args[1].toLowerCase() : "";
|
|
61
|
+
if (!domainToAllow)
|
|
62
|
+
return await sock.sendMessage(groupId, {
|
|
63
|
+
text: "يرجى تحديد دومين للسماح به.",
|
|
64
|
+
});
|
|
65
|
+
if (!antilinkConfig.allowed_domains.includes(domainToAllow)) {
|
|
66
|
+
antilinkConfig.allowed_domains.push(domainToAllow);
|
|
67
|
+
}
|
|
68
|
+
await sock.sendMessage(groupId, {
|
|
69
|
+
text: `✅ تم إضافة '${domainToAllow}' إلى قائمة الدومينات المسموح بها.`,
|
|
70
|
+
});
|
|
71
|
+
break;
|
|
72
|
+
case "disallow":
|
|
73
|
+
const domainToDisallow = args[1] ? args[1].toLowerCase() : "";
|
|
74
|
+
if (!domainToDisallow)
|
|
75
|
+
return await sock.sendMessage(groupId, {
|
|
76
|
+
text: "يرجى تحديد دومين لإزالته.",
|
|
77
|
+
});
|
|
78
|
+
antilinkConfig.allowed_domains = antilinkConfig.allowed_domains.filter(
|
|
79
|
+
(d) => d !== domainToDisallow
|
|
80
|
+
);
|
|
81
|
+
await sock.sendMessage(groupId, {
|
|
82
|
+
text: `☑️ تم إزالة '${domainToDisallow}' من قائمة الدومينات المسموح بها.`,
|
|
83
|
+
});
|
|
84
|
+
break;
|
|
85
|
+
default:
|
|
86
|
+
// Display current status
|
|
87
|
+
let statusReply = `*حالة نظام منع الروابط:*\n\n`;
|
|
88
|
+
statusReply += `الحالة: ${
|
|
89
|
+
antilinkConfig.enabled ? "مفعل ✅" : "معطل ☑️"
|
|
90
|
+
}\n`;
|
|
91
|
+
statusReply += `الوضع: ${antilinkConfig.mode}\n`;
|
|
92
|
+
statusReply += `الدومينات المسموح بها: ${
|
|
93
|
+
antilinkConfig.allowed_domains.join(", ") || "لا يوجد"
|
|
94
|
+
}\n`;
|
|
95
|
+
await sock.sendMessage(groupId, { text: statusReply });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
saveGroupSettings(groupId, settings);
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// --- The Message Handler Logic ---
|
|
103
|
+
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
|
+
);
|
|
106
|
+
|
|
107
|
+
async function handleAntiLink(sock, msg, _legacyConfig, _normalizeJid) {
|
|
108
|
+
const isGroup = msg.key.remoteJid.endsWith("@g.us");
|
|
109
|
+
if (!isGroup) return;
|
|
110
|
+
|
|
111
|
+
const groupId = msg.key.remoteJid;
|
|
112
|
+
const settings = getGroupSettings(groupId);
|
|
113
|
+
const antilinkConfig = settings.antilink;
|
|
114
|
+
|
|
115
|
+
if (!antilinkConfig || !antilinkConfig.enabled) return;
|
|
116
|
+
|
|
117
|
+
const senderId = msg.key.participant || msg.key.remoteJid;
|
|
118
|
+
|
|
119
|
+
const groupMetadata = await sock.groupMetadata(groupId);
|
|
120
|
+
// Centralized checks — handle LID/PN cross-format and bootstrap roster.
|
|
121
|
+
const isOwner = isOwnerJidSync(senderId);
|
|
122
|
+
const isSenderAdmin = isAdminInGroupSync(groupMetadata, senderId);
|
|
123
|
+
|
|
124
|
+
if (isOwner || isSenderAdmin) return;
|
|
125
|
+
|
|
126
|
+
const body =
|
|
127
|
+
msg.message.conversation || msg.message.extendedTextMessage?.text || "";
|
|
128
|
+
if (!linkRegex.test(body)) return; // No link found
|
|
129
|
+
|
|
130
|
+
// --- Link Found, Apply Rules ---
|
|
131
|
+
const foundLinks = body.match(linkRegex);
|
|
132
|
+
const domain = new URL(
|
|
133
|
+
foundLinks[0].startsWith("http") ? foundLinks[0] : `http://${foundLinks[0]}`
|
|
134
|
+
).hostname.replace("www.", "");
|
|
135
|
+
|
|
136
|
+
let shouldDelete = false;
|
|
137
|
+
|
|
138
|
+
switch (antilinkConfig.mode) {
|
|
139
|
+
case "ALL":
|
|
140
|
+
shouldDelete = true;
|
|
141
|
+
break;
|
|
142
|
+
case "WHITELIST":
|
|
143
|
+
if (!antilinkConfig.allowed_domains.includes(domain)) {
|
|
144
|
+
shouldDelete = true;
|
|
145
|
+
}
|
|
146
|
+
break;
|
|
147
|
+
case "BLACKLIST":
|
|
148
|
+
if (antilinkConfig.blocked_domains.includes(domain)) {
|
|
149
|
+
shouldDelete = true;
|
|
150
|
+
}
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (shouldDelete) {
|
|
155
|
+
logger.info(
|
|
156
|
+
`[Anti-Link] Deleting link from ${senderId} in ${groupId}. Domain: ${domain}`
|
|
157
|
+
);
|
|
158
|
+
await sock.sendMessage(groupId, { delete: msg.key });
|
|
159
|
+
await sock.sendMessage(groupId, {
|
|
160
|
+
text: `ممنوع إرسال الروابط هنا يا @${senderId.split("@")[0]}!`,
|
|
161
|
+
mentions: [senderId],
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Export both the command and the handler
|
|
167
|
+
module.exports = { ...command, handleAntiLink };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// file: /commands/group/approveall.js
|
|
2
|
+
const {
|
|
3
|
+
getGroupSettings,
|
|
4
|
+
saveGroupSettings,
|
|
5
|
+
} = require("../../utils/storage.cjs"); // Note the path is ../../
|
|
6
|
+
const logger = require("../../utils/logger.cjs");
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
name: "approveall",
|
|
10
|
+
description: "Manages the auto-approve feature for join requests.",
|
|
11
|
+
usage: "approveall <on|off>",
|
|
12
|
+
chat: "group",
|
|
13
|
+
userAdminRequired: true,
|
|
14
|
+
botAdminRequired: true, // The bot must be an admin to approve requests
|
|
15
|
+
|
|
16
|
+
async execute(sock, msg, args) {
|
|
17
|
+
const groupId = msg.key.remoteJid;
|
|
18
|
+
const option = args[0]?.toLowerCase();
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const settings = getGroupSettings(groupId);
|
|
22
|
+
if (!settings.join_requests) {
|
|
23
|
+
settings.join_requests = { auto_approve_enabled: false };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (option === "on") {
|
|
27
|
+
settings.join_requests.auto_approve_enabled = true;
|
|
28
|
+
saveGroupSettings(groupId, settings);
|
|
29
|
+
return await sock.sendMessage(groupId, {
|
|
30
|
+
text: "✅ تم تفعيل نظام الموافقة التلقائية على طلبات الانضمام.",
|
|
31
|
+
});
|
|
32
|
+
} else if (option === "off") {
|
|
33
|
+
settings.join_requests.auto_approve_enabled = false;
|
|
34
|
+
saveGroupSettings(groupId, settings);
|
|
35
|
+
return await sock.sendMessage(groupId, {
|
|
36
|
+
text: "☑️ تم تعطيل نظام الموافقة التلقائية.",
|
|
37
|
+
});
|
|
38
|
+
} else {
|
|
39
|
+
const status = settings.join_requests.auto_approve_enabled
|
|
40
|
+
? "مفعل ✅"
|
|
41
|
+
: "معطل ☑️";
|
|
42
|
+
return await sock.sendMessage(groupId, {
|
|
43
|
+
text: `حالة الموافقة التلقائية: ${status}.\nاستخدم 'on' أو 'off' للتغيير.`,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
} catch (error) {
|
|
47
|
+
logger.error(
|
|
48
|
+
{ err: error, command: "approveall" },
|
|
49
|
+
"Error in !group approveall command"
|
|
50
|
+
);
|
|
51
|
+
await sock.sendMessage(groupId, { text: "حدث خطأ." });
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// file: /commands/clearwarns.js
|
|
2
|
+
const { clearUserWarnings } = require("../../utils/storage.cjs");
|
|
3
|
+
const logger = require("../../utils/logger.cjs");
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
name: "clearwarns",
|
|
7
|
+
description: "Clears all warnings for a specific user.",
|
|
8
|
+
usage: "clearwarns @عضو",
|
|
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
|
+
// Call the new function from storage.js to delete the record from the database
|
|
25
|
+
clearUserWarnings(groupId, mentionedJid);
|
|
26
|
+
|
|
27
|
+
const replyText = `✅ تم مسح جميع تحذيرات العضو @${
|
|
28
|
+
mentionedJid.split("@")[0]
|
|
29
|
+
} بنجاح.`;
|
|
30
|
+
await sock.sendMessage(groupId, {
|
|
31
|
+
text: replyText,
|
|
32
|
+
mentions: [mentionedJid],
|
|
33
|
+
});
|
|
34
|
+
} catch (error) {
|
|
35
|
+
logger.error({ err: error }, "Error in !clearwarns command");
|
|
36
|
+
await sock.sendMessage(msg.key.remoteJid, {
|
|
37
|
+
text: "حدث خطأ أثناء مسح التحذيرات.",
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// file: /commands/deletenote.js
|
|
2
|
+
const { deleteNote } = require("../../utils/storage.cjs");
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
name: "deletenote",
|
|
6
|
+
description: "Deletes a saved note.",
|
|
7
|
+
usage: "deletenote #الكلمة-المفتاحية",
|
|
8
|
+
chat: "all",
|
|
9
|
+
userAdminRequired: true,
|
|
10
|
+
|
|
11
|
+
async execute(sock, msg, args) {
|
|
12
|
+
const groupId = msg.key.remoteJid;
|
|
13
|
+
const keywordArg = args[0];
|
|
14
|
+
|
|
15
|
+
if (!keywordArg || !keywordArg.startsWith("#")) {
|
|
16
|
+
return await sock.sendMessage(groupId, {
|
|
17
|
+
text: "صيغة غير صحيحة. استخدم: `!deletenote #keyword`",
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const keyword = keywordArg.slice(1).toLowerCase();
|
|
22
|
+
|
|
23
|
+
if (deleteNote(groupId, keyword)) {
|
|
24
|
+
await sock.sendMessage(groupId, {
|
|
25
|
+
text: `☑️ تم حذف الملاحظة \`#${keyword}\` بنجاح.`,
|
|
26
|
+
});
|
|
27
|
+
} else {
|
|
28
|
+
await sock.sendMessage(groupId, {
|
|
29
|
+
text: `⚠️ لم يتم العثور على ملاحظة بالكلمة المفتاحية: \`${keywordArg}\``,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// file: /commands/demote.js
|
|
2
|
+
const logger = require("../../utils/logger.cjs");
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
name: "demote",
|
|
6
|
+
description: "Demotes an admin to a regular member.",
|
|
7
|
+
usage: "demote @عضو",
|
|
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 NOT an admin?
|
|
27
|
+
const targetUser = groupMetadata.participants.find(
|
|
28
|
+
(p) => p.id === targetJid
|
|
29
|
+
);
|
|
30
|
+
if (!targetUser || !targetUser.admin) {
|
|
31
|
+
return await sock.sendMessage(groupId, {
|
|
32
|
+
text: `⚠️ العضو @${targetJid.split("@")[0]} ليس مشرفًا أصلاً.`,
|
|
33
|
+
mentions: [targetJid],
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Safety check: Cannot demote the group creator
|
|
38
|
+
if (targetUser.admin === "superadmin") {
|
|
39
|
+
return await sock.sendMessage(groupId, {
|
|
40
|
+
text: "لا يمكن عزل منشئ الجروب.",
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
await sock.groupParticipantsUpdate(
|
|
46
|
+
groupId,
|
|
47
|
+
[targetJid],
|
|
48
|
+
"demote" // The action is 'demote'
|
|
49
|
+
);
|
|
50
|
+
await sock.sendMessage(groupId, {
|
|
51
|
+
text: `👤 تم عزل @${targetJid.split("@")[0]} من الإشراف.`,
|
|
52
|
+
mentions: [targetJid],
|
|
53
|
+
});
|
|
54
|
+
} catch (error) {
|
|
55
|
+
logger.error(
|
|
56
|
+
{ err: error, command: "demote" },
|
|
57
|
+
"Error in !demote command"
|
|
58
|
+
);
|
|
59
|
+
await sock.sendMessage(groupId, { text: "حدث خطأ أثناء محاولة العزل." });
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
};
|