@prototypevip/baileys 0.1.3 → 0.1.5

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,610 @@
1
+ /**
2
+ * Prototype VIP Baileys Helpers (0.1.5)
3
+ *
4
+ * طبقة helpers سهلة فوق المسارات الأصلية الموجودة في المكتبة
5
+ * (sock.sendMessage → nativeFlow / album / code / event / product ...).
6
+ * مفيش هنا أي بروتوكول جديد: كل دالة بتفوّض لمسار مجرَّب جوه المكتبة.
7
+ */
8
+
9
+ import { proto } from "../../WAProto/index.js";
10
+ import { generateWAMessageFromContent } from "./messages.js";
11
+ // الكلاسات دي مصدَّرة أصلاً من message-builder.js عن طريق Utils/index.js
12
+ import { Button, ButtonV2, Carousel, AIRich, Toolkit } from "./message-builder.js";
13
+
14
+ /* ───────────────────────────── internals ───────────────────────────── */
15
+
16
+ const MEDIA_KEYS = ["image", "video", "document"];
17
+
18
+ const hasMedia = (obj) => MEDIA_KEYS.some((k) => obj?.[k]);
19
+
20
+ const isPrivateJid = (jid) =>
21
+ typeof jid === "string" && (jid.endsWith("@s.whatsapp.net") || jid.endsWith("@lid"));
22
+
23
+ const assertSock = (sock, method = "sendMessage") => {
24
+ if (!sock || typeof sock[method] !== "function") {
25
+ throw new Error(`Invalid socket: sock.${method} is not a function`);
26
+ }
27
+ };
28
+
29
+ const defaultRowId = (label) => `#${String(label || "row").trim().replace(/\s+/g, "_")}`;
30
+
31
+ /**
32
+ * يحوّل أي شكل مدعوم لزر إلى الصيغة اللي بتفهمها المكتبة في { nativeFlow: [...] }:
33
+ * { text, id } | { text, url } | { text, copy } | { text, call } | { text, sections }
34
+ * ويقبل كمان الصيغ البديلة: rowId / display_text / cta_url / copy_code / phone_number
35
+ * والصيغة الخام { name, buttonParamsJson } بتعدّي زي ما هي.
36
+ */
37
+ function normalizeButton(btn) {
38
+ if (!btn || typeof btn !== "object") {
39
+ throw new TypeError("Each button must be an object");
40
+ }
41
+ if (btn.name && btn.buttonParamsJson) return btn; // native format
42
+
43
+ const text = btn.text ?? btn.display_text ?? btn.buttonText ?? btn.title;
44
+ const out = { text };
45
+ if (btn.icon) out.icon = btn.icon;
46
+
47
+ const id = btn.id ?? btn.rowId;
48
+ const url = btn.url ?? btn.cta_url;
49
+ const copy = btn.copy ?? btn.copy_code;
50
+ const call = btn.call ?? btn.phone_number;
51
+
52
+ if (id) out.id = id;
53
+ else if (copy) out.copy = String(copy);
54
+ else if (url) {
55
+ out.url = url;
56
+ if (btn.useWebview) out.useWebview = btn.useWebview;
57
+ } else if (call) out.call = String(call);
58
+ else if (Array.isArray(btn.sections)) out.sections = btn.sections;
59
+ else out.id = defaultRowId(text || "btn"); // default: quick_reply
60
+ return out;
61
+ }
62
+
63
+ /* ───────────────────────────── buttons ───────────────────────────── */
64
+
65
+ /**
66
+ * يبني أزرار Native Flow بالصيغة الخام ({ name, buttonParamsJson })
67
+ * لو عاوز تبعتها مباشرة في interactiveMessage.nativeFlowMessage.buttons.
68
+ */
69
+ export function buildNativeFlowButtons(buttons = []) {
70
+ return buttons.map((raw) => {
71
+ const btn = normalizeButton(raw);
72
+ if (btn.name && btn.buttonParamsJson) return btn;
73
+ const label = btn.text;
74
+ if (btn.id) {
75
+ return { name: "quick_reply", buttonParamsJson: JSON.stringify({ display_text: label || "Button", id: btn.id }) };
76
+ }
77
+ if (btn.copy) {
78
+ return { name: "cta_copy", buttonParamsJson: JSON.stringify({ display_text: label || "Copy", copy_code: btn.copy }) };
79
+ }
80
+ if (btn.url) {
81
+ return {
82
+ name: "cta_url",
83
+ buttonParamsJson: JSON.stringify({ display_text: label || "Open", url: btn.url, merchant_url: btn.url }),
84
+ };
85
+ }
86
+ if (btn.call) {
87
+ return { name: "cta_call", buttonParamsJson: JSON.stringify({ display_text: label || "Call", phone_number: btn.call }) };
88
+ }
89
+ return { name: "single_select", buttonParamsJson: JSON.stringify({ title: label || "Select", sections: btn.sections }) };
90
+ });
91
+ }
92
+
93
+ /**
94
+ * أزرار Native Flow (تفوّض لـ sock.sendMessage → { nativeFlow })
95
+ *
96
+ * await sendButtons(sock, jid, {
97
+ * text: "اختار", footer: "Prototype VIP",
98
+ * buttons: [{ text:"Menu", id:"#menu" }, { text:"Site", url:"https://..." },
99
+ * { text:"Copy", copy:"CODE" }, { text:"Call", call:"2012..." }],
100
+ * // اختياري: image | video | document (بيتحوّل لـ header)، title، subtitle، ai: true
101
+ * }, { quoted: m })
102
+ */
103
+ export async function sendButtons(sock, jid, content = {}, options = {}) {
104
+ assertSock(sock);
105
+ const { text, caption, buttons, ai, ...rest } = content;
106
+
107
+ if (!Array.isArray(buttons) || buttons.length === 0) {
108
+ throw new Error("sendButtons requires a non-empty buttons array");
109
+ }
110
+
111
+ const payload = { ...rest, nativeFlow: buttons.map(normalizeButton) };
112
+
113
+ // مسار nativeFlow في المكتبة بيستخدم text للنص العادي و caption مع الميديا
114
+ if (hasMedia(rest)) {
115
+ payload.caption = caption ?? text ?? "";
116
+ } else {
117
+ payload.text = text ?? caption ?? "";
118
+ }
119
+
120
+ // أيقونة الـ AI: sendMessage بيقبلها كـ content.ai وفي الشات الخاص بس
121
+ const wantAi = ai ?? options.ai ?? options.AI;
122
+ if (wantAi && isPrivateJid(jid)) payload.ai = true;
123
+
124
+ const { ai: _a, AI: _b, ...sendOptions } = options;
125
+ return sock.sendMessage(jid, payload, sendOptions);
126
+ }
127
+
128
+ /**
129
+ * قائمة اختيار. الافتراضي: single_select داخل Native Flow (بيشتغل على الأجهزة الحديثة).
130
+ * مرّر legacy: true لو عاوز listMessage الكلاسيكي.
131
+ */
132
+ export async function sendList(sock, jid, content = {}, options = {}) {
133
+ assertSock(sock);
134
+ const { text = "", footer, title = "", buttonText = "Select", sections, legacy = false, ...rest } = content;
135
+
136
+ if (!Array.isArray(sections) || sections.length === 0) {
137
+ throw new Error("sendList requires a non-empty sections array");
138
+ }
139
+
140
+ if (legacy) {
141
+ const msg = generateWAMessageFromContent(
142
+ jid,
143
+ {
144
+ listMessage: {
145
+ title,
146
+ description: text,
147
+ buttonText,
148
+ listType: proto.Message.ListMessage.ListType.SINGLE_SELECT,
149
+ footerText: footer || undefined,
150
+ sections: sections.map((sec) => ({
151
+ title: sec.title || "",
152
+ rows: (sec.rows || []).map((row) => ({
153
+ title: row.title || row.text || "",
154
+ description: row.description || "",
155
+ rowId: row.rowId || row.id || defaultRowId(row.title || row.text),
156
+ })),
157
+ })),
158
+ },
159
+ },
160
+ { userJid: sock.user?.id, quoted: options.quoted, messageId: options.messageId }
161
+ );
162
+ const additionalNodes = isPrivateJid(jid)
163
+ ? [{ tag: "biz", attrs: {}, content: [{ tag: "list", attrs: { v: "2", type: "product_list" } }] }]
164
+ : [];
165
+ await sock.relayMessage(jid, msg.message, {
166
+ messageId: msg.key.id,
167
+ additionalNodes: [...(options.additionalNodes || []), ...additionalNodes],
168
+ useCachedGroupMetadata: options.useCachedGroupMetadata,
169
+ statusJidList: options.statusJidList,
170
+ });
171
+ return msg;
172
+ }
173
+
174
+ const nfSections = sections.map((sec) => ({
175
+ title: sec.title || "",
176
+ ...(sec.highlight_label ? { highlight_label: sec.highlight_label } : {}),
177
+ rows: (sec.rows || []).map((row) => ({
178
+ header: row.header || "",
179
+ title: row.title || row.text || "",
180
+ description: row.description || "",
181
+ id: row.id || row.rowId || defaultRowId(row.title || row.text),
182
+ })),
183
+ }));
184
+
185
+ return sendButtons(
186
+ sock,
187
+ jid,
188
+ { ...rest, text, footer, title, buttons: [{ text: buttonText, sections: nfSections }] },
189
+ options
190
+ );
191
+ }
192
+
193
+ /** اختصار: أزرار رد سريع من نصوص بس */
194
+ export async function sendQuickButtons(sock, jid, text, buttonLabels = [], options = {}) {
195
+ const buttons = buttonLabels.map((label, i) => ({
196
+ text: label,
197
+ id: `#btn_${i + 1}`,
198
+ }));
199
+ return sendButtons(sock, jid, { text, buttons }, options);
200
+ }
201
+
202
+ /** رسالة بأيقونة AI (شات خاص فقط — في الجروبات بتتبعت عادي من غير الأيقونة) */
203
+ export async function sendAIMessage(sock, jid, content, options = {}) {
204
+ const base = typeof content === "string" ? { text: content } : { ...content };
205
+ if (Array.isArray(base.buttons) && base.buttons.length) {
206
+ return sendButtons(sock, jid, { ...base, ai: true }, options);
207
+ }
208
+ assertSock(sock);
209
+ const { ai: _a, AI: _b, ...sendOptions } = options;
210
+ return sock.sendMessage(jid, isPrivateJid(jid) ? { ...base, ai: true } : base, sendOptions);
211
+ }
212
+
213
+ /* ───────────────────────────── album ───────────────────────────── */
214
+
215
+ /**
216
+ * ألبوم صور/فيديوهات. المكتبة بتتوقع album كمصفوفة، وبتقرأ expectedImageCount/
217
+ * expectedVideoCount من نفس المصفوفة، فبنضيفهم عليها.
218
+ *
219
+ * await sendAlbum(sock, jid, [{ image:{url}, caption:"1" }, { video:{url} }], { quoted: m, delayMs: 1500 })
220
+ */
221
+ export async function sendAlbum(sock, jid, mediaItems = [], options = {}) {
222
+ assertSock(sock);
223
+ if (!Array.isArray(mediaItems) || mediaItems.length === 0) {
224
+ throw new Error("sendAlbum requires a non-empty array of media items");
225
+ }
226
+ for (const [i, item] of mediaItems.entries()) {
227
+ if (!item || (!item.image && !item.video)) {
228
+ throw new Error(`sendAlbum: item [${i}] must contain image or video`);
229
+ }
230
+ }
231
+ const album = Object.assign([...mediaItems], {
232
+ expectedImageCount: mediaItems.filter((i) => i.image).length,
233
+ expectedVideoCount: mediaItems.filter((i) => i.video).length,
234
+ });
235
+ return sock.sendMessage(jid, { album }, options);
236
+ }
237
+
238
+ /* ───────────────────────────── carousel ───────────────────────────── */
239
+
240
+ /**
241
+ * Carousel حقيقي عن طريق كلاسات المكتبة (Button.toCard + Carousel).
242
+ * كل كارت لازم يكون له image أو video.
243
+ *
244
+ * await sendCarousel(sock, jid, {
245
+ * text: "اختار منتج", footer: "Prototype VIP",
246
+ * cards: [{ title:"منتج 1", body:"وصف", image:"https://..." (أو Buffer), buttons:[{ text:"اشتري", id:"#buy1" }] }]
247
+ * })
248
+ */
249
+ export async function sendCarousel(sock, jid, content = {}, options = {}) {
250
+ assertSock(sock, "relayMessage");
251
+ const { text = "", footer = "", cards } = content;
252
+ if (!Array.isArray(cards) || cards.length === 0) {
253
+ throw new Error("sendCarousel requires a non-empty cards array");
254
+ }
255
+
256
+ const carousel = new Carousel(sock).setBody(text);
257
+ if (footer) carousel.setFooter(footer);
258
+
259
+ for (const [i, card] of cards.entries()) {
260
+ const media = card.image ?? card.video;
261
+ if (!media) throw new Error(`sendCarousel: card [${i}] must include image or video`);
262
+
263
+ const b = new Button(sock);
264
+ if (card.title) b.setTitle(card.title);
265
+ if (card.subtitle) b.setSubtitle(card.subtitle);
266
+ b.setBody(card.body || card.caption || card.title || " ");
267
+ if (card.footer) b.setFooter(card.footer);
268
+
269
+ const toData = (m, kind) => (Buffer.isBuffer(m) ? { [kind]: m } : typeof m === "string" ? { [kind]: { url: m } } : { [kind]: m });
270
+ b.setMedia(card.image ? toData(card.image, "image") : toData(card.video, "video"));
271
+
272
+ for (const nb of buildNativeFlowButtons(card.buttons || [])) {
273
+ b.addButton(nb.name, nb.buttonParamsJson);
274
+ }
275
+ carousel.addCard(await b.toCard());
276
+ }
277
+
278
+ await carousel.send(jid, { userJid: sock.user?.id, ...options });
279
+ }
280
+
281
+ /* ───────────────────────────── rich (AI) ───────────────────────────── */
282
+
283
+ /**
284
+ * ⚠️ مهم: واتساب/المكتبة مفيهاش render حقيقي لـ HTML داخل الفقاعة.
285
+ * اللي بيحصل فعلياً: بتتبعت رسالة Rich Response (شكل Meta AI) والـ HTML
286
+ * بيظهر كـ code block ملوّن (syntax highlight) — ده المسار الوحيد المدعوم
287
+ * والمجرَّب في المكتبة (prepareRichResponseMessage).
288
+ */
289
+ export async function sendRichHtml(sock, jid, content, options = {}) {
290
+ assertSock(sock);
291
+ const { title, html = "", summary, footer } = typeof content === "string" ? { html: content } : content || {};
292
+ if (!html) throw new Error("sendRichHtml requires html");
293
+
294
+ return sock.sendMessage(
295
+ jid,
296
+ {
297
+ ...(title ? { headerText: title } : {}),
298
+ ...(summary ? { contentText: summary } : {}),
299
+ code: html,
300
+ language: "html",
301
+ ...(footer ? { footerText: footer } : {}),
302
+ },
303
+ options
304
+ );
305
+ }
306
+
307
+ /** alias قديم — نفس sendRichHtml (اتشال منه المسار الوهمي AIRich.html) */
308
+ export async function sendRichHtmlAdvanced(sock, jid, content, options = {}) {
309
+ return sendRichHtml(sock, jid, content, options);
310
+ }
311
+
312
+ /** رسالة Rich بكود ملوّن بأي لغة */
313
+ export async function sendRichCode(sock, jid, { code, language = "javascript", title, summary, footer } = {}, options = {}) {
314
+ assertSock(sock);
315
+ if (!code) throw new Error("sendRichCode requires code");
316
+ return sock.sendMessage(
317
+ jid,
318
+ {
319
+ ...(title ? { headerText: title } : {}),
320
+ ...(summary ? { contentText: summary } : {}),
321
+ code,
322
+ language,
323
+ ...(footer ? { footerText: footer } : {}),
324
+ },
325
+ options
326
+ );
327
+ }
328
+
329
+ /* ───────────────────────────── misc senders ───────────────────────────── */
330
+
331
+ export async function sendViewOnce(sock, jid, content, options = {}) {
332
+ assertSock(sock);
333
+ const payload = typeof content === "string" ? { text: content } : { ...content };
334
+ return sock.sendMessage(jid, { ...payload, viewOnce: true }, options);
335
+ }
336
+
337
+ export async function sendReaction(sock, jid, key, emoji = "👍") {
338
+ assertSock(sock);
339
+ return sock.sendMessage(jid, { react: { text: emoji, key } });
340
+ }
341
+
342
+ export async function sendPoll(sock, jid, name, values = [], options = {}) {
343
+ assertSock(sock);
344
+ const { selectableCount = 1, ...sendOptions } = options;
345
+ return sock.sendMessage(jid, { poll: { name, values, selectableCount } }, sendOptions);
346
+ }
347
+
348
+ /** منشن لكل أعضاء الجروب (بيرمي خطأ لو معرفش يجيب الأعضاء بدل ما يبعت من غير منشن) */
349
+ export async function sendMentionAll(sock, jid, text, options = {}) {
350
+ assertSock(sock);
351
+ if (!jid.endsWith("@g.us")) throw new Error("sendMentionAll only works in groups");
352
+ const meta = await sock.groupMetadata(jid);
353
+ const mentions = (meta.participants || []).map((p) => p.id).filter(Boolean);
354
+ return sock.sendMessage(jid, { text, mentions }, options);
355
+ }
356
+
357
+ /** options: { name, address, ...sendOptions } */
358
+ export async function sendLocation(sock, jid, degreesLatitude, degreesLongitude, options = {}) {
359
+ assertSock(sock);
360
+ const { name = "", address = "", ...sendOptions } = options;
361
+ return sock.sendMessage(
362
+ jid,
363
+ { location: { degreesLatitude, degreesLongitude, name, address } },
364
+ sendOptions
365
+ );
366
+ }
367
+
368
+ /** contacts: { fullName|name, phone|number, organization|org } أو مصفوفة منهم */
369
+ export async function sendContact(sock, jid, contacts, options = {}) {
370
+ assertSock(sock);
371
+ const clean = (s) => String(s ?? "").replace(/[\r\n;,\\]/g, " ").trim();
372
+ const list = (Array.isArray(contacts) ? contacts : [contacts]).map((c) => {
373
+ const phone = String(c.phone || c.number || "").replace(/\D/g, "");
374
+ if (!phone) throw new Error("sendContact: phone is required");
375
+ const name = clean(c.fullName || c.name || phone);
376
+ const org = clean(c.organization || c.org);
377
+ const vcard =
378
+ `BEGIN:VCARD\nVERSION:3.0\nFN:${name}\n` +
379
+ (org ? `ORG:${org}\n` : "") +
380
+ `TEL;type=CELL;type=VOICE;waid=${phone}:+${phone}\nEND:VCARD`;
381
+ return { displayName: name, vcard };
382
+ });
383
+
384
+ return sock.sendMessage(
385
+ jid,
386
+ {
387
+ contacts: {
388
+ displayName: list.length === 1 ? list[0].displayName : `${list.length} contacts`,
389
+ contacts: list,
390
+ },
391
+ },
392
+ options
393
+ );
394
+ }
395
+
396
+ /** product: { title, description, price, currencyCode, productId, retailerId, url, image|thumbnail } (الصورة مطلوبة) */
397
+ export async function sendProduct(sock, jid, product = {}, options = {}) {
398
+ assertSock(sock);
399
+ const productImage = product.image || product.thumbnail || product.productImage;
400
+ if (!productImage) throw new Error("sendProduct requires an image (image | thumbnail | productImage)");
401
+ const image = typeof productImage === "string" ? { url: productImage } : productImage;
402
+
403
+ return sock.sendMessage(
404
+ jid,
405
+ {
406
+ product: {
407
+ productId: String(product.productId || "1"),
408
+ title: product.title || "Product",
409
+ description: product.description || "",
410
+ currencyCode: product.currencyCode || "USD",
411
+ priceAmount1000: product.price ? Math.round(Number(product.price) * 1000) : 0,
412
+ retailerId: product.retailerId || "prototypevip",
413
+ url: product.url || "",
414
+ productImageCount: 1,
415
+ productImage: image,
416
+ },
417
+ businessOwnerJid: product.businessOwnerJid || sock.user?.id,
418
+ body: product.body || product.description || "",
419
+ footer: product.footer || "",
420
+ },
421
+ options
422
+ );
423
+ }
424
+
425
+ /** sticker: Buffer | { url } | رابط/مسار نصي */
426
+ export async function sendSticker(sock, jid, sticker, options = {}) {
427
+ assertSock(sock);
428
+ const media = typeof sticker === "string" ? { url: sticker } : sticker;
429
+ return sock.sendMessage(jid, { sticker: media }, options);
430
+ }
431
+
432
+ /* ───────────────────────────── calls ───────────────────────────── */
433
+
434
+ /**
435
+ * مكالمة مجدولة (Event بنوع call). مفيش outbound WebRTC call حقيقي في Baileys.
436
+ * callType: "audio" | "video"
437
+ */
438
+ export async function sendScheduledCall(sock, jid, content = {}, options = {}) {
439
+ assertSock(sock);
440
+ const {
441
+ name = "Call",
442
+ description = "",
443
+ startDate = new Date(Date.now() + 5 * 60 * 1000),
444
+ endDate,
445
+ callType = "audio",
446
+ extraGuestsAllowed = true,
447
+ location,
448
+ } = content;
449
+
450
+ const toDate = (d) => (d instanceof Date ? d : new Date(d));
451
+ const start = toDate(startDate);
452
+ if (Number.isNaN(start.getTime())) throw new Error("sendScheduledCall: invalid startDate");
453
+ const end = endDate ? toDate(endDate) : new Date(start.getTime() + 60 * 60 * 1000);
454
+ if (Number.isNaN(end.getTime())) throw new Error("sendScheduledCall: invalid endDate");
455
+
456
+ return sock.sendMessage(
457
+ jid,
458
+ {
459
+ event: {
460
+ name,
461
+ description,
462
+ startDate: start,
463
+ endDate: end,
464
+ call: callType === "video" ? "video" : "audio",
465
+ isScheduleCall: true,
466
+ extraGuestsAllowed,
467
+ location,
468
+ },
469
+ },
470
+ options
471
+ );
472
+ }
473
+
474
+ export async function sendCallInvite(sock, jid, content = {}, options = {}) {
475
+ const {
476
+ name = "مكالمة واردة",
477
+ description = "اضغط للانضمام للمكالمة",
478
+ callType = "audio",
479
+ minutesFromNow = 2,
480
+ } = typeof content === "string" ? { name: content } : content;
481
+
482
+ return sendScheduledCall(
483
+ sock,
484
+ jid,
485
+ {
486
+ name,
487
+ description,
488
+ startDate: new Date(Date.now() + minutesFromNow * 60 * 1000),
489
+ callType,
490
+ extraGuestsAllowed: true,
491
+ },
492
+ options
493
+ );
494
+ }
495
+
496
+ /* ───────────────────────────── JID / LID ───────────────────────────── */
497
+
498
+ export function isLid(jid) {
499
+ return typeof jid === "string" && jid.endsWith("@lid");
500
+ }
501
+
502
+ export function isPn(jid) {
503
+ return typeof jid === "string" && jid.endsWith("@s.whatsapp.net");
504
+ }
505
+
506
+ const stripDevice = (jid) => {
507
+ if (typeof jid !== "string") return jid;
508
+ const [user, server] = jid.split("@");
509
+ return `${user.split(":")[0]}@${server}`;
510
+ };
511
+
512
+ /**
513
+ * LID مش مشتق من رقم التليفون — لازم mapping حقيقي من WhatsApp.
514
+ * بتستعمل sock.signalRepository.lidMapping. بترجّع null لو الـ mapping مش معروف.
515
+ */
516
+ export async function toLid(sock, jid) {
517
+ if (!jid) return jid;
518
+ if (isLid(jid)) return stripDevice(jid);
519
+ const lid = await sock?.signalRepository?.lidMapping?.getLIDForPN?.(stripDevice(jid));
520
+ return lid ? stripDevice(lid) : null;
521
+ }
522
+
523
+ export async function toPn(sock, jid) {
524
+ if (!jid) return jid;
525
+ if (isPn(jid)) return stripDevice(jid);
526
+ const pn = await sock?.signalRepository?.lidMapping?.getPNForLID?.(stripDevice(jid));
527
+ return pn ? stripDevice(pn) : null;
528
+ }
529
+
530
+ /** يشيل رقم الجهاز (:12) من أي JID من غير ما يغيّر نوعه */
531
+ export function normalizeJid(jid) {
532
+ return stripDevice(jid);
533
+ }
534
+
535
+ /* ───────────────────────────── attach ───────────────────────────── */
536
+
537
+ /**
538
+ * يربط كل الـ helpers على sock:
539
+ * attachPrototypeHelpers(sock); await sock.sendButtons(jid, {...})
540
+ */
541
+ export function attachPrototypeHelpers(sock) {
542
+ if (!sock || typeof sock.relayMessage !== "function" || typeof sock.sendMessage !== "function") {
543
+ throw new Error("Invalid socket instance");
544
+ }
545
+
546
+ sock.sendButtons = (jid, content, opts) => sendButtons(sock, jid, content, opts);
547
+ sock.sendList = (jid, content, opts) => sendList(sock, jid, content, opts);
548
+ sock.sendAlbum = (jid, media, opts) => sendAlbum(sock, jid, media, opts);
549
+ sock.sendRichHtml = (jid, content, opts) => sendRichHtml(sock, jid, content, opts);
550
+ sock.sendRichHtmlAdvanced = (jid, content, opts) => sendRichHtmlAdvanced(sock, jid, content, opts);
551
+ sock.sendRichCode = (jid, content, opts) => sendRichCode(sock, jid, content, opts);
552
+ sock.sendAIMessage = (jid, content, opts) => sendAIMessage(sock, jid, content, opts);
553
+ sock.sendQuickButtons = (jid, text, labels, opts) => sendQuickButtons(sock, jid, text, labels, opts);
554
+ sock.sendCarousel = (jid, content, opts) => sendCarousel(sock, jid, content, opts);
555
+ sock.sendViewOnce = (jid, content, opts) => sendViewOnce(sock, jid, content, opts);
556
+ sock.sendReaction = (jid, key, emoji) => sendReaction(sock, jid, key, emoji);
557
+ sock.sendPoll = (jid, name, values, opts) => sendPoll(sock, jid, name, values, opts);
558
+ sock.sendMentionAll = (jid, text, opts) => sendMentionAll(sock, jid, text, opts);
559
+ sock.sendLocation = (jid, lat, lng, opts) => sendLocation(sock, jid, lat, lng, opts);
560
+ sock.sendContact = (jid, contacts, opts) => sendContact(sock, jid, contacts, opts);
561
+ sock.sendProduct = (jid, product, opts) => sendProduct(sock, jid, product, opts);
562
+ sock.sendSticker = (jid, sticker, opts) => sendSticker(sock, jid, sticker, opts);
563
+ sock.sendScheduledCall = (jid, content, opts) => sendScheduledCall(sock, jid, content, opts);
564
+ sock.sendCallInvite = (jid, content, opts) => sendCallInvite(sock, jid, content, opts);
565
+
566
+ // JID / LID
567
+ sock.toLid = (jid) => toLid(sock, jid); // async
568
+ sock.toPn = (jid) => toPn(sock, jid); // async
569
+ sock.isLid = isLid;
570
+ sock.isPn = isPn;
571
+ sock.normalizeJid = normalizeJid;
572
+
573
+ // Builders (من message-builder.js)
574
+ sock.button = () => new Button(sock);
575
+ sock.buttonV2 = () => new ButtonV2(sock);
576
+ sock.carouselBuilder = () => new Carousel(sock);
577
+ sock.aiRich = () => new AIRich(sock);
578
+ sock.Toolkit = Toolkit;
579
+
580
+ return sock;
581
+ }
582
+
583
+ export default {
584
+ buildNativeFlowButtons,
585
+ sendButtons,
586
+ sendList,
587
+ sendAlbum,
588
+ sendRichHtml,
589
+ sendRichHtmlAdvanced,
590
+ sendRichCode,
591
+ sendAIMessage,
592
+ sendQuickButtons,
593
+ sendCarousel,
594
+ sendViewOnce,
595
+ sendReaction,
596
+ sendPoll,
597
+ sendMentionAll,
598
+ sendLocation,
599
+ sendContact,
600
+ sendProduct,
601
+ sendSticker,
602
+ sendScheduledCall,
603
+ sendCallInvite,
604
+ toLid,
605
+ toPn,
606
+ isLid,
607
+ isPn,
608
+ normalizeJid,
609
+ attachPrototypeHelpers,
610
+ };