plogme 2.0.0 → 2.0.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/README.md +536 -389
- package/lib/Socket/chats.js +1 -0
- package/lib/Socket/messages-send.js +93 -19
- package/lib/Socket/newsletter.js +37 -3
- package/lib/Utils/bot-capabilities.js +84 -0
- package/lib/Utils/bot-planning-replay.js +0 -1
- package/lib/Utils/channel-policy.js +25 -1
- package/lib/Utils/index.js +1 -0
- package/lib/Utils/messages.js +11 -6
- package/lib/Utils/meta-compositing.js +7 -0
- package/lib/Utils/rich-message-utils.js +119 -67
- package/lib/index.d.ts +6 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,8 +1,35 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
>
|
|
1
|
+
# Plogme
|
|
2
|
+
|
|
3
|
+
**Plogme** is a Node.js WhatsApp Web client built on the Baileys protocol implementation. It provides authentication, messaging, media uploads, groups, status updates, interactive messages, albums, rich responses, Meta AI-style envelopes, HTML screens, and selected WhatsApp Web protocol features.
|
|
4
|
+
|
|
5
|
+
> [!IMPORTANT]
|
|
6
|
+
> Plogme is a protocol library. WhatsApp server behavior and client rendering can change without notice. Rich responses, HTML primitives, native flows, reels, carousels, and experimental protocol fields may render differently across WhatsApp versions.
|
|
7
|
+
|
|
8
|
+
[](https://www.npmjs.com/package/plogme)
|
|
9
|
+
[](https://nodejs.org/)
|
|
10
|
+
[](https://github.com/crysnovax/baileys)
|
|
11
|
+
|
|
12
|
+
## Contents
|
|
13
|
+
|
|
14
|
+
1. [Installation](#installation)
|
|
15
|
+
2. [Quick start](#quick-start)
|
|
16
|
+
3. [Authentication and pairing](#authentication-and-pairing)
|
|
17
|
+
4. [Sending messages](#sending-messages)
|
|
18
|
+
5. [Media and albums](#media-and-albums)
|
|
19
|
+
6. [Interactive messages](#interactive-messages)
|
|
20
|
+
7. [Rich messages](#rich-messages)
|
|
21
|
+
8. [Rich menus, reels, and grids](#rich-menus-reels-and-grids)
|
|
22
|
+
9. [HTML screens and built-in apps](#html-screens-and-built-in-apps)
|
|
23
|
+
10. [Status updates](#status-updates)
|
|
24
|
+
11. [Groups and group history](#groups-and-group-history)
|
|
25
|
+
12. [Reports and moderation](#reports-and-moderation)
|
|
26
|
+
13. [Experimental bot metadata](#experimental-bot-metadata)
|
|
27
|
+
14. [WhatsApp flows and web views](#whatsapp-flows-and-web-views)
|
|
28
|
+
15. [View-once utilities](#view-once-utilities)
|
|
29
|
+
16. [Connection handling](#connection-handling)
|
|
30
|
+
17. [Security and removed automatic behavior](#security-and-removed-automatic-behavior)
|
|
31
|
+
18. [Development](#development)
|
|
32
|
+
19. [License and support](#license-and-support)
|
|
6
33
|
|
|
7
34
|
## Installation
|
|
8
35
|
|
|
@@ -10,29 +37,32 @@
|
|
|
10
37
|
npm install plogme
|
|
11
38
|
```
|
|
12
39
|
|
|
13
|
-
The
|
|
40
|
+
The package currently requires **Node.js 20 or newer**.
|
|
14
41
|
|
|
15
|
-
|
|
42
|
+
To install the development branch directly from GitHub:
|
|
16
43
|
|
|
17
|
-
|
|
44
|
+
```bash
|
|
45
|
+
npm install github:crysnovax/baileys
|
|
46
|
+
```
|
|
18
47
|
|
|
19
|
-
|
|
20
|
-
- **CRYSNOVA AI:** [Join the CRYSNOVA AI WhatsApp channel](https://whatsapp.com/channel/0029Vb6pe77K0IBn48HLKb38)
|
|
48
|
+
Plogme is an ES module package. Use `import` syntax in Node.js projects configured with `"type": "module"`.
|
|
21
49
|
|
|
22
|
-
|
|
50
|
+
> [!WARNING]
|
|
51
|
+
> Never commit authentication state, private keys, `.env` files, or downloaded session data. Add your session directory to `.gitignore`.
|
|
23
52
|
|
|
24
53
|
## Quick start
|
|
25
54
|
|
|
55
|
+
This is the smallest complete connection example. It saves credentials, reconnects after temporary disconnects, and stops reconnecting after logout.
|
|
56
|
+
|
|
26
57
|
```js
|
|
27
|
-
import {
|
|
28
|
-
makeWASocket,
|
|
58
|
+
import makeWASocket, {
|
|
29
59
|
DisconnectReason,
|
|
30
60
|
useMultiFileAuthState
|
|
31
61
|
} from 'plogme'
|
|
32
62
|
import { Boom } from '@hapi/boom'
|
|
33
63
|
import pino from 'pino'
|
|
34
64
|
|
|
35
|
-
async function
|
|
65
|
+
async function connect() {
|
|
36
66
|
const { state, saveCreds } = await useMultiFileAuthState('./session')
|
|
37
67
|
const sock = makeWASocket({
|
|
38
68
|
auth: state,
|
|
@@ -40,38 +70,60 @@ async function start() {
|
|
|
40
70
|
})
|
|
41
71
|
|
|
42
72
|
sock.ev.on('creds.update', saveCreds)
|
|
73
|
+
|
|
43
74
|
sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
|
|
44
|
-
if (connection === 'open')
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (status !== DisconnectReason.loggedOut) start()
|
|
75
|
+
if (connection === 'open') {
|
|
76
|
+
console.log('Connected to WhatsApp')
|
|
77
|
+
return
|
|
48
78
|
}
|
|
79
|
+
|
|
80
|
+
if (connection !== 'close') return
|
|
81
|
+
|
|
82
|
+
const statusCode = new Boom(lastDisconnect?.error)?.output?.statusCode
|
|
83
|
+
if (statusCode === DisconnectReason.loggedOut) {
|
|
84
|
+
console.error('Logged out. Remove the old session and pair again.')
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
console.log('Connection closed; reconnecting')
|
|
89
|
+
connect().catch(console.error)
|
|
49
90
|
})
|
|
91
|
+
|
|
92
|
+
return sock
|
|
50
93
|
}
|
|
51
94
|
|
|
52
|
-
|
|
95
|
+
await connect()
|
|
53
96
|
```
|
|
54
97
|
|
|
55
|
-
Keep authentication files private. Never commit session credentials, private keys, or `.env` files.
|
|
56
|
-
|
|
57
98
|
## Authentication and pairing
|
|
58
99
|
|
|
100
|
+
### Multi-file authentication
|
|
101
|
+
|
|
102
|
+
`useMultiFileAuthState` is the recommended development setup because credentials and keys are stored as separate files.
|
|
103
|
+
|
|
59
104
|
```js
|
|
60
105
|
const { state, saveCreds } = await useMultiFileAuthState('./session')
|
|
61
|
-
|
|
62
106
|
const sock = makeWASocket({ auth: state })
|
|
63
107
|
sock.ev.on('creds.update', saveCreds)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Pairing code
|
|
64
111
|
|
|
112
|
+
Request a pairing code after creating the socket. Supply a phone number in international format without `+`, spaces, or punctuation.
|
|
113
|
+
|
|
114
|
+
```js
|
|
65
115
|
const code = await sock.requestPairingCode('15551234567')
|
|
66
|
-
console.log(code)
|
|
116
|
+
console.log('Pairing code:', code)
|
|
67
117
|
```
|
|
68
118
|
|
|
69
|
-
|
|
119
|
+
Keep the pairing code private and use it only with the WhatsApp account you control.
|
|
120
|
+
|
|
121
|
+
## Sending messages
|
|
70
122
|
|
|
71
|
-
|
|
123
|
+
### Text, mentions, and reactions
|
|
72
124
|
|
|
73
125
|
```js
|
|
74
|
-
await sock.sendMessage(jid, { text: 'Hello
|
|
126
|
+
await sock.sendMessage(jid, { text: 'Hello from Plogme' })
|
|
75
127
|
|
|
76
128
|
await sock.sendMessage(jid, {
|
|
77
129
|
text: 'Hello @15551234567',
|
|
@@ -79,11 +131,112 @@ await sock.sendMessage(jid, {
|
|
|
79
131
|
})
|
|
80
132
|
|
|
81
133
|
await sock.sendMessage(jid, {
|
|
82
|
-
react: {
|
|
134
|
+
react: {
|
|
135
|
+
text: '👍',
|
|
136
|
+
key: message.key
|
|
137
|
+
}
|
|
83
138
|
})
|
|
84
139
|
```
|
|
85
140
|
|
|
86
|
-
|
|
141
|
+
### WhatsApp Channels
|
|
142
|
+
|
|
143
|
+
Resolve a Channel invite code or a full WhatsApp Channel invite URL without
|
|
144
|
+
automatically following the Channel:
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
const channel = await sock.newsletterGetInviteInfo(
|
|
148
|
+
'https://whatsapp.com/channel/0029Vb6pe77K0IBn48HLKb38'
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
console.log(channel?.id, channel?.name, channel?.description)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
React to a Channel post with its Channel JID and `serverId`. Pass `null`,
|
|
155
|
+
`undefined`, or an empty string to remove the reaction:
|
|
156
|
+
|
|
157
|
+
```js
|
|
158
|
+
await sock.newsletterReactMessage(
|
|
159
|
+
'120363000000000000@newsletter',
|
|
160
|
+
'12345',
|
|
161
|
+
'👍'
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
await sock.newsletterReactMessage(
|
|
165
|
+
'120363000000000000@newsletter',
|
|
166
|
+
'12345',
|
|
167
|
+
null
|
|
168
|
+
)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Chat themes and animated wallpapers
|
|
172
|
+
|
|
173
|
+
Send a chat-theme protocol message with a color scheme and an animated
|
|
174
|
+
wallpaper. `chatThemeId` is normalized to the WhatsApp color-scheme form by
|
|
175
|
+
adding `@Tonal`; use `colorSchemeId` when you already have the complete value.
|
|
176
|
+
`dimLevel` is a normalized value from `0` to `1`.
|
|
177
|
+
|
|
178
|
+
```js
|
|
179
|
+
await sock.sendMessage(jid, {
|
|
180
|
+
chatTheme: {
|
|
181
|
+
chatThemeId: 'Royal-Blue',
|
|
182
|
+
animatedWallpaper: {
|
|
183
|
+
id: 'wallpaper-1',
|
|
184
|
+
dimLevel: 0.25
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
// Equivalent explicit color-scheme form:
|
|
190
|
+
await sock.sendMessage(jid, {
|
|
191
|
+
chatTheme: {
|
|
192
|
+
colorSchemeId: 'Royal-Blue@Tonal',
|
|
193
|
+
animatedWallpaper: {
|
|
194
|
+
animatedWallpaperId: 'wallpaper-1',
|
|
195
|
+
opacity: 25
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Editing and deleting
|
|
202
|
+
|
|
203
|
+
```js
|
|
204
|
+
await sock.sendMessage(jid, {
|
|
205
|
+
text: 'Updated text',
|
|
206
|
+
edit: message.key
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
await sock.sendMessage(jid, {
|
|
210
|
+
caption: 'Updated media caption',
|
|
211
|
+
edit: message.key
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
await sock.sendMessage(jid, {
|
|
215
|
+
delete: message.key
|
|
216
|
+
})
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Forwarding
|
|
220
|
+
|
|
221
|
+
```js
|
|
222
|
+
await sock.sendMessage(jid, {
|
|
223
|
+
forward: message,
|
|
224
|
+
force: true
|
|
225
|
+
})
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Profile name and About status
|
|
229
|
+
|
|
230
|
+
These methods update the connected account's own profile.
|
|
231
|
+
|
|
232
|
+
```js
|
|
233
|
+
await sock.updateProfileName('Cody AI')
|
|
234
|
+
await sock.updateProfileStatus('Building helpful WhatsApp tools')
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Media and albums
|
|
238
|
+
|
|
239
|
+
### Images, video, audio, documents, and stickers
|
|
87
240
|
|
|
88
241
|
```js
|
|
89
242
|
await sock.sendMessage(jid, {
|
|
@@ -94,161 +247,245 @@ await sock.sendMessage(jid, {
|
|
|
94
247
|
await sock.sendMessage(jid, {
|
|
95
248
|
video: { url: './video.mp4' },
|
|
96
249
|
caption: 'Video caption',
|
|
97
|
-
gifPlayback: false
|
|
250
|
+
gifPlayback: false,
|
|
251
|
+
ptv: false
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
await sock.sendMessage(jid, {
|
|
255
|
+
audio: { url: './voice.ogg' },
|
|
256
|
+
mimetype: 'audio/ogg; codecs=opus',
|
|
257
|
+
ptt: true
|
|
98
258
|
})
|
|
99
259
|
|
|
100
260
|
await sock.sendMessage(jid, {
|
|
101
261
|
document: { url: './report.pdf' },
|
|
102
262
|
mimetype: 'application/pdf',
|
|
103
|
-
fileName: 'report.pdf'
|
|
263
|
+
fileName: 'report.pdf',
|
|
264
|
+
caption: 'Monthly report'
|
|
104
265
|
})
|
|
105
266
|
|
|
106
267
|
await sock.sendMessage(jid, {
|
|
107
|
-
|
|
108
|
-
mimetype: 'audio/ogg; codecs=opus'
|
|
268
|
+
sticker: { url: './sticker.webp' }
|
|
109
269
|
})
|
|
110
270
|
```
|
|
111
271
|
|
|
112
|
-
|
|
272
|
+
Remote URLs, local file paths, and buffers are supported where the selected media helper accepts them.
|
|
273
|
+
|
|
274
|
+
> [!WARNING]
|
|
275
|
+
> A URL must point to a real, playable media file. A URL returning an HTML error page, an empty file, or an invalid video container can produce a WhatsApp media verification or download warning even when the library code is correct.
|
|
113
276
|
|
|
114
|
-
|
|
115
|
-
`plogme`, use `updateProfileStatus`—not `updateProfileDescription`—and pass a
|
|
116
|
-
plain string:
|
|
277
|
+
### GIF playback and video notes
|
|
117
278
|
|
|
118
279
|
```js
|
|
119
|
-
await sock.
|
|
120
|
-
|
|
121
|
-
|
|
280
|
+
await sock.sendMessage(jid, {
|
|
281
|
+
video: { url: './animation.mp4' },
|
|
282
|
+
gifPlayback: true,
|
|
283
|
+
caption: 'Animated clip'
|
|
284
|
+
})
|
|
122
285
|
|
|
123
|
-
|
|
124
|
-
|
|
286
|
+
await sock.sendMessage(jid, {
|
|
287
|
+
video: { url: './video-note.mp4' },
|
|
288
|
+
ptv: true
|
|
289
|
+
})
|
|
290
|
+
```
|
|
125
291
|
|
|
126
|
-
|
|
292
|
+
### Albums
|
|
127
293
|
|
|
128
|
-
|
|
129
|
-
supported `contextInfo.isSpoiler` flag:
|
|
294
|
+
Use at least two image or video items. Albums are sent as a parent message followed by separately encrypted media children.
|
|
130
295
|
|
|
131
296
|
```js
|
|
132
|
-
await sock.
|
|
133
|
-
image: { url: './
|
|
134
|
-
|
|
135
|
-
|
|
297
|
+
await sock.sendAlbumMessage(jid, [
|
|
298
|
+
{ type: 'image', data: { url: './one.jpg' } },
|
|
299
|
+
{ type: 'image', data: { url: './two.jpg' } }
|
|
300
|
+
], {
|
|
301
|
+
caption: 'Trip photos'
|
|
136
302
|
})
|
|
137
303
|
```
|
|
138
304
|
|
|
139
|
-
|
|
140
|
-
compatibility fallback. This sends a view-once envelope; it is **not** the
|
|
141
|
-
same as a native spoiler:
|
|
305
|
+
The general `sendMessage` album form is also supported:
|
|
142
306
|
|
|
143
307
|
```js
|
|
144
308
|
await sock.sendMessage(jid, {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
309
|
+
album: [
|
|
310
|
+
{ image: { url: './one.jpg' }, caption: 'First' },
|
|
311
|
+
{ video: { url: './clip.mp4' }, caption: 'Second' }
|
|
312
|
+
]
|
|
148
313
|
})
|
|
149
314
|
```
|
|
150
315
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
316
|
+
Rich-response and bot-forwarded messages cannot be nested as album children. Send them as separate messages.
|
|
317
|
+
|
|
318
|
+
## Interactive messages
|
|
319
|
+
|
|
320
|
+
### Buttons and lists
|
|
154
321
|
|
|
155
322
|
```js
|
|
156
323
|
await sock.sendMessage(jid, {
|
|
157
|
-
text: '
|
|
158
|
-
|
|
324
|
+
text: 'Choose an option',
|
|
325
|
+
footer: 'Plogme',
|
|
326
|
+
buttons: [
|
|
327
|
+
{ text: 'Confirm', id: 'confirm' },
|
|
328
|
+
{ text: 'Cancel', id: 'cancel' }
|
|
329
|
+
]
|
|
159
330
|
})
|
|
160
|
-
```
|
|
161
331
|
|
|
162
|
-
|
|
332
|
+
await sock.sendMessage(jid, {
|
|
333
|
+
text: 'Choose a category',
|
|
334
|
+
title: 'Categories',
|
|
335
|
+
buttonText: 'Open list',
|
|
336
|
+
sections: [
|
|
337
|
+
{
|
|
338
|
+
title: 'Available categories',
|
|
339
|
+
rows: [
|
|
340
|
+
{ title: 'Support', description: 'Get help', rowId: 'support' },
|
|
341
|
+
{ title: 'News', description: 'Read updates', rowId: 'news' }
|
|
342
|
+
]
|
|
343
|
+
}
|
|
344
|
+
]
|
|
345
|
+
})
|
|
346
|
+
```
|
|
163
347
|
|
|
164
|
-
|
|
348
|
+
### Native flow
|
|
165
349
|
|
|
166
350
|
```js
|
|
167
|
-
await sock.
|
|
168
|
-
|
|
351
|
+
await sock.sendWhatsAppFlow(jid, {
|
|
352
|
+
flowId: 'your-flow-id',
|
|
353
|
+
flowToken: 'your-flow-token',
|
|
354
|
+
cta: 'Open form',
|
|
355
|
+
text: 'Complete this form'
|
|
356
|
+
})
|
|
357
|
+
```
|
|
169
358
|
|
|
170
|
-
|
|
359
|
+
For a rich URL call-to-action:
|
|
171
360
|
|
|
172
|
-
|
|
361
|
+
```js
|
|
362
|
+
await sock.sendRichWebview(jid, {
|
|
363
|
+
url: 'https://example.com/app',
|
|
364
|
+
text: 'Open the app',
|
|
365
|
+
buttonText: 'Open',
|
|
366
|
+
useWebview: true
|
|
173
367
|
})
|
|
174
368
|
```
|
|
175
369
|
|
|
176
|
-
|
|
370
|
+
### Interactive table
|
|
177
371
|
|
|
178
372
|
```js
|
|
179
|
-
await sock.
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
373
|
+
await sock.sendInteractiveTable(jid, {
|
|
374
|
+
title: 'Deployment status',
|
|
375
|
+
headers: ['Service', 'State'],
|
|
376
|
+
rows: [
|
|
377
|
+
['API', 'Online'],
|
|
378
|
+
['Worker', 'Online']
|
|
379
|
+
],
|
|
380
|
+
buttons: [],
|
|
381
|
+
footer: 'Updated now'
|
|
183
382
|
})
|
|
184
383
|
```
|
|
185
384
|
|
|
186
|
-
##
|
|
385
|
+
## Rich messages
|
|
187
386
|
|
|
188
|
-
|
|
387
|
+
Rich messages are forwarded GenAI-style envelopes. The sender now routes rich payloads through normal WhatsApp message generation before relay so clients receive the expected message metadata.
|
|
189
388
|
|
|
190
|
-
|
|
191
|
-
|
|
389
|
+
> [!NOTE]
|
|
390
|
+
> Rich rendering is client-dependent. A rich message can be structurally valid while a particular WhatsApp build chooses a fallback rendering.
|
|
192
391
|
|
|
193
|
-
|
|
392
|
+
### Rich text and quotes
|
|
194
393
|
|
|
195
394
|
```js
|
|
196
|
-
await sock.
|
|
197
|
-
text: '
|
|
198
|
-
|
|
199
|
-
|
|
395
|
+
await sock.sendRichText(jid, {
|
|
396
|
+
text: '**Deployment complete**\n\nThe service is online.'
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
await sock.sendMarkdownQuote(jid, {
|
|
400
|
+
text: '> Use the production URL for verification.'
|
|
200
401
|
})
|
|
201
402
|
```
|
|
202
403
|
|
|
203
|
-
|
|
404
|
+
### Copy-to-clipboard actions
|
|
204
405
|
|
|
205
406
|
```js
|
|
206
|
-
await sock.
|
|
207
|
-
text: '
|
|
208
|
-
|
|
209
|
-
copyLabel: 'Copy me'
|
|
407
|
+
await sock.sendCopyButton(jid, {
|
|
408
|
+
text: 'npm install plogme',
|
|
409
|
+
label: 'Copy command'
|
|
210
410
|
})
|
|
211
|
-
```
|
|
212
411
|
|
|
213
|
-
|
|
412
|
+
await sock.sendRichTextCopy(jid, {
|
|
413
|
+
text: 'Install the package first.',
|
|
414
|
+
copyText: 'npm install plogme',
|
|
415
|
+
copyLabel: 'Copy command'
|
|
416
|
+
})
|
|
214
417
|
|
|
215
|
-
```js
|
|
216
418
|
await sock.sendQuoteCopy(jid, {
|
|
217
|
-
text:
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
copyText: 'Use this exact logo image...',
|
|
221
|
-
copyLabel: 'Copy me'
|
|
419
|
+
text: '> Run the tests before publishing.',
|
|
420
|
+
copyText: 'npm test',
|
|
421
|
+
copyLabel: 'Copy test command'
|
|
222
422
|
})
|
|
223
423
|
```
|
|
224
424
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
## Rich links, images, HTML, and menus
|
|
425
|
+
### Rich image and inline links
|
|
228
426
|
|
|
229
427
|
```js
|
|
230
|
-
await sock.sendRichLink(jid, {
|
|
231
|
-
text: 'Open the deployment site',
|
|
232
|
-
url: 'https://example.com'
|
|
233
|
-
})
|
|
234
|
-
|
|
235
428
|
await sock.sendRichImage(jid, {
|
|
236
429
|
url: 'https://example.com/image.jpg',
|
|
237
430
|
mimeType: 'image/jpeg'
|
|
238
431
|
})
|
|
239
432
|
|
|
240
|
-
await sock.
|
|
241
|
-
|
|
433
|
+
await sock.sendRichLink(jid, {
|
|
434
|
+
text: 'Open the documentation',
|
|
435
|
+
links: [
|
|
436
|
+
{
|
|
437
|
+
text: 'Documentation',
|
|
438
|
+
url: 'https://example.com/docs',
|
|
439
|
+
isTrusted: true
|
|
440
|
+
}
|
|
441
|
+
]
|
|
242
442
|
})
|
|
243
443
|
```
|
|
244
444
|
|
|
245
|
-
|
|
445
|
+
### Rich tables, lists, code, and LaTeX
|
|
246
446
|
|
|
247
|
-
|
|
447
|
+
```js
|
|
448
|
+
await sock.sendTable(
|
|
449
|
+
jid,
|
|
450
|
+
'Build status',
|
|
451
|
+
['Service', 'State'],
|
|
452
|
+
[
|
|
453
|
+
{ items: ['API', 'Online'] },
|
|
454
|
+
{ items: ['Worker', 'Online'] }
|
|
455
|
+
]
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
await sock.sendList(jid, 'Next steps', [
|
|
459
|
+
{ title: 'Test', description: 'Run the test suite' },
|
|
460
|
+
{ title: 'Deploy', description: 'Publish the verified build' }
|
|
461
|
+
])
|
|
462
|
+
|
|
463
|
+
await sock.sendCodeBlock(jid, 'console.log("hello")', undefined, {
|
|
464
|
+
language: 'javascript'
|
|
465
|
+
})
|
|
466
|
+
|
|
467
|
+
await sock.sendRichAIResponse(jid, {
|
|
468
|
+
table: {
|
|
469
|
+
title: 'Status',
|
|
470
|
+
headers: ['Name', 'Value'],
|
|
471
|
+
rows: [['version', '2.0.1']]
|
|
472
|
+
}
|
|
473
|
+
})
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
`sendRichAIResponse` also accepts `list`, `codeBlock`, and `latex` content objects. For a pre-existing Meta AI response, `captureAndResendUnifiedResponse(jid, message, quoted)` captures and resends its unified response.
|
|
477
|
+
|
|
478
|
+
## Rich menus, reels, and grids
|
|
479
|
+
|
|
480
|
+
### Original Gen4 rich menu
|
|
481
|
+
|
|
482
|
+
Use `sock.richMenu` for the original Gen4-style menu. This is the primary rich-menu API.
|
|
248
483
|
|
|
249
484
|
```js
|
|
250
485
|
await sock.richMenu(jid, {
|
|
251
|
-
header: {
|
|
486
|
+
header: {
|
|
487
|
+
title: 'Deployment tools'
|
|
488
|
+
},
|
|
252
489
|
body: {
|
|
253
490
|
title: 'Choose an action',
|
|
254
491
|
buttons: [
|
|
@@ -256,136 +493,150 @@ await sock.richMenu(jid, {
|
|
|
256
493
|
{ id: 'logs', text: 'View logs' }
|
|
257
494
|
]
|
|
258
495
|
},
|
|
259
|
-
footer: {
|
|
496
|
+
footer: {
|
|
497
|
+
text: 'Open documentation',
|
|
498
|
+
url: 'https://example.com/docs'
|
|
499
|
+
}
|
|
260
500
|
})
|
|
261
501
|
```
|
|
262
502
|
|
|
263
|
-
|
|
503
|
+
Cards and carousel layout are supported:
|
|
264
504
|
|
|
265
|
-
|
|
505
|
+
```js
|
|
506
|
+
await sock.richMenu(jid, {
|
|
507
|
+
body: {
|
|
508
|
+
carousel: true,
|
|
509
|
+
cards: [
|
|
510
|
+
{
|
|
511
|
+
title: 'Production',
|
|
512
|
+
toast: 'Production selected',
|
|
513
|
+
buttons: [{ id: 'production', text: 'Open' }]
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
title: 'Staging',
|
|
517
|
+
toast: 'Staging selected',
|
|
518
|
+
buttons: [{ id: 'staging', text: 'Open' }]
|
|
519
|
+
}
|
|
520
|
+
]
|
|
521
|
+
}
|
|
522
|
+
})
|
|
523
|
+
```
|
|
266
524
|
|
|
267
|
-
|
|
525
|
+
Validate button IDs on the server. A button ID is an event value, not an authorization grant.
|
|
526
|
+
|
|
527
|
+
### Reels and rich media
|
|
528
|
+
|
|
529
|
+
`sendReels` accepts an array of reel records. The main rich-media path is used for reels, sources, links, tables, and future rich payloads.
|
|
268
530
|
|
|
269
531
|
```js
|
|
270
|
-
await sock.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
caption: 'Second card description',
|
|
286
|
-
buttons: [
|
|
287
|
-
{ id: 'second', text: 'Select second' }
|
|
288
|
-
]
|
|
289
|
-
}
|
|
290
|
-
]
|
|
532
|
+
await sock.sendReels(jid, [
|
|
533
|
+
{
|
|
534
|
+
title: 'Example reel',
|
|
535
|
+
creator: 'Example creator',
|
|
536
|
+
videoUrl: 'https://cdn.example.com/reel.mp4',
|
|
537
|
+
thumbnailUrl: 'https://cdn.example.com/reel.jpg',
|
|
538
|
+
profileIconUrl: 'https://cdn.example.com/avatar.jpg',
|
|
539
|
+
likesCount: 120,
|
|
540
|
+
sharesCount: 8,
|
|
541
|
+
viewCount: 2400,
|
|
542
|
+
reelSource: 'IG',
|
|
543
|
+
isVerified: true
|
|
544
|
+
}
|
|
545
|
+
], undefined, {
|
|
546
|
+
text: 'Featured reels'
|
|
291
547
|
})
|
|
292
548
|
```
|
|
293
549
|
|
|
294
|
-
|
|
550
|
+
Use a valid MP4 or another supported playable video. Test the URL with a media probe before sending it.
|
|
295
551
|
|
|
296
|
-
For a
|
|
552
|
+
For a general rich response:
|
|
297
553
|
|
|
298
554
|
```js
|
|
299
|
-
await sock.
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
{
|
|
555
|
+
await sock.sendRichMedia(jid, {
|
|
556
|
+
contentText: 'A rich response',
|
|
557
|
+
reels: [
|
|
558
|
+
{
|
|
559
|
+
title: 'Short clip',
|
|
560
|
+
url: 'https://cdn.example.com/clip.mp4'
|
|
561
|
+
}
|
|
303
562
|
]
|
|
304
563
|
})
|
|
305
564
|
```
|
|
306
565
|
|
|
307
|
-
|
|
566
|
+
### Rich button grid
|
|
308
567
|
|
|
309
|
-
|
|
568
|
+
`sendRichButtonGrid` sends ordinary message cards with native-flow buttons. It is distinct from the original Gen4 `richMenu` envelope.
|
|
310
569
|
|
|
311
570
|
```js
|
|
312
|
-
await sock.
|
|
313
|
-
|
|
571
|
+
await sock.sendRichButtonGrid(jid, {
|
|
572
|
+
text: 'Choose a service',
|
|
573
|
+
footer: 'Select one',
|
|
574
|
+
cards: [
|
|
314
575
|
{
|
|
315
|
-
|
|
316
|
-
|
|
576
|
+
title: 'Status',
|
|
577
|
+
image: { url: './status.jpg' },
|
|
578
|
+
buttons: [{ text: 'View status', id: 'status' }]
|
|
317
579
|
},
|
|
318
580
|
{
|
|
319
|
-
|
|
320
|
-
|
|
581
|
+
title: 'Logs',
|
|
582
|
+
image: { url: './logs.jpg' },
|
|
583
|
+
buttons: [{ text: 'View logs', id: 'logs' }]
|
|
321
584
|
}
|
|
322
585
|
]
|
|
323
586
|
})
|
|
324
587
|
```
|
|
325
588
|
|
|
326
|
-
|
|
589
|
+
## HTML screens and built-in apps
|
|
327
590
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
The following methods attach newer `WAProto` bot metadata fields through `messageContextInfo.botMetadata`. They are experimental and may have no visible effect unless the receiving client and server support the corresponding first-party feature.
|
|
331
|
-
|
|
332
|
-
### Diagnostics metadata
|
|
591
|
+
### Ordinary HTML message
|
|
333
592
|
|
|
334
593
|
```js
|
|
335
|
-
await sock.
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
toolsUsed: ['web_search', 'image_search'],
|
|
339
|
-
isThinking: true
|
|
594
|
+
await sock.sendHtmlMessage(jid, {
|
|
595
|
+
html: '<h1>Hello</h1><p>Rendered HTML content.</p>',
|
|
596
|
+
trustedSources: []
|
|
340
597
|
})
|
|
341
598
|
```
|
|
342
599
|
|
|
343
|
-
###
|
|
600
|
+
### Tabbed rich HTML screen
|
|
344
601
|
|
|
345
602
|
```js
|
|
346
|
-
await sock.
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
603
|
+
await sock.sendRichHtmlMessage(jid, {
|
|
604
|
+
title: 'Example app',
|
|
605
|
+
url: 'https://your-domain.example',
|
|
606
|
+
trustedSources: ['your-domain.example'],
|
|
607
|
+
tabs: [
|
|
608
|
+
{
|
|
609
|
+
tab_header: 'Home',
|
|
610
|
+
html: '<h1>Home</h1><p>Welcome.</p>'
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
tab_header: 'Help',
|
|
614
|
+
html: '<h2>Help</h2><p>Tap the buttons to continue.</p>'
|
|
615
|
+
}
|
|
616
|
+
]
|
|
351
617
|
})
|
|
352
618
|
```
|
|
353
619
|
|
|
354
|
-
|
|
620
|
+
`trustedSources` and `url` are application-specific. Sanitize user-controlled HTML before sending it.
|
|
355
621
|
|
|
356
|
-
|
|
357
|
-
await sock.sendBotToolResult(jid, {
|
|
358
|
-
text: 'Tool result test',
|
|
359
|
-
toolCallId: `tool-${Date.now()}`,
|
|
360
|
-
resolutionData: {
|
|
361
|
-
status: 'success',
|
|
362
|
-
result: { value: 'Completed' }
|
|
363
|
-
}
|
|
364
|
-
})
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
You may provide a pre-serialized result instead:
|
|
622
|
+
### Built-in screens
|
|
368
623
|
|
|
369
624
|
```js
|
|
370
|
-
await sock.
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
status: 'success',
|
|
375
|
-
value: 'Completed'
|
|
376
|
-
})
|
|
377
|
-
})
|
|
625
|
+
await sock.sendSlotMachine(jid)
|
|
626
|
+
await sock.sendMiniApp(jid, { title: 'Pairing mini-app' })
|
|
627
|
+
await sock.sendWebsitePreview(jid, 'https://example.com')
|
|
628
|
+
await sock.sendLinkPreview(jid, 'https://example.com')
|
|
378
629
|
```
|
|
379
630
|
|
|
380
|
-
|
|
631
|
+
The built-in mini-app uses its configured first-party source. Generic rich HTML remains configurable.
|
|
381
632
|
|
|
382
633
|
## Status updates
|
|
383
634
|
|
|
384
|
-
|
|
635
|
+
`sendStatus` sends a status update and resolves recipients from `statusJidList`.
|
|
385
636
|
|
|
386
637
|
```js
|
|
387
638
|
await sock.sendStatus({
|
|
388
|
-
text: 'Service
|
|
639
|
+
text: 'Service online',
|
|
389
640
|
backgroundColor: 0xff102a43,
|
|
390
641
|
font: 2
|
|
391
642
|
}, {
|
|
@@ -393,198 +644,156 @@ await sock.sendStatus({
|
|
|
393
644
|
})
|
|
394
645
|
```
|
|
395
646
|
|
|
396
|
-
|
|
647
|
+
For status mentions:
|
|
648
|
+
|
|
649
|
+
```js
|
|
650
|
+
await sock.sendStatusMention(
|
|
651
|
+
{ text: 'Welcome to the team' },
|
|
652
|
+
['15551234567@s.whatsapp.net']
|
|
653
|
+
)
|
|
654
|
+
```
|
|
397
655
|
|
|
398
|
-
## Groups
|
|
656
|
+
## Groups and group history
|
|
399
657
|
|
|
400
658
|
```js
|
|
401
659
|
const metadata = await sock.groupMetadata(groupJid)
|
|
660
|
+
|
|
402
661
|
await sock.groupParticipantsUpdate(groupJid, [userJid], 'add')
|
|
403
662
|
await sock.groupParticipantsUpdate(groupJid, [userJid], 'remove')
|
|
404
663
|
await sock.groupSettingUpdate(groupJid, 'announcement')
|
|
405
664
|
```
|
|
406
665
|
|
|
407
|
-
|
|
666
|
+
### Member labels
|
|
408
667
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
`updateMemberLabel` sets the connected account's own custom member label in a
|
|
412
|
-
group. The first argument must be the **group JID** (`123456789-123456@g.us`),
|
|
413
|
-
not the member's individual JID:
|
|
668
|
+
`updateMemberLabel` sets the connected account's own label in a group.
|
|
414
669
|
|
|
415
670
|
```js
|
|
416
671
|
await sock.updateMemberLabel(groupJid, 'Developer')
|
|
672
|
+
await sock.updateMemberLabel(groupJid, '') // remove the label
|
|
417
673
|
```
|
|
418
674
|
|
|
419
|
-
|
|
675
|
+
### Group history sharing
|
|
420
676
|
|
|
421
677
|
```js
|
|
422
|
-
await sock.
|
|
678
|
+
await sock.toggleGroupHistory(groupJid, true) // all members
|
|
679
|
+
await sock.toggleGroupHistory(groupJid, false) // admins only
|
|
423
680
|
```
|
|
424
681
|
|
|
425
|
-
|
|
426
|
-
operation for assigning labels to arbitrary participants; WhatsApp associates
|
|
427
|
-
the label with the account that sends the protocol message.
|
|
682
|
+
## Reports and moderation
|
|
428
683
|
|
|
429
|
-
|
|
684
|
+
The reporting APIs have different wire operations. Use the method that matches the action you intend to perform.
|
|
430
685
|
|
|
431
686
|
```js
|
|
432
|
-
await sock.
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
687
|
+
await sock.reportSpam(
|
|
688
|
+
groupJid,
|
|
689
|
+
[{ id: messageId, from: userJid, t: Math.floor(Date.now() / 1000) }],
|
|
690
|
+
'group_info_report',
|
|
691
|
+
'Example group'
|
|
692
|
+
)
|
|
438
693
|
|
|
439
|
-
await sock.
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
694
|
+
const userAccepted = await sock.reportUser(userJid, 'spam')
|
|
695
|
+
const messageAccepted = await sock.reportMessage(
|
|
696
|
+
userJid,
|
|
697
|
+
messageId,
|
|
698
|
+
'harassment',
|
|
699
|
+
participantJid
|
|
700
|
+
)
|
|
701
|
+
await sock.reportAndBlockUser(userJid, 'scam')
|
|
444
702
|
```
|
|
445
703
|
|
|
446
|
-
|
|
704
|
+
`reportUser` and `reportMessage` return whether WhatsApp acknowledged the request. An acknowledgement is not a guarantee of a particular moderation outcome.
|
|
447
705
|
|
|
448
|
-
##
|
|
706
|
+
## Experimental bot metadata
|
|
707
|
+
|
|
708
|
+
These methods attach experimental fields under `messageContextInfo.botMetadata`.
|
|
449
709
|
|
|
450
710
|
```js
|
|
451
|
-
await sock.
|
|
452
|
-
text: '
|
|
453
|
-
|
|
711
|
+
await sock.sendBotDiagnostics(jid, {
|
|
712
|
+
text: 'Diagnostics test',
|
|
713
|
+
botBackend: 1,
|
|
714
|
+
toolsUsed: ['web_search'],
|
|
715
|
+
isThinking: false
|
|
454
716
|
})
|
|
455
717
|
|
|
456
|
-
await sock.
|
|
457
|
-
text: '
|
|
458
|
-
|
|
718
|
+
await sock.sendBotCommand(jid, {
|
|
719
|
+
text: 'Command test',
|
|
720
|
+
commandName: 'summarize',
|
|
721
|
+
commandDescription: 'Summarize the conversation',
|
|
722
|
+
commandPrompt: 'Summarize the conversation in five bullets'
|
|
459
723
|
})
|
|
460
724
|
|
|
461
|
-
await sock.
|
|
462
|
-
|
|
725
|
+
await sock.sendBotToolResult(jid, {
|
|
726
|
+
text: 'Tool completed',
|
|
727
|
+
toolCallId: 'tool-123',
|
|
728
|
+
resolutionData: { status: 'success', value: 42 }
|
|
463
729
|
})
|
|
464
730
|
```
|
|
465
731
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
## Error handling and reconnection
|
|
732
|
+
These methods do not provide Meta's private verification credentials and must not be used to impersonate a first-party Meta AI message.
|
|
469
733
|
|
|
470
|
-
|
|
734
|
+
## WhatsApp flows and web views
|
|
471
735
|
|
|
472
736
|
```js
|
|
473
|
-
sock.
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
return
|
|
480
|
-
}
|
|
737
|
+
await sock.sendWhatsAppFlow(jid, {
|
|
738
|
+
flowId: 'your-flow-id',
|
|
739
|
+
flowToken: 'your-flow-token',
|
|
740
|
+
cta: 'Open form',
|
|
741
|
+
text: 'Complete this form'
|
|
742
|
+
})
|
|
481
743
|
|
|
482
|
-
|
|
744
|
+
await sock.sendRichWebview(jid, {
|
|
745
|
+
url: 'https://example.com/app',
|
|
746
|
+
text: 'Open the app',
|
|
747
|
+
buttonText: 'Open'
|
|
483
748
|
})
|
|
484
749
|
```
|
|
485
750
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
Version 1.0.6 adds the realvare-style `w:report` methods without replacing plogme's existing evidence-based `reportSpam` method. The methods use different WhatsApp IQ namespaces and should be selected according to the operation you intend to perform.
|
|
751
|
+
Flow identifiers and tokens must belong to a valid business configuration.
|
|
489
752
|
|
|
490
|
-
|
|
491
|
-
| --- | --- | --- |
|
|
492
|
-
| `reportSpam(jid, messages, spamFlow, subject)` | `xmlns="spam"` → `spam_list` | Report a chat or group with message evidence. |
|
|
493
|
-
| `reportUser(jid, reason, waitForAckMs)` | `xmlns="w:report"` → `user` | Report one user. |
|
|
494
|
-
| `reportMessage(jid, messageId, reason, participantJid, waitForAckMs)` | `xmlns="w:report"` → `message` | Report one message. |
|
|
495
|
-
| `reportAndBlockUser(jid, reason, waitForAckMs)` | `w:report` followed by block | Report a user and then block them. |
|
|
753
|
+
## View-once utilities
|
|
496
754
|
|
|
497
755
|
```js
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
'1203630@g.us',
|
|
501
|
-
[{ id: 'ABCD1234', from: '15550001111@s.whatsapp.net', t: 1710000000 }],
|
|
502
|
-
'group_info_report',
|
|
503
|
-
'Example group'
|
|
504
|
-
)
|
|
756
|
+
const info = sock.getViewOnceInfo(message)
|
|
757
|
+
console.log(info)
|
|
505
758
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
'15550001111@s.whatsapp.net', 'ABCD1234', 'harassment',
|
|
510
|
-
'15550001111@s.whatsapp.net'
|
|
511
|
-
)
|
|
512
|
-
await sock.reportAndBlockUser('15550001111@s.whatsapp.net', 'scam')
|
|
759
|
+
if (sock.isViewOnce(message)) {
|
|
760
|
+
await sock.autoVV(message)
|
|
761
|
+
}
|
|
513
762
|
```
|
|
514
763
|
|
|
515
|
-
`
|
|
764
|
+
`autoVV` and `antiVV` are utilities for view-once handling. Their behavior depends on the message shape and client version; use them only for messages you are authorized to process.
|
|
516
765
|
|
|
517
|
-
|
|
766
|
+
## Connection handling
|
|
518
767
|
|
|
519
|
-
|
|
768
|
+
A connection can close because of logout, network interruption, protocol changes, or session invalidation. Avoid starting multiple reconnect loops for the same socket.
|
|
520
769
|
|
|
521
770
|
```js
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
```
|
|
525
|
-
|
|
526
|
-
### Two HTML transports
|
|
527
|
-
|
|
528
|
-
`sendHtmlMessage` sends the ordinary single-screen `FOAHtmlPrimitiveDemoDONOTUSE` envelope. `sendRichHtmlMessage` is the reusable transport for interactive GenAI HTML screens like tabbed games; it uses `GenAIaeacdsnwHtmlPrimitive` inside `FOAIDNixelButtonSheets` and supports multiple tabs.
|
|
771
|
+
sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
|
|
772
|
+
if (connection !== 'close') return
|
|
529
773
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
}
|
|
774
|
+
const statusCode = new Boom(lastDisconnect?.error)?.output?.statusCode
|
|
775
|
+
if (statusCode === DisconnectReason.loggedOut) {
|
|
776
|
+
console.error('Logged out; pair again with a new session')
|
|
777
|
+
return
|
|
778
|
+
}
|
|
535
779
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
trustedSources: ['your-domain.example'],
|
|
540
|
-
tabs: [
|
|
541
|
-
{ tab_header: 'mini-app', html: '<canvas id="game"></canvas><script>/* game */</script>' },
|
|
542
|
-
{ tab_header: 'Instructions', html: '<h2>How to play</h2><p>Tap to jump.</p>' }
|
|
543
|
-
]
|
|
780
|
+
setTimeout(() => {
|
|
781
|
+
connect().catch(console.error)
|
|
782
|
+
}, 3000)
|
|
544
783
|
})
|
|
545
784
|
```
|
|
546
785
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
The built-in `sendSlotMachine` keeps its existing ordinary HTML transport. The attached interactive mini-app is available as `sendMiniApp`; because it uses the embedded-screen envelope, plogme configures that built-in game with the owned `pair.crysnova.link` source:
|
|
550
|
-
|
|
551
|
-
```js
|
|
552
|
-
await sock.sendMiniApp(jid)
|
|
553
|
-
await sock.sendMiniApp(jid, { title: 'My mini-app' })
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
The `sendRichHtmlMessage` function itself remains neutral: user-supplied screens do not inherit `pair.crysnova.link` and should provide their own `url` and `trustedSources` when those fields are needed.
|
|
557
|
-
|
|
558
|
-
### Reusable source grep
|
|
559
|
-
|
|
560
|
-
`grepSource` is exported from `lib/Utils/index.js`, and `sock.grepCode` is available after socket creation. It returns exact relative file paths, one-based line numbers, columns, and matching source lines without invoking a shell.
|
|
561
|
-
|
|
562
|
-
```js
|
|
563
|
-
const matches = sock.grepCode('sendRichHtmlMessage', { maxResults: 20 })
|
|
564
|
-
console.log(matches)
|
|
565
|
-
// [{ file: 'lib/Socket/messages-send.js', line: 2705, column: 12, text: '...' }]
|
|
566
|
-
|
|
567
|
-
const reportMatches = sock.grepCode('reportMessage', { caseSensitive: false })
|
|
568
|
-
const regexMatches = sock.grepCode('report(User|Message)', { regex: true, flags: 'i' })
|
|
569
|
-
```
|
|
570
|
-
|
|
571
|
-
By default the search covers `lib` and `WAProto`; `includeReadme: true` also searches the README.
|
|
572
|
-
|
|
573
|
-
## Changes to Automatic Behaviors
|
|
574
|
-
|
|
575
|
-
Previous versions of Plogme included several related behaviors that have been removed from the npm release:
|
|
786
|
+
## Security and removed automatic behavior
|
|
576
787
|
|
|
577
|
-
|
|
578
|
-
- Anti-rebranding/anti-brand enforcement.
|
|
579
|
-
- Device fingerprinting and remote attestation, which collected machine characteristics such as the hostname, operating system, CPU model, architecture, and Node.js version before sending a fingerprint to an external verification service.
|
|
788
|
+
Plogme does not automatically follow WhatsApp channels, fingerprint the host machine, contact a remote attestation service, or enforce rebranding through runtime code.
|
|
580
789
|
|
|
581
|
-
|
|
790
|
+
> [!NOTE]
|
|
791
|
+
> Official update channels are optional. Users who want updates must join them manually:
|
|
792
|
+
>
|
|
793
|
+
> - [CODY AI WhatsApp channel](https://whatsapp.com/channel/0029Vb8296789inZCpWLuS3T)
|
|
794
|
+
> - [CRYSNOVA AI WhatsApp channel](https://whatsapp.com/channel/0029Vb6pe77K0IBn48HLKb38)
|
|
582
795
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
However, these mechanisms went beyond what should be expected from a library and could result in behavior that users did not explicitly initiate. For transparency, predictability, and a better npm package experience, these mechanisms have now been completely removed.
|
|
586
|
-
|
|
587
|
-
Current Plogme releases do not automatically follow external accounts or channels, do not enforce branding or rebranding restrictions through runtime behavior, and do not fingerprint the host machine or contact a remote attestation service for that purpose. The project's licensing terms still apply; removing runtime enforcement does not grant permission to violate the applicable license.
|
|
796
|
+
The project license still applies. Removing runtime enforcement does not change the license terms.
|
|
588
797
|
|
|
589
798
|
## Development
|
|
590
799
|
|
|
@@ -593,80 +802,18 @@ npm install
|
|
|
593
802
|
npm test
|
|
594
803
|
```
|
|
595
804
|
|
|
596
|
-
Before publishing, verify
|
|
597
|
-
|
|
598
|
-
## License
|
|
599
|
-
|
|
600
|
-
Review the included license before distributing applications built with `plogme`. Keep required notices with redistributed source and binaries.
|
|
601
|
-
|
|
602
|
-
## Support
|
|
603
|
-
|
|
604
|
-
For implementation questions, include the Node.js version, `plogme` version, operating system, relevant error text, and a minimal reproducible example. Never include authentication state or private account data in an issue.
|
|
805
|
+
Before publishing, verify the package contents and ensure no session data, private keys, or environment files are included.
|
|
605
806
|
|
|
807
|
+
The repository includes generated WhatsApp protocol files under `WAProto/`. Changes to generated protocol definitions should be made deliberately and tested against the installed WhatsApp client versions you support.
|
|
606
808
|
|
|
607
|
-
##
|
|
608
|
-
|
|
609
|
-
The bundled WAProto schema is updated to WhatsApp Web version `2.3000.1047412487`, with the generated runtime, TypeScript declarations, and connection version updated together. Selected support includes newsletter follower-invite base/V2 fields, ACP2 receive-state extraction, animated wallpaper encoding, and nested audio in sticker messages. These remain experimental and client-dependent.
|
|
610
|
-
|
|
611
|
-
### Animated wallpaper
|
|
612
|
-
|
|
613
|
-
```js
|
|
614
|
-
await sock.sendMessage(jid, {
|
|
615
|
-
chatTheme: {
|
|
616
|
-
animatedWallpaper: { id: 'wallpaper-id', dimLevel: 0.25 }
|
|
617
|
-
}
|
|
618
|
-
})
|
|
619
|
-
```
|
|
620
|
-
|
|
621
|
-
### Sticker with audio
|
|
809
|
+
## License and support
|
|
622
810
|
|
|
623
|
-
|
|
624
|
-
await sock.sendMessage(jid, {
|
|
625
|
-
sticker: { url: './sticker.webp' },
|
|
626
|
-
stickerAudio: { url: './sound.ogg' }
|
|
627
|
-
})
|
|
628
|
-
```
|
|
811
|
+
Review [`LICENSE`](LICENSE) before redistributing applications or modified source.
|
|
629
812
|
|
|
630
|
-
|
|
813
|
+
For implementation questions, include the Node.js version, Plogme version, operating system, relevant error text, and a minimal reproduction. Do not include authentication state or private account data.
|
|
631
814
|
|
|
632
|
-
|
|
633
|
-
await sock.sendMessage(jid, {
|
|
634
|
-
newsletterFollowerInvite: {
|
|
635
|
-
newsletterJid: '123456789@newsletter',
|
|
636
|
-
newsletterName: 'My newsletter',
|
|
637
|
-
caption: 'Follow this channel'
|
|
638
|
-
}
|
|
639
|
-
})
|
|
640
|
-
|
|
641
|
-
await sock.sendMessage(jid, {
|
|
642
|
-
newsletterFollowerInviteMessageV2: {
|
|
643
|
-
newsletterJid: '123456789@newsletter',
|
|
644
|
-
newsletterName: 'My newsletter',
|
|
645
|
-
caption: 'Follow this channel'
|
|
646
|
-
}
|
|
647
|
-
})
|
|
648
|
-
```
|
|
649
|
-
|
|
650
|
-
### Device-scoped relay options
|
|
651
|
-
|
|
652
|
-
`relayMessage` supports `isSecret`, `protected`, and `me` for supported private-chat device fan-out paths. These select recipients and may add `device_fanout`; they are not encryption, access-control, or delivery guarantees and may not behave identically for groups, statuses, retries, or future clients. They are unrelated to the protobuf field `messageContextInfo.messageSecret`.
|
|
653
|
-
|
|
654
|
-
```js
|
|
655
|
-
await sock.relayMessage(jid, message, {
|
|
656
|
-
messageId: messageId,
|
|
657
|
-
isSecret: true
|
|
658
|
-
})
|
|
659
|
-
```
|
|
660
|
-
|
|
661
|
-
### Explicit album wrapper
|
|
662
|
-
|
|
663
|
-
Albums remain parent/child wire messages: one album header is relayed first, followed by separately encrypted image/video children associated with the parent key. The receiver must group them visually. The explicit wrapper accepts typed media objects and applies a caption to the first child:
|
|
664
|
-
|
|
665
|
-
```js
|
|
666
|
-
await sock.sendAlbumMessage(jid, [
|
|
667
|
-
{ type: 'image', data: { url: './one.jpg' } },
|
|
668
|
-
{ type: 'image', data: { url: './two.jpg' } }
|
|
669
|
-
], { caption: 'Album caption' })
|
|
670
|
-
```
|
|
815
|
+
## References
|
|
671
816
|
|
|
672
|
-
|
|
817
|
+
[1]: https://github.com/crysnovax/baileys "Plogme source repository"
|
|
818
|
+
[2]: https://www.npmjs.com/package/plogme "Plogme on npm"
|
|
819
|
+
[3]: https://github.com/WhiskeySockets/Baileys "Baileys protocol implementation"
|