@prototypevip/baileys 0.1.4 → 0.1.6
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/HELPERS.md +149 -0
- package/lib/Socket/communities.js +2 -0
- package/lib/Socket/messages-send.js +13 -5
- package/lib/Utils/index.js +2 -0
- package/lib/Utils/interactive-message-handler.js +6 -4
- package/lib/Utils/message-builder.js +40 -24
- package/lib/Utils/messages-media.js +6 -3
- package/lib/Utils/messages.js +31 -12
- package/lib/Utils/newsletter-status.js +40 -2
- package/lib/Utils/prototype-helpers.js +732 -0
- package/lib/Utils/view-once.js +160 -0
- package/package.json +12 -3
package/HELPERS.md
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# @prototypevip/baileys — Helpers (v0.1.6)
|
|
2
|
+
|
|
3
|
+
طبقة helpers فوق المسارات الأصلية في المكتبة (`sock.sendMessage` → `nativeFlow` / `album` / `code` / `event` / `product`).
|
|
4
|
+
مفيش بروتوكول جديد هنا؛ كل دالة بتفوّض لمسار موجود ومجرَّب.
|
|
5
|
+
|
|
6
|
+
## Setup
|
|
7
|
+
|
|
8
|
+
```js
|
|
9
|
+
import makeWASocket, { attachPrototypeHelpers } from '@prototypevip/baileys'
|
|
10
|
+
|
|
11
|
+
const sock = makeWASocket({ ... })
|
|
12
|
+
attachPrototypeHelpers(sock)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
الدوال متاحة كمان كـ named exports: `sendButtons(sock, jid, content, options)` … إلخ.
|
|
16
|
+
|
|
17
|
+
## أزرار / قوائم
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
await sock.sendButtons(jid, {
|
|
21
|
+
text: 'اختار', footer: 'Prototype VIP',
|
|
22
|
+
buttons: [
|
|
23
|
+
{ text: 'Menu', id: '#menu' },
|
|
24
|
+
{ text: 'موقع', url: 'https://example.com' },
|
|
25
|
+
{ text: 'نسخ', copy: 'CODE' },
|
|
26
|
+
{ text: 'اتصل', call: '201234567890' }
|
|
27
|
+
]
|
|
28
|
+
}, { quoted: m })
|
|
29
|
+
|
|
30
|
+
// مع ميديا (بتتحوّل لـ header): image | video | document
|
|
31
|
+
await sock.sendButtons(jid, { text: 'كابشن', image: { url: '...' }, buttons: [...] })
|
|
32
|
+
|
|
33
|
+
// أيقونة AI (شات خاص فقط — في الجروب بتتجاهل)
|
|
34
|
+
await sock.sendAIMessage(jid, { text: 'أهلا', buttons: [...] })
|
|
35
|
+
|
|
36
|
+
// قائمة اختيار (single_select داخل Native Flow). legacy: true = listMessage الكلاسيكي
|
|
37
|
+
await sock.sendList(jid, {
|
|
38
|
+
text: 'القائمة', title: 'T', buttonText: 'افتح',
|
|
39
|
+
sections: [{ title: 'قسم', rows: [{ title: 'A', description: 'd', id: '#a' }] }]
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
await sock.sendQuickButtons(jid, 'اختار', ['نعم', 'لا'])
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## ألبوم / Carousel
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
await sock.sendAlbum(jid, [{ image: { url } , caption: '1' }, { video: { url } }], { delayMs: 1500 })
|
|
49
|
+
|
|
50
|
+
// Carousel حقيقي (كل كارت لازم image أو video)
|
|
51
|
+
await sock.sendCarousel(jid, {
|
|
52
|
+
text: 'منتجات', footer: 'Prototype VIP',
|
|
53
|
+
cards: [{ title: 'منتج 1', body: 'وصف', image: 'https://...', buttons: [{ text: 'اشتري', id: '#buy1' }] }]
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Rich (شكل Meta AI)
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
await sock.sendRichCode(jid, { title: 'مثال', code: 'console.log(1)', language: 'javascript' })
|
|
61
|
+
|
|
62
|
+
// ⚠️ HTML: مفيش render حقيقي لـ HTML جوه الفقاعة في المكتبة.
|
|
63
|
+
// sendRichHtml بيبعت الـ HTML كـ code block ملوّن داخل Rich Response.
|
|
64
|
+
await sock.sendRichHtml(jid, { title: 'Card', html: '<div>...</div>' })
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## مكالمات
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
// Scheduled Call Event (مفيش WebRTC outbound حقيقي في Baileys)
|
|
71
|
+
await sock.sendScheduledCall(jid, { name: 'اجتماع', startDate: new Date(Date.now() + 3600_000), callType: 'audio' })
|
|
72
|
+
await sock.sendCallInvite(jid, { name: 'مكالمة سريعة', callType: 'video', minutesFromNow: 2 })
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## متفرقات
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
await sock.sendViewOnce(jid, { image: { url }, caption: 'مرة واحدة' })
|
|
79
|
+
await sock.sendPoll(jid, 'رأيك؟', ['حلو', 'عادي'], { selectableCount: 1 })
|
|
80
|
+
await sock.sendReaction(jid, m.key, '🔥')
|
|
81
|
+
await sock.sendMentionAll(groupJid, 'يا جماعة')
|
|
82
|
+
await sock.sendLocation(jid, 30.04, 31.23, { name: 'القاهرة', address: '...' })
|
|
83
|
+
await sock.sendContact(jid, { fullName: 'أحمد', phone: '201234567890' })
|
|
84
|
+
await sock.sendProduct(jid, { title: 'منتج', price: 100, currencyCode: 'EGP', image: 'https://...' }) // الصورة مطلوبة
|
|
85
|
+
await sock.sendSticker(jid, { url: 'https://.../x.webp' })
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## JID / LID
|
|
89
|
+
|
|
90
|
+
الـ LID **مش** مشتق من رقم التليفون، فالتحويل بيعتمد على mapping حقيقي (`sock.signalRepository.lidMapping`)
|
|
91
|
+
وبيرجّع `null` لو الـ mapping مش معروف.
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
await sock.toLid('2010xxxxxxx@s.whatsapp.net') // → '...@lid' | null (async)
|
|
95
|
+
await sock.toPn('...@lid') // → '...@s.whatsapp.net' | null (async)
|
|
96
|
+
sock.normalizeJid('201...:12@s.whatsapp.net') // يشيل رقم الجهاز بس
|
|
97
|
+
sock.isLid(jid); sock.isPn(jid)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Builders
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
const btn = sock.button() // Button
|
|
104
|
+
const car = sock.carouselBuilder()
|
|
105
|
+
const rich = sock.aiRich()
|
|
106
|
+
// chain methods ثم .send(jid)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 0.1.6 — View-Once لكل الأنواع
|
|
112
|
+
|
|
113
|
+
الـ viewOnce بقى بيشتغل على **أي** نوع رسالة (نص، ملف، أزرار، قوائم، كاروسيل، منتج، لوكيشن، جهة اتصال، Rich ...) مش صورة/فيديو/صوت بس.
|
|
114
|
+
|
|
115
|
+
```js
|
|
116
|
+
// 1) جوه الـ content
|
|
117
|
+
await sock.sendMessage(jid, { text: 'x', nativeFlow: [{ text: 'A', id: '#a' }], viewOnce: true })
|
|
118
|
+
// 2) أو في الـ options (شغال مع كل الـ helpers)
|
|
119
|
+
await sock.sendButtons(jid, { text: 'x', buttons }, { viewOnce: true })
|
|
120
|
+
await sock.sendList(jid, { text: 'x', sections }, { viewOnce: true })
|
|
121
|
+
await sock.sendCarousel(jid, { text: 'x', cards }, { viewOnce: true })
|
|
122
|
+
await sock.sendAlbum(jid, items, { viewOnce: true }) // كل عنصر بيتلف لوحده
|
|
123
|
+
await sock.sendDocument(jid, { url, fileName }, { viewOnce: true })
|
|
124
|
+
// 3) smart: بيوجّه لوحده حسب شكل الـ content
|
|
125
|
+
await sock.sendViewOnce(jid, { text: 'x', cards }) // → sendCarousel
|
|
126
|
+
// 4) Builders
|
|
127
|
+
await new Carousel(sock).setBody('x').addCard(card).setViewOnce().send(jid)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
قيم الـ flag: `true` (viewOnceMessage) · `"v2"` (viewOnceMessageV2) · `"force"` (يلف حتى استيكر/استطلاع/ألبوم) · `{ version: 2, force: true }`.
|
|
131
|
+
|
|
132
|
+
- رسايل التحكم (edit / react / delete / رد التصويت) **مش بتتلف** أبداً.
|
|
133
|
+
- الاستيكر والاستطلاع والألبوم بيتخطوا إلا مع `"force"`.
|
|
134
|
+
- اللف في البروتوكول مضمون؛ سلوك "يختفي بعد أول فتح" للأزرار والكاروسيل بيعتمد على نسخة واتساب عند المستلم.
|
|
135
|
+
- Utils جديدة: `wrapViewOnce` · `unwrapViewOnce` · `isViewOnceContent` · `canWrapViewOnce` · `parseViewOnce`.
|
|
136
|
+
|
|
137
|
+
### Helpers جديدة
|
|
138
|
+
| Helper | الوظيفة |
|
|
139
|
+
|---|---|
|
|
140
|
+
| `sendDocument(jid, {url\|document, fileName, caption}, opts)` | ملف مع mimetype تلقائي |
|
|
141
|
+
| `sendEdit(jid, key, content)` | تعديل رسالة |
|
|
142
|
+
| `sendDelete(jid, key)` | حذف للكل |
|
|
143
|
+
| `sendWithTyping(jid, content, {typingMs, presence})` | "بيكتب..." ثم إرسال |
|
|
144
|
+
|
|
145
|
+
### إصلاحات
|
|
146
|
+
- `text` مع `nativeFlow`/`buttons` كان بيتبعت نص عادي من غير أزرار → اتصلح.
|
|
147
|
+
- `contextInfo`/`mentions` مع viewOnce كانوا بيتكتبوا على الغلاف → بقوا على الرسالة الداخلية.
|
|
148
|
+
- `communities.js`: `isPnUser`/`isLidUser` ناقصين import → اتصلحوا.
|
|
149
|
+
- `sendCarousel` بقى بيرجّع الرسالة المرسلة.
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
} from "../Utils/index.js";
|
|
44
44
|
import { AssociationType } from "../Types/index.js";
|
|
45
45
|
import { getUrlInfo } from "../Utils/link-preview.js";
|
|
46
|
+
import { wrapViewOnce } from "../Utils/view-once.js";
|
|
46
47
|
import { makeKeyedMutex, makeMutex } from "../Utils/make-mutex.js";
|
|
47
48
|
import {
|
|
48
49
|
getMessageReportingToken,
|
|
@@ -1678,10 +1679,12 @@ export const makeMessagesSocket = (config) => {
|
|
|
1678
1679
|
} else {
|
|
1679
1680
|
const interactiveHandler = new InteractiveMessageHandler(Utils_1, waUploadToServer, relayMessage, emitUpsert);
|
|
1680
1681
|
const messageType = interactiveHandler.detectType(content);
|
|
1682
|
+
// View-Once موحَّد (0.1.6): content.viewOnce أو options.viewOnce
|
|
1683
|
+
const viewOnceFlag = content.viewOnce || options.viewOnce;
|
|
1681
1684
|
if (messageType) {
|
|
1682
1685
|
switch (messageType) {
|
|
1683
1686
|
case 'PAYMENT': {
|
|
1684
|
-
const paymentContent = await interactiveHandler.handlePayment(content, options.quoted);
|
|
1687
|
+
const paymentContent = wrapViewOnce(await interactiveHandler.handlePayment(content, options.quoted), viewOnceFlag);
|
|
1685
1688
|
const paymentMsg = generateWAMessageFromContent(jid, paymentContent, { userJid: userJid });
|
|
1686
1689
|
await relayMessage(jid, paymentMsg.message, {
|
|
1687
1690
|
messageId: paymentMsg.key.id,
|
|
@@ -1691,7 +1694,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
1691
1694
|
return paymentMsg;
|
|
1692
1695
|
}
|
|
1693
1696
|
case 'PRODUCT': {
|
|
1694
|
-
const productContent = await interactiveHandler.handleProduct(content, jid, options.quoted);
|
|
1697
|
+
const productContent = wrapViewOnce(await interactiveHandler.handleProduct(content, jid, options.quoted), viewOnceFlag);
|
|
1695
1698
|
const productMsg = await generateWAMessageFromContent(jid, productContent, { quoted: options.quoted });
|
|
1696
1699
|
await relayMessage(jid, productMsg.message, {
|
|
1697
1700
|
messageId: productMsg.key.id,
|
|
@@ -1701,7 +1704,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
1701
1704
|
return productMsg;
|
|
1702
1705
|
}
|
|
1703
1706
|
case 'INTERACTIVE': {
|
|
1704
|
-
const interactiveContent = await interactiveHandler.handleInteractive(content, jid, options.quoted);
|
|
1707
|
+
const interactiveContent = wrapViewOnce(await interactiveHandler.handleInteractive(content, jid, options.quoted), viewOnceFlag);
|
|
1705
1708
|
const interactiveMsg = await generateWAMessageFromContent(jid, interactiveContent, { quoted: options.quoted });
|
|
1706
1709
|
await relayMessage(jid, interactiveMsg.message, {
|
|
1707
1710
|
messageId: interactiveMsg.key.id,
|
|
@@ -1711,7 +1714,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
1711
1714
|
return interactiveMsg;
|
|
1712
1715
|
}
|
|
1713
1716
|
case 'ALBUM':
|
|
1714
|
-
return await interactiveHandler.handleAlbum(content, jid, options.quoted);
|
|
1717
|
+
return await interactiveHandler.handleAlbum(content, jid, options.quoted, viewOnceFlag);
|
|
1715
1718
|
case 'EVENT':
|
|
1716
1719
|
return await interactiveHandler.handleEvent(content, jid, options.quoted);
|
|
1717
1720
|
case 'POLL_RESULT':
|
|
@@ -1846,7 +1849,12 @@ export const makeMessagesSocket = (config) => {
|
|
|
1846
1849
|
|
|
1847
1850
|
if ("album" in content) {
|
|
1848
1851
|
const { delayMs = 1500 } = options;
|
|
1849
|
-
for (const
|
|
1852
|
+
for (const albumItem of content.album) {
|
|
1853
|
+
// View-Once على الألبوم: كل عنصر (صورة/فيديو) بيتلف لوحده، والـ parent مش بيتلف
|
|
1854
|
+
const albumMedia =
|
|
1855
|
+
content.viewOnce && albumItem.viewOnce === undefined
|
|
1856
|
+
? { ...albumItem, viewOnce: content.viewOnce }
|
|
1857
|
+
: albumItem;
|
|
1850
1858
|
const albumMsg = await generateWAMessage(jid, albumMedia, {
|
|
1851
1859
|
logger: logger,
|
|
1852
1860
|
userJid: userJid,
|
package/lib/Utils/index.js
CHANGED
|
@@ -2,6 +2,7 @@ export * from "./make-mutex.js";
|
|
|
2
2
|
export * from "./generics.js";
|
|
3
3
|
export * from "./decode-wa-message.js";
|
|
4
4
|
export * from "./messages.js";
|
|
5
|
+
export * from "./view-once.js";
|
|
5
6
|
export * from "./messages-media.js";
|
|
6
7
|
export * from "./validate-connection.js";
|
|
7
8
|
export * from "./crypto.js";
|
|
@@ -34,3 +35,4 @@ export * from "./integrity-guard.js";
|
|
|
34
35
|
export * from "./newsletter-status.js";
|
|
35
36
|
export * from "./status-helpers.js";
|
|
36
37
|
export * from "./modern-messages.js";
|
|
38
|
+
export * from "./prototype-helpers.js";
|
|
@@ -272,7 +272,7 @@ export default class InteractiveMessageHandler {
|
|
|
272
272
|
};
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
-
async handleAlbum(content, jid, quoted) {
|
|
275
|
+
async handleAlbum(content, jid, quoted, viewOnce) {
|
|
276
276
|
const array = content.albumMessage;
|
|
277
277
|
const album = await this.utils.generateWAMessageFromContent(jid, {
|
|
278
278
|
messageContextInfo: {
|
|
@@ -295,9 +295,11 @@ export default class InteractiveMessageHandler {
|
|
|
295
295
|
this.emitUpsert(album);
|
|
296
296
|
|
|
297
297
|
for (let content of array) {
|
|
298
|
-
const img = await this.utils.generateWAMessage(
|
|
299
|
-
|
|
300
|
-
|
|
298
|
+
const img = await this.utils.generateWAMessage(
|
|
299
|
+
jid,
|
|
300
|
+
viewOnce && content.viewOnce === undefined ? { ...content, viewOnce } : content,
|
|
301
|
+
{ upload: this.waUploadToServer },
|
|
302
|
+
);
|
|
301
303
|
|
|
302
304
|
img.message.messageContextInfo = {
|
|
303
305
|
messageSecret: crypto.randomBytes(32),
|
|
@@ -32,6 +32,7 @@ import crypto from 'crypto';
|
|
|
32
32
|
import ffmpeg from 'fluent-ffmpeg';
|
|
33
33
|
import { PassThrough, Readable } from 'stream';
|
|
34
34
|
import { generateWAMessageFromContent, prepareWAMessageMedia } from './messages.js';
|
|
35
|
+
import { wrapViewOnce } from './view-once.js';
|
|
35
36
|
|
|
36
37
|
// sharp اختيارية (peerDependency) — نحمّلها ديناميكيًا وقت الاستخدام فعليًا
|
|
37
38
|
// بدل static import، عشان لو حد ركّب المكتبة من غير ما يثبتها، المكتبة كلها
|
|
@@ -504,6 +505,13 @@ class BaseBuilder {
|
|
|
504
505
|
this._footer = '';
|
|
505
506
|
this._contextInfo = {};
|
|
506
507
|
this._extraPayload = {};
|
|
508
|
+
this._viewOnce = false;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/** View-Once: true | "v2" | "force" | { version, force } — أو false لإلغائه */
|
|
512
|
+
setViewOnce(flag = true) {
|
|
513
|
+
this._viewOnce = flag;
|
|
514
|
+
return this;
|
|
507
515
|
}
|
|
508
516
|
|
|
509
517
|
setTitle(title) {
|
|
@@ -793,17 +801,21 @@ export class Button extends BaseBuilder {
|
|
|
793
801
|
|
|
794
802
|
async build(jid, { ...options } = {}) {
|
|
795
803
|
const message = await this.toCard();
|
|
804
|
+
const { viewOnce, ...buildOptions } = options;
|
|
796
805
|
|
|
797
806
|
return generateWAMessageFromContent(
|
|
798
807
|
jid,
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
808
|
+
wrapViewOnce(
|
|
809
|
+
{
|
|
810
|
+
...this._extraPayload,
|
|
811
|
+
interactiveMessage: {
|
|
812
|
+
...message,
|
|
813
|
+
contextInfo: this._contextInfo,
|
|
814
|
+
},
|
|
804
815
|
},
|
|
805
|
-
|
|
806
|
-
|
|
816
|
+
viewOnce ?? this._viewOnce
|
|
817
|
+
),
|
|
818
|
+
{ ...buildOptions }
|
|
807
819
|
);
|
|
808
820
|
}
|
|
809
821
|
|
|
@@ -881,9 +893,10 @@ export class ButtonV2 extends BaseBuilder {
|
|
|
881
893
|
|
|
882
894
|
async build(jid, { ...options } = {}) {
|
|
883
895
|
let _thumbnail = this._image ? await Toolkit.resize(Buffer.isBuffer(this._image) ? this._image : await Toolkit.fetchBuffer(this._image, {}, { silent: true }), 300, 300) : null;
|
|
896
|
+
const { viewOnce, ...buildOptions } = options;
|
|
884
897
|
const msg = generateWAMessageFromContent(
|
|
885
898
|
jid,
|
|
886
|
-
{
|
|
899
|
+
wrapViewOnce({
|
|
887
900
|
...this._extraPayload,
|
|
888
901
|
buttonsMessage: {
|
|
889
902
|
contentText: this._body,
|
|
@@ -900,12 +913,11 @@ export class ButtonV2 extends BaseBuilder {
|
|
|
900
913
|
jpegThumbnail: _thumbnail,
|
|
901
914
|
},
|
|
902
915
|
}),
|
|
903
|
-
viewOnce: true,
|
|
904
916
|
contextInfo: this._contextInfo,
|
|
905
917
|
buttons: [...this._buttons],
|
|
906
918
|
},
|
|
907
|
-
},
|
|
908
|
-
{ ...
|
|
919
|
+
}, viewOnce ?? this._viewOnce),
|
|
920
|
+
{ ...buildOptions }
|
|
909
921
|
);
|
|
910
922
|
return msg;
|
|
911
923
|
}
|
|
@@ -963,23 +975,27 @@ export class Carousel extends BaseBuilder {
|
|
|
963
975
|
}
|
|
964
976
|
|
|
965
977
|
build(jid, { ...options } = {}) {
|
|
978
|
+
const { viewOnce, ...buildOptions } = options;
|
|
966
979
|
return generateWAMessageFromContent(
|
|
967
980
|
jid,
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
981
|
+
wrapViewOnce(
|
|
982
|
+
{
|
|
983
|
+
...this._extraPayload,
|
|
984
|
+
interactiveMessage: {
|
|
985
|
+
header: {
|
|
986
|
+
hasMediaAttachment: false,
|
|
987
|
+
},
|
|
988
|
+
body: { text: this._body },
|
|
989
|
+
footer: { text: this._footer },
|
|
990
|
+
contextInfo: this._contextInfo,
|
|
991
|
+
carouselMessage: {
|
|
992
|
+
cards: this._cards,
|
|
993
|
+
},
|
|
979
994
|
},
|
|
980
995
|
},
|
|
981
|
-
|
|
982
|
-
|
|
996
|
+
viewOnce ?? this._viewOnce
|
|
997
|
+
),
|
|
998
|
+
{ ...buildOptions }
|
|
983
999
|
);
|
|
984
1000
|
}
|
|
985
1001
|
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
DEFAULT_ORIGIN,
|
|
18
18
|
MEDIA_HKDF_KEY_MAPPING,
|
|
19
19
|
MEDIA_PATH_MAP,
|
|
20
|
+
NEWSLETTER_MEDIA_PATH_MAP,
|
|
20
21
|
} from "../Defaults/index.js";
|
|
21
22
|
import {
|
|
22
23
|
getBinaryNodeChild,
|
|
@@ -725,7 +726,7 @@ export const getWAUploadToServer = (
|
|
|
725
726
|
{ customUploadHosts, fetchAgent, logger, options },
|
|
726
727
|
refreshMediaConn,
|
|
727
728
|
) => {
|
|
728
|
-
return async (filePath, { mediaType, fileEncSha256B64, timeoutMs }) => {
|
|
729
|
+
return async (filePath, { mediaType, fileEncSha256B64, timeoutMs, newsletter }) => {
|
|
729
730
|
// send a query JSON to obtain the url & auth token to upload our media
|
|
730
731
|
let uploadInfo = await refreshMediaConn(false);
|
|
731
732
|
let urls;
|
|
@@ -745,7 +746,8 @@ export const getWAUploadToServer = (
|
|
|
745
746
|
for (const { hostname } of hosts) {
|
|
746
747
|
logger.debug(`uploading to "${hostname}"`);
|
|
747
748
|
const auth = encodeURIComponent(uploadInfo.auth);
|
|
748
|
-
const
|
|
749
|
+
const mediaPath = (newsletter && NEWSLETTER_MEDIA_PATH_MAP[mediaType]) || MEDIA_PATH_MAP[mediaType];
|
|
750
|
+
const url = `https://${hostname}${mediaPath}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`;
|
|
749
751
|
let result;
|
|
750
752
|
try {
|
|
751
753
|
result = await uploadMedia(
|
|
@@ -758,8 +760,9 @@ export const getWAUploadToServer = (
|
|
|
758
760
|
},
|
|
759
761
|
logger,
|
|
760
762
|
);
|
|
761
|
-
if (result?.url || result?.direct_path) {
|
|
763
|
+
if (result?.url || result?.direct_path || (newsletter && result?.handle)) {
|
|
762
764
|
urls = {
|
|
765
|
+
handle: result.handle,
|
|
763
766
|
mediaUrl: result.url,
|
|
764
767
|
directPath: result.direct_path,
|
|
765
768
|
meta_hmac: result.meta_hmac,
|
package/lib/Utils/messages.js
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
toBuffer,
|
|
38
38
|
} from "./messages-media.js";
|
|
39
39
|
import { shouldIncludeReportingToken } from "./reporting-utils.js";
|
|
40
|
+
import { wrapViewOnce, isViewOnceContent, getInnerContentType, unwrapViewOnce } from "./view-once.js";
|
|
40
41
|
import { isAnimatedWebP, isWebPBuffer, writeExif } from "./sticker-utils.js";
|
|
41
42
|
const MIMETYPE_MAP = {
|
|
42
43
|
image: "image/jpeg",
|
|
@@ -402,6 +403,7 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
402
403
|
fileEncSha256B64: fileSha256B64,
|
|
403
404
|
mediaType: mediaType,
|
|
404
405
|
timeoutMs: options.mediaUploadTimeoutMs,
|
|
406
|
+
newsletter: true,
|
|
405
407
|
});
|
|
406
408
|
await fs.unlink(filePath);
|
|
407
409
|
const obj = WAProto.Message.fromObject({
|
|
@@ -704,7 +706,12 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
704
706
|
) {
|
|
705
707
|
// مضافة من مكتبة itsliaaa: رسالة Rich Response (Meta AI) — جداول/كود ملوّن
|
|
706
708
|
m = prepareRichResponseMessage(message);
|
|
707
|
-
} else if (
|
|
709
|
+
} else if (
|
|
710
|
+
hasNonNullishProperty(message, "text") &&
|
|
711
|
+
// لو فيه أزرار (nativeFlow / buttons) الرسالة تفاعلية مش نص عادي
|
|
712
|
+
!hasNonNullishProperty(message, "nativeFlow") &&
|
|
713
|
+
!(Array.isArray(message.buttons) && message.buttons.length > 0)
|
|
714
|
+
) {
|
|
708
715
|
const extContent = { text: message.text };
|
|
709
716
|
let urlInfo = message.linkPreview;
|
|
710
717
|
if (typeof urlInfo === "undefined") {
|
|
@@ -1020,19 +1027,25 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1020
1027
|
} else {
|
|
1021
1028
|
m = await prepareWAMessageMedia(message, options);
|
|
1022
1029
|
}
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1030
|
+
// View-Once موحَّد (0.1.6): بيشتغل على أي نوع رسالة (مش صورة/فيديو/صوت بس)،
|
|
1031
|
+
// من content.viewOnce أو options.viewOnce. رسايل التحكم (edit/react/delete...) بتتخطى.
|
|
1032
|
+
{
|
|
1033
|
+
const viewOnceFlag =
|
|
1034
|
+
hasOptionalProperty(message, "viewOnce") && !!message.viewOnce
|
|
1035
|
+
? message.viewOnce
|
|
1036
|
+
: options?.viewOnce;
|
|
1037
|
+
if (viewOnceFlag && !hasNonNullishProperty(message, "edit") && !isViewOnceContent(m)) {
|
|
1038
|
+
m = wrapViewOnce(m, viewOnceFlag);
|
|
1039
|
+
}
|
|
1029
1040
|
}
|
|
1030
1041
|
if (
|
|
1031
1042
|
(hasOptionalProperty(message, "mentions") && message.mentions?.length) ||
|
|
1032
1043
|
(hasOptionalProperty(message, "mentionAll") && message.mentionAll)
|
|
1033
1044
|
) {
|
|
1034
|
-
const target = m
|
|
1035
|
-
const messageType =
|
|
1045
|
+
const target = unwrapViewOnce(m);
|
|
1046
|
+
const messageType = isViewOnceContent(m)
|
|
1047
|
+
? getInnerContentType(target)
|
|
1048
|
+
: Object.keys(target)[0];
|
|
1036
1049
|
const key = target[messageType];
|
|
1037
1050
|
if (key && "contextInfo" in key) {
|
|
1038
1051
|
key.contextInfo = key.contextInfo || {};
|
|
@@ -1060,9 +1073,15 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1060
1073
|
};
|
|
1061
1074
|
}
|
|
1062
1075
|
if (hasOptionalProperty(message, "contextInfo") && !!message.contextInfo) {
|
|
1063
|
-
|
|
1064
|
-
const
|
|
1065
|
-
|
|
1076
|
+
// لو الرسالة ملفوفة في viewOnce لازم نكتب الـ contextInfo على الرسالة الداخلية مش على الغلاف
|
|
1077
|
+
const ctxTarget = isViewOnceContent(m) ? unwrapViewOnce(m) : m;
|
|
1078
|
+
const messageType = isViewOnceContent(m)
|
|
1079
|
+
? getInnerContentType(ctxTarget)
|
|
1080
|
+
: Object.keys(m)[0];
|
|
1081
|
+
const key = ctxTarget[messageType];
|
|
1082
|
+
if (!key) {
|
|
1083
|
+
// مفيش نوع رسالة نكتب عليه contextInfo
|
|
1084
|
+
} else if ("contextInfo" in key && !!key.contextInfo) {
|
|
1066
1085
|
key.contextInfo = { ...key.contextInfo, ...message.contextInfo };
|
|
1067
1086
|
} else if (key) {
|
|
1068
1087
|
key.contextInfo = message.contextInfo;
|
|
@@ -85,12 +85,45 @@ const buildMetaNode = ({ interactionType, parentServerId, responseServerId, aiCo
|
|
|
85
85
|
export const withNewsletterStatusAttribution = content => ({
|
|
86
86
|
...content,
|
|
87
87
|
contextInfo: {
|
|
88
|
+
// matches the contextInfo of real channel statuses captured via getNewsletterStatuses
|
|
89
|
+
forwardingScore: 0,
|
|
88
90
|
statusAttributions: [{ type: proto.StatusAttribution.Type.NEWSLETTER_STATUS }],
|
|
89
|
-
featureEligibilities: { canBeReshared: true },
|
|
91
|
+
featureEligibilities: { cannotBeRanked: false, canBeReshared: true, canReceiveMultiReact: true },
|
|
90
92
|
...(content?.contextInfo || {})
|
|
91
93
|
}
|
|
92
94
|
})
|
|
93
95
|
|
|
96
|
+
const hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key)
|
|
97
|
+
|
|
98
|
+
// StatusSourceType: IMAGE 0, VIDEO 1, GIF 2, AUDIO 3, TEXT 4. PairedMediaType: NOT_PAIRED_MEDIA 0.
|
|
99
|
+
// Real statuses (captured from a live channel): text carries textArgb/backgroundArgb/font SYSTEM/previewType NONE
|
|
100
|
+
// and statusSourceType TEXT; media carries pairedMediaType NOT_PAIRED_MEDIA and no statusSourceType.
|
|
101
|
+
export const NATIVE_STATUS_TEXT_ARGB = 0xFFFFFFFF
|
|
102
|
+
export const NATIVE_STATUS_BACKGROUND_ARGB = 0xFF8B6990
|
|
103
|
+
|
|
104
|
+
export const applyNativeStatusShape = (message, { textArgb, backgroundArgb } = {}) => {
|
|
105
|
+
const content = normalizeMessageContent(message)
|
|
106
|
+
if (!content) return message
|
|
107
|
+
|
|
108
|
+
const text = content.extendedTextMessage
|
|
109
|
+
if (text) {
|
|
110
|
+
if (!hasOwn(text, 'textArgb')) text.textArgb = textArgb ?? NATIVE_STATUS_TEXT_ARGB
|
|
111
|
+
if (!hasOwn(text, 'backgroundArgb')) text.backgroundArgb = backgroundArgb ?? NATIVE_STATUS_BACKGROUND_ARGB
|
|
112
|
+
if (!hasOwn(text, 'font')) text.font = 0 // SYSTEM
|
|
113
|
+
text.previewType = 0 // NONE, even when a link preview was generated
|
|
114
|
+
text.contextInfo = text.contextInfo || {}
|
|
115
|
+
text.contextInfo.statusSourceType = 4 // TEXT
|
|
116
|
+
return message
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const media = content.imageMessage || content.videoMessage || content.audioMessage
|
|
120
|
+
if (media) {
|
|
121
|
+
media.contextInfo = media.contextInfo || {}
|
|
122
|
+
media.contextInfo.pairedMediaType = 0 // NOT_PAIRED_MEDIA
|
|
123
|
+
}
|
|
124
|
+
return message
|
|
125
|
+
}
|
|
126
|
+
|
|
94
127
|
const snapshotCallbackNode = (event, node) => ({
|
|
95
128
|
event,
|
|
96
129
|
tag: node?.tag,
|
|
@@ -376,11 +409,14 @@ export const makeNewsletterStatusSender = (sock, config) => async (jid, content,
|
|
|
376
409
|
interactionType: requestedInteractionType,
|
|
377
410
|
aiContent,
|
|
378
411
|
statusAttribution = true,
|
|
412
|
+
statusShape = 'native',
|
|
413
|
+
statusStyle,
|
|
379
414
|
messageId: requestedMessageId,
|
|
380
415
|
ackTimeoutMs,
|
|
381
416
|
resolveServerId,
|
|
382
417
|
serverIdTimeoutMs,
|
|
383
418
|
transport,
|
|
419
|
+
wrapAdminProfile,
|
|
384
420
|
...messageOptions
|
|
385
421
|
} = options
|
|
386
422
|
|
|
@@ -408,6 +444,8 @@ export const makeNewsletterStatusSender = (sock, config) => async (jid, content,
|
|
|
408
444
|
messageId: requestedMessageId || generateMessageIDV2(userJid)
|
|
409
445
|
})
|
|
410
446
|
|
|
447
|
+
if (statusAttribution && statusShape !== 'legacy') applyNativeStatusShape(fullMsg.message, statusStyle)
|
|
448
|
+
|
|
411
449
|
const normalized = normalizeMessageContent(fullMsg.message)
|
|
412
450
|
const mediaType = getNewsletterStatusMediaType(fullMsg.message)
|
|
413
451
|
if (!mediaType && (normalized?.documentMessage || normalized?.stickerMessage)) {
|
|
@@ -424,7 +462,7 @@ export const makeNewsletterStatusSender = (sock, config) => async (jid, content,
|
|
|
424
462
|
|
|
425
463
|
const node = buildNewsletterStatusNode({
|
|
426
464
|
jid,
|
|
427
|
-
message: fullMsg.message,
|
|
465
|
+
message: wrapAdminProfile ? buildNewsletterAdminProfileStatusMessage(fullMsg.message) : fullMsg.message,
|
|
428
466
|
messageId: fullMsg.key.id,
|
|
429
467
|
mediaType,
|
|
430
468
|
mediaHandle: requestedMediaHandle ?? mediaId ?? uploadedMediaHandle,
|