plogme 1.0.3 → 1.0.5
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 +417 -69
- package/WAProto/WAProto.proto +611 -1170
- package/WAProto/index.d.ts +86878 -0
- package/WAProto/index.js +85157 -109306
- package/lib/Defaults/index.js +1 -1
- package/lib/Socket/messages-send.js +97 -4
- package/lib/Utils/messages.js +47 -11
- package/lib/Utils/process-message.js +12 -1
- package/lib/Utils/rich-message-utils.js +32 -1
- package/lib/index.d.ts +39 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# plogme
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`plogme` is a Node.js WhatsApp Web client based on the Baileys protocol implementation. It supports authentication, media, groups, interactive messages, status updates, albums, native flows, rich responses, and experimental Meta AI-style message envelopes.
|
|
4
4
|
|
|
5
|
-
> **
|
|
5
|
+
> **Experimental feature notice:** Rich responses, Meta AI-style metadata, HTML primitives, clipboard addon sections, carousels, and albums use WhatsApp Web protocol structures that may change without notice. Unsupported fields can be ignored, rejected, or rendered differently across WhatsApp versions.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -10,15 +10,7 @@ A Node.js WhatsApp Web API client for building messaging integrations with authe
|
|
|
10
10
|
npm install plogme
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npm install github:crysnovax/baileys
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Requirements
|
|
20
|
-
|
|
21
|
-
The package supports current Node.js LTS releases. Use Node.js 20 or newer for the latest runtime and dependency compatibility.
|
|
13
|
+
The current package version is **1.0.4**. Use Node.js 20 or newer.
|
|
22
14
|
|
|
23
15
|
## Quick start
|
|
24
16
|
|
|
@@ -33,164 +25,452 @@ import pino from 'pino'
|
|
|
33
25
|
|
|
34
26
|
async function start() {
|
|
35
27
|
const { state, saveCreds } = await useMultiFileAuthState('./session')
|
|
36
|
-
const
|
|
28
|
+
const sock = makeWASocket({
|
|
37
29
|
auth: state,
|
|
38
30
|
logger: pino({ level: 'silent' })
|
|
39
31
|
})
|
|
40
32
|
|
|
41
|
-
|
|
42
|
-
|
|
33
|
+
sock.ev.on('creds.update', saveCreds)
|
|
34
|
+
sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
|
|
43
35
|
if (connection === 'open') console.log('Connected')
|
|
44
36
|
if (connection === 'close') {
|
|
45
37
|
const status = new Boom(lastDisconnect?.error)?.output?.statusCode
|
|
46
38
|
if (status !== DisconnectReason.loggedOut) start()
|
|
47
39
|
}
|
|
48
40
|
})
|
|
49
|
-
|
|
50
|
-
socket.ev.on('messages.upsert', async ({ messages }) => {
|
|
51
|
-
const message = messages[0]
|
|
52
|
-
if (!message?.message) return
|
|
53
|
-
await socket.sendMessage(message.key.remoteJid, { text: 'Hello from plogme' })
|
|
54
|
-
})
|
|
55
41
|
}
|
|
56
42
|
|
|
57
43
|
start()
|
|
58
44
|
```
|
|
59
45
|
|
|
60
|
-
|
|
46
|
+
Keep authentication files private. Never commit session credentials, private keys, or `.env` files.
|
|
61
47
|
|
|
62
|
-
|
|
48
|
+
## Authentication and pairing
|
|
63
49
|
|
|
64
50
|
```js
|
|
65
|
-
import { useMultiFileAuthState } from 'plogme'
|
|
66
|
-
|
|
67
51
|
const { state, saveCreds } = await useMultiFileAuthState('./session')
|
|
68
|
-
```
|
|
69
52
|
|
|
70
|
-
|
|
53
|
+
const sock = makeWASocket({ auth: state })
|
|
54
|
+
sock.ev.on('creds.update', saveCreds)
|
|
71
55
|
|
|
72
|
-
|
|
73
|
-
const code = await socket.requestPairingCode('15551234567')
|
|
56
|
+
const code = await sock.requestPairingCode('15551234567')
|
|
74
57
|
console.log(code)
|
|
75
58
|
```
|
|
76
59
|
|
|
77
|
-
|
|
60
|
+
Pairing codes require the socket to be initialized and a valid phone number in international format.
|
|
78
61
|
|
|
79
|
-
##
|
|
62
|
+
## Ordinary text, mentions, reactions, and media
|
|
80
63
|
|
|
81
64
|
```js
|
|
82
|
-
await
|
|
65
|
+
await sock.sendMessage(jid, { text: 'Hello!' })
|
|
83
66
|
|
|
84
|
-
await
|
|
67
|
+
await sock.sendMessage(jid, {
|
|
85
68
|
text: 'Hello @15551234567',
|
|
86
69
|
mentions: ['15551234567@s.whatsapp.net']
|
|
87
70
|
})
|
|
88
71
|
|
|
89
|
-
await
|
|
90
|
-
react: { text: '
|
|
72
|
+
await sock.sendMessage(jid, {
|
|
73
|
+
react: { text: '👍', key: message.key }
|
|
91
74
|
})
|
|
92
75
|
```
|
|
93
76
|
|
|
94
|
-
|
|
77
|
+
Media examples:
|
|
95
78
|
|
|
96
79
|
```js
|
|
97
|
-
await
|
|
80
|
+
await sock.sendMessage(jid, {
|
|
98
81
|
image: { url: './image.jpg' },
|
|
99
82
|
caption: 'Image caption'
|
|
100
83
|
})
|
|
101
84
|
|
|
102
|
-
await
|
|
85
|
+
await sock.sendMessage(jid, {
|
|
103
86
|
video: { url: './video.mp4' },
|
|
104
87
|
caption: 'Video caption',
|
|
105
88
|
gifPlayback: false
|
|
106
89
|
})
|
|
107
90
|
|
|
108
|
-
await
|
|
91
|
+
await sock.sendMessage(jid, {
|
|
109
92
|
document: { url: './report.pdf' },
|
|
110
93
|
mimetype: 'application/pdf',
|
|
111
94
|
fileName: 'report.pdf'
|
|
112
95
|
})
|
|
113
96
|
|
|
114
|
-
await
|
|
97
|
+
await sock.sendMessage(jid, {
|
|
115
98
|
audio: { url: './audio.ogg' },
|
|
116
99
|
mimetype: 'audio/ogg; codecs=opus'
|
|
117
100
|
})
|
|
118
101
|
```
|
|
119
102
|
|
|
120
|
-
##
|
|
103
|
+
## Profile name and About description
|
|
104
|
+
|
|
105
|
+
WhatsApp exposes the personal profile description as the **About status**. In
|
|
106
|
+
`plogme`, use `updateProfileStatus`—not `updateProfileDescription`—and pass a
|
|
107
|
+
plain string:
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
await sock.updateProfileStatus('Building helpful bots')
|
|
111
|
+
await sock.updateProfileName('Cody AI')
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
This updates the connected account's own profile. It does not update a contact
|
|
115
|
+
or group description.
|
|
116
|
+
|
|
117
|
+
## Spoiler messages
|
|
118
|
+
|
|
119
|
+
For media, use `spoiler: true`. The default native mode serializes the
|
|
120
|
+
supported `contextInfo.isSpoiler` flag:
|
|
121
|
+
|
|
122
|
+
```js
|
|
123
|
+
await sock.sendMessage(jid, {
|
|
124
|
+
image: { url: './classified.jpg' },
|
|
125
|
+
caption: 'Open only when ready',
|
|
126
|
+
spoiler: true
|
|
127
|
+
})
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
For clients that do not support native media spoilers, explicitly use the
|
|
131
|
+
compatibility fallback. This sends a view-once envelope; it is **not** the
|
|
132
|
+
same as a native spoiler:
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
await sock.sendMessage(jid, {
|
|
136
|
+
video: { url: './reveal.mp4' },
|
|
137
|
+
spoiler: true,
|
|
138
|
+
mediaSpoilerMode: 'viewOnce'
|
|
139
|
+
})
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
For text, the default fallback uses WhatsApp's rolling-out `||...||` spoiler
|
|
143
|
+
formatting. Native text metadata can be requested with
|
|
144
|
+
`spoilerMode: 'native'`. Rendering remains client-version dependent.
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
await sock.sendMessage(jid, {
|
|
148
|
+
text: 'The ending is revealed here',
|
|
149
|
+
spoiler: true
|
|
150
|
+
})
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Rich text and Markdown
|
|
154
|
+
|
|
155
|
+
`sendRichText` sends Markdown-capable text inside a forwarded GenAI rich-response envelope. Markdown rendering depends on the recipient's WhatsApp client.
|
|
156
|
+
|
|
157
|
+
```js
|
|
158
|
+
await sock.sendRichText(jid, {
|
|
159
|
+
text: `**Heading**
|
|
160
|
+
|
|
161
|
+
Normal text
|
|
162
|
+
|
|
163
|
+
> This is a blockquote`
|
|
164
|
+
})
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
A dedicated blockquote helper is also available:
|
|
168
|
+
|
|
169
|
+
```js
|
|
170
|
+
await sock.sendMarkdownQuote(jid, {
|
|
171
|
+
text: `Here is the instruction:
|
|
172
|
+
|
|
173
|
+
> Use this exact logo image.`
|
|
174
|
+
})
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Copy-to-clipboard controls
|
|
178
|
+
|
|
179
|
+
There are two different clipboard-related features:
|
|
180
|
+
|
|
181
|
+
1. `sendCopyButton` creates the inline GenAI `COPY_TO_CLIPBOARD` addon section.
|
|
182
|
+
2. `sendRichTextCopy` and `sendQuoteCopy` place ordinary Markdown content and a clipboard addon section in the same rich response. The text does **not** need to contain a quote.
|
|
183
|
+
|
|
184
|
+
Normal text with a copy action:
|
|
185
|
+
|
|
186
|
+
```js
|
|
187
|
+
await sock.sendRichTextCopy(jid, {
|
|
188
|
+
text: 'npm install plogme',
|
|
189
|
+
copyText: 'npm install plogme',
|
|
190
|
+
copyLabel: 'Copy command'
|
|
191
|
+
})
|
|
192
|
+
```
|
|
121
193
|
|
|
122
|
-
The
|
|
194
|
+
The same behavior can be requested through `sendRichText` by supplying `copyText`:
|
|
123
195
|
|
|
124
196
|
```js
|
|
125
|
-
await
|
|
197
|
+
await sock.sendRichText(jid, {
|
|
198
|
+
text: 'This is ordinary text with a copy action.',
|
|
199
|
+
copyText: 'This is ordinary text with a copy action.',
|
|
200
|
+
copyLabel: 'Copy me'
|
|
201
|
+
})
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Quote plus copy action:
|
|
205
|
+
|
|
206
|
+
```js
|
|
207
|
+
await sock.sendQuoteCopy(jid, {
|
|
208
|
+
text: `Ready-to-paste instruction:
|
|
209
|
+
|
|
210
|
+
> Use this exact logo image...`,
|
|
211
|
+
copyText: 'Use this exact logo image...',
|
|
212
|
+
copyLabel: 'Copy me'
|
|
213
|
+
})
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
`COPY_TO_CLIPBOARD` is an in-message rich-response action. It is different from the separate copy/forward/like/dislike toolbar that WhatsApp may render below first-party Meta AI messages. That toolbar is client-generated and is not authoritatively controllable through this API.
|
|
217
|
+
|
|
218
|
+
## Rich links, images, HTML, and menus
|
|
219
|
+
|
|
220
|
+
```js
|
|
221
|
+
await sock.sendRichLink(jid, {
|
|
222
|
+
text: 'Open the deployment site',
|
|
223
|
+
url: 'https://example.com'
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
await sock.sendRichImage(jid, {
|
|
227
|
+
url: 'https://example.com/image.jpg',
|
|
228
|
+
mimeType: 'image/jpeg'
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
await sock.sendHtmlMessage(jid, {
|
|
232
|
+
html: '<h1>Hello</h1><p>Rendered HTML content</p>'
|
|
233
|
+
})
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
`sendHtmlMessage` uses an experimental HTML primitive. Do not pass untrusted user input into HTML without sanitizing it.
|
|
237
|
+
|
|
238
|
+
The experimental rich menu helper accepts a header, body buttons, cards, and footer:
|
|
239
|
+
|
|
240
|
+
```js
|
|
241
|
+
await sock.richMenu(jid, {
|
|
242
|
+
header: { title: 'Deploy Suite' },
|
|
243
|
+
body: {
|
|
244
|
+
title: 'Choose an action',
|
|
245
|
+
buttons: [
|
|
246
|
+
{ id: 'deploy', text: 'Deploy' },
|
|
247
|
+
{ id: 'logs', text: 'View logs' }
|
|
248
|
+
]
|
|
249
|
+
},
|
|
250
|
+
footer: { text: 'Example menu' }
|
|
251
|
+
})
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Always validate button IDs on the server. Never treat a user-controlled button ID as an authorization grant.
|
|
255
|
+
|
|
256
|
+
## Carousels and rich button grids
|
|
257
|
+
|
|
258
|
+
Use `sendRichButtonGrid` for media cards with native-flow buttons. A carousel needs at least one valid image, video, or product header per card. The helper accepts either `buttons` or `nativeFlow`.
|
|
259
|
+
|
|
260
|
+
```js
|
|
261
|
+
await sock.sendRichButtonGrid(jid, {
|
|
126
262
|
text: 'Choose an option',
|
|
127
|
-
footer: '
|
|
128
|
-
|
|
129
|
-
{
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
263
|
+
footer: 'Select one',
|
|
264
|
+
cards: [
|
|
265
|
+
{
|
|
266
|
+
image: { url: './first.jpg' },
|
|
267
|
+
title: 'First card',
|
|
268
|
+
caption: 'First card description',
|
|
269
|
+
buttons: [
|
|
270
|
+
{ id: 'first', text: 'Select first' }
|
|
271
|
+
]
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
image: { url: './second.jpg' },
|
|
275
|
+
title: 'Second card',
|
|
276
|
+
caption: 'Second card description',
|
|
277
|
+
buttons: [
|
|
278
|
+
{ id: 'second', text: 'Select second' }
|
|
279
|
+
]
|
|
280
|
+
}
|
|
281
|
+
]
|
|
133
282
|
})
|
|
134
283
|
```
|
|
135
284
|
|
|
136
|
-
|
|
285
|
+
Supported button properties include `id`, `text`, `url`, `call`, and `copy`. Carousel cards must have valid media headers. A card containing only text is not a valid carousel card.
|
|
137
286
|
|
|
138
|
-
|
|
287
|
+
For a single native-flow message:
|
|
139
288
|
|
|
140
289
|
```js
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
290
|
+
await sock.sendMessage(jid, {
|
|
291
|
+
text: 'Open the site',
|
|
292
|
+
nativeFlow: [
|
|
293
|
+
{ url: 'https://example.com', text: 'Open' }
|
|
294
|
+
]
|
|
295
|
+
})
|
|
145
296
|
```
|
|
146
297
|
|
|
147
|
-
|
|
298
|
+
## Media albums
|
|
299
|
+
|
|
300
|
+
Albums are sent as a parent album message followed by associated image or video child messages. Provide at least two media items:
|
|
301
|
+
|
|
302
|
+
```js
|
|
303
|
+
await sock.sendMessage(jid, {
|
|
304
|
+
album: [
|
|
305
|
+
{
|
|
306
|
+
image: { url: './image-1.jpg' },
|
|
307
|
+
caption: 'First image'
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
image: { url: './image-2.jpg' },
|
|
311
|
+
caption: 'Second image'
|
|
312
|
+
}
|
|
313
|
+
]
|
|
314
|
+
})
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Album children are required to be actual `imageMessage` or `videoMessage` payloads. A `botForwardedMessage` or `richResponseMessage` cannot be nested as an album child. If rich text or a rich-response envelope is needed, send it as a separate message before or after the album.
|
|
318
|
+
|
|
319
|
+
## Experimental bot metadata
|
|
320
|
+
|
|
321
|
+
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.
|
|
322
|
+
|
|
323
|
+
### Diagnostics metadata
|
|
324
|
+
|
|
325
|
+
```js
|
|
326
|
+
await sock.sendBotDiagnostics(jid, {
|
|
327
|
+
text: 'Diagnostics test',
|
|
328
|
+
botBackend: 1, // 0 = AAPI, 1 = CLIPPY
|
|
329
|
+
toolsUsed: ['web_search', 'image_search'],
|
|
330
|
+
isThinking: true
|
|
331
|
+
})
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Command metadata
|
|
335
|
+
|
|
336
|
+
```js
|
|
337
|
+
await sock.sendBotCommand(jid, {
|
|
338
|
+
text: 'Command metadata test',
|
|
339
|
+
commandName: 'summarize',
|
|
340
|
+
commandDescription: 'Summarize the current conversation',
|
|
341
|
+
commandPrompt: 'Summarize the current conversation in five bullets'
|
|
342
|
+
})
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Resolved tool-call metadata
|
|
346
|
+
|
|
347
|
+
```js
|
|
348
|
+
await sock.sendBotToolResult(jid, {
|
|
349
|
+
text: 'Tool result test',
|
|
350
|
+
toolCallId: `tool-${Date.now()}`,
|
|
351
|
+
resolutionData: {
|
|
352
|
+
status: 'success',
|
|
353
|
+
result: { value: 'Completed' }
|
|
354
|
+
}
|
|
355
|
+
})
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
You may provide a pre-serialized result instead:
|
|
359
|
+
|
|
360
|
+
```js
|
|
361
|
+
await sock.sendBotToolResult(jid, {
|
|
362
|
+
text: 'Serialized result',
|
|
363
|
+
toolCallId: 'tool-123',
|
|
364
|
+
resolutionDataSerialized: JSON.stringify({
|
|
365
|
+
status: 'success',
|
|
366
|
+
value: 'Completed'
|
|
367
|
+
})
|
|
368
|
+
})
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
These methods do not fabricate Meta's verification certificates or signatures. They should not be treated as a way to impersonate a first-party Meta AI response.
|
|
148
372
|
|
|
149
373
|
## Status updates
|
|
150
374
|
|
|
151
|
-
|
|
375
|
+
Use `backgroundColor` rather than `backgroundArgb` in the high-level API:
|
|
152
376
|
|
|
153
377
|
```js
|
|
154
|
-
await
|
|
378
|
+
await sock.sendStatus({
|
|
155
379
|
text: 'Service status: online',
|
|
156
|
-
|
|
380
|
+
backgroundColor: 0xff102a43,
|
|
157
381
|
font: 2
|
|
382
|
+
}, {
|
|
383
|
+
statusJidList: ['15551234567@s.whatsapp.net']
|
|
158
384
|
})
|
|
159
385
|
```
|
|
160
386
|
|
|
387
|
+
Alternatively, send directly to `status@broadcast` when using the raw status protocol supported by the installed version.
|
|
388
|
+
|
|
389
|
+
## Groups
|
|
390
|
+
|
|
391
|
+
```js
|
|
392
|
+
const metadata = await sock.groupMetadata(groupJid)
|
|
393
|
+
await sock.groupParticipantsUpdate(groupJid, [userJid], 'add')
|
|
394
|
+
await sock.groupParticipantsUpdate(groupJid, [userJid], 'remove')
|
|
395
|
+
await sock.groupSettingUpdate(groupJid, 'announcement')
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
Group administration requires the corresponding privileges. Handle permission failures and disconnected sessions.
|
|
399
|
+
|
|
400
|
+
### Group member labels
|
|
401
|
+
|
|
402
|
+
`updateMemberLabel` sets the connected account's own custom member label in a
|
|
403
|
+
group. The first argument must be the **group JID** (`123456789-123456@g.us`),
|
|
404
|
+
not the member's individual JID:
|
|
405
|
+
|
|
406
|
+
```js
|
|
407
|
+
await sock.updateMemberLabel(groupJid, 'Developer')
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
To remove the label, pass an empty string:
|
|
411
|
+
|
|
412
|
+
```js
|
|
413
|
+
await sock.updateMemberLabel(groupJid, '')
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
Labels are limited to 30 characters by the protocol. This is not an admin
|
|
417
|
+
operation for assigning labels to arbitrary participants; WhatsApp associates
|
|
418
|
+
the label with the account that sends the protocol message.
|
|
419
|
+
|
|
420
|
+
## WhatsApp Flows and web views
|
|
421
|
+
|
|
422
|
+
```js
|
|
423
|
+
await sock.sendWhatsAppFlow(jid, {
|
|
424
|
+
flowId: '1234567890',
|
|
425
|
+
flowToken: 'example-token',
|
|
426
|
+
cta: 'Open flow',
|
|
427
|
+
text: 'Complete this form'
|
|
428
|
+
})
|
|
429
|
+
|
|
430
|
+
await sock.sendRichWebview(jid, {
|
|
431
|
+
url: 'https://example.com/app',
|
|
432
|
+
text: 'Open the app',
|
|
433
|
+
buttonText: 'Open'
|
|
434
|
+
})
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
The flow ID, token, and hosted URL must be valid for the target business configuration.
|
|
438
|
+
|
|
161
439
|
## Message options
|
|
162
440
|
|
|
163
441
|
```js
|
|
164
|
-
await
|
|
442
|
+
await sock.sendMessage(jid, {
|
|
165
443
|
text: 'Disappearing message',
|
|
166
444
|
ephemeralExpiration: 86400
|
|
167
445
|
})
|
|
168
446
|
|
|
169
|
-
await
|
|
447
|
+
await sock.sendMessage(jid, {
|
|
170
448
|
text: 'Updated text',
|
|
171
449
|
edit: message.key
|
|
172
450
|
})
|
|
173
451
|
|
|
174
|
-
await
|
|
452
|
+
await sock.sendMessage(jid, {
|
|
175
453
|
delete: message.key
|
|
176
454
|
})
|
|
177
455
|
```
|
|
178
456
|
|
|
179
|
-
|
|
457
|
+
Raw protocol fields are version-dependent. Unsupported fields may be ignored or rejected by WhatsApp.
|
|
180
458
|
|
|
181
459
|
## Error handling and reconnection
|
|
182
460
|
|
|
183
|
-
Connections
|
|
461
|
+
Connections may close because of logout, network interruption, protocol updates, or server-side session invalidation. Avoid starting multiple concurrent connection loops.
|
|
184
462
|
|
|
185
463
|
```js
|
|
186
|
-
|
|
464
|
+
sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
|
|
187
465
|
if (connection !== 'close') return
|
|
466
|
+
|
|
188
467
|
const status = new Boom(lastDisconnect?.error)?.output?.statusCode
|
|
189
468
|
if (status === DisconnectReason.loggedOut) {
|
|
190
|
-
console.error('
|
|
469
|
+
console.error('Logged out; remove the old session and pair again.')
|
|
191
470
|
return
|
|
192
471
|
}
|
|
193
|
-
|
|
472
|
+
|
|
473
|
+
console.log('Connection interrupted; reconnect with backoff.')
|
|
194
474
|
})
|
|
195
475
|
```
|
|
196
476
|
|
|
@@ -201,12 +481,80 @@ npm install
|
|
|
201
481
|
npm test
|
|
202
482
|
```
|
|
203
483
|
|
|
204
|
-
Before
|
|
484
|
+
Before publishing, verify generated protobuf files, package contents, version metadata, and that no authentication state or environment files are included.
|
|
205
485
|
|
|
206
486
|
## License
|
|
207
487
|
|
|
208
|
-
Review the
|
|
488
|
+
Review the included license before distributing applications built with `plogme`. Keep required notices with redistributed source and binaries.
|
|
209
489
|
|
|
210
490
|
## Support
|
|
211
491
|
|
|
212
|
-
For implementation questions, include the Node.js version,
|
|
492
|
+
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.
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
## Ported WhatsApp Web protocol features
|
|
496
|
+
|
|
497
|
+
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.
|
|
498
|
+
|
|
499
|
+
### Animated wallpaper
|
|
500
|
+
|
|
501
|
+
```js
|
|
502
|
+
await sock.sendMessage(jid, {
|
|
503
|
+
chatTheme: {
|
|
504
|
+
animatedWallpaper: { id: 'wallpaper-id', dimLevel: 0.25 }
|
|
505
|
+
}
|
|
506
|
+
})
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
### Sticker with audio
|
|
510
|
+
|
|
511
|
+
```js
|
|
512
|
+
await sock.sendMessage(jid, {
|
|
513
|
+
sticker: { url: './sticker.webp' },
|
|
514
|
+
stickerAudio: { url: './sound.ogg' }
|
|
515
|
+
})
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### Newsletter follower invites
|
|
519
|
+
|
|
520
|
+
```js
|
|
521
|
+
await sock.sendMessage(jid, {
|
|
522
|
+
newsletterFollowerInvite: {
|
|
523
|
+
newsletterJid: '123456789@newsletter',
|
|
524
|
+
newsletterName: 'My newsletter',
|
|
525
|
+
caption: 'Follow this channel'
|
|
526
|
+
}
|
|
527
|
+
})
|
|
528
|
+
|
|
529
|
+
await sock.sendMessage(jid, {
|
|
530
|
+
newsletterFollowerInviteMessageV2: {
|
|
531
|
+
newsletterJid: '123456789@newsletter',
|
|
532
|
+
newsletterName: 'My newsletter',
|
|
533
|
+
caption: 'Follow this channel'
|
|
534
|
+
}
|
|
535
|
+
})
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### Device-scoped relay options
|
|
539
|
+
|
|
540
|
+
`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`.
|
|
541
|
+
|
|
542
|
+
```js
|
|
543
|
+
await sock.relayMessage(jid, message, {
|
|
544
|
+
messageId: messageId,
|
|
545
|
+
isSecret: true
|
|
546
|
+
})
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
### Explicit album wrapper
|
|
550
|
+
|
|
551
|
+
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:
|
|
552
|
+
|
|
553
|
+
```js
|
|
554
|
+
await sock.sendAlbumMessage(jid, [
|
|
555
|
+
{ type: 'image', data: { url: './one.jpg' } },
|
|
556
|
+
{ type: 'image', data: { url: './two.jpg' } }
|
|
557
|
+
], { caption: 'Album caption' })
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
Rich HTML and bot-forwarded messages cannot be album children; send those separately. Album grouping is still dependent on WhatsApp server/client behavior.
|