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 CHANGED
@@ -1,8 +1,8 @@
1
1
  # plogme
2
2
 
3
- A Node.js WhatsApp Web API client for building messaging integrations with authentication, media transfer, groups, interactive messages, status updates, and rich message composition.
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
- > **Package naming note:** npm scoped packages require a scope and package name. The valid package form is `plogme`; the bare string `@plogme` is a scope, not an installable package name by itself.
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
- For a repository checkout:
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 socket = makeWASocket({
28
+ const sock = makeWASocket({
37
29
  auth: state,
38
30
  logger: pino({ level: 'silent' })
39
31
  })
40
32
 
41
- socket.ev.on('creds.update', saveCreds)
42
- socket.ev.on('connection.update', ({ connection, lastDisconnect }) => {
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
- ## Authentication
46
+ Keep authentication files private. Never commit session credentials, private keys, or `.env` files.
61
47
 
62
- The recommended authentication state is multi-file authentication:
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
- Pairing codes can be requested after the socket has initialized:
53
+ const sock = makeWASocket({ auth: state })
54
+ sock.ev.on('creds.update', saveCreds)
71
55
 
72
- ```js
73
- const code = await socket.requestPairingCode('15551234567')
56
+ const code = await sock.requestPairingCode('15551234567')
74
57
  console.log(code)
75
58
  ```
76
59
 
77
- Keep authentication files private. Do not commit session credentials, private keys, or environment files to a public repository.
60
+ Pairing codes require the socket to be initialized and a valid phone number in international format.
78
61
 
79
- ## Text, mentions, and reactions
62
+ ## Ordinary text, mentions, reactions, and media
80
63
 
81
64
  ```js
82
- await socket.sendMessage(jid, { text: 'Hello!' })
65
+ await sock.sendMessage(jid, { text: 'Hello!' })
83
66
 
84
- await socket.sendMessage(jid, {
67
+ await sock.sendMessage(jid, {
85
68
  text: 'Hello @15551234567',
86
69
  mentions: ['15551234567@s.whatsapp.net']
87
70
  })
88
71
 
89
- await socket.sendMessage(jid, {
90
- react: { text: '✅', key: message.key }
72
+ await sock.sendMessage(jid, {
73
+ react: { text: '👍', key: message.key }
91
74
  })
92
75
  ```
93
76
 
94
- ## Media
77
+ Media examples:
95
78
 
96
79
  ```js
97
- await socket.sendMessage(jid, {
80
+ await sock.sendMessage(jid, {
98
81
  image: { url: './image.jpg' },
99
82
  caption: 'Image caption'
100
83
  })
101
84
 
102
- await socket.sendMessage(jid, {
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 socket.sendMessage(jid, {
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 socket.sendMessage(jid, {
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
- ## Interactive messages
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 client supports button, list, template, carousel, and native-flow message structures exposed by the installed library version. A basic button example is:
194
+ The same behavior can be requested through `sendRichText` by supplying `copyText`:
123
195
 
124
196
  ```js
125
- await socket.sendMessage(jid, {
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: 'Menu',
128
- buttons: [
129
- { buttonId: 'confirm', buttonText: { displayText: 'Confirm' }, type: 1 },
130
- { buttonId: 'cancel', buttonText: { displayText: 'Cancel' }, type: 1 }
131
- ],
132
- headerType: 1
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
- Always validate button input on the server. A button identifier is user-controlled input and must not be treated as an authorization grant.
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
- ## Groups
287
+ For a single native-flow message:
139
288
 
140
289
  ```js
141
- const metadata = await socket.groupMetadata(groupJid)
142
- await socket.groupParticipantsUpdate(groupJid, [userJid], 'add')
143
- await socket.groupParticipantsUpdate(groupJid, [userJid], 'remove')
144
- await socket.groupSettingUpdate(groupJid, 'announcement')
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
- Group administration calls require the connected account to have the corresponding privileges. Applications should handle permission failures and disconnected sessions gracefully.
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
- Status messages can be sent with text, images, videos, captions, and mentions according to the message content supported by the current protocol version:
375
+ Use `backgroundColor` rather than `backgroundArgb` in the high-level API:
152
376
 
153
377
  ```js
154
- await socket.sendMessage('status@broadcast', {
378
+ await sock.sendStatus({
155
379
  text: 'Service status: online',
156
- backgroundArgb: 0xff102a43,
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 socket.sendMessage(jid, {
442
+ await sock.sendMessage(jid, {
165
443
  text: 'Disappearing message',
166
444
  ephemeralExpiration: 86400
167
445
  })
168
446
 
169
- await socket.sendMessage(jid, {
447
+ await sock.sendMessage(jid, {
170
448
  text: 'Updated text',
171
449
  edit: message.key
172
450
  })
173
451
 
174
- await socket.sendMessage(jid, {
452
+ await sock.sendMessage(jid, {
175
453
  delete: message.key
176
454
  })
177
455
  ```
178
456
 
179
- Use raw protocol options only when the target client and installed library version support them. Unsupported message fields can be rejected by WhatsApp or ignored by the recipient.
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 can close because of logout, network interruption, protocol updates, or server-side session invalidation. Applications should distinguish logout from recoverable failures and should avoid starting multiple concurrent connection loops.
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
- socket.ev.on('connection.update', ({ connection, lastDisconnect }) => {
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('Session logged out; remove the old session and pair again.')
469
+ console.error('Logged out; remove the old session and pair again.')
191
470
  return
192
471
  }
193
- console.log('Connection interrupted; reconnect using a backoff strategy.')
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 opening a pull request, run the test suite, check the generated package contents, and confirm that no credentials or session files are included.
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 repository license before distributing applications built with this package. Keep required notices with redistributed source and binaries.
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, package version, operating system, relevant error text, and a minimal reproducible example. Never include authentication state or private account data in an issue.
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.