@vanzxy/baileys 1.6.0 → 1.6.2
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/WAProto/index.js +59524 -33708
- package/lib/Utils/MessageBuilder.js +151 -30
- package/lib/Utils/MessageBuilder_d.ts +1 -0
- package/lib/Utils/process-message.js +48 -1
- package/package.json +1 -1
|
@@ -696,6 +696,58 @@ class Button extends BaseBuilder {
|
|
|
696
696
|
return this;
|
|
697
697
|
}
|
|
698
698
|
|
|
699
|
+
// Vanz@Add 26-08-26 --- setBloksWidget(): Meta's Bloks/A2UI format, a sibling field to
|
|
700
|
+
// nativeFlowMessage on interactiveMessage (NOT part of the AIRich rich-response system —
|
|
701
|
+
// this is a real native interactive UI: checkboxes/text fields/buttons actually work).
|
|
702
|
+
// Captured traffic gives the A2UI payload as a flat `components` array where every node has
|
|
703
|
+
// an `id` and children/child reference OTHER nodes by id string. Authoring that by hand is
|
|
704
|
+
// error-prone (dangling ids, ordering), so this accepts a plain nested tree instead —
|
|
705
|
+
// { component, ...props, children: [...] } / { component, ...props, child: {...} } — and
|
|
706
|
+
// flattens it into that array itself, auto-assigning ids.
|
|
707
|
+
#flattenBloks(tree, out, counter = { n: 0 }, id = 'root') {
|
|
708
|
+
if (!tree || typeof tree !== 'object') throw new TypeError('setBloksWidget: every node needs a "component" type');
|
|
709
|
+
const { component, children, child, ...props } = tree;
|
|
710
|
+
if (typeof component !== 'string' || !component) throw new TypeError('setBloksWidget: every node needs a "component" type');
|
|
711
|
+
|
|
712
|
+
const node = { id, component, ...props };
|
|
713
|
+
|
|
714
|
+
if (Array.isArray(children)) {
|
|
715
|
+
node.children = children.map((c) => this.#flattenBloks(c, out, counter, `n${counter.n++}`));
|
|
716
|
+
} else if (child) {
|
|
717
|
+
node.child = this.#flattenBloks(child, out, counter, `n${counter.n++}`);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
out.push(node);
|
|
721
|
+
return id;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Set a Bloks/A2UI native widget (`bloksWidget`, `type: "im_a2ui"`) — a real interactive
|
|
726
|
+
* screen (images, video, checkboxes, text fields, buttons that fire an `action`), not a
|
|
727
|
+
* static card. Pass a nested tree; ids are assigned automatically.
|
|
728
|
+
* @param {Record<string, any>} tree Root node, e.g. `{ component: 'Column', children: [...] }`.
|
|
729
|
+
* @param {{uuid?: string, catalogId?: string, surfaceId?: string, version?: string}} [options]
|
|
730
|
+
*/
|
|
731
|
+
setBloksWidget(tree, { uuid = crypto.randomUUID(), catalogId = 'https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json', surfaceId, version = 'v0.9' } = {}) {
|
|
732
|
+
const components = [];
|
|
733
|
+
this.#flattenBloks(tree, components);
|
|
734
|
+
|
|
735
|
+
this._bloksWidget = {
|
|
736
|
+
uuid,
|
|
737
|
+
data: JSON.stringify({
|
|
738
|
+
version,
|
|
739
|
+
createSurface: {
|
|
740
|
+
surfaceId: surfaceId ?? `starcore-widget=${uuid}`,
|
|
741
|
+
catalogId,
|
|
742
|
+
components,
|
|
743
|
+
},
|
|
744
|
+
}),
|
|
745
|
+
type: 'im_a2ui',
|
|
746
|
+
};
|
|
747
|
+
|
|
748
|
+
return this;
|
|
749
|
+
}
|
|
750
|
+
|
|
699
751
|
/**
|
|
700
752
|
* Low-level escape hatch: push a raw native-flow button by name. Prefer the
|
|
701
753
|
* dedicated `add*()` helpers below when one exists — they validate the
|
|
@@ -1290,22 +1342,26 @@ class Button extends BaseBuilder {
|
|
|
1290
1342
|
|
|
1291
1343
|
/** @returns {Record<string, any>} The final content object, without generating/wrapping a WAMessage. Useful when composing this interactive card into something else (e.g. Carousel). */
|
|
1292
1344
|
async build(jid, { ...options } = {}) {
|
|
1293
|
-
if (this._buttons.length === 0) {
|
|
1294
|
-
throw new Error('Button requires at least one button (use addReply/addUrl/addCall/addSelection/addButton/...)');
|
|
1345
|
+
if (this._buttons.length === 0 && !this._bloksWidget) {
|
|
1346
|
+
throw new Error('Button requires at least one button (use addReply/addUrl/addCall/addSelection/addButton/...) or a Bloks widget (setBloksWidget())');
|
|
1295
1347
|
}
|
|
1296
1348
|
|
|
1297
|
-
if (this.#isLoneSingleSelect()) {
|
|
1349
|
+
if (this._buttons.length > 0 && this.#isLoneSingleSelect()) {
|
|
1298
1350
|
return generateWAMessageFromContent(jid, { ...this._extraPayload, ...this.#toListMessage() }, { ...options });
|
|
1299
1351
|
}
|
|
1300
1352
|
|
|
1301
|
-
const message = await this.toCard();
|
|
1353
|
+
const message = this._buttons.length > 0 ? await this.toCard() : {};
|
|
1302
1354
|
|
|
1303
1355
|
return generateWAMessageFromContent(
|
|
1304
1356
|
jid,
|
|
1305
1357
|
{
|
|
1358
|
+
...(this._bloksWidget && {
|
|
1359
|
+
messageContextInfo: { messageSecret: crypto.randomBytes(32) },
|
|
1360
|
+
}),
|
|
1306
1361
|
...this._extraPayload,
|
|
1307
1362
|
interactiveMessage: {
|
|
1308
1363
|
...message,
|
|
1364
|
+
...(this._bloksWidget && { bloksWidget: this._bloksWidget }),
|
|
1309
1365
|
contextInfo: this._contextInfo,
|
|
1310
1366
|
},
|
|
1311
1367
|
},
|
|
@@ -1328,12 +1384,25 @@ class Button extends BaseBuilder {
|
|
|
1328
1384
|
const bizContent = this.#isLoneSingleSelect()
|
|
1329
1385
|
? [{ tag: 'list', attrs: { v: '2', type: 'product_list' } }]
|
|
1330
1386
|
: [
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1387
|
+
...(this._buttons.length > 0
|
|
1388
|
+
? [
|
|
1389
|
+
{
|
|
1390
|
+
tag: 'interactive',
|
|
1391
|
+
attrs: { type: 'native_flow', v: '1' },
|
|
1392
|
+
content: [this.#buildNativeFlowNode()],
|
|
1393
|
+
},
|
|
1394
|
+
]
|
|
1395
|
+
: []),
|
|
1396
|
+
...(this._bloksWidget
|
|
1397
|
+
? [
|
|
1398
|
+
{
|
|
1399
|
+
tag: 'quality_control',
|
|
1400
|
+
attrs: { decision_id: crypto.randomUUID().replace(/-/g, ''), source_type: 'third_party' },
|
|
1401
|
+
content: [{ tag: 'decision_source', attrs: { value: 'df' } }],
|
|
1402
|
+
},
|
|
1403
|
+
]
|
|
1404
|
+
: []),
|
|
1405
|
+
];
|
|
1337
1406
|
|
|
1338
1407
|
await this.#client.relayMessage(msg.key.remoteJid, msg.message, {
|
|
1339
1408
|
messageId: msg.key.id,
|
|
@@ -1776,9 +1845,10 @@ class AIRich extends BaseBuilder {
|
|
|
1776
1845
|
}
|
|
1777
1846
|
|
|
1778
1847
|
return (...args) => {
|
|
1779
|
-
const opts = args.find((a) => a && typeof a === 'object' && !Array.isArray(a) && !Buffer.isBuffer(a) && ('id' in a || 'insertAt' in a));
|
|
1848
|
+
const opts = args.find((a) => a && typeof a === 'object' && !Array.isArray(a) && !Buffer.isBuffer(a) && ('id' in a || 'insertAt' in a || 'replace' in a));
|
|
1780
1849
|
const id = opts?.id;
|
|
1781
1850
|
const insertAt = opts?.insertAt;
|
|
1851
|
+
const replace = opts?.replace;
|
|
1782
1852
|
|
|
1783
1853
|
const subBefore = target._submessages.length;
|
|
1784
1854
|
const secBefore = target._sections.length;
|
|
@@ -1810,6 +1880,31 @@ class AIRich extends BaseBuilder {
|
|
|
1810
1880
|
const lastSec = anchor.secItems[anchor.secItems.length - 1];
|
|
1811
1881
|
const secIdx = lastSec ? target._sections.indexOf(lastSec) + 1 : target._sections.length;
|
|
1812
1882
|
target._sections.splice(secIdx, 0, ...secItems);
|
|
1883
|
+
} else if (replace) {
|
|
1884
|
+
// replace: delete the old block's items at their current positions,
|
|
1885
|
+
// then insert new items at the same positions
|
|
1886
|
+
const old = target._blocks.get(replace);
|
|
1887
|
+
if (!old) throw new Error(`replace: no block registered with id "${replace}" (register it first with { id: "${replace}" })`);
|
|
1888
|
+
|
|
1889
|
+
let subIdx = old.subItems.length > 0 ? target._submessages.indexOf(old.subItems[0]) : target._submessages.length;
|
|
1890
|
+
if (subIdx === -1) subIdx = target._submessages.length;
|
|
1891
|
+
for (const item of old.subItems) {
|
|
1892
|
+
const i = target._submessages.indexOf(item);
|
|
1893
|
+
if (i !== -1) target._submessages.splice(i, 1);
|
|
1894
|
+
}
|
|
1895
|
+
target._submessages.splice(subIdx, 0, ...subItems);
|
|
1896
|
+
|
|
1897
|
+
let secIdx = old.secItems.length > 0 ? target._sections.indexOf(old.secItems[0]) : target._sections.length;
|
|
1898
|
+
if (secIdx === -1) secIdx = target._sections.length;
|
|
1899
|
+
for (const item of old.secItems) {
|
|
1900
|
+
const i = target._sections.indexOf(item);
|
|
1901
|
+
if (i !== -1) target._sections.splice(i, 1);
|
|
1902
|
+
}
|
|
1903
|
+
target._sections.splice(secIdx, 0, ...secItems);
|
|
1904
|
+
|
|
1905
|
+
target._blocks.delete(replace);
|
|
1906
|
+
if (id) target._blocks.set(id, { subItems, secItems });
|
|
1907
|
+
else target._blocks.set(replace, { subItems, secItems });
|
|
1813
1908
|
} else {
|
|
1814
1909
|
target._submessages.push(...subItems);
|
|
1815
1910
|
target._sections.push(...secItems);
|
|
@@ -2014,21 +2109,38 @@ class AIRich extends BaseBuilder {
|
|
|
2014
2109
|
}
|
|
2015
2110
|
|
|
2016
2111
|
addSource(sources = [], { resolveUrl = false } = {}) {
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2112
|
+
// Accept 3 formats:
|
|
2113
|
+
// 1. Array of objects: [{ icon, url, title, subtitle }] — from v4.7 example
|
|
2114
|
+
// 2. Array of string arrays: [['iconUrl', 'url', 'text']]
|
|
2115
|
+
// 3. Single string array (shorthand for format 2): ['iconUrl', 'url', 'text']
|
|
2116
|
+
const isObjArray = Array.isArray(sources) && sources.every((item) => item && typeof item === 'object' && !Array.isArray(item));
|
|
2117
|
+
const isStrArrayArray = Array.isArray(sources) && sources.every((item) => Array.isArray(item) && item.every((v) => typeof v === 'string'));
|
|
2118
|
+
const isFlatStrArray = Array.isArray(sources) && sources.every((item) => typeof item === 'string');
|
|
2119
|
+
|
|
2120
|
+
if (!isObjArray && !isStrArrayArray && !isFlatStrArray) {
|
|
2121
|
+
throw new TypeError('addSource(): pass an array of objects { icon, url, title, subtitle } or string arrays [iconUrl, url, text]');
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
let normalized;
|
|
2125
|
+
if (isObjArray) {
|
|
2126
|
+
normalized = sources.map((item) => ({
|
|
2127
|
+
icon: item.icon ?? item.iconUrl ?? item.favicon ?? '',
|
|
2128
|
+
url: item.url ?? '',
|
|
2129
|
+
text: item.title ?? item.displayName ?? item.text ?? '',
|
|
2130
|
+
subtitle: item.subtitle ?? 'AI',
|
|
2131
|
+
}));
|
|
2132
|
+
} else {
|
|
2133
|
+
const arr = isFlatStrArray ? [sources] : sources;
|
|
2134
|
+
normalized = arr.map(([icon = '', url = '', text = '']) => ({ icon, url, text, subtitle: 'AI' }));
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
const source = normalized.map(({ icon, url, text, subtitle }) => ({
|
|
2026
2138
|
source_type: 'THIRD_PARTY',
|
|
2027
|
-
source_display_name: text
|
|
2028
|
-
source_subtitle:
|
|
2029
|
-
source_url: url
|
|
2139
|
+
source_display_name: text,
|
|
2140
|
+
source_subtitle: subtitle,
|
|
2141
|
+
source_url: url,
|
|
2030
2142
|
favicon: {
|
|
2031
|
-
url: Toolkit.resolveMedia(this.#client, icon
|
|
2143
|
+
url: Toolkit.resolveMedia(this.#client, icon, 'image', { resolveUrl }),
|
|
2032
2144
|
mime_type: 'image/jpeg',
|
|
2033
2145
|
width: 16,
|
|
2034
2146
|
height: 16,
|
|
@@ -2587,8 +2699,10 @@ class AIRich extends BaseBuilder {
|
|
|
2587
2699
|
const items = Array.isArray(data) ? data : [data];
|
|
2588
2700
|
|
|
2589
2701
|
items.forEach((item, i) => {
|
|
2590
|
-
|
|
2591
|
-
|
|
2702
|
+
// header.title or top-level title required
|
|
2703
|
+
const hasTitle = item?.title || item?.header?.title;
|
|
2704
|
+
if (!hasTitle) {
|
|
2705
|
+
throw new TypeError(`addWidget() item[${i}] is missing a required "title" (or "header.title")`);
|
|
2592
2706
|
}
|
|
2593
2707
|
const ctas = item.ctas ?? item.actions;
|
|
2594
2708
|
if (!Array.isArray(ctas) || !ctas.length) {
|
|
@@ -2598,22 +2712,29 @@ class AIRich extends BaseBuilder {
|
|
|
2598
2712
|
|
|
2599
2713
|
this._submessages.push({
|
|
2600
2714
|
messageType: 2,
|
|
2601
|
-
messageText: items.map((item) => item.title).join(', '),
|
|
2715
|
+
messageText: items.map((item) => item.header?.title ?? item.title).join(', '),
|
|
2602
2716
|
});
|
|
2603
2717
|
|
|
2604
2718
|
const widgets = items.map((item) => {
|
|
2605
2719
|
const ctas = item.ctas ?? item.actions;
|
|
2720
|
+
// header accepts either a string title (legacy) or an object { title, subtitle }
|
|
2721
|
+
const headerTitle = item.header?.title ?? item.title;
|
|
2722
|
+
const headerSubtitle = item.header?.subtitle ?? item.subtitle ?? undefined;
|
|
2606
2723
|
return {
|
|
2607
|
-
header: {
|
|
2724
|
+
header: {
|
|
2725
|
+
title: headerTitle,
|
|
2726
|
+
...(headerSubtitle !== undefined && { subtitle: headerSubtitle }),
|
|
2727
|
+
__typename: 'GenAI3PExtWidgetStandardHeader',
|
|
2728
|
+
},
|
|
2608
2729
|
body: {
|
|
2609
2730
|
sections: item.sections ?? [],
|
|
2610
2731
|
ctas: ctas.map((cta, idx) => ({
|
|
2611
2732
|
label: cta.label ?? '',
|
|
2612
2733
|
state: cta.state ?? 'PENDING',
|
|
2613
2734
|
kind: cta.kind ?? 'OTHER',
|
|
2614
|
-
tool_call_id: cta.tool_call_id ?? String(idx).padStart(2, '0'),
|
|
2735
|
+
tool_call_id: cta.tool_call_id ?? cta.id ?? String(idx).padStart(2, '0'),
|
|
2615
2736
|
...(cta.toast !== false && {
|
|
2616
|
-
toast: { label: typeof cta.toast === 'string' ? cta.toast :
|
|
2737
|
+
toast: { label: typeof cta.toast === 'string' ? cta.toast : headerTitle, __typename: 'GenAI3PExtWidgetToast' },
|
|
2617
2738
|
}),
|
|
2618
2739
|
__typename: 'GenAI3PExtWidgetCTA',
|
|
2619
2740
|
})),
|
|
@@ -45,6 +45,7 @@ export class Button extends BaseBuilder {
|
|
|
45
45
|
setMedia(obj: Record<string, any>): this;
|
|
46
46
|
clearButtons(): this;
|
|
47
47
|
setParams(obj: Record<string, any>): this;
|
|
48
|
+
setBloksWidget(tree: Record<string, any>, options?: { uuid?: string; catalogId?: string; surfaceId?: string; version?: string }): this;
|
|
48
49
|
addButton(name: string, params: string | Record<string, any>): this;
|
|
49
50
|
makeRow(header?: string, title?: string, description?: string, id?: string): this;
|
|
50
51
|
makeSection(title?: string, highlight_label?: string): this;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Boom } from '@hapi/boom';
|
|
2
|
+
import $protobuf from 'protobufjs/minimal.js';
|
|
3
|
+
const { Reader } = $protobuf;
|
|
2
4
|
import { proto } from '../../WAProto/index.js';
|
|
3
5
|
import { WAMessageStubType } from '../Types/index.js';
|
|
4
6
|
import { getContentType, normalizeMessageContent } from '../Utils/messages.js';
|
|
@@ -14,6 +16,51 @@ const REAL_MSG_STUB_TYPES = new Set([
|
|
|
14
16
|
WAMessageStubType.CALL_MISSED_VOICE
|
|
15
17
|
]);
|
|
16
18
|
const REAL_MSG_REQ_ME_STUB_TYPES = new Set([WAMessageStubType.GROUP_PARTICIPANT_ADD]);
|
|
19
|
+
// Vanz@Fix 26-08-26 --- the 26-08-26 WAProto refresh dropped `LIDMigrationMappingSyncPayload`
|
|
20
|
+
// / `LIDMigrationMapping` from the published schema; only the opaque envelope
|
|
21
|
+
// `LIDMigrationMappingSyncMessage { encodedMappingPayload: bytes }` is generated now.
|
|
22
|
+
// Nothing suggests the *inner* wire layout actually changed (WA's extractor just stopped
|
|
23
|
+
// walking this nested message), so we decode it by hand instead of guessing a new shape.
|
|
24
|
+
// TODO: verify against live traffic on the next audit pass — if Meta did change the inner
|
|
25
|
+
// layout this will start throwing and LID/PN pairs will silently stop syncing.
|
|
26
|
+
function decodeLidMigrationMappingSyncPayload(buf) {
|
|
27
|
+
const r = Reader.create(buf);
|
|
28
|
+
const out = { pnToLidMappings: [], chatDbMigrationTimestamp: undefined };
|
|
29
|
+
while (r.pos < r.len) {
|
|
30
|
+
const tag = r.uint32();
|
|
31
|
+
switch (tag >>> 3) {
|
|
32
|
+
case 1: {
|
|
33
|
+
const len = r.uint32();
|
|
34
|
+
const end = r.pos + len;
|
|
35
|
+
const entry = { pn: undefined, assignedLid: undefined, latestLid: undefined };
|
|
36
|
+
while (r.pos < end) {
|
|
37
|
+
const t2 = r.uint32();
|
|
38
|
+
switch (t2 >>> 3) {
|
|
39
|
+
case 1:
|
|
40
|
+
entry.pn = r.uint64();
|
|
41
|
+
break;
|
|
42
|
+
case 2:
|
|
43
|
+
entry.assignedLid = r.uint64();
|
|
44
|
+
break;
|
|
45
|
+
case 3:
|
|
46
|
+
entry.latestLid = r.uint64();
|
|
47
|
+
break;
|
|
48
|
+
default:
|
|
49
|
+
r.skipType(t2 & 7);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
out.pnToLidMappings.push(entry);
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
case 2:
|
|
56
|
+
out.chatDbMigrationTimestamp = r.uint64();
|
|
57
|
+
break;
|
|
58
|
+
default:
|
|
59
|
+
r.skipType(tag & 7);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
17
64
|
async function storeTcTokensFromHistorySync(chats, signalRepository, keyStore, logger) {
|
|
18
65
|
const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
|
|
19
66
|
const candidates = [];
|
|
@@ -462,7 +509,7 @@ const processMessage = async (message, { shouldProcessHistoryMsg, placeholderRes
|
|
|
462
509
|
break;
|
|
463
510
|
case proto.Message.ProtocolMessage.Type.LID_MIGRATION_MAPPING_SYNC:
|
|
464
511
|
const encodedPayload = protocolMsg.lidMigrationMappingSyncMessage?.encodedMappingPayload;
|
|
465
|
-
const { pnToLidMappings, chatDbMigrationTimestamp } =
|
|
512
|
+
const { pnToLidMappings, chatDbMigrationTimestamp } = decodeLidMigrationMappingSyncPayload(encodedPayload);
|
|
466
513
|
logger?.debug({ pnToLidMappings, chatDbMigrationTimestamp }, 'got lid mappings and chat db migration timestamp');
|
|
467
514
|
const pairs = [];
|
|
468
515
|
for (const { pn, latestLid, assignedLid } of pnToLidMappings) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vanzxy/baileys",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.2",
|
|
4
4
|
"description": "Enhanced Baileys fork by Vanzxy — based on @itsliaaa/baileys + @whiskeysockets/baileys with fixes for audio group status and clean media without newsletter button.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./lib/index.js",
|