@rennzsync/baileys 10.2.4 → 10.5.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/README.id.md +231 -66
- package/README.md +96 -29
- package/WAProto/WAProto.proto +8 -0
- package/WAProto/index.d.ts +121 -0
- package/WAProto/index.js +393 -0
- package/lib/Defaults/index.js +28 -3
- package/lib/Socket/messages-send.js +13 -1
- package/lib/Utils/a2ui.js +210 -0
- package/lib/Utils/index.js +2 -0
- package/lib/Utils/messages.js +33 -0
- package/lib/Utils/rich-menu.js +211 -0
- package/package.json +1 -1
- package/lib/Defaults/index.js.map +0 -1
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* a2ui.js — widget A2UI (Agent-to-UI) lewat interactiveMessage.bloksWidget.
|
|
3
|
+
*
|
|
4
|
+
* Builder deklaratif (Text, Image, Video, Button, Card, Column, Row, Divider,
|
|
5
|
+
* CheckBox, TextField, ChoicePicker) + listCard, lalu dikirim sebagai
|
|
6
|
+
* interactiveMessage dengan field bloksWidget (proto field 8) dan node <biz>
|
|
7
|
+
* native_flow.
|
|
8
|
+
*
|
|
9
|
+
* PERINGATAN (sama seperti rich-*.js): format ini internal WhatsApp dan tidak
|
|
10
|
+
* dijamin dirender di semua client — relayMessage bisa sukses tanpa error
|
|
11
|
+
* walau widget tidak tampil di sisi penerima.
|
|
12
|
+
*
|
|
13
|
+
* Pakai:
|
|
14
|
+
* const ui = new A2UI()
|
|
15
|
+
* const t = ui.text('Halo', { variant: 'h1' })
|
|
16
|
+
* ui.root([t])
|
|
17
|
+
* await sock.sendA2UI(jid, { a2ui: ui, bodyText: 'Widget' })
|
|
18
|
+
* // atau: sendA2UIWidget(sock, jid, { a2ui: ui })
|
|
19
|
+
*/
|
|
20
|
+
import { randomBytes, randomUUID } from 'crypto';
|
|
21
|
+
|
|
22
|
+
const DEFAULT_CATALOG_ID = 'https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json';
|
|
23
|
+
|
|
24
|
+
export class A2UI {
|
|
25
|
+
constructor({ catalogId = DEFAULT_CATALOG_ID, version = 'v0.9' } = {}) {
|
|
26
|
+
this._version = version;
|
|
27
|
+
this._catalogId = catalogId;
|
|
28
|
+
this._components = new Map();
|
|
29
|
+
this._counter = 0;
|
|
30
|
+
this._rootChildren = [];
|
|
31
|
+
}
|
|
32
|
+
#nextId(prefix) {
|
|
33
|
+
return `${prefix}_${(this._counter++).toString(36)}`;
|
|
34
|
+
}
|
|
35
|
+
#reg(id, component, extra = {}) {
|
|
36
|
+
id ??= this.#nextId(component.toLowerCase());
|
|
37
|
+
if (this._components.has(id))
|
|
38
|
+
throw new Error(`Component id "${id}" already used`);
|
|
39
|
+
this._components.set(id, { id, component, ...extra });
|
|
40
|
+
return id;
|
|
41
|
+
}
|
|
42
|
+
text(text, { id, variant = 'body' } = {}) {
|
|
43
|
+
return this.#reg(id, 'Text', { text, variant });
|
|
44
|
+
}
|
|
45
|
+
image(url, { id, variant, fit = 'cover' } = {}) {
|
|
46
|
+
return this.#reg(id, 'Image', { url, ...(variant ? { variant } : {}), fit });
|
|
47
|
+
}
|
|
48
|
+
video(url, { id } = {}) {
|
|
49
|
+
return this.#reg(id, 'Video', { url });
|
|
50
|
+
}
|
|
51
|
+
checkbox(label, { id, value = false } = {}) {
|
|
52
|
+
return this.#reg(id, 'CheckBox', { label, value });
|
|
53
|
+
}
|
|
54
|
+
textField(label, { id, variant = 'text' } = {}) {
|
|
55
|
+
return this.#reg(id, 'TextField', { label, variant });
|
|
56
|
+
}
|
|
57
|
+
button(childId, { id, variant = 'primary', action } = {}) {
|
|
58
|
+
if (!childId)
|
|
59
|
+
throw new TypeError('button(childId) requires the id of a child component (e.g. from .text())');
|
|
60
|
+
return this.#reg(id, 'Button', { child: childId, variant, ...(action ? { action } : {}) });
|
|
61
|
+
}
|
|
62
|
+
card(childId, { id } = {}) {
|
|
63
|
+
return this.#reg(id, 'Card', { child: childId });
|
|
64
|
+
}
|
|
65
|
+
column(children = [], { id, justify, align } = {}) {
|
|
66
|
+
if (!children.length)
|
|
67
|
+
throw new TypeError('column(children) requires at least one child id');
|
|
68
|
+
return this.#reg(id, 'Column', { children, ...(justify ? { justify } : {}), ...(align ? { align } : {}) });
|
|
69
|
+
}
|
|
70
|
+
row(children = [], { id } = {}) {
|
|
71
|
+
if (!children.length)
|
|
72
|
+
throw new TypeError('row(children) requires at least one child id');
|
|
73
|
+
return this.#reg(id, 'Row', { children });
|
|
74
|
+
}
|
|
75
|
+
divider({ id } = {}) {
|
|
76
|
+
return this.#reg(id, 'Divider', {});
|
|
77
|
+
}
|
|
78
|
+
choicePicker(label, options, { id, variant = 'mutuallyExclusive', value, displayStyle = 'checkbox', filterable = false } = {}) {
|
|
79
|
+
if (!Array.isArray(options) || !options.length) {
|
|
80
|
+
throw new TypeError('choicePicker(label, options) requires a non-empty options array of {label, value}');
|
|
81
|
+
}
|
|
82
|
+
return this.#reg(id, 'ChoicePicker', {
|
|
83
|
+
label,
|
|
84
|
+
variant,
|
|
85
|
+
...(value !== undefined ? { value } : {}),
|
|
86
|
+
options,
|
|
87
|
+
displayStyle,
|
|
88
|
+
filterable
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
root(children) {
|
|
92
|
+
if (!Array.isArray(children) || !children.length) {
|
|
93
|
+
throw new TypeError('root(children) requires a non-empty array of top-level component ids');
|
|
94
|
+
}
|
|
95
|
+
this._rootChildren = children;
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
listCard({ title, items, fallbackText, uuid = randomUUID() } = {}) {
|
|
99
|
+
if (!title)
|
|
100
|
+
throw new TypeError('listCard requires a title');
|
|
101
|
+
if (!Array.isArray(items) || !items.length) {
|
|
102
|
+
throw new TypeError('listCard requires a non-empty items array');
|
|
103
|
+
}
|
|
104
|
+
this._listCardPayload = {
|
|
105
|
+
uuid,
|
|
106
|
+
data: JSON.stringify({
|
|
107
|
+
type: 'list_card',
|
|
108
|
+
title,
|
|
109
|
+
fallback_text: fallbackText ?? '',
|
|
110
|
+
items: items.map(it => ({
|
|
111
|
+
asset_id: it.assetId ?? randomUUID().replace(/-/g, '').slice(0, 17),
|
|
112
|
+
asset_type: it.assetType ?? 'PRODUCT_ITEM',
|
|
113
|
+
title: it.title,
|
|
114
|
+
trailing_label: it.price ?? it.trailingLabel ?? '',
|
|
115
|
+
trailing_emphasis: it.emphasis ?? 'strong'
|
|
116
|
+
}))
|
|
117
|
+
}),
|
|
118
|
+
type: 'im_a2ui',
|
|
119
|
+
fallback: fallbackText ?? ''
|
|
120
|
+
};
|
|
121
|
+
return this;
|
|
122
|
+
}
|
|
123
|
+
build({ uuid = randomUUID(), surfaceId, type = 'im_a2ui', wrapped = true } = {}) {
|
|
124
|
+
if (this._listCardPayload)
|
|
125
|
+
return this._listCardPayload;
|
|
126
|
+
if (!this._rootChildren.length)
|
|
127
|
+
throw new Error('Call root([...ids]) before build()');
|
|
128
|
+
const root = { id: 'root', component: 'Column', children: this._rootChildren };
|
|
129
|
+
const components = [root, ...this._components.values()];
|
|
130
|
+
const data = wrapped
|
|
131
|
+
? {
|
|
132
|
+
version: this._version,
|
|
133
|
+
createSurface: {
|
|
134
|
+
surfaceId: surfaceId ?? `starcore-widget=${uuid}`,
|
|
135
|
+
catalogId: this._catalogId,
|
|
136
|
+
components
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
: { components };
|
|
140
|
+
return {
|
|
141
|
+
uuid,
|
|
142
|
+
data: JSON.stringify(data),
|
|
143
|
+
type
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Ambil jid user dari sock / objek user / string (sama seperti normalizeUserJid di rich-classic.js). */
|
|
149
|
+
const resolveUserJid = (client) => client?.user?.id || client?.authState?.creds?.me?.id;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Kirim widget A2UI.
|
|
153
|
+
* @param {object} client socket (butuh relayMessage; opsional user.id)
|
|
154
|
+
* @param {string} jid
|
|
155
|
+
* @param {object} options { a2ui, bodyText, footer, buttons, contextInfo, expiration, quoted, type, wrapped, singleScreen }
|
|
156
|
+
*/
|
|
157
|
+
export async function sendA2UIWidget(client, jid, { a2ui, bodyText = '', footer = '', buttons = [], contextInfo = {}, expiration, quoted, type = 'im_a2ui', wrapped = true, singleScreen = false } = {}) {
|
|
158
|
+
if (!client || typeof client.relayMessage !== 'function')
|
|
159
|
+
throw new Error('Socket is required');
|
|
160
|
+
if (!(a2ui instanceof A2UI))
|
|
161
|
+
throw new TypeError('a2ui must be an A2UI instance');
|
|
162
|
+
const { generateWAMessageFromContent } = await import('./messages.js');
|
|
163
|
+
const nativeFlowMessage = buttons && buttons.length
|
|
164
|
+
? {
|
|
165
|
+
buttons: buttons.map(b => ({
|
|
166
|
+
name: b.name ?? 'cta_url',
|
|
167
|
+
buttonParamsJson: typeof b.params === 'string' ? b.params : JSON.stringify(b.params ?? {})
|
|
168
|
+
})),
|
|
169
|
+
messageParamsJson: '{}',
|
|
170
|
+
messageVersion: 1
|
|
171
|
+
}
|
|
172
|
+
: { messageParamsJson: '' };
|
|
173
|
+
const ctx = expiration || Object.keys(contextInfo).length
|
|
174
|
+
? { contextInfo: { ...(expiration ? { expiration } : {}), ...contextInfo } }
|
|
175
|
+
: {};
|
|
176
|
+
const interactiveMessage = singleScreen
|
|
177
|
+
? {
|
|
178
|
+
nativeFlowMessage,
|
|
179
|
+
bloksWidget: a2ui.build({ type, wrapped }),
|
|
180
|
+
...ctx
|
|
181
|
+
}
|
|
182
|
+
: {
|
|
183
|
+
...(footer !== undefined ? { header: { hasMediaAttachment: false } } : {}),
|
|
184
|
+
...(bodyText !== undefined ? { body: { text: bodyText } } : {}),
|
|
185
|
+
...(footer !== undefined ? { footer: { text: footer } } : {}),
|
|
186
|
+
nativeFlowMessage,
|
|
187
|
+
bloksWidget: a2ui.build({ type, wrapped }),
|
|
188
|
+
...ctx
|
|
189
|
+
};
|
|
190
|
+
const msg = generateWAMessageFromContent(jid, {
|
|
191
|
+
messageContextInfo: { messageSecret: randomBytes(32) },
|
|
192
|
+
interactiveMessage
|
|
193
|
+
}, { quoted, userJid: resolveUserJid(client) });
|
|
194
|
+
await client.relayMessage(msg.key.remoteJid, msg.message, {
|
|
195
|
+
messageId: msg.key.id,
|
|
196
|
+
additionalNodes: [
|
|
197
|
+
{
|
|
198
|
+
tag: 'biz',
|
|
199
|
+
attrs: { actual_actors: '2', host_storage: '2', privacy_mode_ts: String(Math.floor(Date.now() / 1e3)) },
|
|
200
|
+
content: [
|
|
201
|
+
{ tag: 'interactive', attrs: { type: 'native_flow', v: '1' }, content: [{ tag: 'native_flow', attrs: { v: '9', name: 'mixed' } }] },
|
|
202
|
+
{ tag: 'quality_control', attrs: { decision_id: randomUUID().replace(/-/g, ''), source_type: 'third_party' }, content: [{ tag: 'decision_source', attrs: { value: 'df' } }] }
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
]
|
|
206
|
+
});
|
|
207
|
+
return msg;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export default { A2UI, sendA2UIWidget };
|
package/lib/Utils/index.js
CHANGED
|
@@ -23,5 +23,7 @@ export * from './media-processor.js';
|
|
|
23
23
|
export * from './rich-webui.js';
|
|
24
24
|
export * from './rich-classic.js';
|
|
25
25
|
export * from './rich-carousel.js';
|
|
26
|
+
export * from './rich-menu.js';
|
|
27
|
+
export * from './a2ui.js';
|
|
26
28
|
export * from './use-sqlite-auth-state.js';
|
|
27
29
|
//# sourceMappingURL=index.js.map
|
package/lib/Utils/messages.js
CHANGED
|
@@ -563,6 +563,39 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
563
563
|
}
|
|
564
564
|
};
|
|
565
565
|
}
|
|
566
|
+
// viewOnceV2 / viewOnceV2Extension: bungkus pesan (termasuk teks) ke
|
|
567
|
+
// viewOnceMessageV2 / viewOnceMessageV2Extension. Untuk teks, flag viewOnce
|
|
568
|
+
// di dalam extendedTextMessage ikut di-set (sama seperti plugin `vo`).
|
|
569
|
+
const wantV2Ext = hasOptionalProperty(message, 'viewOnceV2Extension') && !!message.viewOnceV2Extension;
|
|
570
|
+
const wantV2 = hasOptionalProperty(message, 'viewOnceV2') && !!message.viewOnceV2;
|
|
571
|
+
if (wantV2Ext || wantV2) {
|
|
572
|
+
const innerType = Object.keys(m)[0];
|
|
573
|
+
const inner = m[innerType];
|
|
574
|
+
if (inner && typeof inner === 'object') {
|
|
575
|
+
if (innerType === 'extendedTextMessage') {
|
|
576
|
+
inner.viewOnce = true;
|
|
577
|
+
}
|
|
578
|
+
if (innerType === 'extendedTextMessage' || 'contextInfo' in inner) {
|
|
579
|
+
inner.contextInfo = inner.contextInfo || {};
|
|
580
|
+
if (inner.contextInfo.forwardingScore === undefined) {
|
|
581
|
+
inner.contextInfo.forwardingScore = 1;
|
|
582
|
+
}
|
|
583
|
+
if (inner.contextInfo.isForwarded === undefined) {
|
|
584
|
+
inner.contextInfo.isForwarded = true;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
const messageContextInfo = m.messageContextInfo;
|
|
589
|
+
if (messageContextInfo) {
|
|
590
|
+
delete m.messageContextInfo;
|
|
591
|
+
}
|
|
592
|
+
m = wantV2Ext
|
|
593
|
+
? { viewOnceMessageV2Extension: { message: m } }
|
|
594
|
+
: { viewOnceMessageV2: { message: m } };
|
|
595
|
+
if (messageContextInfo) {
|
|
596
|
+
m.messageContextInfo = messageContextInfo;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
566
599
|
if (shouldIncludeReportingToken(m)) {
|
|
567
600
|
m.messageContextInfo = m.messageContextInfo || {};
|
|
568
601
|
if (!m.messageContextInfo.messageSecret) {
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rich-menu.js — menu "rich response" (tombol + kartu geser + header gambar +
|
|
3
|
+
* footer open-URL) lewat botForwardedMessage > richResponseMessage >
|
|
4
|
+
* unifiedResponse, porting dari @vansnowi/baileys (`richMenu`).
|
|
5
|
+
*
|
|
6
|
+
* PERINGATAN (sama seperti rich-webui.js / rich-carousel.js):
|
|
7
|
+
* unifiedResponse adalah format GenAI internal WhatsApp — relayMessage bisa
|
|
8
|
+
* sukses tanpa error walau pesan tidak dirender di client penerima, dan nama
|
|
9
|
+
* typename-nya bisa berubah antar-versi WA. Tombol di sini adalah CTA widget
|
|
10
|
+
* dengan toast, BUKAN quick-reply yang mengirim balik pesan ke bot.
|
|
11
|
+
*
|
|
12
|
+
* Content yang dikembalikan siap dipakai dengan
|
|
13
|
+
* generateWAMessageFromContent(jid, content, { userJid }) lalu
|
|
14
|
+
* relayMessage(jid, msg.message, { messageId: msg.key.id }).
|
|
15
|
+
* Atau langsung: sock.richMenu(jid, content).
|
|
16
|
+
*/
|
|
17
|
+
import { randomBytes } from 'crypto';
|
|
18
|
+
|
|
19
|
+
const IMAGE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
20
|
+
/** ForwardOrigin.META_AI di proto */
|
|
21
|
+
const FORWARD_ORIGIN_META_AI = 4;
|
|
22
|
+
|
|
23
|
+
const toolCallId = () => randomBytes(8).toString('hex');
|
|
24
|
+
|
|
25
|
+
const section = (viewModel) => ({
|
|
26
|
+
__typename: 'GenAIUnifiedResponseSection',
|
|
27
|
+
view_model: viewModel
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const single = (primitive) => section({
|
|
31
|
+
__typename: 'GenAISingleLayoutViewModel',
|
|
32
|
+
primitive
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
/** Gambar inline: dirender lewat inline entity LaTeX 1 titik (trik agar gambar muncul di dalam teks). */
|
|
36
|
+
const inlineImagePrimitive = (image, { width, height, padding }) => ({
|
|
37
|
+
__typename: 'GenAIMarkdownTextUXPrimitive',
|
|
38
|
+
text: '{{header}}.{{/header}}',
|
|
39
|
+
inline_entities: [
|
|
40
|
+
{
|
|
41
|
+
__typename: 'GenAITextInlineEntity',
|
|
42
|
+
key: 'header',
|
|
43
|
+
metadata: {
|
|
44
|
+
__typename: 'GenAILatexItem',
|
|
45
|
+
latex_expression: '.',
|
|
46
|
+
font_height: 24,
|
|
47
|
+
padding,
|
|
48
|
+
latex_image: {
|
|
49
|
+
__typename: 'GenAIMediaItem',
|
|
50
|
+
mime_type: image.mime_type || 'image/png',
|
|
51
|
+
url: image.url,
|
|
52
|
+
url_fallback: image.url,
|
|
53
|
+
width: image.width || width,
|
|
54
|
+
height: image.height || height,
|
|
55
|
+
expiration_timestamp_ms: Date.now() + IMAGE_TTL_MS
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const normalizeButton = (btn, fallbackToast) => {
|
|
63
|
+
const label = typeof btn === 'string' ? btn : btn?.label ?? btn?.text ?? '';
|
|
64
|
+
const toast = typeof btn === 'object' && btn?.toast !== undefined ? btn.toast : fallbackToast;
|
|
65
|
+
return {
|
|
66
|
+
label: String(label),
|
|
67
|
+
state: 'PENDING',
|
|
68
|
+
kind: 'OTHER',
|
|
69
|
+
tool_call_id: toolCallId(),
|
|
70
|
+
toast: {
|
|
71
|
+
label: toast || '',
|
|
72
|
+
__typename: 'GenAI3PExtWidgetToast'
|
|
73
|
+
},
|
|
74
|
+
__typename: 'GenAI3PExtWidgetCTA'
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const widgetPrimitive = (title, buttons, toast) => ({
|
|
79
|
+
__typename: 'GenAI3PExtWidgetPrimitive',
|
|
80
|
+
header: {
|
|
81
|
+
__typename: 'GenAI3PExtWidgetStandardHeader',
|
|
82
|
+
title: title || ''
|
|
83
|
+
},
|
|
84
|
+
body: {
|
|
85
|
+
__typename: 'GenAI3PExtCalendarEventList',
|
|
86
|
+
ctas: buttons.map(btn => normalizeButton(btn, toast)),
|
|
87
|
+
sections: []
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Bangun content pesan rich menu (belum dikirim).
|
|
93
|
+
*
|
|
94
|
+
* @param {object} content
|
|
95
|
+
* @param {object} [content.header] { title, image: { url, inline, mime_type, width, height }, disclaimer, disclaimerText }
|
|
96
|
+
* @param {object} [content.body] { title, buttons: string[], toast } atau
|
|
97
|
+
* { carousel: true | row: true, cards: [{ title, buttons, toast }] }
|
|
98
|
+
* @param {object} [content.footer] { text, url, image: { url, mime_type, width, height } } — CTA open-URL hanya dibuat jika `url` diisi
|
|
99
|
+
* @param {object} [content.contextInfo] override contextInfo richResponseMessage
|
|
100
|
+
* @returns {object} content object (kompatibel proto.Message)
|
|
101
|
+
*/
|
|
102
|
+
export function buildRichMenuMessage(content = {}) {
|
|
103
|
+
const { header, body, footer } = content;
|
|
104
|
+
const sections = [];
|
|
105
|
+
let messageContextInfo;
|
|
106
|
+
if (header) {
|
|
107
|
+
const { disclaimer = false, disclaimerText = ' ', image, title = '' } = header;
|
|
108
|
+
if (disclaimer) {
|
|
109
|
+
messageContextInfo = { botMetadata: { messageDisclaimerText: disclaimerText } };
|
|
110
|
+
}
|
|
111
|
+
if (title) {
|
|
112
|
+
sections.push(single({ __typename: 'FOATextPrimitive', text: '# ' + title }));
|
|
113
|
+
}
|
|
114
|
+
if (image?.url) {
|
|
115
|
+
if (image.inline) {
|
|
116
|
+
sections.push(single(inlineImagePrimitive(image, { width: 500, height: 500, padding: 4 })));
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
sections.push(single({
|
|
120
|
+
__typename: 'GenAIImagePrimitive',
|
|
121
|
+
preview_image: {
|
|
122
|
+
__typename: 'GenAIMediaItem',
|
|
123
|
+
mime_type: image.mime_type || 'image/png',
|
|
124
|
+
url: image.url
|
|
125
|
+
},
|
|
126
|
+
full_image: {
|
|
127
|
+
__typename: 'GenAIMediaItem',
|
|
128
|
+
mime_type: image.mime_type || 'image/png',
|
|
129
|
+
url: image.url
|
|
130
|
+
}
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (body) {
|
|
136
|
+
const { cards, buttons, title = '', toast = '', carousel = false, row = false } = body;
|
|
137
|
+
if (carousel || row) {
|
|
138
|
+
if (cards?.length) {
|
|
139
|
+
sections.push(section({
|
|
140
|
+
__typename: carousel ? 'GenAIHScrollLayoutViewModel' : 'GenAIActionRowLayoutViewModel',
|
|
141
|
+
primitives: cards.map(card => widgetPrimitive(card?.title, card?.buttons || [], card?.toast))
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else if (buttons?.length) {
|
|
146
|
+
sections.push(single(widgetPrimitive(title, buttons, toast)));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (footer) {
|
|
150
|
+
const { text = '', url = '', image } = footer;
|
|
151
|
+
const primitives = [];
|
|
152
|
+
if (url) {
|
|
153
|
+
primitives.push({
|
|
154
|
+
__typename: 'GenAIFooterActionPrimitive',
|
|
155
|
+
cta_text: text || 'Open',
|
|
156
|
+
cta_type: 'OPEN_URL',
|
|
157
|
+
cta_url: url
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
if (image?.url) {
|
|
161
|
+
primitives.push(inlineImagePrimitive(image, { width: 100, height: 100, padding: -5 }));
|
|
162
|
+
}
|
|
163
|
+
if (primitives.length) {
|
|
164
|
+
sections.push({
|
|
165
|
+
view_model: {
|
|
166
|
+
__typename: 'GenAIActionRowLayoutViewModel',
|
|
167
|
+
primitives
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (!sections.length) {
|
|
173
|
+
throw new TypeError('richMenu: header/body/footer kosong — isi minimal satu (title, image, buttons/cards, atau footer.url)');
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
...(messageContextInfo ? { messageContextInfo } : {}),
|
|
177
|
+
botForwardedMessage: {
|
|
178
|
+
message: {
|
|
179
|
+
richResponseMessage: {
|
|
180
|
+
unifiedResponse: {
|
|
181
|
+
data: Buffer.from(JSON.stringify({ sections })).toString('base64')
|
|
182
|
+
},
|
|
183
|
+
contextInfo: {
|
|
184
|
+
isForwarded: true,
|
|
185
|
+
forwardOrigin: FORWARD_ORIGIN_META_AI,
|
|
186
|
+
...(content.contextInfo ?? {})
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Bangun + kirim rich menu lewat socket.
|
|
196
|
+
* @param {object} sock Baileys socket (harus punya relayMessage)
|
|
197
|
+
* @param {string} jid JID tujuan
|
|
198
|
+
* @param {object} content lihat buildRichMenuMessage
|
|
199
|
+
* @param {object} [options] { messageId }
|
|
200
|
+
*/
|
|
201
|
+
export async function sendRichMenu(sock, jid, content = {}, options = {}) {
|
|
202
|
+
if (!sock || typeof sock.relayMessage !== 'function') {
|
|
203
|
+
throw new TypeError('sendRichMenu: "sock" harus instance Baileys socket yang punya relayMessage()');
|
|
204
|
+
}
|
|
205
|
+
const message = buildRichMenuMessage(content);
|
|
206
|
+
const messageId = options.messageId || ('3EB0' + randomBytes(18).toString('hex').toUpperCase());
|
|
207
|
+
await sock.relayMessage(jid, message, { messageId });
|
|
208
|
+
return { messageId, message };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export default { buildRichMenuMessage, sendRichMenu };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rennzsync/baileys",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "10.
|
|
4
|
+
"version": "10.5.0",
|
|
5
5
|
"description": "renz/baileys — WhatsApp Multi-Device library rebased @whiskeysockets/baileys 7.0.0-rc14, using the original libsignal (GPL-3.0) Signal Protocol engine. Maintained by RennZz-Dev. Focus: multimedia WhatsApp bots, low RAM footprint.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "RennZz-Dev (https://github.com/renzxhub)",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/Defaults/index.ts"],"names":[],"mappings":"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"}
|