@prototypevip/baileys 0.0.7 → 0.0.9

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.
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * baileys-antiban CLI
4
+ * Usage: npx baileys-antiban <command> [options]
5
+ */
6
+ import * as fs from 'fs';
7
+ import { StateManager } from './persist.js';
8
+ import { resolveConfig } from './presets.js';
9
+ import { applyPatch, unpatchFile } from './patch.js';
10
+ const args = process.argv.slice(2);
11
+ const command = args[0];
12
+ function parseArgs(argv) {
13
+ const result = {};
14
+ for (let i = 1; i < argv.length; i++) {
15
+ const arg = argv[i];
16
+ if (arg.startsWith('--')) {
17
+ const key = arg.slice(2);
18
+ const next = argv[i + 1];
19
+ if (next && !next.startsWith('--')) {
20
+ result[key] = next;
21
+ i++;
22
+ }
23
+ else {
24
+ result[key] = true;
25
+ }
26
+ }
27
+ }
28
+ return result;
29
+ }
30
+ function cmdStatus(opts) {
31
+ const statePath = opts['state'];
32
+ let warmupInfo = 'No state file (in-memory mode)';
33
+ let savedAt = 'N/A';
34
+ if (statePath) {
35
+ const mgr = new StateManager(statePath);
36
+ const state = mgr.load();
37
+ if (state) {
38
+ const now = Date.now();
39
+ const dayMs = 86400000;
40
+ const currentDay = Math.floor((now - state.warmup.startedAt) / dayMs);
41
+ warmupInfo = state.warmup.graduated
42
+ ? 'Graduated (warmup complete)'
43
+ : `Day ${currentDay + 1}, sent today: ${state.warmup.dailyCounts[currentDay] || 0}`;
44
+ savedAt = new Date(state.savedAt).toISOString();
45
+ }
46
+ else {
47
+ warmupInfo = 'State file missing or corrupt';
48
+ }
49
+ }
50
+ const output = {
51
+ warmup: warmupInfo,
52
+ savedAt,
53
+ statePath: statePath || null,
54
+ };
55
+ if (opts['json']) {
56
+ console.log(JSON.stringify(output, null, 2));
57
+ }
58
+ else {
59
+ console.log('═══ baileys-antiban status ═══');
60
+ console.log(`Warmup: ${output.warmup}`);
61
+ console.log(`Saved: ${output.savedAt}`);
62
+ console.log(`State: ${output.statePath || 'none'}`);
63
+ }
64
+ }
65
+ function cmdReset(opts) {
66
+ const statePath = opts['state'];
67
+ if (!statePath) {
68
+ console.error('Error: --state <path> required for reset');
69
+ process.exit(1);
70
+ }
71
+ if (!fs.existsSync(statePath)) {
72
+ console.log('State file does not exist — nothing to reset');
73
+ return;
74
+ }
75
+ fs.unlinkSync(statePath);
76
+ console.log(`✅ State file deleted: ${statePath}`);
77
+ }
78
+ function cmdWarmupSimulate(opts) {
79
+ const days = parseInt(opts['simulate'] || '7', 10);
80
+ const presetName = opts['preset'] || 'conservative';
81
+ const cfg = resolveConfig(presetName);
82
+ console.log(`\nWarmup simulation — preset: ${presetName}, days: ${days}`);
83
+ console.log('─'.repeat(50));
84
+ const startDate = new Date();
85
+ for (let day = 0; day < days; day++) {
86
+ const date = new Date(startDate);
87
+ date.setDate(date.getDate() + day);
88
+ const dayName = date.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' });
89
+ const limit = Math.round(cfg.day1Limit * Math.pow(cfg.growthFactor, day));
90
+ const bar = '█'.repeat(Math.min(30, Math.round(limit / 10)));
91
+ console.log(`Day ${String(day + 1).padStart(2)} ${dayName.padEnd(15)} ${String(limit).padStart(5)} msgs/day ${bar}`);
92
+ }
93
+ console.log('─'.repeat(50));
94
+ console.log(`Day ${days + 1}+: graduated (unlimited by warmup)\n`);
95
+ }
96
+ function cmdPatch(opts) {
97
+ const searchPaths = opts['path'] ? [opts['path']] : undefined;
98
+ const preset = opts['preset'] || 'conservative';
99
+ const minDelay = opts['min-delay'] ? parseInt(opts['min-delay']) : 1500;
100
+ const maxDelay = opts['max-delay'] ? parseInt(opts['max-delay']) : 4000;
101
+ const typingIndicator = opts['no-typing'] ? false : true;
102
+ const dryRun = !!opts['dry-run'];
103
+ const force = !!opts['force'];
104
+ const result = applyPatch({
105
+ baileysPaths: searchPaths,
106
+ preset,
107
+ minDelay,
108
+ maxDelay,
109
+ typingIndicator,
110
+ dryRun,
111
+ });
112
+ if (result.alreadyPatched && !force) {
113
+ console.log(result.message);
114
+ process.exit(0);
115
+ }
116
+ if (!result.success) {
117
+ console.error('✗ ' + result.message);
118
+ process.exit(1);
119
+ }
120
+ console.log(result.message);
121
+ }
122
+ function cmdUnpatch(opts) {
123
+ const targetFile = opts['file'];
124
+ if (!targetFile) {
125
+ console.error('Usage: npx baileys-antiban unpatch --file <path-to-patched-file>');
126
+ process.exit(1);
127
+ }
128
+ const result = unpatchFile(targetFile);
129
+ if (!result.success) {
130
+ console.error('✗ ' + result.message);
131
+ process.exit(1);
132
+ }
133
+ console.log(result.message);
134
+ }
135
+ // Main
136
+ const opts = parseArgs(args);
137
+ switch (command) {
138
+ case 'status':
139
+ cmdStatus(opts);
140
+ break;
141
+ case 'reset':
142
+ cmdReset(opts);
143
+ break;
144
+ case 'warmup':
145
+ if (opts['simulate']) {
146
+ cmdWarmupSimulate(opts);
147
+ }
148
+ else {
149
+ console.error('Usage: npx baileys-antiban warmup --simulate <days> [--preset conservative|moderate|aggressive]');
150
+ process.exit(1);
151
+ }
152
+ break;
153
+ case 'patch':
154
+ cmdPatch(opts);
155
+ break;
156
+ case 'unpatch':
157
+ cmdUnpatch(opts);
158
+ break;
159
+ default:
160
+ console.log('baileys-antiban v3.0');
161
+ console.log('');
162
+ console.log('Commands:');
163
+ console.log(' status [--state <path>] [--json] Show warmup and health status');
164
+ console.log(' reset --state <path> Delete state file');
165
+ console.log(' warmup --simulate <days> [--preset] Show warmup schedule');
166
+ console.log(' patch [--path <baileys-dir>] [--preset] [--min-delay] [--max-delay] [--no-typing] [--dry-run]');
167
+ console.log(' unpatch --file <patched-file> Restore file from backup');
168
+ console.log('');
169
+ console.log('Examples:');
170
+ console.log(' npx baileys-antiban status --state ./antiban-state.json');
171
+ console.log(' npx baileys-antiban warmup --simulate 7 --preset moderate');
172
+ console.log(' npx baileys-antiban reset --state ./antiban-state.json');
173
+ console.log(' npx baileys-antiban patch');
174
+ console.log(' npx baileys-antiban patch --path ./node_modules/@openclaw/whatsapp/node_modules/baileys');
175
+ console.log(' npx baileys-antiban patch --preset moderate --min-delay 1000 --max-delay 3000');
176
+ console.log(' npx baileys-antiban unpatch --file ./node_modules/baileys/lib/socket/index.js');
177
+ }
@@ -440,31 +440,36 @@ export const extractGroupMetadata = (result) => {
440
440
  memberAddMode: memberAddMode,
441
441
  participants: getBinaryNodeChildren(group, "participant").map(
442
442
  ({ attrs }) => {
443
- const id = jidNormalizedUser(attrs.jid);
444
- const phone = attrs.phone_number
445
- ? jidNormalizedUser(attrs.phone_number)
446
- : isPnUser(id)
447
- ? id
448
- : undefined;
449
- const lid = attrs.lid
450
- ? jidNormalizedUser(attrs.lid)
451
- : isLidUser(id)
452
- ? id
453
- : undefined;
443
+ // مهم: الـ id هنا لازم يفضل زي ما السيرفر بعته (attrs.jid) بالظبط,
444
+ // من غير ما نلمسه أو نبدله بـ lid/phone بديل. السبب: القيمة دي هي
445
+ // اللي بتتبعت زي ما هي في participantsList لـ getUSyncDevices وفي
446
+ // الـ addressing_mode بتاع الرسالة الصادرة (messages-send.js) —
447
+ // فلو الجروب addressingMode="pn" لازم كل الـ ids تكون بصيغة PN,
448
+ // ولو "lid" لازم تكون كلها LID. أي تبديل هنا بيسبب mismatch بين
449
+ // هوية المشارك والـ addressing_mode المعلن، وده اللي كان بيطلع
450
+ // "بيشفر"/Waiting for this message لمشاركين الـ username بالذات —
451
+ // مش مشكلة سيشن عشان كده افتكاك السيشن وإعادة الدخول ما كانتش بتحل حاجة.
452
+ // عرض الرقم/اليوزرنيم للمستخدم (لو الرقم مخفي) اتحل بالفعل في مكان
453
+ // تاني (resolveDisplayIdentity) وده المكان الصح ليه، مش هنا.
454
+ const id = attrs.jid;
454
455
  return {
455
- // لو مفيش رقم ظاهر (حساب يوزرنيم/خاصية إخفاء الرقم)، استخدم الـ
456
- // LID بدل ما تسيبها undefined — undefined كانت بتوصل لقايمة
457
- // المشاركين اللي بتتبعت لـ getUSyncDevices وتكسر استعلام
458
- // الأجهزة للجروب كله، مع إن الاستعلام المباشر بالـ LID بيرجع
459
- // فورًا لو استخدمناه صح
460
- id: phone || lid,
461
- lid: lid,
462
- jid: phone || lid,
463
- admin: attrs.type || false,
456
+ id,
457
+ // jid هنا نفس قيمة id بالظبط (مش قيمة تانية زي الكود القديم) —
458
+ // مُبقّاها بس عشان بعض أوامر البوت (كتم.js, مجتمع.js, muteSystem.js,
459
+ // context/group.js) بتقرا p.jid كـ fallback؛ ما بتأثرش على
460
+ // participantsList/addressing_mode لأنها زي id بالظبط دلوقتي
461
+ jid: id,
462
+ phoneNumber:
463
+ isLidUser(id) && isPnUser(attrs.phone_number)
464
+ ? attrs.phone_number
465
+ : undefined,
466
+ lid: isPnUser(id) && isLidUser(attrs.lid) ? attrs.lid : undefined,
467
+ username: attrs.participant_username || attrs.username || undefined,
468
+ admin: attrs.type || null,
464
469
  };
465
470
  },
466
471
  ),
467
472
  ephemeralDuration: eph ? +eph : undefined,
468
473
  };
469
474
  return metadata;
470
- };
475
+ };
@@ -1,79 +1,161 @@
1
1
  import { DEFAULT_CONNECTION_CONFIG } from "../Defaults/index.js";
2
2
  import { makeCommunitiesSocket } from "./communities.js";
3
- import { wrapSocket, wrapSocketWithFingerprint } from "../AntiBan/wrapper.js";
4
- import { resolveConfig, PRESETS } from "../AntiBan/presets.js";
5
3
 
6
4
  /**
7
- * Create a WhatsApp socket.
8
- *
9
- * Optional anti-ban protection (integrated from baileys-antiban) can be enabled via config.antiBan:
10
- *
11
- * // Disabled (default) — normal Baileys behavior
12
- * makeWASocket({ auth, ... })
13
- *
14
- * // Enable with safe defaults (conservative preset)
15
- * makeWASocket({ auth, antiBan: true })
16
- *
17
- * // Enable with a named preset: 'conservative' | 'moderate' | 'aggressive' | 'high-volume'
18
- * makeWASocket({ auth, antiBan: 'moderate' })
19
- *
20
- * // Enable with full custom config (see baileys-antiban README)
21
- * makeWASocket({
22
- * auth,
23
- * antiBan: {
24
- * maxPerMinute: 8,
25
- * maxPerHour: 200,
26
- * warmUp: { enabled: true },
27
- * // ... or nested style still accepted with deprecation warning
28
- * }
29
- * })
30
- *
31
- * When enabled, the returned socket has the same API plus:
32
- * sock.antiban — AntiBan instance
33
- * sock.antiban.getStats() — current rate / health / risk stats
5
+ * Optional AntiBan — مقفول افتراضياً.
6
+ * لتفعيله مرّر في إعدادات makeWASocket:
7
+ * makeWASocket({ antiBan: true }) // moderate preset
8
+ * makeWASocket({ antiBan: 'conservative' }) // preset name
9
+ * makeWASocket({ antiBan: { maxPerMinute: 8, ... } }) // config مخصص
10
+ * makeWASocket({ antiBan: false }) // صراحة مقفول (الافتراضي)
34
11
  *
35
- * Extra advanced options (optional):
36
- * antiBanWarmUpState — restore previous warm-up state
37
- * antiBanWrapOptions — extra wrap options (groupOpGuard, legitimacySignals, etc.)
38
- *
39
- * You can also use the low-level helpers directly:
40
- * import { wrapSocket, wrapSocketWithFingerprint, PRESETS } from '@prototypevip/baileys'
12
+ * الحالة الاختيارية:
13
+ * antiBanWarmUpState — حالة الـ warmup المحفوظة
14
+ * antiBanWrapOptions — خيارات إضافية لـ wrapSocket
15
+ */
16
+
17
+ let _wrapSocket = null;
18
+ let _wrapSocketWithFingerprint = null;
19
+ let _PRESETS = {};
20
+ let _antiBanLoadError = null;
21
+
22
+ async function loadAntiBan() {
23
+ if (_wrapSocket) return true;
24
+ if (_antiBanLoadError) return false;
25
+ try {
26
+ const mod = await import("../AntiBan/index.js");
27
+ _wrapSocket = mod.wrapSocket;
28
+ _wrapSocketWithFingerprint = mod.wrapSocketWithFingerprint;
29
+ _PRESETS = mod.PRESETS || {};
30
+ return true;
31
+ } catch (e) {
32
+ // fallback: try wrapper export path
33
+ try {
34
+ const wrap = await import("../AntiBan/wrapper.js");
35
+ const presets = await import("../AntiBan/presets.js").catch(() => ({}));
36
+ _wrapSocket = wrap.wrapSocket;
37
+ _wrapSocketWithFingerprint = wrap.wrapSocketWithFingerprint;
38
+ _PRESETS = presets.PRESETS || {};
39
+ return true;
40
+ } catch (e2) {
41
+ _antiBanLoadError = e2;
42
+ console.warn(
43
+ "[@prototypevip/baileys] AntiBan module failed to load — continuing without it:",
44
+ e2?.message || e2
45
+ );
46
+ return false;
47
+ }
48
+ }
49
+ }
50
+
51
+ // Sync load attempt for ESM top-level (best-effort)
52
+ try {
53
+ const wrap = await import("../AntiBan/wrapper.js");
54
+ _wrapSocket = wrap.wrapSocket;
55
+ _wrapSocketWithFingerprint = wrap.wrapSocketWithFingerprint;
56
+ try {
57
+ const presets = await import("../AntiBan/presets.js");
58
+ _PRESETS = presets.PRESETS || {};
59
+ } catch {}
60
+ } catch (e) {
61
+ _antiBanLoadError = e;
62
+ }
63
+
64
+ /**
65
+ * Create a WhatsApp socket.
66
+ * AntiBan مقفول افتراضياً — فعّله عبر config.antiBan
41
67
  */
42
68
  const makeWASocket = (config = {}) => {
43
- // Extract anti-ban related options so they don't leak into Baileys internal config
44
69
  const {
45
- antiBan,
70
+ antiBan = false, // مقفول افتراضياً
46
71
  antiBanWarmUpState,
47
72
  antiBanWrapOptions,
48
73
  ...restConfig
49
- } = config;
74
+ } = config || {};
50
75
 
51
76
  delete restConfig.userDevicesCache;
52
77
  const newConfig = { ...DEFAULT_CONNECTION_CONFIG, ...restConfig };
53
78
 
54
79
  let sock;
55
80
  const originalGetMessage = newConfig.getMessage;
56
- newConfig.getMessage = async function (key) {
57
- return originalGetMessage.call(sock || this, key);
58
- };
81
+ if (typeof originalGetMessage === "function") {
82
+ newConfig.getMessage = async function (key) {
83
+ return originalGetMessage.call(sock || this, key);
84
+ };
85
+ }
59
86
 
60
87
  sock = makeCommunitiesSocket(newConfig);
61
88
 
62
- // Optional anti-ban wrapping
63
- if (antiBan) {
64
- let antibanConfig;
65
- if (antiBan === true) {
66
- antibanConfig = resolveConfig(undefined); // conservative defaults
67
- } else if (typeof antiBan === "string") {
68
- antibanConfig = resolveConfig(antiBan);
69
- } else {
70
- antibanConfig = antiBan; // user object (supports both flat & nested)
89
+ // ── AntiBan (اختياري) ──
90
+ const enabled =
91
+ antiBan === true ||
92
+ (typeof antiBan === "string" && antiBan.length > 0) ||
93
+ (antiBan && typeof antiBan === "object");
94
+
95
+ if (enabled) {
96
+ if (typeof _wrapSocket !== "function") {
97
+ console.warn(
98
+ "[@prototypevip/baileys] antiBan requested but module not available — socket without protection"
99
+ );
100
+ return sock;
101
+ }
102
+
103
+ const antiBanConfig =
104
+ antiBan === true
105
+ ? "moderate"
106
+ : antiBan; // string preset or object config
107
+
108
+ try {
109
+ sock = _wrapSocket(
110
+ sock,
111
+ antiBanConfig,
112
+ antiBanWarmUpState,
113
+ antiBanWrapOptions || {}
114
+ );
115
+ if (newConfig.logger?.info) {
116
+ newConfig.logger.info(
117
+ { antiBan: typeof antiBanConfig === "string" ? antiBanConfig : "custom" },
118
+ "AntiBan protection enabled"
119
+ );
120
+ }
121
+ } catch (e) {
122
+ console.warn(
123
+ "[@prototypevip/baileys] AntiBan wrap failed — continuing without it:",
124
+ e?.message || e
125
+ );
71
126
  }
72
- sock = wrapSocket(sock, antibanConfig, antiBanWarmUpState, antiBanWrapOptions);
73
127
  }
74
128
 
75
129
  return sock;
76
130
  };
77
131
 
132
+ /** Pass-through stubs when antiban is not loaded; real functions when loaded */
133
+ const wrapSocket = (sock, ...args) =>
134
+ typeof _wrapSocket === "function" ? _wrapSocket(sock, ...args) : sock;
135
+
136
+ const wrapSocketWithFingerprint = (makeSock, opts, wrapOpts) => {
137
+ if (typeof _wrapSocketWithFingerprint === "function") {
138
+ return _wrapSocketWithFingerprint(makeSock, opts, wrapOpts);
139
+ }
140
+ return typeof makeSock === "function" ? makeSock(opts || {}) : makeSock;
141
+ };
142
+
143
+ const PRESETS = new Proxy(
144
+ {},
145
+ {
146
+ get(_, prop) {
147
+ return _PRESETS[prop];
148
+ },
149
+ ownKeys() {
150
+ return Reflect.ownKeys(_PRESETS);
151
+ },
152
+ getOwnPropertyDescriptor(_, prop) {
153
+ if (prop in _PRESETS) {
154
+ return { enumerable: true, configurable: true, value: _PRESETS[prop] };
155
+ }
156
+ },
157
+ }
158
+ );
159
+
78
160
  export default makeWASocket;
79
- export { wrapSocket, wrapSocketWithFingerprint, PRESETS };
161
+ export { makeWASocket, wrapSocket, wrapSocketWithFingerprint, PRESETS };
@@ -845,9 +845,31 @@ export const makeMessagesSocket = (config) => {
845
845
  return {};
846
846
  })(),
847
847
  ]);
848
- const participantsList = groupData?.participants
849
- ? groupData.participants.map((p) => p.id)
850
- : [];
848
+ // نبني قائمة المشاركين + بدائل LID/PN عشان حسابات اليوزرنيم
849
+ // (اللي أحياناً USync بيرجع فاضي لو استعلمنا بصيغة واحدة بس)
850
+ const participantsList = [];
851
+ const seenParticipantUsers = new Set();
852
+ const addParticipantJid = (jid) => {
853
+ if (!jid || typeof jid !== "string") return;
854
+ const normalized = jidNormalizedUser(jid);
855
+ const user = jidDecode(normalized)?.user;
856
+ if (!user || seenParticipantUsers.has(user + ":" + (normalized.split("@")[1] || ""))) {
857
+ // نسمح بنفس الـ user بصيغتين مختلفتين (pn + lid) بس نمنع التكرار الحرفي
858
+ }
859
+ if (participantsList.includes(normalized) || participantsList.includes(jid)) return;
860
+ participantsList.push(normalized);
861
+ if (user) seenParticipantUsers.add(user);
862
+ };
863
+ if (groupData?.participants) {
864
+ for (const p of groupData.participants) {
865
+ addParticipantJid(p.id);
866
+ // بديل LID لو الـ id رقم (PN) — مهم لحسابات اليوزرنيم
867
+ if (p.lid) addParticipantJid(p.lid);
868
+ // بديل الرقم لو الـ id LID
869
+ if (p.phoneNumber) addParticipantJid(p.phoneNumber);
870
+ if (p.jid && p.jid !== p.id) addParticipantJid(p.jid);
871
+ }
872
+ }
851
873
  if (groupData?.ephemeralDuration && groupData.ephemeralDuration > 0) {
852
874
  additionalAttributes = {
853
875
  ...additionalAttributes,
@@ -855,13 +877,54 @@ export const makeMessagesSocket = (config) => {
855
877
  };
856
878
  }
857
879
  if (isStatus && statusJidList) {
858
- participantsList.push(...statusJidList);
880
+ for (const s of statusJidList) addParticipantJid(s);
859
881
  }
860
- const additionalDevices = await getUSyncDevices(
882
+ let additionalDevices = await getUSyncDevices(
861
883
  participantsList,
862
884
  !!useUserDevicesCache,
863
885
  false,
864
886
  );
887
+ // لو في مشاركين معرفين في الميتاداتا ومفيش أي device ليهم في النتيجة
888
+ // (شائع مع username/LID)، نعيد الاستعلام من غير كاش وبمهلة أطول
889
+ // على الـ JIDs الناقصة فقط — من غير ما نأثر على باقي الجروب
890
+ if (groupData?.participants?.length) {
891
+ const coveredUsers = new Set(
892
+ additionalDevices
893
+ .map((d) => d.user || jidDecode(d.jid)?.user)
894
+ .filter(Boolean),
895
+ );
896
+ const missingJids = [];
897
+ for (const p of groupData.participants) {
898
+ const candidates = [p.id, p.lid, p.phoneNumber, p.jid].filter(Boolean);
899
+ const userIds = candidates
900
+ .map((j) => jidDecode(jidNormalizedUser(j))?.user)
901
+ .filter(Boolean);
902
+ if (userIds.some((u) => coveredUsers.has(u))) continue;
903
+ // جرب LID أولاً لو موجود (أحسن لليوزرنيم)، وإلا id الأصلي
904
+ if (p.lid) missingJids.push(jidNormalizedUser(p.lid));
905
+ else if (p.id) missingJids.push(jidNormalizedUser(p.id));
906
+ else if (p.phoneNumber) missingJids.push(jidNormalizedUser(p.phoneNumber));
907
+ }
908
+ if (missingJids.length > 0) {
909
+ logger.debug(
910
+ { missing: missingJids.length, jids: missingJids.slice(0, 5) },
911
+ "retrying USync for participants with no devices (likely username/LID)",
912
+ );
913
+ try {
914
+ const retryDevices = await getUSyncDevices(
915
+ missingJids,
916
+ false, // no cache
917
+ false,
918
+ 8000, // longer timeout
919
+ );
920
+ if (retryDevices?.length) {
921
+ additionalDevices = [...additionalDevices, ...retryDevices];
922
+ }
923
+ } catch (e) {
924
+ logger.warn({ e }, "USync retry for missing participants failed");
925
+ }
926
+ }
927
+ }
865
928
  devices.push(...additionalDevices);
866
929
  if (isGroup) {
867
930
  additionalAttributes = {
@@ -0,0 +1,256 @@
1
+ /**
2
+ * AI Rich Formatter helpers
3
+ * Extracted helpers for parsing AI responses and sending unified rich messages
4
+ * via AIRich (message-builder).
5
+ *
6
+ * Used by AI bot commands (Claude, Gemini, etc.)
7
+ */
8
+
9
+ import { AIRich } from './message-builder.js';
10
+
11
+ /**
12
+ * Parse a markdown table string into rows (string[][])
13
+ * First row is typically the header.
14
+ */
15
+ export function parseMarkdownTable(tableString) {
16
+ const lines = tableString.trim().split('\n');
17
+ const rows = [];
18
+
19
+ for (let line of lines) {
20
+ line = line.trim();
21
+ if (line.replace(/[\s|:-]/g, '') === '') continue;
22
+
23
+ let items = line.split('|').map((c) => c.trim());
24
+ if (items[0] === '') items.shift();
25
+ if (items[items.length - 1] === '') items.pop();
26
+
27
+ if (items.length > 0) rows.push(items);
28
+ }
29
+ return rows;
30
+ }
31
+
32
+ /**
33
+ * Extract code blocks, tables, sources, and special action tags
34
+ * (exec / group / commands / db) from an AI response text.
35
+ */
36
+ export function extractElements(text) {
37
+ const blocks = [];
38
+ const execBlocks = [];
39
+ const groupBlocks = [];
40
+ const commandsBlocks = [];
41
+ const dbBlocks = [];
42
+ const tables = [];
43
+ const sources = [];
44
+ let clean = text;
45
+
46
+ const codeRegex = /```(\w*)\n?([\s\S]*?)```/g;
47
+ let codeMatch;
48
+ while ((codeMatch = codeRegex.exec(text)) !== null) {
49
+ const lang = codeMatch[1].trim().toLowerCase() || 'javascript';
50
+ const code = codeMatch[2].trim();
51
+ if (code) {
52
+ if (lang === 'exec') execBlocks.push(code);
53
+ else if (lang === 'group') groupBlocks.push(code);
54
+ else if (lang === 'commands') commandsBlocks.push(code);
55
+ else if (lang === 'db') dbBlocks.push(code);
56
+ else blocks.push({ lang, code });
57
+ }
58
+ clean = clean.replace(codeMatch[0], '');
59
+ }
60
+
61
+ // Safety net: model wrote [group]/[db]/[commands]/[exec] without real code fences
62
+ const looseTagRegex =
63
+ /\[(group|db|commands|exec)(?:\s+\w+)?\]\s*\n?([\s\S]*?)(?=\n\[(?:group|db|commands|exec)(?:\s+\w+)?\]|$)/gi;
64
+ let looseMatch;
65
+ while ((looseMatch = looseTagRegex.exec(clean)) !== null) {
66
+ const tag = looseMatch[1].toLowerCase();
67
+ const payload = looseMatch[2].trim();
68
+ if (!payload) continue;
69
+ if (tag === 'exec') execBlocks.push(payload);
70
+ else if (tag === 'group') groupBlocks.push(payload);
71
+ else if (tag === 'commands') commandsBlocks.push(payload);
72
+ else if (tag === 'db') dbBlocks.push(payload);
73
+ clean = clean.replace(looseMatch[0], '');
74
+ }
75
+
76
+ const linkRegex = /\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g;
77
+ let linkMatch;
78
+ const seenUrls = new Set();
79
+ while ((linkMatch = linkRegex.exec(clean)) !== null) {
80
+ const label = linkMatch[1].trim();
81
+ const url = linkMatch[2].trim();
82
+ if (!seenUrls.has(url)) {
83
+ seenUrls.add(url);
84
+ sources.push({ label, url });
85
+ }
86
+ }
87
+
88
+ linkRegex.lastIndex = 0;
89
+ clean = clean.replace(linkRegex, '$1');
90
+
91
+ const lines = clean.split('\n');
92
+ let currentTableLines = [];
93
+ let newCleanLines = [];
94
+ let inTable = false;
95
+
96
+ for (let line of lines) {
97
+ if (line.trim().startsWith('|') && line.trim().endsWith('|')) {
98
+ inTable = true;
99
+ currentTableLines.push(line);
100
+ } else {
101
+ if (inTable) {
102
+ const parsed = parseMarkdownTable(currentTableLines.join('\n'));
103
+ if (parsed.length > 0) tables.push(parsed);
104
+ currentTableLines = [];
105
+ inTable = false;
106
+ }
107
+ newCleanLines.push(line);
108
+ }
109
+ }
110
+ if (inTable && currentTableLines.length > 0) {
111
+ const parsed = parseMarkdownTable(currentTableLines.join('\n'));
112
+ if (parsed.length > 0) tables.push(parsed);
113
+ }
114
+
115
+ clean = newCleanLines.join('\n').trim();
116
+ const finalMergedCode = blocks
117
+ .map((b) => b.code)
118
+ .join('\n\n// ──────────────────────────────────────────\n\n');
119
+ const primaryLang = blocks.length > 0 ? blocks[0].lang : 'javascript';
120
+
121
+ return {
122
+ clean,
123
+ finalMergedCode,
124
+ primaryLang,
125
+ hasCode: blocks.length > 0,
126
+ tables,
127
+ sources,
128
+ execBlocks,
129
+ groupBlocks,
130
+ commandsBlocks,
131
+ dbBlocks,
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Build and send a unified rich AI message (text + tables + code + sources + action results).
137
+ * Falls back to plain text if the rich send fails.
138
+ */
139
+ export async function sendUnifiedMetaMessage(
140
+ prototype,
141
+ chatId,
142
+ m,
143
+ {
144
+ textOnly,
145
+ tables,
146
+ hasCode,
147
+ finalMergedCode,
148
+ primaryLang,
149
+ sources,
150
+ sig,
151
+ execResults,
152
+ groupResults,
153
+ commandsResults,
154
+ dbResults,
155
+ }
156
+ ) {
157
+ try {
158
+ const rich = new AIRich(prototype);
159
+
160
+ // 1. Text
161
+ rich.addText((textOnly || '🤖 تم إنشاء الرد بنجاح') + (sig || ''));
162
+
163
+ // 2. Tables
164
+ if (tables && tables.length > 0) {
165
+ for (const tableRows of tables) {
166
+ if (Array.isArray(tableRows) && tableRows.length > 0) {
167
+ rich.addTable(tableRows);
168
+ }
169
+ }
170
+ }
171
+
172
+ // 3. Colored code
173
+ if (hasCode && finalMergedCode) {
174
+ rich.addCode(primaryLang, finalMergedCode);
175
+ }
176
+
177
+ // 4. Exec results (file edit/create/delete)
178
+ if (Array.isArray(execResults) && execResults.length > 0) {
179
+ for (const r of execResults) {
180
+ rich.addText(`${r.ok ? '✅' : '❌'} تنفيذ: ${r.command}`);
181
+ rich.addCode('bash', r.output);
182
+ }
183
+ }
184
+
185
+ // 5. Group action results
186
+ if (Array.isArray(groupResults) && groupResults.length > 0) {
187
+ for (const r of groupResults) {
188
+ rich.addText(`${r.ok ? '✅' : '❌'} ${r.output}`);
189
+ }
190
+ }
191
+
192
+ // 6. Commands list/count results
193
+ if (Array.isArray(commandsResults) && commandsResults.length > 0) {
194
+ for (const r of commandsResults) {
195
+ rich.addText(`${r.ok ? '✅' : '❌'} ${r.output}`);
196
+ }
197
+ }
198
+
199
+ // 7. Database results
200
+ if (Array.isArray(dbResults) && dbResults.length > 0) {
201
+ for (const r of dbResults) {
202
+ rich.addText(`${r.ok ? '✅' : '❌'} نتيجة قاعدة البيانات:`);
203
+ rich.addCode('json', r.output);
204
+ }
205
+ }
206
+
207
+ // 8. Sources ("See details")
208
+ if (Array.isArray(sources) && sources.length > 0) {
209
+ const defaultIcon = 'https://files.catbox.moe/9qffq1.jpg';
210
+ rich.addSource(sources.slice(0, 10).map((s) => [defaultIcon, s.url, s.label]));
211
+ }
212
+
213
+ await rich.send(chatId, { quoted: m });
214
+ } catch (sendErr) {
215
+ console.warn('⚠️ فشل إرسال الرسالة الغنية، إرسال نسخة نصية بديلة:', sendErr.message);
216
+
217
+ let fallbackText = textOnly || '🤖 تم إنشاء الرد بنجاح';
218
+ if (hasCode && finalMergedCode) {
219
+ fallbackText += `\n\n\`\`\`${primaryLang}\n${finalMergedCode}\n\`\`\``;
220
+ }
221
+ if (Array.isArray(execResults) && execResults.length > 0) {
222
+ fallbackText +=
223
+ '\n\n' +
224
+ execResults
225
+ .map(
226
+ (r) =>
227
+ `${r.ok ? '✅' : '❌'} ${r.command}\n\`\`\`bash\n${r.output}\n\`\`\``
228
+ )
229
+ .join('\n\n');
230
+ }
231
+ if (Array.isArray(groupResults) && groupResults.length > 0) {
232
+ fallbackText +=
233
+ '\n\n' + groupResults.map((r) => `${r.ok ? '✅' : '❌'} ${r.output}`).join('\n');
234
+ }
235
+ if (Array.isArray(commandsResults) && commandsResults.length > 0) {
236
+ fallbackText +=
237
+ '\n\n' +
238
+ commandsResults.map((r) => `${r.ok ? '✅' : '❌'} ${r.output}`).join('\n');
239
+ }
240
+ if (Array.isArray(dbResults) && dbResults.length > 0) {
241
+ fallbackText +=
242
+ '\n\n' +
243
+ dbResults
244
+ .map((r) => `${r.ok ? '✅' : '❌'} \`\`\`json\n${r.output}\n\`\`\``)
245
+ .join('\n');
246
+ }
247
+ if (Array.isArray(sources) && sources.length > 0) {
248
+ fallbackText +=
249
+ `\n\n🔗 *مصادر:*\n` +
250
+ sources.slice(0, 10).map((s) => `• ${s.label}: ${s.url}`).join('\n');
251
+ }
252
+ fallbackText += sig || '';
253
+
254
+ await prototype.sendMessage(chatId, { text: fallbackText }, { quoted: m });
255
+ }
256
+ }
@@ -27,4 +27,5 @@ export * from "./use-prototype-auth-state.js";
27
27
  export * from "./sticker-utils.js";
28
28
  export * from "./rich-message-utils.js";
29
29
  export * from "./message-builder.js";
30
+ export * from "./ai-rich-formatter.js";
30
31
  export * from "./constants.js";
package/lib/index.js CHANGED
@@ -9,9 +9,7 @@ export * from "./WAM/index.js";
9
9
  export * from "./WAUSync/index.js";
10
10
  export * from "./Store/cache-factory.js";
11
11
 
12
- // Anti-ban toolkit (optional, integrated from baileys-antiban)
13
- export * from "./AntiBan/index.js";
14
- export { wrapSocket, wrapSocketWithFingerprint } from "./Socket/index.js";
12
+ export { wrapSocket, wrapSocketWithFingerprint, PRESETS } from "./Socket/index.js";
15
13
 
16
14
  export { makeWASocket };
17
15
  export default makeWASocket;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@prototypevip/baileys",
3
- "version": "0.0.7",
4
- "description": "Baileys fork with optional integrated anti-ban protection (rate limiting, warm-up, health monitoring, human-like messaging)",
3
+ "version": "0.0.9",
4
+ "description": "Baileys fork (@prototypevip/baileys) — clean, without anti-ban",
5
5
  "main": "lib/index.js",
6
6
  "type": "module",
7
7
  "scripts": {
@@ -34,11 +34,7 @@
34
34
  "whatsapp-bot",
35
35
  "whatsapp-chat",
36
36
  "whatsapp-group",
37
- "whatsapp-web",
38
- "anti-ban",
39
- "rate-limit",
40
- "warmup",
41
- "baileys-antiban"
37
+ "whatsapp-web"
42
38
  ],
43
39
  "homepage": "https://github.com/GSK-F/baileys",
44
40
  "author": "The Prototype",