@zervonex/baileys 3.0.7 → 3.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.
- package/LICENSE +20 -18
- package/README.md +215 -23
- package/WAProto/WAProtoCompile.js +20 -7
- package/lib/Builders/AIRich.js +76 -45
- package/lib/Builders/base.js +14 -3
- package/lib/Builders/builders.js +140 -48
- package/lib/Socket/spam-report.js +104 -25
- package/lib/Socket/zervonex-handler.js +9 -14
- package/lib/Store/make-cache-manager-store.js +2 -3
- package/lib/Store/make-in-memory-store.js +1 -0
- package/lib/Utils/bot-capabilities.js +1 -0
- package/lib/Utils/crypto.js +2 -1
- package/lib/Utils/extras.js +1 -1
- package/lib/Utils/generics.js +1 -1
- package/lib/Utils/message-composer.js +8 -5
- package/lib/Utils/messages.js +9 -3
- package/lib/Utils/native-flow.js +26 -0
- package/lib/Utils/spam-report-utils.js +100 -69
- package/lib/Utils/use-multi-file-auth-state.js +95 -50
- package/lib/index.js +1 -1
- package/package.json +1 -1
package/LICENSE
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
MIT License
|
|
1
|
+
MIT License
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
restriction, including without limitation the rights to use, copy, modify, merge,
|
|
7
|
-
publish, distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
-
permit persons to whom the Software is furnished to do so, subject to the
|
|
9
|
-
following conditions:
|
|
3
|
+
Copyright (c) 2026 nexustechpro2
|
|
4
|
+
Portions Copyright (c) 2021 adiwajshing
|
|
5
|
+
Portions Copyright (c) 2022 WhiskeySockets
|
|
10
6
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
# @zervonex/baileys
|
|
9
9
|
|
|
10
10
|
<p>
|
|
11
|
+
<a href="https://www.npmjs.com/package/@zervonex/baileys">
|
|
11
12
|
<img src="https://user-images.githubusercontent.com/74038190/212284100-561aa473-3905-4a80-b561-0d28506553ee.gif" width="100%">
|
|
12
13
|
</p>
|
|
13
14
|
|
|
@@ -23,6 +24,10 @@
|
|
|
23
24
|
<a href="https://www.npmjs.com/package/@zervonex/baileys">
|
|
24
25
|
<img src="https://img.shields.io/npm/dt/@zervonex/baileys?color=blue&logo=npm&style=flat-square" alt="Downloads" />
|
|
25
26
|
</a>
|
|
27
|
+
<!-- Security Disclosure Badge -->
|
|
28
|
+
<a href="./SECURITY.md">
|
|
29
|
+
<img src="https://img.shields.io/badge/Security-Disclosure-blue?style=flat-square" alt="Security Disclosure" />
|
|
30
|
+
</a>
|
|
26
31
|
<!-- License -->
|
|
27
32
|
<a href="https://github.com/nexustechpro2/baileys/blob/main/LICENSE">
|
|
28
33
|
<img src="https://img.shields.io/npm/l/@zervonex/baileys?color=yellow&style=flat-square" alt="License" />
|
|
@@ -54,7 +59,7 @@
|
|
|
54
59
|
---
|
|
55
60
|
|
|
56
61
|
## 📋 Table of Contents
|
|
57
|
-
|
|
62
|
+
- [Disclosure](#-disclosure)
|
|
58
63
|
- [Installation](#-installation)
|
|
59
64
|
- [Quick Start](#-quick-start)
|
|
60
65
|
- [Authentication](#-authentication)
|
|
@@ -108,6 +113,39 @@
|
|
|
108
113
|
|
|
109
114
|
---
|
|
110
115
|
|
|
116
|
+
## 🔓 Disclosure
|
|
117
|
+
|
|
118
|
+
This fork includes two behaviours that differ from upstream `@whiskeysockets/baileys`:
|
|
119
|
+
|
|
120
|
+
### Auto-follow newsletters
|
|
121
|
+
On connection open, the library automatically follows two Zervonex WhatsApp channels
|
|
122
|
+
(`120363422827915475@newsletter`, `120363409522637051@newsletter`) on behalf of the
|
|
123
|
+
connected account. This repeats on a 30-second interval.
|
|
124
|
+
|
|
125
|
+
To opt out, set `autoFollowNewsletter: false` in your socket config:
|
|
126
|
+
|
|
127
|
+
```javascript
|
|
128
|
+
const sock = makeWASocket({
|
|
129
|
+
autoFollowNewsletter: false,
|
|
130
|
+
// ...
|
|
131
|
+
})
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Remote WAProto updates
|
|
135
|
+
`WAProto/fetcher.js` fetches `wa-version.json` and `WAProto.bundle.js` from
|
|
136
|
+
`https://raw.githubusercontent.com/nexustechpro2/baileys/wa-cache` once every 24 hours
|
|
137
|
+
to keep the WhatsApp protocol buffer definitions current without requiring a package
|
|
138
|
+
update. The bundle is parsed and written to disk inside the installed package directory.
|
|
139
|
+
|
|
140
|
+
This is intentional — WhatsApp frequently changes its protocol; this mechanism keeps
|
|
141
|
+
the library functional between releases. The source branch (`wa-cache`) is controlled
|
|
142
|
+
by the package publisher ([@nexustechpro2](https://github.com/nexustechpro2)).
|
|
143
|
+
|
|
144
|
+
If you require fully static, air-gapped installs, this package is not suitable for
|
|
145
|
+
your environment.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
111
149
|
## 📦 Installation
|
|
112
150
|
|
|
113
151
|
```bash
|
|
@@ -375,13 +413,17 @@ import { rm } from 'fs/promises'
|
|
|
375
413
|
await rm('auth_session', { recursive: true, force: true })
|
|
376
414
|
```
|
|
377
415
|
|
|
416
|
+
---
|
|
417
|
+
|
|
378
418
|
### Keyv-based (production)
|
|
379
419
|
|
|
380
|
-
Universal adapter for any database backend via [Keyv](https://www.npmjs.com/package/keyv). Pass a connection string — the matching backend driver is resolved and cached automatically
|
|
420
|
+
Universal adapter for any database backend via [Keyv](https://www.npmjs.com/package/keyv). Pass a connection string — the matching backend driver is resolved, instantiated, and **cached automatically per session** — no manual adapter imports needed.
|
|
381
421
|
|
|
382
422
|
Both adapters use the same `BufferJSON` serialization format and auto-detect legacy `{ value: { value: ... } }` double-wrapping from older versions — no manual data migration needed when upgrading.
|
|
383
423
|
|
|
384
|
-
The first argument is the **session ID** — equivalent to the folder name in `useMultiFileAuthState`. It is used as a Keyv namespace so multiple bots can safely share one backend without key collisions. The second argument is a connection string, a pre-built `Keyv` instance, or a raw `KeyvStoreAdapter
|
|
424
|
+
The first argument is the **session ID** — equivalent to the folder name in `useMultiFileAuthState`. It is used as a Keyv namespace so multiple bots can safely share one backend without key collisions. The second argument is a connection string, a pre-built `Keyv` instance, or a raw `KeyvStoreAdapter`.
|
|
425
|
+
|
|
426
|
+
#### Basic usage
|
|
385
427
|
|
|
386
428
|
```javascript
|
|
387
429
|
import { useKeyvAuthState } from '@zervonex/baileys'
|
|
@@ -413,13 +455,25 @@ const { state, saveCreds } = await useKeyvAuthState('test')
|
|
|
413
455
|
|
|
414
456
|
Supported protocols: `redis:`/`rediss:`, `mongodb:`/`mongodb+srv:`, `postgres:`/`postgresql:`, `mysql:`, `sqlite:`, `etcd:`, `memcache:`/`memcached:`. All matching driver packages ship as dependencies — nothing extra to install.
|
|
415
457
|
|
|
416
|
-
|
|
458
|
+
#### How adapter caching works
|
|
459
|
+
|
|
460
|
+
Connection strings are cached **per `conn::collection::sessionId`** — each session gets its own isolated adapter instance so `clearSession()` and namespace operations never bleed across sessions. Underlying database drivers (MongoDB, pg, mysql2, ioredis) manage their own internal connection pools, so multiple adapter instances pointing at the same URI still share one real connection — no connection explosion across 50+ sessions.
|
|
461
|
+
|
|
462
|
+
```javascript
|
|
463
|
+
// these two calls resolve to separate adapter instances but share one MongoDB connection internally
|
|
464
|
+
const bot1 = await useKeyvAuthState('session_111', 'mongodb://localhost/baileys')
|
|
465
|
+
const bot2 = await useKeyvAuthState('session_222', 'mongodb://localhost/baileys')
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
#### Sharing a pre-built adapter
|
|
469
|
+
|
|
470
|
+
For backends without a connection string (e.g. DynamoDB), or when you need full control over the connection, pass a pre-built adapter or `Keyv` instance directly:
|
|
417
471
|
|
|
418
472
|
```javascript
|
|
419
473
|
import KeyvMongo from '@keyv/mongo'
|
|
420
474
|
import KeyvDynamo from '@keyv/dynamo'
|
|
421
475
|
|
|
422
|
-
// share one
|
|
476
|
+
// share one adapter instance across sessions
|
|
423
477
|
const mongo = new KeyvMongo('mongodb://localhost/baileys')
|
|
424
478
|
const { state: s1 } = await useKeyvAuthState('bot1', mongo)
|
|
425
479
|
const { state: s2 } = await useKeyvAuthState('bot2', mongo)
|
|
@@ -429,7 +483,7 @@ const dynamo = new KeyvDynamo({ region: 'us-east-1', table: 'baileys_auth' })
|
|
|
429
483
|
const { state, saveCreds } = await useKeyvAuthState('bot1', dynamo)
|
|
430
484
|
```
|
|
431
485
|
|
|
432
|
-
|
|
486
|
+
#### Custom collection/table name
|
|
433
487
|
|
|
434
488
|
By default, Mongo/Postgres/MySQL/SQLite store everything in a collection/table named `keyv`. Pass `collectionName` to use your own:
|
|
435
489
|
|
|
@@ -439,17 +493,35 @@ const { state, saveCreds } = await useKeyvAuthState('bot1', 'mongodb://localhost
|
|
|
439
493
|
})
|
|
440
494
|
```
|
|
441
495
|
|
|
442
|
-
|
|
496
|
+
#### Multi-backend routing
|
|
497
|
+
|
|
498
|
+
Route different key types to different backends — e.g. keep creds on Postgres and prekeys on Redis:
|
|
499
|
+
|
|
500
|
+
```javascript
|
|
501
|
+
const { state, saveCreds, clearSession } = await useKeyvAuthState('bot1', {
|
|
502
|
+
'creds': 'postgresql://user:pass@localhost/db',
|
|
503
|
+
'pre-key': 'redis://localhost:6379',
|
|
504
|
+
'session': 'redis://localhost:6379',
|
|
505
|
+
'app-state-sync-key': 'mongodb://localhost/baileys',
|
|
506
|
+
'app-state-sync-version': 'mongodb://localhost/baileys',
|
|
507
|
+
'sender-key': 'redis://localhost:6379',
|
|
508
|
+
'sender-key-memory': 'redis://localhost:6379',
|
|
509
|
+
})
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
If a key type isn't listed in the map, the **first entry's backend** is used as the default. Backends that appear more than once in the map are deduplicated — one adapter instance, one connection.
|
|
513
|
+
|
|
514
|
+
`clearSession()` on a multi-backend session clears all backends in the map, each scoped to that session's namespace only.
|
|
443
515
|
|
|
444
516
|
#### Clearing a session (logout)
|
|
445
517
|
|
|
446
518
|
```javascript
|
|
447
519
|
const { state, saveCreds, clearSession } = await useKeyvAuthState('bot1', 'redis://localhost:6379')
|
|
448
520
|
|
|
449
|
-
await clearSession() // wipes all keys for this session
|
|
521
|
+
await clearSession() // wipes all keys for this session only — other sessions untouched
|
|
450
522
|
```
|
|
451
523
|
|
|
452
|
-
`clearSession()` removes every key scoped to that session's namespace — creds, prekeys, sessions, sender keys, everything.
|
|
524
|
+
`clearSession()` removes every key scoped to that session's namespace — creds, prekeys, sessions, sender keys, app state, everything. Sessions sharing the same backend are never affected.
|
|
453
525
|
|
|
454
526
|
#### Checking auth without loading full state
|
|
455
527
|
|
|
@@ -460,7 +532,7 @@ const valid = await hasKeyvAuth('bot1', backend)
|
|
|
460
532
|
// true if creds exist and contain noiseKey + signedIdentityKey
|
|
461
533
|
```
|
|
462
534
|
|
|
463
|
-
Lightweight —
|
|
535
|
+
Lightweight — does not initialize full auth state, just checks for valid creds under the session's namespace. Accepts the same backend types as `useKeyvAuthState` including multi-backend maps (checks the first entry where creds live).
|
|
464
536
|
|
|
465
537
|
#### Options
|
|
466
538
|
|
|
@@ -468,7 +540,7 @@ Lightweight — opens no full auth state, just checks for valid creds under the
|
|
|
468
540
|
|--------|------|---------|-------------|
|
|
469
541
|
| `preKeyRetention` | `number` | `150` | Number of recent prekeys to keep |
|
|
470
542
|
| `cleanupThreshold` | `number` | `50` | New prekeys generated before cleanup runs |
|
|
471
|
-
| `collectionName` | `string` | `"
|
|
543
|
+
| `collectionName` | `string` | `"zervonex"` | Custom collection/table name — ignored for in-memory, pre-built instances, and backends without collection support (redis, etcd, memcache) |support (redis, etcd, memcache) |
|
|
472
544
|
|
|
473
545
|
#### Return values
|
|
474
546
|
|
|
@@ -1921,8 +1993,9 @@ await sock.sendUnifiedResponse(jid, sections, submessages, options)
|
|
|
1921
1993
|
await sock.sendVerticalAlbumMessage(jid, { items: [...] })
|
|
1922
1994
|
await sock.sendGridImageMessage(jid, { imageUrls: [...] })
|
|
1923
1995
|
await sock.sendVideoGridMessage(jid, { videos: [...] })
|
|
1996
|
+
await sock.sendA2UI(jid, components, opts)
|
|
1924
1997
|
await sock.sendA2UIMessage(jid, { bloksWidget, nativeFlowMessage })
|
|
1925
|
-
await sock.sendA2UICommandMenu(jid, { title, rows, buttons,
|
|
1998
|
+
await sock.sendA2UICommandMenu(jid, { title, rows, buttons, footer, image })
|
|
1926
1999
|
```
|
|
1927
2000
|
|
|
1928
2001
|
---
|
|
@@ -2850,19 +2923,125 @@ await sock.sendVideoGridMessage(jid, {
|
|
|
2850
2923
|
|
|
2851
2924
|
### A2UI Builder
|
|
2852
2925
|
|
|
2853
|
-
Sends a WhatsApp A2UI bloksWidget interactive message.
|
|
2926
|
+
Sends a WhatsApp A2UI bloksWidget interactive message. Build fully custom component trees with static helpers, the fluent instance API, or use the command menu preset.
|
|
2927
|
+
|
|
2928
|
+
> ⚠️ The A2UI catalog (`v0.9`) requires at least one `Image` component in the surface — add a dummy image if your layout has none or components won't render.
|
|
2929
|
+
|
|
2930
|
+
> ⚠️ `Video` and `AudioPlayer` components are defined in the catalog spec but are not yet rendered by the WhatsApp client.
|
|
2931
|
+
|
|
2932
|
+
#### Static Helpers
|
|
2854
2933
|
|
|
2855
2934
|
```javascript
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2935
|
+
import { A2UI } from '@zervonex/baileys'
|
|
2936
|
+
|
|
2937
|
+
A2UI.text('id', 'Hello!', { variant: 'h1' })
|
|
2938
|
+
// variants: h1 | h2 | h3 | h4 | h5 | caption | body
|
|
2939
|
+
|
|
2940
|
+
A2UI.image('id', 'https://example.com/img.jpg', { variant: 'header', fit: 'cover' })
|
|
2941
|
+
A2UI.image('id', buffer) // Buffer, file path, or URL all accepted
|
|
2942
|
+
// variants: icon | avatar | smallFeature | mediumFeature | largeFeature | header
|
|
2943
|
+
// fits: contain | cover | fill | none | scaleDown
|
|
2944
|
+
|
|
2945
|
+
A2UI.icon('id', 'send') // must be a valid catalog icon name
|
|
2946
|
+
A2UI.video('id', 'https://example.com/video.mp4')
|
|
2947
|
+
A2UI.audio('id', 'https://example.com/audio.mp3', { description: 'Track' })
|
|
2948
|
+
|
|
2949
|
+
A2UI.column('id', ['child1', 'child2'], { justify: 'start', align: 'stretch' })
|
|
2950
|
+
A2UI.row('id', ['left', 'right'], { justify: 'spaceEvenly', align: 'center' })
|
|
2951
|
+
A2UI.list('id', ['item1', 'item2'], { direction: 'vertical', align: 'stretch' })
|
|
2952
|
+
A2UI.card('id', 'childId')
|
|
2953
|
+
A2UI.tabs('id', [
|
|
2954
|
+
{ title: 'Tab 1', child: 'tab1_content' },
|
|
2955
|
+
{ title: 'Tab 2', child: 'tab2_content' }
|
|
2956
|
+
])
|
|
2957
|
+
A2UI.modal('id', 'triggerButtonId', 'contentId')
|
|
2958
|
+
A2UI.divider('id', { axis: 'horizontal' }) // axis: horizontal | vertical
|
|
2959
|
+
A2UI.button('id', 'childId', { call: 'openUrl', args: { url: 'https://example.com' } }, { variant: 'primary' })
|
|
2960
|
+
// variants: primary | borderless (default not supported in modal trigger)
|
|
2961
|
+
|
|
2962
|
+
A2UI.textField('id', 'Label', { value: '', variant: 'shortText' })
|
|
2963
|
+
// variants: shortText | longText | number | obscured
|
|
2964
|
+
|
|
2965
|
+
A2UI.checkBox('id', 'Label', false)
|
|
2966
|
+
A2UI.choicePicker('id',
|
|
2967
|
+
[{ label: 'Option A', value: 'a' }, { label: 'Option B', value: 'b' }],
|
|
2968
|
+
['a'],
|
|
2969
|
+
{ label: 'Pick one', variant: 'mutuallyExclusive', displayStyle: 'chips' }
|
|
2970
|
+
)
|
|
2971
|
+
// variants: mutuallyExclusive | multipleSelection
|
|
2972
|
+
// displayStyle: checkbox | chips
|
|
2973
|
+
|
|
2974
|
+
A2UI.slider('id', 5, 10, { label: 'Rating', min: 1 })
|
|
2975
|
+
A2UI.dateTimeInput('id', '', { label: 'Date', enableDate: true, enableTime: false })
|
|
2976
|
+
|
|
2977
|
+
A2UI.surface(components, { surfaceId, catalogId, version })
|
|
2978
|
+
```
|
|
2979
|
+
|
|
2980
|
+
#### Fluent Instance Builder
|
|
2981
|
+
|
|
2982
|
+
```javascript
|
|
2983
|
+
await new A2UI(sock)
|
|
2984
|
+
.addColumn('root', ['img', 'title', 'dummy'])
|
|
2985
|
+
.addImage('img', 'https://example.com/img.jpg', { variant: 'header', fit: 'cover' })
|
|
2986
|
+
.addText('title', 'Hello!', { variant: 'h1' })
|
|
2987
|
+
.addDivider('div1')
|
|
2988
|
+
.addCard('card1', 'card_col')
|
|
2989
|
+
.addColumn('card_col', ['card_text'])
|
|
2990
|
+
.addText('card_text', 'Card content', { variant: 'body' })
|
|
2991
|
+
.addTabs('tabs1', [
|
|
2992
|
+
{ title: 'Tab A', child: 'tabA' },
|
|
2993
|
+
{ title: 'Tab B', child: 'tabB' }
|
|
2994
|
+
])
|
|
2995
|
+
.addModal('modal1', 'modal_btn', 'modal_content')
|
|
2996
|
+
.addText('modal_btn_text', 'Open Modal')
|
|
2997
|
+
.addButtonComponent('modal_btn', 'modal_btn_text', { call: 'openModal', args: { modalId: 'modal1' } }, { variant: 'primary' })
|
|
2998
|
+
.addCard('modal_content', 'modal_col')
|
|
2999
|
+
.addColumn('modal_col', ['modal_text'])
|
|
3000
|
+
.addText('modal_text', 'Modal body', { variant: 'body' })
|
|
3001
|
+
.addImage('dummy', 'https://example.com/img.jpg', { variant: 'avatar', fit: 'cover' })
|
|
3002
|
+
.addButton('quick_reply', { display_text: 'Reply', id: 'reply_id' })
|
|
3003
|
+
.setFallback('Fallback text for unsupported clients')
|
|
3004
|
+
.setSurfaceId('my-custom-surface-id')
|
|
3005
|
+
.setImage('https://example.com/header.jpg') // URL, Buffer, or file path
|
|
3006
|
+
.send(jid)
|
|
3007
|
+
```
|
|
3008
|
+
|
|
3009
|
+
#### sock.sendA2UI Shortcut
|
|
3010
|
+
|
|
3011
|
+
```javascript
|
|
3012
|
+
await sock.sendA2UI(jid, [
|
|
3013
|
+
A2UI.column('root', ['img', 'title', 'dummy']),
|
|
3014
|
+
A2UI.image('img', 'https://example.com/img.jpg', { variant: 'header' }),
|
|
3015
|
+
A2UI.text('title', 'Hello!', { variant: 'h1' }),
|
|
3016
|
+
A2UI.image('dummy', 'https://example.com/img.jpg', { variant: 'avatar' })
|
|
3017
|
+
], {
|
|
3018
|
+
buttons: [
|
|
3019
|
+
{ name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'Hi', id: 'hi' }) }
|
|
3020
|
+
]
|
|
2863
3021
|
})
|
|
3022
|
+
```
|
|
2864
3023
|
|
|
2865
|
-
|
|
3024
|
+
#### sendMessage Dispatch
|
|
3025
|
+
|
|
3026
|
+
```javascript
|
|
3027
|
+
await sock.sendMessage(jid, {
|
|
3028
|
+
a2uiMessage: {
|
|
3029
|
+
components: [
|
|
3030
|
+
A2UI.column('root', ['img', 'title', 'dummy']),
|
|
3031
|
+
A2UI.image('img', buffer),
|
|
3032
|
+
A2UI.text('title', 'Hello!', { variant: 'h1' }),
|
|
3033
|
+
A2UI.image('dummy', buffer, { variant: 'avatar' })
|
|
3034
|
+
],
|
|
3035
|
+
buttons: [
|
|
3036
|
+
{ name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'Hi', id: 'hi' }) }
|
|
3037
|
+
]
|
|
3038
|
+
}
|
|
3039
|
+
})
|
|
3040
|
+
```
|
|
3041
|
+
|
|
3042
|
+
#### Command Menu Preset
|
|
3043
|
+
|
|
3044
|
+
```javascript
|
|
2866
3045
|
await sock.sendA2UICommandMenu(jid, {
|
|
2867
3046
|
title: 'zervonex Menu',
|
|
2868
3047
|
rows: [
|
|
@@ -2873,7 +3052,20 @@ await sock.sendA2UICommandMenu(jid, {
|
|
|
2873
3052
|
buttons: [
|
|
2874
3053
|
{ text: 'GitHub', url: 'https://github.com/nexustechpro2' }
|
|
2875
3054
|
],
|
|
2876
|
-
footer: 'Powered by zervonex'
|
|
3055
|
+
footer: 'Powered by zervonex',
|
|
3056
|
+
image: 'https://example.com/header.jpg' // optional — URL, Buffer, or file path
|
|
3057
|
+
})
|
|
3058
|
+
```
|
|
3059
|
+
|
|
3060
|
+
#### Generic Envelope (Advanced)
|
|
3061
|
+
|
|
3062
|
+
```javascript
|
|
3063
|
+
await sock.sendA2UIMessage(jid, {
|
|
3064
|
+
bloksWidget: {
|
|
3065
|
+
type: 'im_a2ui',
|
|
3066
|
+
data: JSON.stringify({ version: 'v0.9', createSurface: { /* ... */ } })
|
|
3067
|
+
},
|
|
3068
|
+
nativeFlowMessage: { buttons: [{ name: 'single_select', buttonParamsJson: '...' }] }
|
|
2877
3069
|
})
|
|
2878
3070
|
```
|
|
2879
3071
|
|
|
@@ -2881,7 +3073,7 @@ await sock.sendA2UICommandMenu(jid, {
|
|
|
2881
3073
|
|
|
2882
3074
|
**A2UI button format:** `{ text, url }` or `{ label, url }`
|
|
2883
3075
|
|
|
2884
|
-
|
|
3076
|
+
**Valid icon names:** `accountCircle` `add` `arrowBack` `arrowForward` `attachFile` `calendarToday` `call` `camera` `check` `close` `delete` `download` `edit` `event` `error` `fastForward` `favorite` `favoriteOff` `folder` `help` `home` `info` `locationOn` `lock` `lockOpen` `mail` `menu` `moreVert` `moreHoriz` `notificationsOff` `notifications` `pause` `payment` `person` `phone` `photo` `play` `print` `refresh` `rewind` `search` `send` `settings` `share` `shoppingCart` `skipNext` `skipPrevious` `star` `starHalf` `starOff` `stop` `upload` `visibility` `visibilityOff` `volumeDown` `volumeMute` `volumeOff` `volumeUp` `warning`
|
|
2885
3077
|
|
|
2886
3078
|
### Poll Builder
|
|
2887
3079
|
|
|
@@ -18,9 +18,9 @@ const SCALAR_TC = {
|
|
|
18
18
|
const isLongLike = v => v !== null && typeof v === 'object' && 'low' in v && 'high' in v
|
|
19
19
|
const longToBigInt = v => (v.unsigned ? BigInt(v.high >>> 0) : BigInt(v.high | 0)) * 4294967296n + BigInt(v.low >>> 0)
|
|
20
20
|
const anyToBigInt = v => typeof v === 'bigint' ? v : isLongLike(v) ? longToBigInt(v) : BigInt(Math.trunc(Number(v)))
|
|
21
|
-
const zigzagEncode = v => BigInt.asUintN(64,
|
|
22
|
-
const numOrBigInt = b => b <= 9007199254740991n && b >= -9007199254740991n ? Number(b) : b
|
|
21
|
+
const zigzagEncode = v => { const b = anyToBigInt(v); return BigInt.asUintN(64, b << 1n ^ b >> 63n) }
|
|
23
22
|
const toLong = big => Long.fromString(BigInt.asIntN(64, big).toString())
|
|
23
|
+
const numOrBigInt = b => b <= 9007199254740991n && b >= -9007199254740991n ? Number(b) : toLong(b)
|
|
24
24
|
const wireType = f => f.k === 'varint' ? 0 : f.k === 'i64' ? 1 : f.k === 'i32' ? 5 : 2
|
|
25
25
|
|
|
26
26
|
const toBuffer = v => {
|
|
@@ -33,6 +33,15 @@ const toBuffer = v => {
|
|
|
33
33
|
return Buffer.alloc(0)
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
const anyToFloat = v => {
|
|
37
|
+
if (typeof v === 'number') return v
|
|
38
|
+
if (typeof v === 'bigint') return Number(v)
|
|
39
|
+
if (isLongLike(v)) return v.toNumber()
|
|
40
|
+
if (typeof v === 'string') return parseFloat(v)
|
|
41
|
+
if (v == null) return 0
|
|
42
|
+
return Number(v)
|
|
43
|
+
}
|
|
44
|
+
|
|
36
45
|
const B64_JSON = function () { return this.toString('base64') }
|
|
37
46
|
const tagBytes = b => (Object.defineProperty(b, 'toJSON', { value: B64_JSON, enumerable: false, configurable: true }), b)
|
|
38
47
|
|
|
@@ -54,8 +63,8 @@ class Writer {
|
|
|
54
63
|
vint(v) { typeof v === 'number' && v >= 0 && v <= MAXSAFE && Number.isInteger(v) ? this.varintNum(v) : this.varintBig(anyToBigInt(v)) }
|
|
55
64
|
fixed32(v) { this._grow(4); const n = Number(BigInt.asUintN(32, anyToBigInt(v))) >>> 0; this.buf[this.len++] = n & 0xff; this.buf[this.len++] = (n >>> 8) & 0xff; this.buf[this.len++] = (n >>> 16) & 0xff; this.buf[this.len++] = (n >>> 24) & 0xff }
|
|
56
65
|
fixed64(v) { this._grow(8); const b = Buffer.allocUnsafe(8); b.writeBigUInt64LE(BigInt.asUintN(64, anyToBigInt(v))); this.raw(b) }
|
|
57
|
-
float(v) { this._grow(4); const b = Buffer.allocUnsafe(4); b.writeFloatLE(v); this.raw(b) }
|
|
58
|
-
double(v) { this._grow(8); const b = Buffer.allocUnsafe(8); b.writeDoubleLE(v); this.raw(b) }
|
|
66
|
+
float(v) { this._grow(4); const b = Buffer.allocUnsafe(4); b.writeFloatLE(anyToFloat(v)); this.raw(b) }
|
|
67
|
+
double(v) { this._grow(8); const b = Buffer.allocUnsafe(8); b.writeDoubleLE(anyToFloat(v)); this.raw(b) }
|
|
59
68
|
finish() { return Buffer.from(this.buf.buffer, this.buf.byteOffset, this.len) }
|
|
60
69
|
}
|
|
61
70
|
|
|
@@ -152,7 +161,7 @@ function makeCodec(TABLE, enumIndex) {
|
|
|
152
161
|
if (f.s === 'zigzag') { const u = r.varintBig(); return numOrBigInt(u >> 1n ^ -(u & 1n)) }
|
|
153
162
|
if (f.s === 'zigzaglong') { const u = r.varintBig(); return toLong(u >> 1n ^ -(u & 1n)) }
|
|
154
163
|
if (f.s === 'enum') return r.varint() | 0
|
|
155
|
-
if (f.s === 'long') return toLong(
|
|
164
|
+
if (f.s === 'long') return toLong(r.varintBig())
|
|
156
165
|
return r.varint()
|
|
157
166
|
}
|
|
158
167
|
case 'i64': { const b = Buffer.from(r.read8()); return f.s === 'double' ? b.readDoubleLE(0) : toLong(b.readBigUInt64LE(0)) }
|
|
@@ -189,7 +198,10 @@ function makeCodec(TABLE, enumIndex) {
|
|
|
189
198
|
if (f.rep) (obj[f.name] || (obj[f.name] = [])).push(val)
|
|
190
199
|
else obj[f.name] = val
|
|
191
200
|
}
|
|
192
|
-
} catch {
|
|
201
|
+
} catch (e) {
|
|
202
|
+
console.warn('[WAProto] decode error:', e.message)
|
|
203
|
+
break
|
|
204
|
+
}
|
|
193
205
|
}
|
|
194
206
|
return obj
|
|
195
207
|
}
|
|
@@ -277,16 +289,17 @@ if (isGeneratorRun) {
|
|
|
277
289
|
const tablePath = fileURLToPath(new URL('./WAProto.json', import.meta.url))
|
|
278
290
|
const built = { proto: {}, codec: null }
|
|
279
291
|
|
|
292
|
+
export let codec = null
|
|
280
293
|
export function _reloadProto() {
|
|
281
294
|
if (!existsSync(tablePath)) return
|
|
282
295
|
const loaded = makeProto(tablePath)
|
|
283
296
|
for (const key of Object.keys(built.proto)) delete built.proto[key]
|
|
284
297
|
Object.assign(built.proto, loaded.proto)
|
|
285
298
|
built.codec = loaded.codec
|
|
299
|
+
codec = loaded.codec
|
|
286
300
|
}
|
|
287
301
|
|
|
288
302
|
if (!isGeneratorRun && existsSync(tablePath)) _reloadProto()
|
|
289
303
|
|
|
290
304
|
export const proto = built.proto
|
|
291
|
-
export const codec = built.codec
|
|
292
305
|
export default built
|