baileys-joss 2.0.0 → 2.0.1

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.
Files changed (2) hide show
  1. package/README.md +2920 -1312
  2. package/package.json +146 -146
package/README.md CHANGED
@@ -1,1312 +1,2920 @@
1
- <h1 align='center'><img alt="Baileys logo" src="https://raw.githubusercontent.com/WhiskeySockets/Baileys/refs/heads/master/Media/logo.png" height="75"/></h1>
2
-
3
- <div align='center'>Baileys is a WebSockets-based TypeScript library for interacting with the WhatsApp Web API.</div>
4
-
5
-
6
- > [!CAUTION]
7
- > NOTICE OF BREAKING CHANGE.
8
- >
9
- > As of 7.0.0, multiple breaking changes were introduced into the library.
10
- >
11
- > Please check out https://whiskey.so/migrate-latest for more information.
12
-
13
- # Important Note
14
- This is a temporary README.md, the new guide is in development and will this file will be replaced with .github/README.md (already a default on GitHub).
15
-
16
- New guide link: https://baileys.wiki
17
-
18
- # Get Support
19
-
20
- If you'd like business to enterprise-level support from Rajeh, the current maintainer of Baileys, you can book a video chat. Book a 1 hour time slot by contacting him on Discord or pre-ordering [here](https://purpshell.dev/book). The earlier you pre-order the better, as his time slots usually fill up very quickly. He offers immense value per hour and will answer all your questions before the time runs out.
21
-
22
- If you are a business, we encourage you to contribute back to the high development costs of the project and to feed the maintainers who dump tens of hours a week on this. You can do so by booking meetings or sponsoring below. All support, even in bona fide / contribution hours, is welcome by businesses of all sizes. This is not condoning or endorsing businesses to use the library. See the Disclaimer below.
23
-
24
- # Sponsor
25
- If you'd like to financially support this project, you can do so by supporting the current maintainer [here](https://purpshell.dev/sponsor).
26
-
27
- # Disclaimer
28
- This project is not affiliated, associated, authorized, endorsed by, or in any way officially connected with WhatsApp or any of its subsidiaries or its affiliates.
29
- The official WhatsApp website can be found at whatsapp.com. "WhatsApp" as well as related names, marks, emblems and images are registered trademarks of their respective owners.
30
-
31
- The maintainers of Baileys do not in any way condone the use of this application in practices that violate the Terms of Service of WhatsApp. The maintainers of this application call upon the personal responsibility of its users to use this application in a fair way, as it is intended to be used.
32
- Use at your own discretion. Do not spam people with this. We discourage any stalkerware, bulk or automated messaging usage.
33
-
34
- ##
35
-
36
- - Baileys does not require Selenium or any other browser to be interface with WhatsApp Web, it does so directly using a **WebSocket**.
37
- - Not running Selenium or Chromium saves you like **half a gig** of ram :/
38
- - Baileys supports interacting with the multi-device & web versions of WhatsApp.
39
- - Thank you to [@pokearaujo](https://github.com/pokearaujo/multidevice) for writing his observations on the workings of WhatsApp Multi-Device. Also, thank you to [@Sigalor](https://github.com/sigalor/whatsapp-web-reveng) for writing his observations on the workings of WhatsApp Web and thanks to [@Rhymen](https://github.com/Rhymen/go-whatsapp/) for the __go__ implementation.
40
-
41
- > [!IMPORTANT]
42
- > The original repository had to be removed by the original author - we now continue development in this repository here.
43
- This is the only official repository and is maintained by the community.
44
- > **Join the Discord [here](https://discord.gg/WeJM5FP9GG)**
45
-
46
- ## Example
47
-
48
- Do check out & run [example.ts](Example/example.ts) to see an example usage of the library.
49
- The script covers most common use cases.
50
- To run the example script, download or clone the repo and then type the following in a terminal:
51
- 1. ``` cd path/to/Baileys ```
52
- 2. ``` yarn ```
53
- 3. ``` yarn example ```
54
-
55
- ## Install
56
-
57
- Use the stable version:
58
- ```
59
- yarn add @whiskeysockets/baileys
60
- ```
61
-
62
- Use the edge version (no guarantee of stability, but latest fixes + features)
63
- ```
64
- yarn add github:WhiskeySockets/Baileys
65
- ```
66
-
67
- Then import your code using:
68
- ```ts
69
- import makeWASocket from '@whiskeysockets/baileys'
70
- ```
71
-
72
- # Links
73
-
74
- - [Discord](https://discord.gg/WeJM5FP9GG)
75
- - [Docs](https://baileys.wiki/docs/intro/)
76
-
77
- # Index
78
-
79
- - [Connecting Account](#connecting-account)
80
- - [Connect with QR-CODE](#starting-socket-with-qr-code)
81
- - [Connect with Pairing Code](#starting-socket-with-pairing-code)
82
- - [Receive Full History](#receive-full-history)
83
- - [Important Notes About Socket Config](#important-notes-about-socket-config)
84
- - [Caching Group Metadata (Recommended)](#caching-group-metadata-recommended)
85
- - [Improve Retry System & Decrypt Poll Votes](#improve-retry-system--decrypt-poll-votes)
86
- - [Receive Notifications in Whatsapp App](#receive-notifications-in-whatsapp-app)
87
-
88
- - [Save Auth Info](#saving--restoring-sessions)
89
- - [Handling Events](#handling-events)
90
- - [Example to Start](#example-to-start)
91
- - [Decrypt Poll Votes](#decrypt-poll-votes)
92
- - [Summary of Events on First Connection](#summary-of-events-on-first-connection)
93
- - [Implementing a Data Store](#implementing-a-data-store)
94
- - [Whatsapp IDs Explain](#whatsapp-ids-explain)
95
- - [Utility Functions](#utility-functions)
96
- - [Sending Messages](#sending-messages)
97
- - [Non-Media Messages](#non-media-messages)
98
- - [Text Message](#text-message)
99
- - [Quote Message](#quote-message-works-with-all-types)
100
- - [Mention User](#mention-user-works-with-most-types)
101
- - [Forward Messages](#forward-messages)
102
- - [Location Message](#location-message)
103
- - [Contact Message](#contact-message)
104
- - [Reaction Message](#reaction-message)
105
- - [Pin Message](#pin-message)
106
- - [Poll Message](#poll-message)
107
- - [Sending with Link Preview](#sending-messages-with-link-previews)
108
- - [Media Messages](#media-messages)
109
- - [Gif Message](#gif-message)
110
- - [Video Message](#video-message)
111
- - [Audio Message](#audio-message)
112
- - [Image Message](#image-message)
113
- - [ViewOnce Message](#view-once-message)
114
- - [Modify Messages](#modify-messages)
115
- - [Delete Messages (for everyone)](#deleting-messages-for-everyone)
116
- - [Edit Messages](#editing-messages)
117
- - [Manipulating Media Messages](#manipulating-media-messages)
118
- - [Thumbnail in Media Messages](#thumbnail-in-media-messages)
119
- - [Downloading Media Messages](#downloading-media-messages)
120
- - [Re-upload Media Message to Whatsapp](#re-upload-media-message-to-whatsapp)
121
- - [Reject Call](#reject-call)
122
- - [Send States in Chat](#send-states-in-chat)
123
- - [Reading Messages](#reading-messages)
124
- - [Update Presence](#update-presence)
125
- - [Modifying Chats](#modifying-chats)
126
- - [Archive a Chat](#archive-a-chat)
127
- - [Mute/Unmute a Chat](#muteunmute-a-chat)
128
- - [Mark a Chat Read/Unread](#mark-a-chat-readunread)
129
- - [Delete a Message for Me](#delete-a-message-for-me)
130
- - [Delete a Chat](#delete-a-chat)
131
- - [Star/Unstar a Message](#starunstar-a-message)
132
- - [Disappearing Messages](#disappearing-messages)
133
- - [User Querys](#user-querys)
134
- - [Check If ID Exists in Whatsapp](#check-if-id-exists-in-whatsapp)
135
- - [Query Chat History (groups too)](#query-chat-history-groups-too)
136
- - [Fetch Status](#fetch-status)
137
- - [Fetch Profile Picture (groups too)](#fetch-profile-picture-groups-too)
138
- - [Fetch Bussines Profile (such as description or category)](#fetch-bussines-profile-such-as-description-or-category)
139
- - [Fetch Someone's Presence (if they're typing or online)](#fetch-someones-presence-if-theyre-typing-or-online)
140
- - [Change Profile](#change-profile)
141
- - [Change Profile Status](#change-profile-status)
142
- - [Change Profile Name](#change-profile-name)
143
- - [Change Display Picture (groups too)](#change-display-picture-groups-too)
144
- - [Remove display picture (groups too)](#remove-display-picture-groups-too)
145
- - [Groups](#groups)
146
- - [Create a Group](#create-a-group)
147
- - [Add/Remove or Demote/Promote](#addremove-or-demotepromote)
148
- - [Change Subject (name)](#change-subject-name)
149
- - [Change Description](#change-description)
150
- - [Change Settings](#change-settings)
151
- - [Leave a Group](#leave-a-group)
152
- - [Get Invite Code](#get-invite-code)
153
- - [Revoke Invite Code](#revoke-invite-code)
154
- - [Join Using Invitation Code](#join-using-invitation-code)
155
- - [Get Group Info by Invite Code](#get-group-info-by-invite-code)
156
- - [Query Metadata (participants, name, description...)](#query-metadata-participants-name-description)
157
- - [Join using groupInviteMessage](#join-using-groupinvitemessage)
158
- - [Get Request Join List](#get-request-join-list)
159
- - [Approve/Reject Request Join](#approvereject-request-join)
160
- - [Get All Participating Groups Metadata](#get-all-participating-groups-metadata)
161
- - [Toggle Ephemeral](#toggle-ephemeral)
162
- - [Change Add Mode](#change-add-mode)
163
- - [Privacy](#privacy)
164
- - [Block/Unblock User](#blockunblock-user)
165
- - [Get Privacy Settings](#get-privacy-settings)
166
- - [Get BlockList](#get-blocklist)
167
- - [Update LastSeen Privacy](#update-lastseen-privacy)
168
- - [Update Online Privacy](#update-online-privacy)
169
- - [Update Profile Picture Privacy](#update-profile-picture-privacy)
170
- - [Update Status Privacy](#update-status-privacy)
171
- - [Update Read Receipts Privacy](#update-read-receipts-privacy)
172
- - [Update Groups Add Privacy](#update-groups-add-privacy)
173
- - [Update Default Disappearing Mode](#update-default-disappearing-mode)
174
- - [Broadcast Lists & Stories](#broadcast-lists--stories)
175
- - [Send Broadcast & Stories](#send-broadcast--stories)
176
- - [Query a Broadcast List's Recipients & Name](#query-a-broadcast-lists-recipients--name)
177
- - [Writing Custom Functionality](#writing-custom-functionality)
178
- - [Enabling Debug Level in Baileys Logs](#enabling-debug-level-in-baileys-logs)
179
- - [How Whatsapp Communicate With Us](#how-whatsapp-communicate-with-us)
180
- - [Register a Callback for Websocket Events](#register-a-callback-for-websocket-events)
181
-
182
- ## Connecting Account
183
-
184
- WhatsApp provides a multi-device API that allows Baileys to be authenticated as a second WhatsApp client by scanning a **QR code** or **Pairing Code** with WhatsApp on your phone.
185
-
186
- > [!NOTE]
187
- > **[Here](#example-to-start) is a simple example of event handling**
188
-
189
- > [!TIP]
190
- > **You can see all supported socket configs in the [SocketConfig type alias](https://baileys.wiki/docs/api/type-aliases/SocketConfig/) (Recommended)**
191
-
192
- ### Starting socket with **QR-CODE**
193
-
194
- > [!TIP]
195
- > You can customize browser name if you connect with **QR-CODE**, with `Browser` constant, we have some browsers config, **see the [BrowsersMap type alias](https://baileys.wiki/docs/api/type-aliases/BrowsersMap/)**
196
-
197
- ```ts
198
- import makeWASocket from '@whiskeysockets/baileys'
199
-
200
- const sock = makeWASocket({
201
- // can provide additional config here
202
- browser: Browsers.ubuntu('My App'),
203
- printQRInTerminal: true
204
- })
205
- ```
206
-
207
- If the connection is successful, you will see a QR code printed on your terminal screen, scan it with WhatsApp on your phone and you'll be logged in!
208
-
209
- ### Starting socket with **Pairing Code**
210
-
211
-
212
- > [!IMPORTANT]
213
- > Pairing Code isn't Mobile API, it's a method to connect Whatsapp Web without QR-CODE, you can connect only with one device, see [here](https://faq.whatsapp.com/1324084875126592/?cms_platform=web)
214
-
215
- The phone number can't have `+` or `()` or `-`, only numbers, you must provide country code
216
-
217
- ```ts
218
- import makeWASocket from '@whiskeysockets/baileys'
219
-
220
- const sock = makeWASocket({
221
- // can provide additional config here
222
- printQRInTerminal: false //need to be false
223
- })
224
-
225
- if (!sock.authState.creds.registered) {
226
- const number = 'XXXXXXXXXXX'
227
- const code = await sock.requestPairingCode(number)
228
- console.log(code)
229
- }
230
- ```
231
-
232
- ### Receive Full History
233
-
234
- 1. Set `syncFullHistory` as `true`
235
- 2. Baileys, by default, use chrome browser config
236
- - If you'd like to emulate a desktop connection (and receive more message history), this browser setting to your Socket config:
237
-
238
- ```ts
239
- const sock = makeWASocket({
240
- ...otherOpts,
241
- // can use Windows, Ubuntu here too
242
- browser: Browsers.macOS('Desktop'),
243
- syncFullHistory: true
244
- })
245
- ```
246
-
247
- ## Important Notes About Socket Config
248
-
249
- ### Caching Group Metadata (Recommended)
250
- - If you use baileys for groups, we recommend you to set `cachedGroupMetadata` in socket config, you need to implement a cache like this:
251
-
252
- ```ts
253
- const groupCache = new NodeCache({stdTTL: 5 * 60, useClones: false})
254
-
255
- const sock = makeWASocket({
256
- cachedGroupMetadata: async (jid) => groupCache.get(jid)
257
- })
258
-
259
- sock.ev.on('groups.update', async ([event]) => {
260
- const metadata = await sock.groupMetadata(event.id)
261
- groupCache.set(event.id, metadata)
262
- })
263
-
264
- sock.ev.on('group-participants.update', async (event) => {
265
- const metadata = await sock.groupMetadata(event.id)
266
- groupCache.set(event.id, metadata)
267
- })
268
- ```
269
-
270
- ### Improve Retry System & Decrypt Poll Votes
271
- - If you want to improve sending message, retrying when error occurs and decrypt poll votes, you need to have a store and set `getMessage` config in socket like this:
272
- ```ts
273
- const sock = makeWASocket({
274
- getMessage: async (key) => await getMessageFromStore(key)
275
- })
276
- ```
277
-
278
- ### Receive Notifications in Whatsapp App
279
- - If you want to receive notifications in whatsapp app, set `markOnlineOnConnect` to `false`
280
- ```ts
281
- const sock = makeWASocket({
282
- markOnlineOnConnect: false
283
- })
284
- ```
285
- ## Saving & Restoring Sessions
286
-
287
- You obviously don't want to keep scanning the QR code every time you want to connect.
288
-
289
- So, you can load the credentials to log back in:
290
- ```ts
291
- import makeWASocket, { useMultiFileAuthState } from '@whiskeysockets/baileys'
292
-
293
- const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys')
294
-
295
- // will use the given state to connect
296
- // so if valid credentials are available -- it'll connect without QR
297
- const sock = makeWASocket({ auth: state })
298
-
299
- // this will be called as soon as the credentials are updated
300
- sock.ev.on('creds.update', saveCreds)
301
- ```
302
-
303
- > [!IMPORTANT]
304
- > `useMultiFileAuthState` is a utility function to help save the auth state in a single folder, this function serves as a good guide to help write auth & key states for SQL/no-SQL databases, which I would recommend in any production grade system.
305
-
306
- > [!NOTE]
307
- > When a message is received/sent, due to signal sessions needing updating, the auth keys (`authState.keys`) will update. Whenever that happens, you must save the updated keys (`authState.keys.set()` is called). Not doing so will prevent your messages from reaching the recipient & cause other unexpected consequences. The `useMultiFileAuthState` function automatically takes care of that, but for any other serious implementation -- you will need to be very careful with the key state management.
308
-
309
- ## Handling Events
310
-
311
- - Baileys uses the EventEmitter syntax for events.
312
- They're all nicely typed up, so you shouldn't have any issues with an Intellisense editor like VS Code.
313
-
314
- > [!IMPORTANT]
315
- > **The events are in the [BaileysEventMap type alias](https://baileys.wiki/docs/api/type-aliases/BaileysEventMap/)**, it's important you see all events
316
-
317
- You can listen to these events like this:
318
- ```ts
319
- const sock = makeWASocket()
320
- sock.ev.on('messages.upsert', ({ messages }) => {
321
- console.log('got messages', messages)
322
- })
323
- ```
324
-
325
- ### Example to Start
326
-
327
- > [!NOTE]
328
- > This example includes basic auth storage too
329
-
330
- > [!NOTE]
331
- > For reliable serialization of the authentication state, especially when storing as JSON, always use the BufferJSON utility.
332
-
333
- ```ts
334
- import makeWASocket, { DisconnectReason, useMultiFileAuthState } from '@whiskeysockets/baileys'
335
- import { Boom } from '@hapi/boom'
336
-
337
- async function connectToWhatsApp () {
338
- const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys')
339
- const sock = makeWASocket({
340
- // can provide additional config here
341
- auth: state,
342
- printQRInTerminal: true
343
- })
344
- sock.ev.on('connection.update', (update) => {
345
- const { connection, lastDisconnect } = update
346
- if(connection === 'close') {
347
- const shouldReconnect = (lastDisconnect.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut
348
- console.log('connection closed due to ', lastDisconnect.error, ', reconnecting ', shouldReconnect)
349
- // reconnect if not logged out
350
- if(shouldReconnect) {
351
- connectToWhatsApp()
352
- }
353
- } else if(connection === 'open') {
354
- console.log('opened connection')
355
- }
356
- })
357
- sock.ev.on('messages.upsert', event => {
358
- for (const m of event.messages) {
359
- console.log(JSON.stringify(m, undefined, 2))
360
-
361
- console.log('replying to', m.key.remoteJid)
362
- await sock.sendMessage(m.key.remoteJid!, { text: 'Hello Word' })
363
- }
364
- })
365
-
366
- // to storage creds (session info) when it updates
367
- sock.ev.on('creds.update', saveCreds)
368
- }
369
- // run in main file
370
- connectToWhatsApp()
371
- ```
372
-
373
- > [!IMPORTANT]
374
- > In `messages.upsert` it's recommended to use a loop like `for (const message of event.messages)` to handle all messages in array
375
-
376
- ### Decrypt Poll Votes
377
-
378
- - By default poll votes are encrypted and handled in `messages.update`
379
- - That's a simple example
380
- ```ts
381
- sock.ev.on('messages.update', event => {
382
- for(const { key, update } of event) {
383
- if(update.pollUpdates) {
384
- const pollCreation = await getMessage(key)
385
- if(pollCreation) {
386
- console.log(
387
- 'got poll update, aggregation: ',
388
- getAggregateVotesInPollMessage({
389
- message: pollCreation,
390
- pollUpdates: update.pollUpdates,
391
- })
392
- )
393
- }
394
- }
395
- }
396
- })
397
- ```
398
-
399
- - `getMessage` is a [store](#implementing-a-data-store) implementation (in your end)
400
-
401
- ### Summary of Events on First Connection
402
-
403
- 1. When you connect first time, `connection.update` will be fired requesting you to restart sock
404
- 2. Then, history messages will be received in `messaging.history-set`
405
-
406
- ## Implementing a Data Store
407
-
408
- - Baileys does not come with a defacto storage for chats, contacts, or messages. However, a simple in-memory implementation has been provided. The store listens for chat updates, new messages, message updates, etc., to always have an up-to-date version of the data.
409
-
410
- > [!IMPORTANT]
411
- > I highly recommend building your own data store, as storing someone's entire chat history in memory is a terrible waste of RAM.
412
-
413
- It can be used as follows:
414
-
415
- ```ts
416
- import makeWASocket, { makeInMemoryStore } from '@whiskeysockets/baileys'
417
- // the store maintains the data of the WA connection in memory
418
- // can be written out to a file & read from it
419
- const store = makeInMemoryStore({ })
420
- // can be read from a file
421
- store.readFromFile('./baileys_store.json')
422
- // saves the state to a file every 10s
423
- setInterval(() => {
424
- store.writeToFile('./baileys_store.json')
425
- }, 10_000)
426
-
427
- const sock = makeWASocket({ })
428
- // will listen from this socket
429
- // the store can listen from a new socket once the current socket outlives its lifetime
430
- store.bind(sock.ev)
431
-
432
- sock.ev.on('chats.upsert', () => {
433
- // can use 'store.chats' however you want, even after the socket dies out
434
- // 'chats' => a KeyedDB instance
435
- console.log('got chats', store.chats.all())
436
- })
437
-
438
- sock.ev.on('contacts.upsert', () => {
439
- console.log('got contacts', Object.values(store.contacts))
440
- })
441
-
442
- ```
443
-
444
- The store also provides some simple functions such as `loadMessages` that utilize the store to speed up data retrieval.
445
-
446
- ## Whatsapp IDs Explain
447
-
448
- - `id` is the WhatsApp ID, called `jid` too, of the person or group you're sending the message to.
449
- - It must be in the format ```[country code][phone number]@s.whatsapp.net```
450
- - Example for people: ```+19999999999@s.whatsapp.net```.
451
- - For groups, it must be in the format ``` 123456789-123345@g.us ```.
452
- - For broadcast lists, it's `[timestamp of creation]@broadcast`.
453
- - For stories, the ID is `status@broadcast`.
454
-
455
- ## Utility Functions
456
-
457
- - `getContentType`, returns the content type for any message
458
- - `getDevice`, returns the device from message
459
- - `makeCacheableSignalKeyStore`, make auth store more fast
460
- - `downloadContentFromMessage`, download content from any message
461
-
462
- ## Sending Messages
463
-
464
- - Send all types of messages with a single function
465
- - **In the [AnyMessageContent type alias](https://baileys.wiki/docs/api/type-aliases/AnyMessageContent/) you can see all message contents supported, like text message**
466
- - **In the [MiscMessageGenerationOptions type alias](https://baileys.wiki/docs/api/type-aliases/MiscMessageGenerationOptions/) you can see all options supported, like quote message**
467
-
468
- ```ts
469
- const jid: string
470
- const content: AnyMessageContent
471
- const options: MiscMessageGenerationOptions
472
-
473
- sock.sendMessage(jid, content, options)
474
- ```
475
-
476
- ### Non-Media Messages
477
-
478
- #### Text Message
479
- ```ts
480
- await sock.sendMessage(jid, { text: 'hello word' })
481
- ```
482
-
483
- #### Quote Message (works with all types)
484
- ```ts
485
- await sock.sendMessage(jid, { text: 'hello word' }, { quoted: message })
486
- ```
487
-
488
- #### Mention User (works with most types)
489
- - @number is to mention in text, it's optional
490
- ```ts
491
- await sock.sendMessage(
492
- jid,
493
- {
494
- text: '@12345678901',
495
- mentions: ['12345678901@s.whatsapp.net']
496
- }
497
- )
498
- ```
499
-
500
- #### Forward Messages
501
- - You need to have message object, can be retrieved from [store](#implementing-a-data-store) or use a [message](https://baileys.wiki/docs/api/type-aliases/WAMessage/) object
502
- ```ts
503
- const msg = getMessageFromStore() // implement this on your end
504
- await sock.sendMessage(jid, { forward: msg }) // WA forward the message!
505
- ```
506
-
507
- #### Location Message
508
- ```ts
509
- await sock.sendMessage(
510
- jid,
511
- {
512
- location: {
513
- degreesLatitude: 24.121231,
514
- degreesLongitude: 55.1121221
515
- }
516
- }
517
- )
518
- ```
519
- #### Contact Message
520
- ```ts
521
- const vcard = 'BEGIN:VCARD\n' // metadata of the contact card
522
- + 'VERSION:3.0\n'
523
- + 'FN:Jeff Singh\n' // full name
524
- + 'ORG:Ashoka Uni;\n' // the organization of the contact
525
- + 'TEL;type=CELL;type=VOICE;waid=911234567890:+91 12345 67890\n' // WhatsApp ID + phone number
526
- + 'END:VCARD'
527
-
528
- await sock.sendMessage(
529
- id,
530
- {
531
- contacts: {
532
- displayName: 'Jeff',
533
- contacts: [{ vcard }]
534
- }
535
- }
536
- )
537
- ```
538
-
539
- #### Reaction Message
540
- - You need to pass the key of message, you can retrieve from [store](#implementing-a-data-store) or use a [key](https://baileys.wiki/docs/api/type-aliases/WAMessageKey/) object
541
- ```ts
542
- await sock.sendMessage(
543
- jid,
544
- {
545
- react: {
546
- text: '💖', // use an empty string to remove the reaction
547
- key: message.key
548
- }
549
- }
550
- )
551
- ```
552
-
553
- #### Pin Message
554
- - You need to pass the key of message, you can retrieve from [store](#implementing-a-data-store) or use a [key](https://baileys.wiki/docs/api/type-aliases/WAMessageKey/) object
555
-
556
- - Time can be:
557
-
558
- | Time | Seconds |
559
- |-------|----------------|
560
- | 24h | 86.400 |
561
- | 7d | 604.800 |
562
- | 30d | 2.592.000 |
563
-
564
- ```ts
565
- await sock.sendMessage(
566
- jid,
567
- {
568
- pin: {
569
- type: 1, // 0 to remove
570
- time: 86400
571
- key: message.key
572
- }
573
- }
574
- )
575
- ```
576
-
577
- #### Poll Message
578
- ```ts
579
- await sock.sendMessage(
580
- jid,
581
- {
582
- poll: {
583
- name: 'My Poll',
584
- values: ['Option 1', 'Option 2', ...],
585
- selectableCount: 1,
586
- toAnnouncementGroup: false // or true
587
- }
588
- }
589
- )
590
- ```
591
-
592
- ### Sending Messages with Link Previews
593
-
594
- 1. By default, wa does not have link generation when sent from the web
595
- 2. Baileys has a function to generate the content for these link previews
596
- 3. To enable this function's usage, add `link-preview-js` as a dependency to your project with `yarn add link-preview-js`
597
- 4. Send a link:
598
- ```ts
599
- await sock.sendMessage(
600
- jid,
601
- {
602
- text: 'Hi, this was sent using https://github.com/whiskeysockets/baileys'
603
- }
604
- )
605
- ```
606
-
607
- ### Media Messages
608
-
609
- Sending media (video, stickers, images) is easier & more efficient than ever.
610
-
611
- > [!NOTE]
612
- > In media messages, you can pass `{ stream: Stream }` or `{ url: Url }` or `Buffer` directly, you can see more in the [WAMediaUpload type alias](https://baileys.wiki/docs/api/type-aliases/WAMediaUpload/)
613
-
614
- - When specifying a media url, Baileys never loads the entire buffer into memory; it even encrypts the media as a readable stream.
615
-
616
- > [!TIP]
617
- > It's recommended to use Stream or Url to save memory
618
-
619
- #### Gif Message
620
- - Whatsapp doesn't support `.gif` files, that's why we send gifs as common `.mp4` video with `gifPlayback` flag
621
- ```ts
622
- await sock.sendMessage(
623
- jid,
624
- {
625
- video: fs.readFileSync('Media/ma_gif.mp4'),
626
- caption: 'hello word',
627
- gifPlayback: true
628
- }
629
- )
630
- ```
631
-
632
- #### Video Message
633
- ```ts
634
- await sock.sendMessage(
635
- id,
636
- {
637
- video: {
638
- url: './Media/ma_gif.mp4'
639
- },
640
- caption: 'hello word',
641
- ptv: false // if set to true, will send as a `video note`
642
- }
643
- )
644
- ```
645
-
646
- #### Audio Message
647
- - To audio message work in all devices you need to convert with some tool like `ffmpeg` with this flags:
648
- ```bash
649
- codec: libopus //ogg file
650
- ac: 1 //one channel
651
- avoid_negative_ts
652
- make_zero
653
- ```
654
- - Example:
655
- ```bash
656
- ffmpeg -i input.mp4 -avoid_negative_ts make_zero -ac 1 output.ogg
657
- ```
658
- ```ts
659
- await sock.sendMessage(
660
- jid,
661
- {
662
- audio: {
663
- url: './Media/audio.mp3'
664
- },
665
- mimetype: 'audio/mp4'
666
- }
667
- )
668
- ```
669
-
670
- #### Image Message
671
- ```ts
672
- await sock.sendMessage(
673
- id,
674
- {
675
- image: {
676
- url: './Media/ma_img.png'
677
- },
678
- caption: 'hello word'
679
- }
680
- )
681
- ```
682
-
683
- #### View Once Message
684
-
685
- - You can send all messages above as `viewOnce`, you only need to pass `viewOnce: true` in content object
686
-
687
- ```ts
688
- await sock.sendMessage(
689
- id,
690
- {
691
- image: {
692
- url: './Media/ma_img.png'
693
- },
694
- viewOnce: true, //works with video, audio too
695
- caption: 'hello word'
696
- }
697
- )
698
- ```
699
-
700
- ## Modify Messages
701
-
702
- ### Deleting Messages (for everyone)
703
-
704
- ```ts
705
- const msg = await sock.sendMessage(jid, { text: 'hello word' })
706
- await sock.sendMessage(jid, { delete: msg.key })
707
- ```
708
-
709
- **Note:** deleting for oneself is supported via `chatModify`, see in [this section](#modifying-chats)
710
-
711
- ### Editing Messages
712
-
713
- - You can pass all editable contents here
714
- ```ts
715
- await sock.sendMessage(jid, {
716
- text: 'updated text goes here',
717
- edit: response.key,
718
- });
719
- ```
720
-
721
- ## Manipulating Media Messages
722
-
723
- ### Thumbnail in Media Messages
724
- - For media messages, the thumbnail can be generated automatically for images & stickers provided you add `jimp` or `sharp` as a dependency in your project using `yarn add jimp` or `yarn add sharp`.
725
- - Thumbnails for videos can also be generated automatically, though, you need to have `ffmpeg` installed on your system.
726
-
727
- ### Downloading Media Messages
728
-
729
- If you want to save the media you received
730
- ```ts
731
- import { createWriteStream } from 'fs'
732
- import { downloadMediaMessage, getContentType } from '@whiskeysockets/baileys'
733
-
734
- sock.ev.on('messages.upsert', async ({ [m] }) => {
735
- if (!m.message) return // if there is no text or media message
736
- const messageType = getContentType(m) // get what type of message it is (text, image, video...)
737
-
738
- // if the message is an image
739
- if (messageType === 'imageMessage') {
740
- // download the message
741
- const stream = await downloadMediaMessage(
742
- m,
743
- 'stream', // can be 'buffer' too
744
- { },
745
- {
746
- logger,
747
- // pass this so that baileys can request a reupload of media
748
- // that has been deleted
749
- reuploadRequest: sock.updateMediaMessage
750
- }
751
- )
752
- // save to file
753
- const writeStream = createWriteStream('./my-download.jpeg')
754
- stream.pipe(writeStream)
755
- }
756
- }
757
- ```
758
-
759
- ### Re-upload Media Message to Whatsapp
760
-
761
- - WhatsApp automatically removes old media from their servers. For the device to access said media -- a re-upload is required by another device that has it. This can be accomplished using:
762
- ```ts
763
- await sock.updateMediaMessage(msg)
764
- ```
765
-
766
- ## Reject Call
767
-
768
- - You can obtain `callId` and `callFrom` from `call` event
769
-
770
- ```ts
771
- await sock.rejectCall(callId, callFrom)
772
- ```
773
-
774
- ## Send States in Chat
775
-
776
- ### Reading Messages
777
- - A set of message [keys](https://baileys.wiki/docs/api/type-aliases/WAMessageKey/) must be explicitly marked read now.
778
- - You cannot mark an entire 'chat' read as it were with Baileys Web.
779
- This means you have to keep track of unread messages.
780
-
781
- ```ts
782
- const key: WAMessageKey
783
- // can pass multiple keys to read multiple messages as well
784
- await sock.readMessages([key])
785
- ```
786
-
787
- The message ID is the unique identifier of the message that you are marking as read.
788
- On a `WAMessage`, the `messageID` can be accessed using ```messageID = message.key.id```.
789
-
790
- ### Update Presence
791
-
792
- - ``` presence ``` can be one of the values in the [WAPresence type alias](https://baileys.wiki/docs/api/type-aliases/WAPresence/)
793
- - The presence expires after about 10 seconds.
794
- - This lets the person/group with `jid` know whether you're online, offline, typing etc.
795
-
796
- ```ts
797
- await sock.sendPresenceUpdate('available', jid)
798
- ```
799
-
800
- > [!NOTE]
801
- > If a desktop client is active, WA doesn't send push notifications to the device. If you would like to receive said notifications -- mark your Baileys client offline using `sock.sendPresenceUpdate('unavailable')`
802
-
803
- ## Modifying Chats
804
-
805
- WA uses an encrypted form of communication to send chat/app updates. This has been implemented mostly and you can send the following updates:
806
-
807
- > [!IMPORTANT]
808
- > If you mess up one of your updates, WA can log you out of all your devices and you'll have to log in again.
809
-
810
- ### Archive a Chat
811
- ```ts
812
- const lastMsgInChat = await getLastMessageInChat(jid) // implement this on your end
813
- await sock.chatModify({ archive: true, lastMessages: [lastMsgInChat] }, jid)
814
- ```
815
- ### Mute/Unmute a Chat
816
-
817
- - Supported times:
818
-
819
- | Time | Miliseconds |
820
- |-------|-----------------|
821
- | Remove | null |
822
- | 8h | 86.400.000 |
823
- | 7d | 604.800.000 |
824
-
825
- ```ts
826
- // mute for 8 hours
827
- await sock.chatModify({ mute: 8 * 60 * 60 * 1000 }, jid)
828
- // unmute
829
- await sock.chatModify({ mute: null }, jid)
830
- ```
831
- ### Mark a Chat Read/Unread
832
- ```ts
833
- const lastMsgInChat = await getLastMessageInChat(jid) // implement this on your end
834
- // mark it unread
835
- await sock.chatModify({ markRead: false, lastMessages: [lastMsgInChat] }, jid)
836
- ```
837
-
838
- ### Delete a Message for Me
839
- ```ts
840
- await sock.chatModify(
841
- {
842
- clear: {
843
- messages: [
844
- {
845
- id: 'ATWYHDNNWU81732J',
846
- fromMe: true,
847
- timestamp: '1654823909'
848
- }
849
- ]
850
- }
851
- },
852
- jid
853
- )
854
-
855
- ```
856
- ### Delete a Chat
857
- ```ts
858
- const lastMsgInChat = await getLastMessageInChat(jid) // implement this on your end
859
- await sock.chatModify({
860
- delete: true,
861
- lastMessages: [
862
- {
863
- key: lastMsgInChat.key,
864
- messageTimestamp: lastMsgInChat.messageTimestamp
865
- }
866
- ]
867
- },
868
- jid
869
- )
870
- ```
871
- ### Pin/Unpin a Chat
872
- ```ts
873
- await sock.chatModify({
874
- pin: true // or `false` to unpin
875
- },
876
- jid
877
- )
878
- ```
879
- ### Star/Unstar a Message
880
- ```ts
881
- await sock.chatModify({
882
- star: {
883
- messages: [
884
- {
885
- id: 'messageID',
886
- fromMe: true // or `false`
887
- }
888
- ],
889
- star: true // - true: Star Message; false: Unstar Message
890
- }
891
- },
892
- jid
893
- )
894
- ```
895
-
896
- ### Disappearing Messages
897
-
898
- - Ephemeral can be:
899
-
900
- | Time | Seconds |
901
- |-------|----------------|
902
- | Remove | 0 |
903
- | 24h | 86.400 |
904
- | 7d | 604.800 |
905
- | 90d | 7.776.000 |
906
-
907
- - You need to pass in **Seconds**, default is 7 days
908
-
909
- ```ts
910
- // turn on disappearing messages
911
- await sock.sendMessage(
912
- jid,
913
- // this is 1 week in seconds -- how long you want messages to appear for
914
- { disappearingMessagesInChat: WA_DEFAULT_EPHEMERAL }
915
- )
916
-
917
- // will send as a disappearing message
918
- await sock.sendMessage(jid, { text: 'hello' }, { ephemeralExpiration: WA_DEFAULT_EPHEMERAL })
919
-
920
- // turn off disappearing messages
921
- await sock.sendMessage(
922
- jid,
923
- { disappearingMessagesInChat: false }
924
- )
925
- ```
926
-
927
- ## User Querys
928
-
929
- ### Check If ID Exists in Whatsapp
930
- ```ts
931
- const [result] = await sock.onWhatsApp(jid)
932
- if (result.exists) console.log (`${jid} exists on WhatsApp, as jid: ${result.jid}`)
933
- ```
934
-
935
- ### Query Chat History (groups too)
936
-
937
- - You need to have oldest message in chat
938
- ```ts
939
- const msg = await getOldestMessageInChat(jid) // implement this on your end
940
- await sock.fetchMessageHistory(
941
- 50, //quantity (max: 50 per query)
942
- msg.key,
943
- msg.messageTimestamp
944
- )
945
- ```
946
- - Messages will be received in `messaging.history-set` event
947
-
948
- ### Fetch Status
949
- ```ts
950
- const status = await sock.fetchStatus(jid)
951
- console.log('status: ' + status)
952
- ```
953
-
954
- ### Fetch Profile Picture (groups too)
955
- - To get the display picture of some person/group
956
- ```ts
957
- // for low res picture
958
- const ppUrl = await sock.profilePictureUrl(jid)
959
- console.log(ppUrl)
960
-
961
- // for high res picture
962
- const ppUrl = await sock.profilePictureUrl(jid, 'image')
963
- ```
964
-
965
- ### Fetch Bussines Profile (such as description or category)
966
- ```ts
967
- const profile = await sock.getBusinessProfile(jid)
968
- console.log('business description: ' + profile.description + ', category: ' + profile.category)
969
- ```
970
-
971
- ### Fetch Someone's Presence (if they're typing or online)
972
- ```ts
973
- // the presence update is fetched and called here
974
- sock.ev.on('presence.update', console.log)
975
-
976
- // request updates for a chat
977
- await sock.presenceSubscribe(jid)
978
- ```
979
-
980
- ## Change Profile
981
-
982
- ### Change Profile Status
983
- ```ts
984
- await sock.updateProfileStatus('Hello World!')
985
- ```
986
- ### Change Profile Name
987
- ```ts
988
- await sock.updateProfileName('My name')
989
- ```
990
- ### Change Display Picture (groups too)
991
- - To change your display picture or a group's
992
-
993
- > [!NOTE]
994
- > Like media messages, you can pass `{ stream: Stream }` or `{ url: Url }` or `Buffer` directly, you can see more in the [WAMediaUpload type alias](https://baileys.wiki/docs/api/type-aliases/WAMediaUpload/)
995
-
996
- ```ts
997
- await sock.updateProfilePicture(jid, { url: './new-profile-picture.jpeg' })
998
- ```
999
- ### Remove display picture (groups too)
1000
- ```ts
1001
- await sock.removeProfilePicture(jid)
1002
- ```
1003
-
1004
- ## Groups
1005
-
1006
- - To change group properties you need to be admin
1007
-
1008
- ### Create a Group
1009
- ```ts
1010
- // title & participants
1011
- const group = await sock.groupCreate('My Fab Group', ['1234@s.whatsapp.net', '4564@s.whatsapp.net'])
1012
- console.log('created group with id: ' + group.gid)
1013
- await sock.sendMessage(group.id, { text: 'hello there' }) // say hello to everyone on the group
1014
- ```
1015
- ### Add/Remove or Demote/Promote
1016
- ```ts
1017
- // id & people to add to the group (will throw error if it fails)
1018
- await sock.groupParticipantsUpdate(
1019
- jid,
1020
- ['abcd@s.whatsapp.net', 'efgh@s.whatsapp.net'],
1021
- 'add' // replace this parameter with 'remove' or 'demote' or 'promote'
1022
- )
1023
- ```
1024
- ### Change Subject (name)
1025
- ```ts
1026
- await sock.groupUpdateSubject(jid, 'New Subject!')
1027
- ```
1028
- ### Change Description
1029
- ```ts
1030
- await sock.groupUpdateDescription(jid, 'New Description!')
1031
- ```
1032
- ### Change Settings
1033
- ```ts
1034
- // only allow admins to send messages
1035
- await sock.groupSettingUpdate(jid, 'announcement')
1036
- // allow everyone to send messages
1037
- await sock.groupSettingUpdate(jid, 'not_announcement')
1038
- // allow everyone to modify the group's settings -- like display picture etc.
1039
- await sock.groupSettingUpdate(jid, 'unlocked')
1040
- // only allow admins to modify the group's settings
1041
- await sock.groupSettingUpdate(jid, 'locked')
1042
- ```
1043
- ### Leave a Group
1044
- ```ts
1045
- // will throw error if it fails
1046
- await sock.groupLeave(jid)
1047
- ```
1048
- ### Get Invite Code
1049
- - To create link with code use `'https://chat.whatsapp.com/' + code`
1050
- ```ts
1051
- const code = await sock.groupInviteCode(jid)
1052
- console.log('group code: ' + code)
1053
- ```
1054
- ### Revoke Invite Code
1055
- ```ts
1056
- const code = await sock.groupRevokeInvite(jid)
1057
- console.log('New group code: ' + code)
1058
- ```
1059
- ### Join Using Invitation Code
1060
- - Code can't have `https://chat.whatsapp.com/`, only code
1061
- ```ts
1062
- const response = await sock.groupAcceptInvite(code)
1063
- console.log('joined to: ' + response)
1064
- ```
1065
- ### Get Group Info by Invite Code
1066
- ```ts
1067
- const response = await sock.groupGetInviteInfo(code)
1068
- console.log('group information: ' + response)
1069
- ```
1070
- ### Query Metadata (participants, name, description...)
1071
- ```ts
1072
- const metadata = await sock.groupMetadata(jid)
1073
- console.log(metadata.id + ', title: ' + metadata.subject + ', description: ' + metadata.desc)
1074
- ```
1075
- ### Join using `groupInviteMessage`
1076
- ```ts
1077
- const response = await sock.groupAcceptInviteV4(jid, groupInviteMessage)
1078
- console.log('joined to: ' + response)
1079
- ```
1080
- ### Get Request Join List
1081
- ```ts
1082
- const response = await sock.groupRequestParticipantsList(jid)
1083
- console.log(response)
1084
- ```
1085
- ### Approve/Reject Request Join
1086
- ```ts
1087
- const response = await sock.groupRequestParticipantsUpdate(
1088
- jid, // group id
1089
- ['abcd@s.whatsapp.net', 'efgh@s.whatsapp.net'],
1090
- 'approve' // or 'reject'
1091
- )
1092
- console.log(response)
1093
- ```
1094
- ### Get All Participating Groups Metadata
1095
- ```ts
1096
- const response = await sock.groupFetchAllParticipating()
1097
- console.log(response)
1098
- ```
1099
- ### Toggle Ephemeral
1100
-
1101
- - Ephemeral can be:
1102
-
1103
- | Time | Seconds |
1104
- |-------|----------------|
1105
- | Remove | 0 |
1106
- | 24h | 86.400 |
1107
- | 7d | 604.800 |
1108
- | 90d | 7.776.000 |
1109
-
1110
- ```ts
1111
- await sock.groupToggleEphemeral(jid, 86400)
1112
- ```
1113
-
1114
- ### Change Add Mode
1115
- ```ts
1116
- await sock.groupMemberAddMode(
1117
- jid,
1118
- 'all_member_add' // or 'admin_add'
1119
- )
1120
- ```
1121
-
1122
- ## Privacy
1123
-
1124
- ### Block/Unblock User
1125
- ```ts
1126
- await sock.updateBlockStatus(jid, 'block') // Block user
1127
- await sock.updateBlockStatus(jid, 'unblock') // Unblock user
1128
- ```
1129
- ### Get Privacy Settings
1130
- ```ts
1131
- const privacySettings = await sock.fetchPrivacySettings(true)
1132
- console.log('privacy settings: ' + privacySettings)
1133
- ```
1134
- ### Get BlockList
1135
- ```ts
1136
- const response = await sock.fetchBlocklist()
1137
- console.log(response)
1138
- ```
1139
- ### Update LastSeen Privacy
1140
- ```ts
1141
- const value = 'all' // 'contacts' | 'contact_blacklist' | 'none'
1142
- await sock.updateLastSeenPrivacy(value)
1143
- ```
1144
- ### Update Online Privacy
1145
- ```ts
1146
- const value = 'all' // 'match_last_seen'
1147
- await sock.updateOnlinePrivacy(value)
1148
- ```
1149
- ### Update Profile Picture Privacy
1150
- ```ts
1151
- const value = 'all' // 'contacts' | 'contact_blacklist' | 'none'
1152
- await sock.updateProfilePicturePrivacy(value)
1153
- ```
1154
- ### Update Status Privacy
1155
- ```ts
1156
- const value = 'all' // 'contacts' | 'contact_blacklist' | 'none'
1157
- await sock.updateStatusPrivacy(value)
1158
- ```
1159
- ### Update Read Receipts Privacy
1160
- ```ts
1161
- const value = 'all' // 'none'
1162
- await sock.updateReadReceiptsPrivacy(value)
1163
- ```
1164
- ### Update Groups Add Privacy
1165
- ```ts
1166
- const value = 'all' // 'contacts' | 'contact_blacklist'
1167
- await sock.updateGroupsAddPrivacy(value)
1168
- ```
1169
- ### Update Default Disappearing Mode
1170
-
1171
- - Like [this](#disappearing-messages), ephemeral can be:
1172
-
1173
- | Time | Seconds |
1174
- |-------|----------------|
1175
- | Remove | 0 |
1176
- | 24h | 86.400 |
1177
- | 7d | 604.800 |
1178
- | 90d | 7.776.000 |
1179
-
1180
- ```ts
1181
- const ephemeral = 86400
1182
- await sock.updateDefaultDisappearingMode(ephemeral)
1183
- ```
1184
-
1185
- ## Broadcast Lists & Stories
1186
-
1187
- ### Send Broadcast & Stories
1188
- - Messages can be sent to broadcasts & stories. You need to add the following message options in sendMessage, like this:
1189
- ```ts
1190
- await sock.sendMessage(
1191
- jid,
1192
- {
1193
- image: {
1194
- url: url
1195
- },
1196
- caption: caption
1197
- },
1198
- {
1199
- backgroundColor: backgroundColor,
1200
- font: font,
1201
- statusJidList: statusJidList,
1202
- broadcast: true
1203
- }
1204
- )
1205
- ```
1206
- - Message body can be a `extendedTextMessage` or `imageMessage` or `videoMessage` or `voiceMessage`, see the [AnyRegularMessageContent type alias](https://baileys.wiki/docs/api/type-aliases/AnyRegularMessageContent/)
1207
- - You can add `backgroundColor` and other options in the message options, see the [MiscMessageGenerationOptions type alias](https://baileys.wiki/docs/api/type-aliases/MiscMessageGenerationOptions/)
1208
- - `broadcast: true` enables broadcast mode
1209
- - `statusJidList`: a list of people that you can get which you need to provide, which are the people who will get this status message.
1210
-
1211
- - You can send messages to broadcast lists the same way you send messages to groups & individual chats.
1212
- - Right now, WA Web does not support creating broadcast lists, but you can still delete them.
1213
- - Broadcast IDs are in the format `12345678@broadcast`
1214
- ### Query a Broadcast List's Recipients & Name
1215
- ```ts
1216
- const bList = await sock.getBroadcastListInfo('1234@broadcast')
1217
- console.log (`list name: ${bList.name}, recps: ${bList.recipients}`)
1218
- ```
1219
-
1220
- ## Writing Custom Functionality
1221
- Baileys is written with custom functionality in mind. Instead of forking the project & re-writing the internals, you can simply write your own extensions.
1222
-
1223
- ### Enabling Debug Level in Baileys Logs
1224
- First, enable the logging of unhandled messages from WhatsApp by setting:
1225
- ```ts
1226
- const sock = makeWASocket({
1227
- logger: P({ level: 'debug' }),
1228
- })
1229
- ```
1230
- This will enable you to see all sorts of messages WhatsApp sends in the console.
1231
-
1232
- ### How Whatsapp Communicate With Us
1233
-
1234
- > [!TIP]
1235
- > If you want to learn whatsapp protocol, we recommend to study about Libsignal Protocol and Noise Protocol
1236
-
1237
- - **Example:** Functionality to track the battery percentage of your phone. You enable logging and you'll see a message about your battery pop up in the console:
1238
- ```
1239
- {
1240
- "level": 10,
1241
- "fromMe": false,
1242
- "frame": {
1243
- "tag": "ib",
1244
- "attrs": {
1245
- "from": "@s.whatsapp.net"
1246
- },
1247
- "content": [
1248
- {
1249
- "tag": "edge_routing",
1250
- "attrs": {},
1251
- "content": [
1252
- {
1253
- "tag": "routing_info",
1254
- "attrs": {},
1255
- "content": {
1256
- "type": "Buffer",
1257
- "data": [8,2,8,5]
1258
- }
1259
- }
1260
- ]
1261
- }
1262
- ]
1263
- },
1264
- "msg":"communication"
1265
- }
1266
- ```
1267
-
1268
- The `'frame'` is what the message received is, it has three components:
1269
- - `tag` -- what this frame is about (eg. message will have 'message')
1270
- - `attrs` -- a string key-value pair with some metadata (contains ID of the message usually)
1271
- - `content` -- the actual data (eg. a message node will have the actual message content in it)
1272
- - read more about this format [here](/src/WABinary/readme.md)
1273
-
1274
- ### Register a Callback for Websocket Events
1275
-
1276
- > [!TIP]
1277
- > Recommended to see `onMessageReceived` function in `socket.ts` file to understand how websockets events are fired
1278
-
1279
- ```ts
1280
- // for any message with tag 'edge_routing'
1281
- sock.ws.on('CB:edge_routing', (node: BinaryNode) => { })
1282
-
1283
- // for any message with tag 'edge_routing' and id attribute = abcd
1284
- sock.ws.on('CB:edge_routing,id:abcd', (node: BinaryNode) => { })
1285
-
1286
- // for any message with tag 'edge_routing', id attribute = abcd & first content node routing_info
1287
- sock.ws.on('CB:edge_routing,id:abcd,routing_info', (node: BinaryNode) => { })
1288
- ```
1289
-
1290
- # License
1291
- Copyright (c) 2025 Rajeh Taher/WhiskeySockets
1292
-
1293
- Licensed under the MIT License:
1294
- Permission is hereby granted, free of charge, to any person obtaining a copy
1295
- of this software and associated documentation files (the "Software"), to deal
1296
- in the Software without restriction, including without limitation the rights
1297
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1298
- copies of the Software, and to permit persons to whom the Software is
1299
- furnished to do so, subject to the following conditions:
1300
-
1301
- The above copyright notice and this permission notice shall be included in all
1302
- copies or substantial portions of the Software.
1303
-
1304
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1305
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1306
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1307
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1308
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1309
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1310
- SOFTWARE.
1311
-
1312
- Thus, the maintainers of the project can't be held liable for any potential misuse of this project.
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/firdausmntp/Baileys-Joss/main/Media/logo.png" alt="Baileys-Joss" width="200"/>
3
+ </p>
4
+
5
+ <h1 align="center">🚀 Baileys-Joss</h1>
6
+
7
+ <p align="center">
8
+ <b>WhatsApp Web API Library dengan Fitur Ekstra Premium</b><br>
9
+ Fork dari <a href="https://github.com/WhiskeySockets/Baileys">Baileys</a> dengan penambahan fitur Interactive Button, Albums, AI Message, LID Plotting, dan lainnya.
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="https://www.npmjs.com/package/baileys-joss"><img src="https://img.shields.io/npm/v/baileys-joss?color=brightgreen&label=npm&style=for-the-badge&logo=npm" alt="npm version"/></a>
14
+ <a href="https://www.npmjs.com/package/baileys-joss"><img src="https://img.shields.io/npm/dm/baileys-joss?color=blue&style=for-the-badge&logo=npm" alt="npm downloads"/></a>
15
+ <a href="https://www.npmjs.com/package/baileys-joss"><img src="https://img.shields.io/npm/dt/baileys-joss?color=blue&style=for-the-badge" alt="npm total downloads"/></a>
16
+ </p>
17
+
18
+ <p align="center">
19
+ <a href="https://github.com/firdausmntp/Baileys-Joss/blob/main/LICENSE"><img src="https://img.shields.io/github/license/firdausmntp/Baileys-Joss?style=for-the-badge&logo=opensourceinitiative&logoColor=white" alt="license"/></a>
20
+ <a href="https://github.com/firdausmntp/Baileys-Joss/stargazers"><img src="https://img.shields.io/github/stars/firdausmntp/Baileys-Joss?style=for-the-badge&logo=github&color=gold" alt="stars"/></a>
21
+ <a href="https://github.com/firdausmntp/Baileys-Joss/network/members"><img src="https://img.shields.io/github/forks/firdausmntp/Baileys-Joss?style=for-the-badge&logo=github&color=purple" alt="forks"/></a>
22
+ <a href="https://github.com/firdausmntp/Baileys-Joss/issues"><img src="https://img.shields.io/github/issues/firdausmntp/Baileys-Joss?style=for-the-badge&logo=github&color=red" alt="issues"/></a>
23
+ </p>
24
+
25
+ <p align="center">
26
+ <a href="#-installation">📦 Installation</a> •
27
+ <a href="#-features">✨ Features</a> •
28
+ <a href="#-quick-start">🚀 Quick Start</a>
29
+ <a href="#-use-case-examples">💡 Examples</a>
30
+ <a href="#-api-reference">📋 API</a> •
31
+ <a href="#-contributing">🤝 Contributing</a>
32
+ </p>
33
+
34
+ <p align="center">
35
+ <b>📖 Documentation:</b>
36
+ <a href="./README.md">🇮🇩 Indonesia</a> |
37
+ <a href="./README.md">🇺🇸 English</a>
38
+ </p>
39
+
40
+ <p align="center">
41
+ <b>🎮 Full Demo:</b>
42
+ <a href="./Example/wa-baileys-joss-complete.js">📁 Complete JavaScript Demo</a> ·
43
+ <a href="./Example/example.ts">📘 TypeScript Example</a>
44
+ </p>
45
+
46
+ ---
47
+
48
+ ## Kenapa Baileys-Joss?
49
+
50
+ <table>
51
+ <tr>
52
+ <th align="center">🎯 Feature</th>
53
+ <th align="center">Baileys Original</th>
54
+ <th align="center">Baileys-Joss</th>
55
+ </tr>
56
+ <tr>
57
+ <td>🖱️ Interactive Buttons</td>
58
+ <td align="center">❌</td>
59
+ <td align="center">✅</td>
60
+ </tr>
61
+ <tr>
62
+ <td>📋 List Messages</td>
63
+ <td align="center">❌</td>
64
+ <td align="center">✅</td>
65
+ </tr>
66
+ <tr>
67
+ <td>📋 Copy Code Button</td>
68
+ <td align="center">❌</td>
69
+ <td align="center">✅</td>
70
+ </tr>
71
+ <tr>
72
+ <td>🔗 URL Buttons</td>
73
+ <td align="center">❌</td>
74
+ <td align="center">✅</td>
75
+ </tr>
76
+ <tr>
77
+ <td>🔄 Combined Button Types</td>
78
+ <td align="center">❌</td>
79
+ <td align="center">✅</td>
80
+ </tr>
81
+ <tr>
82
+ <td>🎨 Native Flow Messages</td>
83
+ <td align="center">❌</td>
84
+ <td align="center">✅</td>
85
+ </tr>
86
+ <tr>
87
+ <td>👤 LID/SenderPn Plotting</td>
88
+ <td align="center">❌</td>
89
+ <td align="center">✅</td>
90
+ </tr>
91
+ <tr>
92
+ <td>🖼️ Album Messages</td>
93
+ <td align="center">❌</td>
94
+ <td align="center">✅</td>
95
+ </tr>
96
+ <tr>
97
+ <td>🤖 AI Message Style</td>
98
+ <td align="center">❌</td>
99
+ <td align="center">✅</td>
100
+ </tr>
101
+ <tr>
102
+ <td>📊 Poll Creation</td>
103
+ <td align="center">✅</td>
104
+ <td align="center">✅ Enhanced</td>
105
+ </tr>
106
+ <tr>
107
+ <td>📢 Newsletter/Channel Control</td>
108
+ <td align="center">✅</td>
109
+ <td align="center">✅ Enhanced</td>
110
+ </tr>
111
+ <tr>
112
+ <td>🔐 Custom Pairing Code</td>
113
+ <td align="center">❌</td>
114
+ <td align="center">✅</td>
115
+ </tr>
116
+ <tr>
117
+ <td>📷 HD Profile Pictures</td>
118
+ <td align="center">✅</td>
119
+ <td align="center">✅</td>
120
+ </tr>
121
+ <tr>
122
+ <td>🔧 Fixed Button Delivery</td>
123
+ <td align="center">❌</td>
124
+ <td align="center">✅</td>
125
+ </tr>
126
+ <tr>
127
+ <td>📸 HD Images (Uncompressed)</td>
128
+ <td align="center">❌</td>
129
+ <td align="center">✅</td>
130
+ </tr>
131
+ <tr>
132
+ <td>🎬 HD Videos (Uncompressed)</td>
133
+ <td align="center">❌</td>
134
+ <td align="center">✅</td>
135
+ </tr>
136
+ <tr>
137
+ <td>🌄 Panorama Profile Picture</td>
138
+ <td align="center">❌</td>
139
+ <td align="center">✅</td>
140
+ </tr>
141
+ <tr>
142
+ <td>📅 Message Scheduling</td>
143
+ <td align="center">❌</td>
144
+ <td align="center">✅</td>
145
+ </tr>
146
+ <tr>
147
+ <td>📨 Bulk Messaging</td>
148
+ <td align="center">❌</td>
149
+ <td align="center">✅</td>
150
+ </tr>
151
+ <tr>
152
+ <td>🔄 Auto Reply System</td>
153
+ <td align="center">❌</td>
154
+ <td align="center">✅</td>
155
+ </tr>
156
+ <tr>
157
+ <td>📇 Contact Card (vCard)</td>
158
+ <td align="center">⚠️ Basic</td>
159
+ <td align="center">✅ Enhanced</td>
160
+ </tr>
161
+ <tr>
162
+ <td>📺 Status/Story Posting</td>
163
+ <td align="center">⚠️ Basic</td>
164
+ <td align="center">✅ Enhanced</td>
165
+ </tr>
166
+ <tr>
167
+ <td>📋 Message Templates</td>
168
+ <td align="center">❌</td>
169
+ <td align="center">✅</td>
170
+ </tr>
171
+ <tr>
172
+ <td>📡 Broadcast Manager</td>
173
+ <td align="center">❌</td>
174
+ <td align="center">✅</td>
175
+ </tr>
176
+ <tr>
177
+ <td>⌨️ Typing Indicator</td>
178
+ <td align="center">⚠️ Basic</td>
179
+ <td align="center">✅ Enhanced</td>
180
+ </tr>
181
+ <tr>
182
+ <td>✅ Read Receipt Control</td>
183
+ <td align="center">⚠️ Basic</td>
184
+ <td align="center">✅ Enhanced</td>
185
+ </tr>
186
+ <tr>
187
+ <td>🔍 Message Search</td>
188
+ <td align="center">❌</td>
189
+ <td align="center">✅</td>
190
+ </tr>
191
+ <tr>
192
+ <td>📊 Chat Analytics</td>
193
+ <td align="center">❌</td>
194
+ <td align="center">✅</td>
195
+ </tr>
196
+ <tr>
197
+ <td>💾 Chat Export</td>
198
+ <td align="center">❌</td>
199
+ <td align="center">✅</td>
200
+ </tr>
201
+ <tr>
202
+ <td>🔗 QR Code Generator</td>
203
+ <td align="center">⚠️ Basic</td>
204
+ <td align="center">✅ Enhanced</td>
205
+ </tr>
206
+ <tr>
207
+ <td>📁 Media Downloader</td>
208
+ <td align="center">❌</td>
209
+ <td align="center">✅</td>
210
+ </tr>
211
+ <tr>
212
+ <td>🎮 Mini Games</td>
213
+ <td align="center">❌</td>
214
+ <td align="center">✅ Tested</td>
215
+ </tr>
216
+ <tr>
217
+ <td>🔍 Content Detector</td>
218
+ <td align="center">❌</td>
219
+ <td align="center">✅ Tested</td>
220
+ </tr>
221
+ <tr>
222
+ <td>🛡️ Anti-Spam System</td>
223
+ <td align="center">❌</td>
224
+ <td align="center">✅ Tested</td>
225
+ </tr>
226
+ <tr>
227
+ <td>🔗 Link Scanner</td>
228
+ <td align="center">❌</td>
229
+ <td align="center">✅ Tested</td>
230
+ </tr>
231
+ <tr>
232
+ <td>📝 Activity Logger</td>
233
+ <td align="center">❌</td>
234
+ <td align="center">✅ Tested</td>
235
+ </tr>
236
+ <tr>
237
+ <td>🎭 Meme Generator</td>
238
+ <td align="center">❌</td>
239
+ <td align="center">✅ Tested</td>
240
+ </tr>
241
+ <tr>
242
+ <td>🍅 Pomodoro Timer</td>
243
+ <td align="center">❌</td>
244
+ <td align="center">✅ Tested</td>
245
+ </tr>
246
+ <tr>
247
+ <td>💬 Quote Generator</td>
248
+ <td align="center">❌</td>
249
+ <td align="center">✅ Tested</td>
250
+ </tr>
251
+ <tr>
252
+ <td>🌤️ Weather Bot</td>
253
+ <td align="center">❌</td>
254
+ <td align="center">✅ Tested</td>
255
+ </tr>
256
+ </table>
257
+
258
+ ---
259
+
260
+ ## 📦 Installation
261
+
262
+ ```bash
263
+ # npm
264
+ npm install baileys-joss@2.0.1
265
+
266
+ # Yarn
267
+ yarn add baileys-joss@2.0.1
268
+
269
+ # pnpm
270
+ pnpm add baileys-joss@2.0.1
271
+ ```
272
+
273
+ <details>
274
+ <summary><b>📦 Via package.json (Fork Override)</b></summary>
275
+
276
+ ```json
277
+ // Untuk mengganti @whiskeysockets/baileys
278
+ {
279
+ "dependencies": {
280
+ "@whiskeysockets/baileys": "npm:baileys-joss"
281
+ }
282
+ }
283
+
284
+ // Untuk mengganti @adiwajshing/baileys
285
+ {
286
+ "dependencies": {
287
+ "@adiwajshing/baileys": "npm:baileys-joss"
288
+ }
289
+ }
290
+ ```
291
+
292
+ </details>
293
+
294
+ ---
295
+
296
+ ## 🚀 Quick Start
297
+
298
+ The complete runnable demo is in [`Example/wa-baileys-joss-complete.js`](./Example/wa-baileys-joss-complete.js). It demonstrates connection handling, QR display, message routing, media helpers, interactive messages, LID/senderPn utilities, and the custom helper modules exported by this fork.
299
+
300
+ ### Minimal TypeScript connection
301
+
302
+ ```ts
303
+ import makeWASocket, {
304
+ DisconnectReason,
305
+ useMultiFileAuthState,
306
+ } from "baileys-joss";
307
+ import P from "pino";
308
+
309
+ const start = async () => {
310
+ const { state, saveCreds } = await useMultiFileAuthState("./auth_info");
311
+ const sock = makeWASocket({
312
+ auth: state,
313
+ logger: P({ level: "silent" }),
314
+ });
315
+
316
+ sock.ev.on("creds.update", saveCreds);
317
+ sock.ev.on("connection.update", ({ connection, lastDisconnect, qr }) => {
318
+ if (qr) console.log("Scan this QR with WhatsApp:", qr);
319
+ if (connection === "open") console.log("WhatsApp connection opened");
320
+ if (
321
+ connection === "close" &&
322
+ (lastDisconnect?.error as { output?: { statusCode?: number } })?.output
323
+ ?.statusCode !== DisconnectReason.loggedOut
324
+ ) {
325
+ void start();
326
+ }
327
+ });
328
+
329
+ sock.ev.on("messages.upsert", async ({ messages }) => {
330
+ const message = messages[0];
331
+ const jid = message?.key.remoteJid;
332
+ const text =
333
+ message?.message?.conversation ??
334
+ message?.message?.extendedTextMessage?.text;
335
+ if (jid && text === "!ping") await sock.sendMessage(jid, { text: "pong" });
336
+ });
337
+ };
338
+
339
+ void start();
340
+ ```
341
+
342
+ > For the full event loop, pairing-code flow, media examples, interactive messages, albums, groups, and custom helpers, use the complete demo linked above.
343
+
344
+ ## 🎯 Features
345
+
346
+ <details open>
347
+ <summary><h3>🖱️ Interactive Messages & Buttons</h3></summary>
348
+
349
+ Fitur button interactive yang lebih lengkap dan mudah digunakan:
350
+
351
+ ```typescript
352
+ import {
353
+ generateInteractiveButtonMessage,
354
+ generateInteractiveListMessage,
355
+ generateTemplateMessage,
356
+ generateCombinedButtons,
357
+ generateCopyCodeButton,
358
+ generateUrlButtonMessage,
359
+ generateQuickReplyButtons,
360
+ } from "baileys-joss";
361
+
362
+ // Quick Reply Buttons
363
+ const quickButtons = generateQuickReplyButtons(
364
+ "Pilih opsi di bawah ini:",
365
+ [
366
+ { id: "btn-1", displayText: "✅ Setuju" },
367
+ { id: "btn-2", displayText: "❌ Tolak" },
368
+ { id: "btn-3", displayText: "📞 Hubungi CS" },
369
+ ],
370
+ { footer: "Powered by Baileys-Joss" },
371
+ );
372
+
373
+ await sock.sendMessage(jid, quickButtons);
374
+
375
+ // URL Button
376
+ const urlButton = generateUrlButtonMessage(
377
+ "Kunjungi website kami untuk info lebih lanjut",
378
+ [{ displayText: "🌐 Buka Website", url: "https://example.com" }],
379
+ { title: "Info Produk", footer: "Click untuk membuka" },
380
+ );
381
+
382
+ await sock.sendMessage(jid, urlButton);
383
+
384
+ // Copy Code Button (untuk OTP, kode promo, dll)
385
+ const copyButton = generateCopyCodeButton(
386
+ "Kode OTP Anda adalah:",
387
+ "123456",
388
+ "📋 Copy Kode",
389
+ );
390
+
391
+ await sock.sendMessage(jid, copyButton);
392
+
393
+ // Combined Buttons (mix URL, Reply, Copy, Call)
394
+ const combinedButtons = generateCombinedButtons(
395
+ "Pilih aksi:",
396
+ [
397
+ { type: "reply", displayText: "🛒 Pesan Sekarang", id: "order" },
398
+ { type: "url", displayText: "🌐 Website", url: "https://example.com" },
399
+ { type: "call", displayText: "📞 Telepon", phoneNumber: "+6281234567890" },
400
+ { type: "copy", displayText: "📋 Copy Promo", copyCode: "PROMO2024" },
401
+ ],
402
+ { title: "Menu Utama", footer: "Baileys-Joss" },
403
+ );
404
+
405
+ await sock.sendMessage(jid, combinedButtons);
406
+
407
+ // List Message
408
+ const listMessage = generateInteractiveListMessage({
409
+ title: "📋 Menu Produk",
410
+ buttonText: "Lihat Menu",
411
+ description: "Silahkan pilih produk yang diinginkan",
412
+ footer: "Ketik nomor untuk memesan",
413
+ sections: [
414
+ {
415
+ title: "Makanan",
416
+ rows: [
417
+ {
418
+ rowId: "nasi-goreng",
419
+ title: "Nasi Goreng",
420
+ description: "Rp 25.000",
421
+ },
422
+ { rowId: "mie-goreng", title: "Mie Goreng", description: "Rp 22.000" },
423
+ ],
424
+ },
425
+ {
426
+ title: "Minuman",
427
+ rows: [
428
+ { rowId: "es-teh", title: "Es Teh", description: "Rp 5.000" },
429
+ { rowId: "kopi", title: "Kopi", description: "Rp 10.000" },
430
+ ],
431
+ },
432
+ ],
433
+ });
434
+
435
+ await sock.sendMessage(jid, listMessage);
436
+ ```
437
+
438
+ </details>
439
+
440
+ <details>
441
+ <summary><h3>🖼️ Album Messages (Carousel)</h3></summary>
442
+
443
+ Kirim beberapa gambar/video sekaligus dalam format album:
444
+
445
+ ```typescript
446
+ // Send Album (grouped images/videos)
447
+ const albumMedia = [
448
+ { image: { url: "https://example.com/pic1.jpg" }, caption: "Photo 1" },
449
+ { image: { url: "https://example.com/pic2.jpg" }, caption: "Photo 2" },
450
+ { video: { url: "https://example.com/video.mp4" }, caption: "Video" },
451
+ ];
452
+
453
+ await sock.sendMessage(jid, {
454
+ album: albumMedia,
455
+ caption: "My Album 📸",
456
+ });
457
+
458
+ // Album dari file lokal
459
+ const localAlbum = [
460
+ { image: fs.readFileSync("./image1.jpg") },
461
+ { image: fs.readFileSync("./image2.jpg") },
462
+ { video: fs.readFileSync("./video.mp4"), gifPlayback: true },
463
+ ];
464
+
465
+ await sock.sendMessage(jid, { album: localAlbum });
466
+ ```
467
+
468
+ </details>
469
+
470
+ <details>
471
+ <summary><h3>🤖 AI Message Style</h3></summary>
472
+
473
+ Tambahkan ikon AI stylish pada pesan:
474
+
475
+ ```typescript
476
+ // Kirim pesan dengan AI icon
477
+ await sock.sendMessage(jid, {
478
+ text: "Halo! Saya adalah asisten AI Anda 🤖",
479
+ ai: true, // Menampilkan ikon AI pada pesan
480
+ });
481
+
482
+ // AI dengan media
483
+ await sock.sendMessage(jid, {
484
+ image: { url: "https://example.com/ai-generated.jpg" },
485
+ caption: "Generated by AI",
486
+ ai: true,
487
+ });
488
+ ```
489
+
490
+ </details>
491
+
492
+ <details>
493
+ <summary><h3>📸 HD Images & Videos (Uncompressed)</h3></summary>
494
+
495
+ Kirim gambar dan video tanpa kompresi dalam kualitas HD:
496
+
497
+ ```typescript
498
+ // Kirim gambar HD (kualitas tinggi, tidak dikompres)
499
+ await sock.sendMessage(jid, {
500
+ image: { url: "./photo-hd.jpg" },
501
+ caption: "Foto HD 📸",
502
+ hd: true, // Kirim tanpa kompresi
503
+ });
504
+
505
+ // Kirim video HD
506
+ await sock.sendMessage(jid, {
507
+ video: { url: "./video-4k.mp4" },
508
+ caption: "Video 4K 🎬",
509
+ hd: true, // Kualitas asli
510
+ });
511
+
512
+ // HD dengan media dari URL
513
+ await sock.sendMessage(jid, {
514
+ image: { url: "https://example.com/high-res-photo.jpg" },
515
+ hd: true,
516
+ caption: "High Resolution Photo",
517
+ });
518
+ ```
519
+
520
+ </details>
521
+
522
+ <details>
523
+ <summary><h3>🌄 Panorama Profile Picture</h3></summary>
524
+
525
+ Set foto profil panorama (wide) tanpa cropping:
526
+
527
+ ```typescript
528
+ // Set panorama profile picture (tidak di-crop square)
529
+ await sock.updatePanoramaProfilePicture(myJid, { url: "./panorama.jpg" });
530
+
531
+ // Dengan opsi kustom
532
+ await sock.updatePanoramaProfilePicture(
533
+ myJid,
534
+ { url: "./wide-photo.jpg" },
535
+ {
536
+ maxWidth: 1080, // Maximum width
537
+ quality: 90, // JPEG quality (1-100)
538
+ },
539
+ );
540
+
541
+ // Set panorama untuk grup
542
+ await sock.updatePanoramaProfilePicture(groupJid, {
543
+ url: "./group-banner.jpg",
544
+ });
545
+
546
+ // Atau gunakan profile picture biasa (square crop)
547
+ await sock.updateProfilePicture(myJid, { url: "./square-photo.jpg" });
548
+ ```
549
+
550
+ </details>
551
+
552
+ <details>
553
+ <summary><h3>📊 Poll Creation</h3></summary>
554
+
555
+ Buat polling dengan mudah:
556
+
557
+ ```typescript
558
+ // Create a Poll
559
+ const pollMessage = {
560
+ name: "🎨 Warna Favorit?",
561
+ values: ["🔴 Merah", "🔵 Biru", "🟢 Hijau", "🟡 Kuning"],
562
+ selectableCount: 1, // Jumlah pilihan yang bisa dipilih
563
+ };
564
+
565
+ await sock.sendMessage(jid, { poll: pollMessage });
566
+
567
+ // Multi-select Poll
568
+ const multiPoll = {
569
+ name: "🍕 Topping Pizza Favorit?",
570
+ values: ["Pepperoni", "Mushroom", "Cheese", "Olive", "Bacon"],
571
+ selectableCount: 3, // Bisa pilih hingga 3 opsi
572
+ };
573
+
574
+ await sock.sendMessage(jid, { poll: multiPoll });
575
+ ```
576
+
577
+ </details>
578
+
579
+ <details>
580
+ <summary><h3>📢 Newsletter/Channel Control</h3></summary>
581
+
582
+ Kelola WhatsApp Channel dengan lengkap:
583
+
584
+ ```typescript
585
+ // Create Newsletter/Channel
586
+ await sock.newsletterCreate("My Channel", "Channel description");
587
+
588
+ // Update channel info
589
+ await sock.newsletterUpdateName(channelJid, "New Channel Name");
590
+ await sock.newsletterUpdateDescription(channelJid, "Updated description");
591
+ await sock.newsletterUpdatePicture(channelJid, {
592
+ url: "https://example.com/pic.jpg",
593
+ });
594
+
595
+ // Follow/Unfollow
596
+ await sock.newsletterFollow(channelJid);
597
+ await sock.newsletterUnfollow(channelJid);
598
+
599
+ // Mute/Unmute
600
+ await sock.newsletterMute(channelJid);
601
+ await sock.newsletterUnmute(channelJid);
602
+
603
+ // Send reaction to channel message
604
+ await sock.newsletterReactMessage(channelJid, "server_id", "🔥");
605
+
606
+ // Get channel metadata
607
+ const metadata = await sock.newsletterMetadata("jid", channelJid);
608
+ console.log("Subscribers:", metadata.subscribers);
609
+
610
+ // Admin operations
611
+ await sock.newsletterChangeOwner(channelJid, newOwnerLid);
612
+ await sock.newsletterDemote(channelJid, adminLid);
613
+ const adminCount = await sock.newsletterAdminCount(channelJid);
614
+ ```
615
+
616
+ </details>
617
+
618
+ <details>
619
+ <summary><h3>🔐 Custom Pairing Code</h3></summary>
620
+
621
+ Generate custom alphanumeric pairing code:
622
+
623
+ ```typescript
624
+ // Standard pairing code
625
+ const code = await sock.requestPairingCode("6281234567890");
626
+ console.log("Your Pairing Code:", code);
627
+
628
+ // Custom alphanumeric pairing code
629
+ const customCode = await sock.requestPairingCode("6281234567890", "MYCODE12");
630
+ console.log("Your Custom Code:", customCode);
631
+ ```
632
+
633
+ </details>
634
+
635
+ <details>
636
+ <summary><h3>📍 LID & SenderPn Plotting</h3></summary>
637
+
638
+ Utilities untuk mengelola JID, LID (Linked ID), dan senderPn:
639
+
640
+ ```typescript
641
+ import {
642
+ parseJid,
643
+ getSenderPn,
644
+ getCurrentSenderInfo,
645
+ isSelf,
646
+ plotJid,
647
+ normalizePhoneToJid,
648
+ extractPhoneNumber,
649
+ formatJidDisplay,
650
+ isSameUser,
651
+ getJidVariants,
652
+ getRemoteJidFromMessage,
653
+ createJidPlotter,
654
+ } from "baileys-joss";
655
+
656
+ // Get info tentang current session (senderPn)
657
+ const senderInfo = getCurrentSenderInfo(sock.authState);
658
+ console.log("Phone:", senderInfo.phoneNumber);
659
+ console.log("Phone JID:", senderInfo.phoneJid);
660
+ console.log("LID:", senderInfo.lid);
661
+ console.log("Device ID:", senderInfo.deviceId);
662
+ console.log("Name:", senderInfo.pushName);
663
+
664
+ // Parse JID untuk info lengkap
665
+ const jidInfo = parseJid("6281234567890@s.whatsapp.net");
666
+ console.log("User:", jidInfo.user);
667
+ console.log("Is LID:", jidInfo.isLid);
668
+ console.log("Is PN:", jidInfo.isPn);
669
+ console.log("Device:", jidInfo.device);
670
+
671
+ // Check apakah JID adalah diri sendiri
672
+ const isMe = isSelf(someJid, senderInfo);
673
+
674
+ // Normalize berbagai format nomor
675
+ const jid = normalizePhoneToJid("+62 812-3456-7890"); // -> 6281234567890@s.whatsapp.net
676
+
677
+ // Extract phone number dari JID
678
+ const phone = extractPhoneNumber("6281234567890@s.whatsapp.net"); // -> 6281234567890
679
+
680
+ // Format untuk display
681
+ const display = formatJidDisplay("6281234567890:1@s.whatsapp.net", {
682
+ showDevice: true,
683
+ showType: true,
684
+ }); // -> 6281234567890:1 (PN)
685
+
686
+ // Compare dua JID
687
+ const same = isSameUser(
688
+ "6281234567890@s.whatsapp.net",
689
+ "6281234567890:1@s.whatsapp.net",
690
+ ); // true
691
+
692
+ // Get sender dari message
693
+ sock.ev.on("messages.upsert", async ({ messages }) => {
694
+ for (const msg of messages) {
695
+ const { chatJid, senderJid } = getRemoteJidFromMessage(msg);
696
+ console.log("Chat:", chatJid);
697
+ console.log("Sender:", senderJid);
698
+ }
699
+ });
700
+
701
+ // Advanced: Create plotter dengan LID mapping support
702
+ const plotter = createJidPlotter(
703
+ sock.lidMapping.getLIDForPN.bind(sock.lidMapping),
704
+ sock.lidMapping.getPNForLID.bind(sock.lidMapping),
705
+ );
706
+
707
+ const plotted = await plotter.plotBidirectional("6281234567890@s.whatsapp.net");
708
+ console.log("Phone:", plotted.pn);
709
+ console.log("LID:", plotted.lid);
710
+ ```
711
+
712
+ </details>
713
+
714
+ <details>
715
+ <summary><h3>📍 Location Sharing</h3></summary>
716
+
717
+ Kirim lokasi dengan mudah:
718
+
719
+ ```typescript
720
+ // Send location
721
+ await sock.sendMessage(jid, {
722
+ location: {
723
+ degreesLatitude: -6.2088,
724
+ degreesLongitude: 106.8456,
725
+ name: "Jakarta, Indonesia",
726
+ address: "Jl. Sudirman, Jakarta Pusat",
727
+ },
728
+ });
729
+ ```
730
+
731
+ </details>
732
+
733
+ <details>
734
+ <summary><h3>👥 Group Management</h3></summary>
735
+
736
+ Kelola grup dengan lengkap:
737
+
738
+ ```typescript
739
+ // Create group
740
+ const group = await sock.groupCreate("My Group", [
741
+ "6281234567890@s.whatsapp.net",
742
+ ]);
743
+
744
+ // Update group info
745
+ await sock.groupUpdateSubject(groupJid, "New Group Name");
746
+ await sock.groupUpdateDescription(groupJid, "New description");
747
+
748
+ // Manage participants
749
+ await sock.groupParticipantsUpdate(
750
+ groupJid,
751
+ ["6281234567890@s.whatsapp.net"],
752
+ "add",
753
+ );
754
+ await sock.groupParticipantsUpdate(
755
+ groupJid,
756
+ ["6281234567890@s.whatsapp.net"],
757
+ "remove",
758
+ );
759
+ await sock.groupParticipantsUpdate(
760
+ groupJid,
761
+ ["6281234567890@s.whatsapp.net"],
762
+ "promote",
763
+ );
764
+ await sock.groupParticipantsUpdate(
765
+ groupJid,
766
+ ["6281234567890@s.whatsapp.net"],
767
+ "demote",
768
+ );
769
+
770
+ // Get group metadata
771
+ const metadata = await sock.groupMetadata(groupJid);
772
+ console.log("Group Name:", metadata.subject);
773
+ console.log("Members:", metadata.participants.length);
774
+
775
+ // Group settings
776
+ await sock.groupSettingUpdate(groupJid, "announcement"); // Only admins can send
777
+ await sock.groupSettingUpdate(groupJid, "not_announcement"); // Everyone can send
778
+ await sock.groupSettingUpdate(groupJid, "locked"); // Only admins can edit info
779
+ await sock.groupSettingUpdate(groupJid, "unlocked"); // Everyone can edit info
780
+
781
+ // Leave group
782
+ await sock.groupLeave(groupJid);
783
+ ```
784
+
785
+ </details>
786
+
787
+ ---
788
+
789
+ ## 🚀 Quick Start
790
+
791
+ ```typescript
792
+ import makeWASocket, {
793
+ useMultiFileAuthState,
794
+ DisconnectReason,
795
+ // Interactive Message features
796
+ generateQuickReplyButtons,
797
+ generateInteractiveListMessage,
798
+ generateCombinedButtons,
799
+ // JID Plotting features
800
+ getCurrentSenderInfo,
801
+ parseJid,
802
+ isSelf,
803
+ } from "baileys-joss";
804
+
805
+ async function startBot() {
806
+ const { state, saveCreds } = await useMultiFileAuthState("auth_session");
807
+
808
+ const sock = makeWASocket({
809
+ auth: state,
810
+ printQRInTerminal: true,
811
+ });
812
+
813
+ sock.ev.on("creds.update", saveCreds);
814
+
815
+ sock.ev.on("connection.update", (update) => {
816
+ const { connection, lastDisconnect } = update;
817
+
818
+ if (connection === "close") {
819
+ const shouldReconnect =
820
+ lastDisconnect?.error?.output?.statusCode !==
821
+ DisconnectReason.loggedOut;
822
+ if (shouldReconnect) {
823
+ startBot();
824
+ }
825
+ } else if (connection === "open") {
826
+ console.log("✅ Connected!");
827
+
828
+ // Get sender info
829
+ const sender = getCurrentSenderInfo(sock.authState);
830
+ console.log("📱 Logged in as:", sender?.phoneNumber);
831
+ }
832
+ });
833
+
834
+ sock.ev.on("messages.upsert", async ({ messages }) => {
835
+ const msg = messages[0];
836
+ if (!msg.message || msg.key.fromMe) return;
837
+
838
+ const text =
839
+ msg.message.conversation || msg.message.extendedTextMessage?.text || "";
840
+
841
+ if (text === "/menu") {
842
+ // Kirim interactive buttons
843
+ const buttons = generateQuickReplyButtons(
844
+ "🤖 Bot Menu\n\nPilih opsi:",
845
+ [
846
+ { id: "help", displayText: "❓ Bantuan" },
847
+ { id: "info", displayText: "ℹ️ Info" },
848
+ { id: "order", displayText: "🛒 Order" },
849
+ ],
850
+ { footer: "Baileys-Joss Bot" },
851
+ );
852
+
853
+ await sock.sendMessage(msg.key.remoteJid!, buttons);
854
+ }
855
+
856
+ if (text === "/poll") {
857
+ // Kirim poll
858
+ await sock.sendMessage(msg.key.remoteJid!, {
859
+ poll: {
860
+ name: "🗳️ Vote sekarang!",
861
+ values: ["Option A", "Option B", "Option C"],
862
+ selectableCount: 1,
863
+ },
864
+ });
865
+ }
866
+
867
+ if (text === "/ai") {
868
+ // Kirim pesan dengan AI style
869
+ await sock.sendMessage(msg.key.remoteJid!, {
870
+ text: "Halo! Saya asisten AI Anda 🤖",
871
+ ai: true,
872
+ });
873
+ }
874
+ });
875
+ }
876
+
877
+ startBot();
878
+ ```
879
+
880
+ ---
881
+
882
+ ## 💡 Use Case Examples
883
+
884
+ <details>
885
+ <summary><b>📬 Newsletter Control</b></summary>
886
+
887
+ ```typescript
888
+ // Create a newsletter
889
+ await sock.newsletterCreate("My Updates Channel", "Stay updated!");
890
+
891
+ // Update description
892
+ await sock.newsletterUpdateDescription(channelJid, "Fresh updates weekly 🔥");
893
+
894
+ // Send reaction to channel message
895
+ await sock.newsletterReactMessage(channelJid, "server_id", "❤️");
896
+
897
+ // Fetch channel messages
898
+ const messages = await sock.newsletterFetchMessages("jid", channelJid, 10);
899
+ ```
900
+
901
+ </details>
902
+
903
+ <details>
904
+ <summary><b>📌 Interactive Messaging</b></summary>
905
+
906
+ ```typescript
907
+ // Native Flow Buttons
908
+ const buttons = generateCombinedButtons(
909
+ "Welcome to our service! 🎉",
910
+ [
911
+ { type: "reply", displayText: "📋 View Menu", id: "menu" },
912
+ {
913
+ type: "url",
914
+ displayText: "🌐 Visit Website",
915
+ url: "https://example.com",
916
+ },
917
+ { type: "call", displayText: "📞 Call Us", phoneNumber: "+1234567890" },
918
+ { type: "copy", displayText: "📋 Copy Promo", copyCode: "SAVE20" },
919
+ ],
920
+ { title: "Welcome!", footer: "Powered by Baileys-Joss" },
921
+ );
922
+
923
+ await sock.sendMessage(jid, buttons);
924
+ ```
925
+
926
+ </details>
927
+
928
+ <details>
929
+ <summary><b>🖼️ Send Album</b></summary>
930
+
931
+ ```typescript
932
+ const media = [
933
+ { image: { url: "https://example.com/pic1.jpg" }, caption: "Photo 1 📸" },
934
+ { image: { url: "https://example.com/pic2.jpg" }, caption: "Photo 2 📸" },
935
+ { video: { url: "https://example.com/clip.mp4" }, caption: "Video 🎬" },
936
+ ];
937
+
938
+ await sock.sendMessage(jid, {
939
+ album: media,
940
+ caption: "My Vacation Memories 🌴",
941
+ });
942
+ ```
943
+
944
+ </details>
945
+
946
+ <details>
947
+ <summary><b>🔐 Pairing with Custom Code</b></summary>
948
+
949
+ ```typescript
950
+ // Request standard pairing code
951
+ const code = await sock.requestPairingCode("6281234567890");
952
+ console.log("Your Pairing Code:", code);
953
+
954
+ // Request custom pairing code
955
+ const customCode = await sock.requestPairingCode("6281234567890", "BAILEYS1");
956
+ console.log("Your Custom Code:", customCode);
957
+ ```
958
+
959
+ </details>
960
+
961
+ <details>
962
+ <summary><b>📊 Poll Creation</b></summary>
963
+
964
+ ```typescript
965
+ const pollMessage = {
966
+ name: "🎬 Film Favorit Weekend Ini?",
967
+ values: ["🦸 Superhero Movie", "😂 Comedy", "😱 Horror", "💑 Romance"],
968
+ selectableCount: 1,
969
+ };
970
+
971
+ await sock.sendMessage(jid, { poll: pollMessage });
972
+ ```
973
+
974
+ </details>
975
+
976
+ <details>
977
+ <summary><b>📍 Location Sharing</b></summary>
978
+
979
+ ```typescript
980
+ await sock.sendMessage(jid, {
981
+ location: {
982
+ degreesLatitude: -6.2088,
983
+ degreesLongitude: 106.8456,
984
+ name: "Monas Jakarta",
985
+ address: "Jalan Medan Merdeka, Jakarta Pusat",
986
+ },
987
+ });
988
+ ```
989
+
990
+ </details>
991
+
992
+ <details>
993
+ <summary><b>👥 Group Management</b></summary>
994
+
995
+ ```typescript
996
+ // Create group
997
+ const group = await sock.groupCreate("My Awesome Group", [
998
+ "6281234567890@s.whatsapp.net",
999
+ "6281234567891@s.whatsapp.net",
1000
+ ]);
1001
+
1002
+ console.log("Group created:", group.id);
1003
+
1004
+ // Update group settings
1005
+ await sock.groupUpdateSubject(group.id, "Updated Group Name");
1006
+ await sock.groupSettingUpdate(group.id, "announcement");
1007
+
1008
+ // Add members
1009
+ await sock.groupParticipantsUpdate(
1010
+ group.id,
1011
+ ["6281234567892@s.whatsapp.net"],
1012
+ "add",
1013
+ );
1014
+ ```
1015
+
1016
+ </details>
1017
+
1018
+ ---
1019
+
1020
+ ## 🆕 Baileys-Joss v2.0.1 New Features
1021
+
1022
+ <details>
1023
+ <summary><h3>📅 Message Scheduling</h3></summary>
1024
+
1025
+ Jadwalkan pesan untuk dikirim otomatis di waktu tertentu:
1026
+
1027
+ ```typescript
1028
+ import { createMessageScheduler } from "baileys-joss";
1029
+
1030
+ // Buat scheduler
1031
+ const scheduler = createMessageScheduler(
1032
+ (jid, content) => sock.sendMessage(jid, content),
1033
+ {
1034
+ onSent: (scheduled, message) => {
1035
+ console.log(`Pesan terkirim ke ${scheduled.jid}`);
1036
+ },
1037
+ onFailed: (scheduled, error) => {
1038
+ console.log(`Gagal kirim: ${error.message}`);
1039
+ },
1040
+ },
1041
+ );
1042
+
1043
+ // Jadwalkan pesan untuk waktu tertentu
1044
+ const scheduled = scheduler.schedule(
1045
+ "6281234567890@s.whatsapp.net",
1046
+ { text: "Selamat Ulang Tahun! 🎂" },
1047
+ new Date("2024-12-25 09:00:00"),
1048
+ );
1049
+
1050
+ // Jadwalkan dengan delay (30 menit dari sekarang)
1051
+ scheduler.scheduleDelay(jid, { text: "Reminder!" }, 30 * 60 * 1000);
1052
+
1053
+ // Cancel scheduled message
1054
+ scheduler.cancel(scheduled.id);
1055
+
1056
+ // Get all pending messages
1057
+ const pending = scheduler.getPending();
1058
+
1059
+ // Stop scheduler
1060
+ scheduler.stop();
1061
+ ```
1062
+
1063
+ </details>
1064
+
1065
+ <details>
1066
+ <summary><h3>📨 Bulk Messaging</h3></summary>
1067
+
1068
+ Kirim pesan massal ke banyak kontak dengan rate limiting:
1069
+
1070
+ ```typescript
1071
+ import { createBulkSender, sendBulkMessages } from "baileys-joss";
1072
+
1073
+ // Method 1: Create bulk sender instance
1074
+ const bulkSender = createBulkSender(
1075
+ (jid, content) => sock.sendMessage(jid, content),
1076
+ {
1077
+ delayBetweenMessages: 2000, // 2 detik delay
1078
+ randomDelay: 1000, // Random 0-1 detik tambahan
1079
+ maxRetries: 2,
1080
+ onProgress: (progress) => {
1081
+ console.log(
1082
+ `Progress: ${progress.sent}/${progress.total} (${progress.percentage}%)`,
1083
+ );
1084
+ },
1085
+ onComplete: (results) => {
1086
+ const success = results.filter((r) => r.success).length;
1087
+ console.log(`Selesai: ${success}/${results.length} berhasil`);
1088
+ },
1089
+ },
1090
+ );
1091
+
1092
+ // Kirim pesan yang sama ke banyak JID
1093
+ const jids = [
1094
+ "6281234567890@s.whatsapp.net",
1095
+ "6281234567891@s.whatsapp.net",
1096
+ "6281234567892@s.whatsapp.net",
1097
+ ];
1098
+
1099
+ const results = await bulkSender.sendToMany(jids, { text: "Hello everyone!" });
1100
+
1101
+ // Kirim pesan berbeda ke JID berbeda
1102
+ const messages = [
1103
+ { jid: "6281...@s.whatsapp.net", content: { text: "Hi John!" } },
1104
+ { jid: "6282...@s.whatsapp.net", content: { text: "Hi Jane!" } },
1105
+ ];
1106
+ await bulkSender.send(messages);
1107
+
1108
+ // Method 2: Quick helper
1109
+ await sendBulkMessages((jid, content) => sock.sendMessage(jid, content), jids, {
1110
+ text: "Broadcast message",
1111
+ });
1112
+ ```
1113
+
1114
+ </details>
1115
+
1116
+ <details>
1117
+ <summary><h3>🔄 Auto Reply System</h3></summary>
1118
+
1119
+ Sistem balasan otomatis berdasarkan keyword/pattern:
1120
+
1121
+ ```typescript
1122
+ import { createAutoReply } from "baileys-joss";
1123
+
1124
+ // Buat auto-reply handler
1125
+ const autoReply = createAutoReply(
1126
+ (jid, content, options) => sock.sendMessage(jid, content, options),
1127
+ (jid, presence) => sock.sendPresenceUpdate(presence, jid),
1128
+ {
1129
+ simulateTyping: true,
1130
+ typingDuration: 1500,
1131
+ globalCooldown: 1000,
1132
+ },
1133
+ );
1134
+
1135
+ // Tambah rule berdasarkan keywords
1136
+ autoReply.addRule({
1137
+ keywords: ["harga", "price", "biaya"],
1138
+ response: { text: "Silakan cek katalog kami di example.com" },
1139
+ cooldown: 60000, // 1 menit cooldown per user
1140
+ quoted: true, // Reply dengan quote
1141
+ });
1142
+
1143
+ // Rule dengan regex pattern
1144
+ autoReply.addRule({
1145
+ pattern: /^(hi|hello|halo|hay)/i,
1146
+ response: { text: "Hai! Ada yang bisa dibantu? 😊" },
1147
+ privateOnly: true, // Hanya di private chat
1148
+ });
1149
+
1150
+ // Rule dengan dynamic response
1151
+ autoReply.addRule({
1152
+ keywords: ["order"],
1153
+ response: async (message, match) => {
1154
+ const sender = message.key.remoteJid;
1155
+ return { text: `Terima kasih sudah order! ID: ${Date.now()}` };
1156
+ },
1157
+ });
1158
+
1159
+ // Rule untuk grup saja
1160
+ autoReply.addRule({
1161
+ exactMatch: "/menu",
1162
+ response: { text: "Menu:\n1. Help\n2. Info\n3. Contact" },
1163
+ groupsOnly: true,
1164
+ });
1165
+
1166
+ // Proses pesan masuk
1167
+ sock.ev.on("messages.upsert", async ({ messages }) => {
1168
+ for (const msg of messages) {
1169
+ if (!msg.key.fromMe) {
1170
+ await autoReply.processMessage(msg);
1171
+ }
1172
+ }
1173
+ });
1174
+ ```
1175
+
1176
+ </details>
1177
+
1178
+ <details>
1179
+ <summary><h3>📇 Contact Card (vCard)</h3></summary>
1180
+
1181
+ Kirim kartu kontak lengkap dengan detail:
1182
+
1183
+ ```typescript
1184
+ import {
1185
+ createContactCard,
1186
+ createContactCards,
1187
+ quickContact,
1188
+ generateVCard,
1189
+ } from "baileys-joss";
1190
+
1191
+ // Quick contact (simple)
1192
+ const simple = quickContact("John Doe", "+628123456789", {
1193
+ organization: "PT Example",
1194
+ email: "john@example.com",
1195
+ });
1196
+ await sock.sendMessage(jid, createContactCard(simple));
1197
+
1198
+ // Full vCard with all details
1199
+ const fullContact = {
1200
+ fullName: "Dr. John Doe",
1201
+ displayName: "John D.",
1202
+ organization: "Hospital ABC",
1203
+ title: "Senior Doctor",
1204
+ phones: [
1205
+ { number: "+628123456789", type: "CELL" },
1206
+ { number: "+622112345678", type: "WORK" },
1207
+ ],
1208
+ emails: [
1209
+ { email: "john@hospital.com", type: "WORK" },
1210
+ { email: "john.personal@gmail.com", type: "HOME" },
1211
+ ],
1212
+ urls: [{ url: "https://linkedin.com/in/johndoe", type: "WORK" }],
1213
+ addresses: [
1214
+ {
1215
+ street: "Jl. Sudirman No. 123",
1216
+ city: "Jakarta",
1217
+ state: "DKI Jakarta",
1218
+ postalCode: "12345",
1219
+ country: "Indonesia",
1220
+ type: "WORK",
1221
+ },
1222
+ ],
1223
+ birthday: "1990-05-15",
1224
+ note: "Met at conference 2024",
1225
+ };
1226
+
1227
+ await sock.sendMessage(jid, createContactCard(fullContact));
1228
+
1229
+ // Send multiple contacts
1230
+ const contacts = [
1231
+ quickContact("Alice", "+628111111111"),
1232
+ quickContact("Bob", "+628222222222"),
1233
+ quickContact("Charlie", "+628333333333"),
1234
+ ];
1235
+ await sock.sendMessage(jid, createContactCards(contacts));
1236
+
1237
+ // Generate raw vCard string
1238
+ const vcardString = generateVCard(fullContact);
1239
+ console.log(vcardString);
1240
+ ```
1241
+
1242
+ </details>
1243
+
1244
+ <details>
1245
+ <summary><h3>📺 Status/Story Posting</h3></summary>
1246
+
1247
+ Posting status WhatsApp (foto, video, text):
1248
+
1249
+ ```typescript
1250
+ import {
1251
+ StatusHelper,
1252
+ STATUS_BROADCAST_JID,
1253
+ STATUS_BACKGROUNDS,
1254
+ STATUS_FONTS,
1255
+ } from "baileys-joss";
1256
+
1257
+ const statusJid = STATUS_BROADCAST_JID; // 'status@broadcast'
1258
+
1259
+ // Post text status
1260
+ await sock.sendMessage(
1261
+ statusJid,
1262
+ StatusHelper.text("Hello World! 🌍", STATUS_BACKGROUNDS.solid.green),
1263
+ );
1264
+
1265
+ // Post image status
1266
+ const imageBuffer = fs.readFileSync("./my-photo.jpg");
1267
+ await sock.sendMessage(
1268
+ statusJid,
1269
+ StatusHelper.image(imageBuffer, "Beautiful day! ☀️"),
1270
+ );
1271
+
1272
+ // Post video status
1273
+ const videoBuffer = fs.readFileSync("./my-video.mp4");
1274
+ await sock.sendMessage(
1275
+ statusJid,
1276
+ StatusHelper.video(videoBuffer, "Check this out! 🎬"),
1277
+ );
1278
+
1279
+ // Post GIF status
1280
+ await sock.sendMessage(statusJid, StatusHelper.gif(gifBuffer, "Animated! 🎭"));
1281
+
1282
+ // Custom text status with font
1283
+ import { createTextStatus } from "baileys-joss";
1284
+
1285
+ await sock.sendMessage(
1286
+ statusJid,
1287
+ createTextStatus({
1288
+ text: "Custom styled status!",
1289
+ backgroundColor: STATUS_BACKGROUNDS.solid.purple,
1290
+ font: STATUS_FONTS.DANCING,
1291
+ textColor: "#FFFFFF",
1292
+ }),
1293
+ );
1294
+ ```
1295
+
1296
+ </details>
1297
+
1298
+ <details>
1299
+ <summary><h3>📋 Message Templates</h3></summary>
1300
+
1301
+ Template pesan siap pakai untuk berbagai keperluan:
1302
+
1303
+ ```typescript
1304
+ import {
1305
+ createTemplateManager,
1306
+ renderTemplate,
1307
+ PRESET_TEMPLATES,
1308
+ } from "baileys-joss";
1309
+
1310
+ // Create manager with preset templates
1311
+ const templates = createTemplateManager(true);
1312
+
1313
+ // Render preset template
1314
+ const orderConfirmation = templates.render("order_confirmation", {
1315
+ orderId: "ORD-12345",
1316
+ customerName: "John Doe",
1317
+ orderDate: "2024-01-15",
1318
+ items: "1x Product A\n2x Product B",
1319
+ total: "150,000",
1320
+ });
1321
+
1322
+ await sock.sendMessage(jid, { text: orderConfirmation });
1323
+
1324
+ // Create custom template
1325
+ templates.create({
1326
+ name: "Welcome Message",
1327
+ content: `Halo {{name}}! 👋
1328
+
1329
+ Selamat datang di {{company}}!
1330
+
1331
+ Berikut layanan kami:
1332
+ {{services}}
1333
+
1334
+ Contact: {{phone:021-12345678}}`,
1335
+ category: "greeting",
1336
+ });
1337
+
1338
+ // Render custom template
1339
+ const welcome = templates.render("welcome_message", {
1340
+ name: "Budi",
1341
+ company: "PT Example",
1342
+ services: "- Layanan A\n- Layanan B\n- Layanan C",
1343
+ });
1344
+
1345
+ // Quick template rendering (tanpa manager)
1346
+ const quick = renderTemplate(
1347
+ "Hi {{name}}, your order #{{orderId}} is {{status:processing}}",
1348
+ { name: "Alice", orderId: "123" },
1349
+ );
1350
+
1351
+ // List available templates
1352
+ const allTemplates = templates.getAll();
1353
+ const invoiceTemplates = templates.getByCategory("invoice");
1354
+
1355
+ // Export/Import templates
1356
+ const exportJson = templates.export();
1357
+ templates.import(exportJson, true); // true = overwrite existing
1358
+ ```
1359
+
1360
+ </details>
1361
+
1362
+ <details>
1363
+ <summary><h3>📡 Broadcast Manager</h3></summary>
1364
+
1365
+ Kelola dan kirim ke broadcast list:
1366
+
1367
+ ```typescript
1368
+ import { createBroadcastManager } from "baileys-joss";
1369
+
1370
+ const broadcast = createBroadcastManager((jid, content) =>
1371
+ sock.sendMessage(jid, content),
1372
+ );
1373
+
1374
+ // Create broadcast list
1375
+ const customerList = broadcast.create({
1376
+ name: "VIP Customers",
1377
+ description: "Premium customers for promo",
1378
+ recipients: ["6281234567890@s.whatsapp.net", "6281234567891@s.whatsapp.net"],
1379
+ });
1380
+
1381
+ // Add more recipients
1382
+ broadcast.addRecipients(customerList.id, ["6281234567892@s.whatsapp.net"]);
1383
+
1384
+ // Send to broadcast list
1385
+ const result = await broadcast.broadcast(
1386
+ customerList.id,
1387
+ { text: "🎉 Special promo for VIP customers!" },
1388
+ {
1389
+ delay: 2000,
1390
+ onProgress: (sent, total, jid) => {
1391
+ console.log(`Sending ${sent}/${total}: ${jid}`);
1392
+ },
1393
+ },
1394
+ );
1395
+
1396
+ console.log(`Sent: ${result.sent}, Failed: ${result.failed}`);
1397
+
1398
+ // Get statistics
1399
+ const stats = broadcast.getStats();
1400
+ console.log(
1401
+ `Total lists: ${stats.totalLists}, Recipients: ${stats.totalRecipients}`,
1402
+ );
1403
+
1404
+ // Export/Import lists
1405
+ const json = broadcast.export();
1406
+ broadcast.import(json);
1407
+ ```
1408
+
1409
+ </details>
1410
+
1411
+ <details>
1412
+ <summary><h3>⌨️ Typing Indicator</h3></summary>
1413
+
1414
+ Simulasi sedang mengetik:
1415
+
1416
+ ```typescript
1417
+ import { createTypingIndicator } from "baileys-joss";
1418
+
1419
+ const typing = createTypingIndicator((jid, presence) =>
1420
+ sock.sendPresenceUpdate(presence, jid),
1421
+ );
1422
+
1423
+ // Start typing
1424
+ await typing.startTyping(jid, {
1425
+ duration: 5000, // Auto pause after 5 seconds
1426
+ autoPause: true,
1427
+ });
1428
+
1429
+ // Start recording (for voice notes)
1430
+ await typing.startRecording(jid, { duration: 3000 });
1431
+
1432
+ // Stop typing
1433
+ await typing.stopTyping(jid);
1434
+
1435
+ // Simulate typing then send
1436
+ await typing.simulateTyping(jid, 2000, async () => {
1437
+ return sock.sendMessage(jid, { text: "Hello!" });
1438
+ });
1439
+
1440
+ // Stop all typing indicators
1441
+ await typing.stopAll();
1442
+ ```
1443
+
1444
+ </details>
1445
+
1446
+ <details>
1447
+ <summary><h3>✅ Read Receipt Control</h3></summary>
1448
+
1449
+ Kontrol centang biru:
1450
+
1451
+ ```typescript
1452
+ import { createReadReceiptController } from "baileys-joss";
1453
+
1454
+ const readReceipts = createReadReceiptController(
1455
+ (jid, participant, messageIds) =>
1456
+ sock.readMessages([{ remoteJid: jid, id: messageIds[0] }]),
1457
+ {
1458
+ enabled: true,
1459
+ readDelay: 1000, // 1 second delay
1460
+ excludeJids: ["blocked@s.whatsapp.net"],
1461
+ },
1462
+ );
1463
+
1464
+ // Toggle read receipts
1465
+ readReceipts.disable(); // Stop sending read receipts
1466
+ readReceipts.enable(); // Resume
1467
+
1468
+ // Check status
1469
+ console.log(readReceipts.isEnabled());
1470
+
1471
+ // Manual read (respects config)
1472
+ await readReceipts.markRead(jid, participant, ["messageId123"]);
1473
+
1474
+ // Force read (ignores config)
1475
+ await readReceipts.forceMarkRead(jid, participant, ["messageId123"]);
1476
+
1477
+ // Update config
1478
+ readReceipts.setConfig({
1479
+ enabled: true,
1480
+ readDelay: 2000,
1481
+ });
1482
+ ```
1483
+
1484
+ </details>
1485
+
1486
+ <details>
1487
+ <summary><h3>🔍 Message Search</h3></summary>
1488
+
1489
+ Cari pesan dalam chat:
1490
+
1491
+ ```typescript
1492
+ import { createMessageSearch, searchMessages } from "baileys-joss";
1493
+
1494
+ // Create search manager
1495
+ const search = createMessageSearch();
1496
+
1497
+ // Add messages to index
1498
+ search.addMessages(chatMessages);
1499
+
1500
+ // Search by text
1501
+ const results = search.search("harga produk", {
1502
+ caseSensitive: false,
1503
+ limit: 20,
1504
+ messageTypes: ["text", "image"], // Include captions
1505
+ fromDate: new Date("2024-01-01"),
1506
+ });
1507
+
1508
+ for (const result of results) {
1509
+ console.log(`Found: "${result.matchedText}"`);
1510
+ console.log(`Score: ${result.relevanceScore}`);
1511
+ console.log(`Message ID: ${result.message.key.id}`);
1512
+ }
1513
+
1514
+ // Search with regex
1515
+ const regexResults = search.searchRegex(/order\s*#?\d+/i);
1516
+
1517
+ // Get messages by type
1518
+ const images = search.getByType("image");
1519
+ const videos = search.getByType("video");
1520
+
1521
+ // Get messages by sender
1522
+ const fromSender = search.getBySender("6281234567890@s.whatsapp.net");
1523
+
1524
+ // Quick search (without manager)
1525
+ const quickResults = searchMessages(messages, "keyword", {
1526
+ jid: specificChatJid,
1527
+ fromMe: false,
1528
+ });
1529
+ ```
1530
+
1531
+ </details>
1532
+
1533
+ <details>
1534
+ <summary><h3>📊 Chat Analytics</h3></summary>
1535
+
1536
+ Statistik dan analitik chat:
1537
+
1538
+ ```typescript
1539
+ import { createChatAnalytics } from "baileys-joss";
1540
+
1541
+ const analytics = createChatAnalytics();
1542
+
1543
+ // Add messages for analysis
1544
+ analytics.addMessages(allMessages);
1545
+
1546
+ // Get stats for specific chat
1547
+ const chatStats = analytics.getChatStats("6281234567890@s.whatsapp.net");
1548
+
1549
+ console.log(`Total messages: ${chatStats.totalMessages}`);
1550
+ console.log(`From me: ${chatStats.messagesFromMe}`);
1551
+ console.log(`Media count: ${chatStats.mediaCount}`);
1552
+ console.log(`Links shared: ${chatStats.linkCount}`);
1553
+ console.log(`Emojis used: ${chatStats.emojiCount}`);
1554
+ console.log(`Most active hour: ${chatStats.mostActiveHour}:00`);
1555
+ console.log(`Most active day: ${chatStats.mostActiveDay}`);
1556
+ console.log(`Avg messages/day: ${chatStats.averageMessagesPerDay}`);
1557
+
1558
+ // Message breakdown by type
1559
+ console.log("Messages by type:", chatStats.messagesByType);
1560
+ // { text: 150, image: 45, video: 12, audio: 8, ... }
1561
+
1562
+ // Global stats across all chats
1563
+ const globalStats = analytics.getGlobalStats();
1564
+ console.log(`Total chats: ${globalStats.totalChats}`);
1565
+ console.log(`Total messages: ${globalStats.totalMessages}`);
1566
+ console.log(`Most active chat: ${globalStats.mostActiveChat?.jid}`);
1567
+
1568
+ // Activity analysis
1569
+ const hourlyActivity = analytics.getActivityByHour(jid); // Array[24]
1570
+ const dailyActivity = analytics.getActivityByDay(jid); // { Sunday: 10, Monday: 25, ... }
1571
+
1572
+ // Top participants in group
1573
+ const topParticipants = analytics.getTopParticipants(groupJid, 5);
1574
+ for (const p of topParticipants) {
1575
+ console.log(`${p.participant}: ${p.count} messages`);
1576
+ }
1577
+ ```
1578
+
1579
+ </details>
1580
+
1581
+ <details>
1582
+ <summary><h3>💾 Chat Export</h3></summary>
1583
+
1584
+ Export chat ke JSON, HTML, TXT, atau CSV:
1585
+
1586
+ ```typescript
1587
+ import { createChatExporter, exportChat } from "baileys-joss";
1588
+
1589
+ const exporter = createChatExporter();
1590
+
1591
+ // Add messages
1592
+ exporter.addMessages(jid, chatMessages);
1593
+
1594
+ // Export to JSON
1595
+ const jsonExport = exporter.export(jid, {
1596
+ format: "json",
1597
+ includeMediaInfo: true,
1598
+ includeMetadata: true,
1599
+ });
1600
+
1601
+ fs.writeFileSync(jsonExport.filename, jsonExport.content);
1602
+
1603
+ // Export to HTML (readable format)
1604
+ const htmlExport = exporter.export(jid, {
1605
+ format: "html",
1606
+ title: "Chat dengan John",
1607
+ dateFormat: "YYYY-MM-DD HH:mm",
1608
+ });
1609
+
1610
+ fs.writeFileSync(htmlExport.filename, htmlExport.content);
1611
+
1612
+ // Export to TXT (simple text)
1613
+ const txtExport = exporter.export(jid, {
1614
+ format: "txt",
1615
+ dateRange: {
1616
+ start: new Date("2024-01-01"),
1617
+ end: new Date("2024-12-31"),
1618
+ },
1619
+ });
1620
+
1621
+ // Export to CSV (for spreadsheet)
1622
+ const csvExport = exporter.export(jid, {
1623
+ format: "csv",
1624
+ includeMetadata: true,
1625
+ });
1626
+
1627
+ // Quick export (without manager)
1628
+ const result = exportChat(messages, jid, { format: "json" });
1629
+ console.log(`Exported ${result.messageCount} messages`);
1630
+
1631
+ // Export all chats
1632
+ const allExports = exporter.exportAll({ format: "json" });
1633
+ ```
1634
+
1635
+ </details>
1636
+
1637
+ <details>
1638
+ <summary><h3>🎮 Mini Games (UNTESTED ⚠️)</h3></summary>
1639
+
1640
+ Game sederhana untuk chat interaktif:
1641
+
1642
+ ```typescript
1643
+ import {
1644
+ MiniGamesManager,
1645
+ createTicTacToeGame,
1646
+ createQuiz,
1647
+ } from "baileys-joss";
1648
+
1649
+ // Create games manager
1650
+ const games = new MiniGamesManager();
1651
+
1652
+ // 🎯 Guess Number Game
1653
+ const guessSession = games.startGuessNumber("6281234567890@s.whatsapp.net", {
1654
+ minNumber: 1,
1655
+ maxNumber: 100,
1656
+ maxAttempts: 7,
1657
+ });
1658
+
1659
+ // Handle guess
1660
+ const result = games.handleGuess(guessSession.id, 50);
1661
+ if (result.correct) {
1662
+ await sock.sendMessage(jid, {
1663
+ text: `🎉 Correct! The number was ${result.answer}`,
1664
+ });
1665
+ } else {
1666
+ await sock.sendMessage(jid, {
1667
+ text: `${result.hint} (${result.remaining} attempts left)`,
1668
+ });
1669
+ }
1670
+
1671
+ // 🧠 Quiz Game
1672
+ const quizSession = games.startQuiz(jid, {
1673
+ questions: [
1674
+ {
1675
+ question: "Apa ibukota Indonesia?",
1676
+ options: ["Jakarta", "Bandung", "Surabaya", "Medan"],
1677
+ correctIndex: 0,
1678
+ explanation: "Jakarta adalah ibukota Indonesia sejak 1945",
1679
+ },
1680
+ ],
1681
+ category: "geography",
1682
+ });
1683
+ await sock.sendMessage(jid, {
1684
+ text: `📝 Quiz:\n${quizSession.data.currentQuestion}\n\n${quizSession.data.options.map((o, i) => `${i + 1}. ${o}`).join("\n")}`,
1685
+ });
1686
+
1687
+ // ❌⭕ TicTacToe Game
1688
+ const tttSession = games.startTicTacToe(jid, player1Jid, player2Jid);
1689
+ const moveResult = games.handleMove(tttSession.id, player1Jid, 4); // Center position
1690
+
1691
+ await sock.sendMessage(jid, {
1692
+ text: `${games.renderBoard(tttSession)}\n\n${moveResult.message}`,
1693
+ });
1694
+
1695
+ // 🎲 Quick Games
1696
+ const dice = games.rollDice(); // { value: 1-6, emoji: '🎲' }
1697
+ const coin = games.flipCoin(); // { result: 'heads'|'tails', emoji: '🪙' }
1698
+ const rps = games.playRockPaperScissors("rock", "scissors"); // { winner: 'player1', ... }
1699
+
1700
+ // 🏆 Leaderboard
1701
+ const leaderboard = games.getLeaderboard(10);
1702
+ ```
1703
+
1704
+ </details>
1705
+
1706
+ <details>
1707
+ <summary><h3>🔍 Content Detector (UNTESTED ⚠️)</h3></summary>
1708
+
1709
+ Deteksi otomatis berbagai jenis konten dalam pesan:
1710
+
1711
+ ```typescript
1712
+ import {
1713
+ ContentDetector,
1714
+ ContentFilter,
1715
+ hasLinks,
1716
+ hasPhoneNumbers,
1717
+ hasEmails,
1718
+ hasMediaContent,
1719
+ } from "baileys-joss";
1720
+
1721
+ // Create detector
1722
+ const detector = new ContentDetector();
1723
+
1724
+ // Full analysis
1725
+ const result = detector.analyze(message);
1726
+ console.log("Has media:", result.hasMedia);
1727
+ console.log("Media type:", result.mediaType);
1728
+ console.log("Links found:", result.links);
1729
+ console.log("Phone numbers:", result.phoneNumbers);
1730
+ console.log("Emails:", result.emails);
1731
+ console.log("Mentions:", result.mentions);
1732
+ console.log("Hashtags:", result.hashtags);
1733
+ console.log("Word count:", result.wordCount);
1734
+
1735
+ // Quick checks
1736
+ if (hasLinks(message)) {
1737
+ console.log("Message contains links!");
1738
+ }
1739
+ if (hasPhoneNumbers(message)) {
1740
+ console.log("Message contains phone numbers!");
1741
+ }
1742
+ if (hasMediaContent(message)) {
1743
+ console.log("Message has media attachment!");
1744
+ }
1745
+
1746
+ // Content filtering
1747
+ const filter = new ContentFilter({
1748
+ blockLinks: true,
1749
+ blockedDomains: ["spam.com", "malware.xyz"],
1750
+ blockPhoneNumbers: false,
1751
+ sensitiveKeywords: ["spam", "promo", "click here"],
1752
+ maxMessageLength: 1000,
1753
+ });
1754
+
1755
+ const filterResult = filter.check(message);
1756
+ if (!filterResult.allowed) {
1757
+ await sock.sendMessage(jid, {
1758
+ text: `⚠️ Message blocked: ${filterResult.blockedReason}`,
1759
+ });
1760
+ }
1761
+ ```
1762
+
1763
+ </details>
1764
+
1765
+ <details>
1766
+ <summary><h3>🛡️ Anti-Spam System (UNTESTED ⚠️)</h3></summary>
1767
+
1768
+ Sistem untuk mendeteksi dan mencegah spam:
1769
+
1770
+ ```typescript
1771
+ import { AntiSpamManager } from "baileys-joss";
1772
+
1773
+ // Create anti-spam manager
1774
+ const antispam = new AntiSpamManager({
1775
+ maxMessagesPerMinute: 15,
1776
+ maxDuplicates: 3,
1777
+ duplicateWindow: 60000, // 1 minute
1778
+ minMessageDelay: 500,
1779
+ whitelist: ["admin@s.whatsapp.net"],
1780
+ onSpamDetected: async (jid, message, result) => {
1781
+ console.log(`Spam detected from ${jid}: ${result.reason}`);
1782
+ if (result.action === "mute") {
1783
+ await sock.sendMessage(jid, { text: "⚠️ You are muted for spamming!" });
1784
+ }
1785
+ },
1786
+ });
1787
+
1788
+ // Add custom spam pattern
1789
+ antispam.addRule({
1790
+ id: "promo_spam",
1791
+ name: "Promotional Spam",
1792
+ type: "pattern",
1793
+ enabled: true,
1794
+ config: { patterns: [/FREE\s+\d+\s+TOKEN/i, /CLICK\s+HERE.*WIN/i] },
1795
+ action: "delete",
1796
+ });
1797
+
1798
+ // Check incoming messages
1799
+ sock.ev.on("messages.upsert", async ({ messages }) => {
1800
+ for (const msg of messages) {
1801
+ const result = await antispam.checkMessage(msg);
1802
+
1803
+ if (result.isSpam) {
1804
+ console.log(
1805
+ `🛡️ Spam blocked! Score: ${result.score}, Reason: ${result.reason}`,
1806
+ );
1807
+
1808
+ // Handle based on action
1809
+ if (result.action === "delete") {
1810
+ await sock.sendMessage(msg.key.remoteJid, { delete: msg.key });
1811
+ } else if (result.action === "warn") {
1812
+ await sock.sendMessage(msg.key.remoteJid, {
1813
+ text: `⚠️ Warning: ${result.reason}`,
1814
+ });
1815
+ }
1816
+ continue;
1817
+ }
1818
+
1819
+ // Process non-spam message...
1820
+ }
1821
+ });
1822
+
1823
+ // Whitelist trusted users
1824
+ antispam.whitelist("trusteduser@s.whatsapp.net");
1825
+
1826
+ // Mute spammer temporarily
1827
+ antispam.muteUser("spammer@s.whatsapp.net", 3600); // 1 hour
1828
+
1829
+ // Ban repeat offender
1830
+ antispam.banUser("baduser@s.whatsapp.net");
1831
+
1832
+ // Get spam statistics
1833
+ const stats = antispam.getStats();
1834
+ console.log("Total spam blocked:", stats.totalBlocked);
1835
+ ```
1836
+
1837
+ </details>
1838
+
1839
+ <details>
1840
+ <summary><h3>🔗 Link Scanner (UNTESTED ⚠️)</h3></summary>
1841
+
1842
+ Scan dan validasi URL untuk keamanan:
1843
+
1844
+ ```typescript
1845
+ import { LinkScanner, scanUrls, isUrlSafe } from "baileys-joss";
1846
+
1847
+ // Create scanner
1848
+ const scanner = new LinkScanner({
1849
+ followRedirects: true,
1850
+ maxRedirects: 5,
1851
+ timeout: 5000,
1852
+ enablePhishingDetection: true,
1853
+ });
1854
+
1855
+ // Scan single URL
1856
+ const result = await scanner.scanUrl("https://suspicious-link.xyz/login");
1857
+ console.log("Safe:", result.safe);
1858
+ console.log("Risk level:", result.riskLevel); // 'safe' | 'low' | 'medium' | 'high' | 'critical'
1859
+ console.log("Threats:", result.threats);
1860
+ console.log("Details:", result.details);
1861
+
1862
+ // Scan all URLs in a message
1863
+ const messageResults = await scanner.scanMessage(message);
1864
+ for (const urlResult of messageResults) {
1865
+ if (!urlResult.safe) {
1866
+ await sock.sendMessage(jid, {
1867
+ text: `⚠️ Warning! Suspicious link detected:\n${urlResult.url}\n\nRisk: ${urlResult.riskLevel}\nThreats: ${urlResult.threats.join(", ")}`,
1868
+ });
1869
+ }
1870
+ }
1871
+
1872
+ // Quick safety check
1873
+ const isSafe = await scanner.isUrlSafe("https://example.com");
1874
+
1875
+ // Add custom patterns
1876
+ scanner.addPhishingPattern(/banking.*verify.*account/i);
1877
+ scanner.addTrustedDomain("mytrusted.com");
1878
+ scanner.addMaliciousDomain("known-scam.xyz");
1879
+
1880
+ // Auto-scan in message handler
1881
+ sock.ev.on("messages.upsert", async ({ messages }) => {
1882
+ for (const msg of messages) {
1883
+ const scanResults = await scanner.scanMessage(msg);
1884
+ const dangerous = scanResults.filter(
1885
+ (r) => r.riskLevel === "high" || r.riskLevel === "critical",
1886
+ );
1887
+
1888
+ if (dangerous.length > 0) {
1889
+ await sock.sendMessage(
1890
+ msg.key.remoteJid,
1891
+ {
1892
+ text: `🚨 Dangerous link(s) detected! ${dangerous.length} threat(s) found.`,
1893
+ },
1894
+ { quoted: msg },
1895
+ );
1896
+ }
1897
+ }
1898
+ });
1899
+ ```
1900
+
1901
+ </details>
1902
+
1903
+ <details>
1904
+ <summary><h3>📝 Activity Logger (UNTESTED ⚠️)</h3></summary>
1905
+
1906
+ Logging aktivitas untuk audit trail:
1907
+
1908
+ ```typescript
1909
+ import { ActivityLogger } from "baileys-joss";
1910
+
1911
+ // Create logger
1912
+ const logger = new ActivityLogger({
1913
+ fileLogging: true,
1914
+ logFilePath: "./logs/bot-activity.log",
1915
+ maxMemoryEntries: 1000,
1916
+ maxFileSize: 10 * 1024 * 1024, // 10MB
1917
+ minLevel: "info",
1918
+ categories: ["message", "user", "group", "bot"],
1919
+ onLog: (entry) => {
1920
+ if (entry.level === "error") {
1921
+ // Send alert to admin
1922
+ console.error(`[ALERT] ${entry.action}: ${entry.details.message}`);
1923
+ }
1924
+ },
1925
+ });
1926
+
1927
+ // Log message activity
1928
+ logger.logMessage(message, "received", {
1929
+ processed: true,
1930
+ responseTime: 150,
1931
+ });
1932
+
1933
+ // Log user action
1934
+ logger.logUserAction("6281234567890@s.whatsapp.net", "command_executed", {
1935
+ command: "/help",
1936
+ success: true,
1937
+ });
1938
+
1939
+ // Log group action
1940
+ logger.logGroupAction("groupjid@g.us", "member_added", {
1941
+ addedBy: "admin@s.whatsapp.net",
1942
+ newMember: "newuser@s.whatsapp.net",
1943
+ });
1944
+
1945
+ // Log custom activity
1946
+ logger.log({
1947
+ level: "info",
1948
+ category: "bot",
1949
+ action: "scheduled_task",
1950
+ actor: "system",
1951
+ details: { task: "daily_backup", status: "completed" },
1952
+ });
1953
+
1954
+ // Query logs
1955
+ const recentErrors = logger.query({
1956
+ level: "error",
1957
+ fromDate: new Date(Date.now() - 24 * 60 * 60 * 1000), // Last 24 hours
1958
+ limit: 50,
1959
+ });
1960
+
1961
+ // Get statistics
1962
+ const stats = logger.getStats();
1963
+ console.log("Total entries:", stats.totalEntries);
1964
+ console.log("By level:", stats.byLevel);
1965
+ console.log("Top actors:", stats.topActors);
1966
+
1967
+ // Export logs
1968
+ const jsonLogs = logger.export("json");
1969
+ const csvLogs = logger.export("csv");
1970
+ ```
1971
+
1972
+ </details>
1973
+
1974
+ <details>
1975
+ <summary><h3>🎭 Meme Generator (UNTESTED ⚠️)</h3></summary>
1976
+
1977
+ Generate meme sederhana dengan text overlay:
1978
+
1979
+ ```typescript
1980
+ import {
1981
+ MemeGenerator,
1982
+ MEME_TEMPLATES,
1983
+ drakeMeme,
1984
+ expandingBrainMeme,
1985
+ thisIsFineMeme,
1986
+ } from "baileys-joss";
1987
+
1988
+ // Create generator
1989
+ const meme = new MemeGenerator();
1990
+
1991
+ // List available templates
1992
+ const templates = meme.getTemplates();
1993
+ console.log(
1994
+ "Available templates:",
1995
+ templates.map((t) => t.name),
1996
+ );
1997
+
1998
+ // Generate Drake meme (quick helper)
1999
+ const drake = drakeMeme(
2000
+ "Debugging code manually", // Rejected
2001
+ "Using console.log everywhere", // Approved
2002
+ );
2003
+ await sock.sendMessage(jid, { text: drake.htmlContent });
2004
+
2005
+ // Generate Expanding Brain meme
2006
+ const brain = expandingBrainMeme([
2007
+ "Using var",
2008
+ "Using let",
2009
+ "Using const",
2010
+ "Using TypeScript",
2011
+ ]);
2012
+ await sock.sendMessage(jid, { text: brain.htmlContent });
2013
+
2014
+ // Generate "This is Fine" meme
2015
+ const fine = thisIsFineMeme("Production is on fire but it's fine");
2016
+ await sock.sendMessage(jid, { text: fine.htmlContent });
2017
+
2018
+ // Custom meme with template
2019
+ const custom = meme.generateTextMeme({
2020
+ template: "distracted",
2021
+ texts: {
2022
+ boyfriend: "Me",
2023
+ girlfriend: "My deadlines",
2024
+ other: "New side project",
2025
+ },
2026
+ fontSize: 24,
2027
+ fontColor: "#ffffff",
2028
+ });
2029
+
2030
+ // Generate SVG meme (for advanced use)
2031
+ const svgMeme = meme.generateSvgMeme({
2032
+ template: "two_buttons",
2033
+ texts: {
2034
+ button1: "Sleep early",
2035
+ button2: "One more episode",
2036
+ },
2037
+ });
2038
+
2039
+ // Send as formatted text meme
2040
+ await sock.sendMessage(jid, {
2041
+ text: `🎭 *MEME*\n\n${custom.htmlContent}`,
2042
+ });
2043
+ ```
2044
+
2045
+ </details>
2046
+
2047
+ <details>
2048
+ <summary><h3>🍅 Pomodoro Timer (UNTESTED ⚠️)</h3></summary>
2049
+
2050
+ Timer produktivitas dengan teknik Pomodoro:
2051
+
2052
+ ```typescript
2053
+ import { PomodoroManager, DEFAULT_POMODORO_CONFIG } from "baileys-joss";
2054
+
2055
+ // Create pomodoro manager
2056
+ const pomodoro = new PomodoroManager();
2057
+
2058
+ // Register event handler
2059
+ pomodoro.onEvent(async (event) => {
2060
+ const jid = event.session.jid;
2061
+
2062
+ switch (event.type) {
2063
+ case "work_start":
2064
+ await sock.sendMessage(jid, {
2065
+ text: `🍅 *WORK SESSION STARTED*\n\n⏱️ Duration: 25 minutes\n🎯 Session: ${event.session.currentSession}/${event.session.totalSessions}\n\n💪 Stay focused!`,
2066
+ });
2067
+ break;
2068
+ case "work_end":
2069
+ await sock.sendMessage(jid, {
2070
+ text: `✅ *WORK SESSION COMPLETE!*\n\n🎉 Great job! Time for a break.\n\nType /break to start break timer.`,
2071
+ });
2072
+ break;
2073
+ case "break_start":
2074
+ await sock.sendMessage(jid, {
2075
+ text: `☕ *BREAK TIME*\n\n⏱️ Duration: 5 minutes\n\n🧘 Relax and recharge!`,
2076
+ });
2077
+ break;
2078
+ case "break_end":
2079
+ await sock.sendMessage(jid, {
2080
+ text: `⏰ *BREAK OVER!*\n\nReady for next session?\nType /work to continue.`,
2081
+ });
2082
+ break;
2083
+ }
2084
+ });
2085
+
2086
+ // Start work session
2087
+ const session = pomodoro.start("6281234567890@s.whatsapp.net", {
2088
+ workDuration: 25, // 25 minutes
2089
+ shortBreakDuration: 5, // 5 minutes
2090
+ longBreakDuration: 15, // 15 minutes
2091
+ sessionsBeforeLongBreak: 4,
2092
+ autoStartBreaks: true,
2093
+ });
2094
+
2095
+ // Pause/Resume
2096
+ pomodoro.pause(jid);
2097
+ pomodoro.resume(jid);
2098
+
2099
+ // Start break manually
2100
+ pomodoro.startBreak(jid);
2101
+
2102
+ // Get current status
2103
+ const status = pomodoro.status(jid);
2104
+ console.log("Status:", status.status); // 'work' | 'short_break' | 'long_break' | 'paused'
2105
+ console.log("Time remaining:", status.remainingTime);
2106
+ console.log("Current session:", status.currentSession);
2107
+
2108
+ // Stop and reset
2109
+ pomodoro.stop(jid);
2110
+
2111
+ // Get statistics
2112
+ const stats = pomodoro.stats(jid);
2113
+ console.log("Total work sessions:", stats.totalWorkSessions);
2114
+ console.log("Total work minutes:", stats.totalWorkMinutes);
2115
+ console.log("Current streak:", stats.currentStreak);
2116
+ ```
2117
+
2118
+ </details>
2119
+
2120
+ <details>
2121
+ <summary><h3>💬 Quote Generator (UNTESTED ⚠️)</h3></summary>
2122
+
2123
+ Random quotes dan quotes harian:
2124
+
2125
+ ```typescript
2126
+ import {
2127
+ QuoteManager,
2128
+ getRandomQuote,
2129
+ getQuoteOfTheDay,
2130
+ getMotivationalQuote,
2131
+ getIslamicQuote,
2132
+ getFunnyQuote,
2133
+ quoteCommand,
2134
+ QUOTES,
2135
+ } from "baileys-joss";
2136
+
2137
+ // Quick helpers
2138
+ const random = getRandomQuote();
2139
+ await sock.sendMessage(jid, {
2140
+ text: `💬 *Quote*\n\n"${random.text}"\n\n— ${random.author}`,
2141
+ });
2142
+
2143
+ // Quote of the day (same quote all day)
2144
+ const qotd = getQuoteOfTheDay();
2145
+ await sock.sendMessage(jid, {
2146
+ text: `📅 *Quote of the Day*\n\n"${qotd.quote.text}"\n\n— ${qotd.quote.author}`,
2147
+ });
2148
+
2149
+ // Category-specific quotes
2150
+ const motivational = getMotivationalQuote();
2151
+ const islamic = getIslamicQuote();
2152
+ const funny = getFunnyQuote();
2153
+
2154
+ // Using QuoteManager for more control
2155
+ const quoteManager = new QuoteManager();
2156
+
2157
+ // Get quote by category
2158
+ const loveQuote = quoteManager.getByCategory("love");
2159
+ const wisdomQuote = quoteManager.getByCategory("wisdom");
2160
+
2161
+ // Get quote by language
2162
+ const indonesianQuote = quoteManager.getByLanguage("id");
2163
+ const englishQuote = quoteManager.getByLanguage("en");
2164
+
2165
+ // Search quotes
2166
+ const searchResults = quoteManager.search("success");
2167
+
2168
+ // Add custom quote
2169
+ quoteManager.addQuote({
2170
+ id: "custom1",
2171
+ text: "My custom inspirational quote",
2172
+ author: "Me",
2173
+ category: "inspirational",
2174
+ language: "en",
2175
+ });
2176
+
2177
+ // Parse command (for bot integration)
2178
+ // Supported: /quote, /quote motivational, /quote islamic, /quote funny
2179
+ const commandResult = quoteCommand("/quote motivational");
2180
+ await sock.sendMessage(jid, { text: commandResult });
2181
+
2182
+ // Send random quote with formatting
2183
+ const formatted = quoteManager.format(getRandomQuote(), {
2184
+ style: "fancy", // 'simple' | 'fancy' | 'minimal'
2185
+ includeCategory: true,
2186
+ });
2187
+ await sock.sendMessage(jid, { text: formatted });
2188
+ ```
2189
+
2190
+ </details>
2191
+
2192
+ <details>
2193
+ <summary><h3>🌤️ Weather Bot (UNTESTED ⚠️)</h3></summary>
2194
+
2195
+ Informasi cuaca dengan integrasi OpenWeatherMap:
2196
+
2197
+ ```typescript
2198
+ import {
2199
+ WeatherBot,
2200
+ getWeather,
2201
+ getSimpleWeather,
2202
+ weatherCommand,
2203
+ } from "baileys-joss";
2204
+
2205
+ // Create weather bot (with API key for full features)
2206
+ const weather = new WeatherBot({
2207
+ apiKey: "YOUR_OPENWEATHERMAP_API_KEY", // Optional: enables API mode
2208
+ units: "metric", // 'metric' | 'imperial'
2209
+ language: "id",
2210
+ defaultCity: "Jakarta",
2211
+ });
2212
+
2213
+ // Get weather for a city
2214
+ const data = await weather.getWeather("Jakarta");
2215
+ await sock.sendMessage(jid, {
2216
+ text: `🌤️ *Weather in ${data.city}, ${data.country}*\n\n🌡️ Temperature: ${data.temperature}°C\n🤒 Feels like: ${data.feelsLike}°C\n💧 Humidity: ${data.humidity}%\n💨 Wind: ${data.windSpeed} m/s\n☁️ Condition: ${data.description}\n\n🌅 Sunrise: ${new Date(data.sunrise * 1000).toLocaleTimeString()}\n🌇 Sunset: ${new Date(data.sunset * 1000).toLocaleTimeString()}`,
2217
+ });
2218
+
2219
+ // Quick helper (uses sample data if no API key)
2220
+ const simpleWeather = await getSimpleWeather("Tokyo");
2221
+ await sock.sendMessage(jid, { text: simpleWeather });
2222
+
2223
+ // Get 5-day forecast
2224
+ const forecast = await weather.getForecast("Singapore");
2225
+ let forecastText = `📅 *5-Day Forecast for ${forecast.city}*\n\n`;
2226
+ for (const day of forecast.forecasts.slice(0, 5)) {
2227
+ forecastText += `${day.date}: ${day.temperature}°C, ${day.description}\n`;
2228
+ }
2229
+ await sock.sendMessage(jid, { text: forecastText });
2230
+
2231
+ // Weather alerts
2232
+ const alerts = await weather.getAlerts("Miami");
2233
+ if (alerts.length > 0) {
2234
+ for (const alert of alerts) {
2235
+ await sock.sendMessage(jid, {
2236
+ text: `⚠️ *Weather Alert*\n\n${alert.event}\n\nSeverity: ${alert.severity}\n${alert.description}`,
2237
+ });
2238
+ }
2239
+ }
2240
+
2241
+ // Parse weather command (for bot integration)
2242
+ // Supported: /weather Jakarta, /cuaca Bandung
2243
+ const result = await weatherCommand("/weather Singapore", weather);
2244
+ await sock.sendMessage(jid, { text: result });
2245
+
2246
+ // Format with emoji
2247
+ const formatted = weather.formatWeather(data, {
2248
+ includeEmoji: true,
2249
+ includeDetails: true,
2250
+ language: "id",
2251
+ });
2252
+ await sock.sendMessage(jid, { text: formatted });
2253
+ ```
2254
+
2255
+ </details>
2256
+
2257
+ <details>
2258
+ <summary><h3>🔗 QR Code Generator</h3></summary>
2259
+
2260
+ Generate QR code custom untuk pairing:
2261
+
2262
+ ```typescript
2263
+ import {
2264
+ createQRGenerator,
2265
+ QRHelper,
2266
+ createQRHandler,
2267
+ createWhatsAppQR,
2268
+ } from "baileys-joss";
2269
+
2270
+ // Quick QR generation
2271
+ const terminalQR = await QRHelper.terminal(pairingCode);
2272
+ console.log(terminalQR);
2273
+
2274
+ // Generate SVG QR
2275
+ const svgQR = await QRHelper.svg(pairingCode, {
2276
+ size: 300,
2277
+ foregroundColor: "#128C7E", // WhatsApp green
2278
+ backgroundColor: "#FFFFFF",
2279
+ });
2280
+
2281
+ // Generate base64 QR (for web)
2282
+ const base64QR = await QRHelper.base64(pairingCode);
2283
+
2284
+ // Generate PNG buffer
2285
+ const pngBuffer = await QRHelper.buffer(pairingCode);
2286
+ fs.writeFileSync("qr.png", pngBuffer);
2287
+
2288
+ // Custom generator
2289
+ const generator = createQRGenerator({
2290
+ size: 256,
2291
+ errorCorrectionLevel: "H",
2292
+ foregroundColor: "#000000",
2293
+ margin: 4,
2294
+ });
2295
+
2296
+ const qr = await generator.generate(data);
2297
+
2298
+ // WhatsApp-styled QR
2299
+ const waQR = await createWhatsAppQR(pairingCode);
2300
+
2301
+ // Use as QR event handler
2302
+ const sock = makeWASocket({
2303
+ // ...
2304
+ printQRInTerminal: false,
2305
+ });
2306
+
2307
+ sock.ev.on("connection.update", async ({ qr }) => {
2308
+ if (qr) {
2309
+ const qrHandler = createQRHandler({
2310
+ maxAttempts: 5,
2311
+ onQR: (rendered, raw, attempt) => {
2312
+ console.log(`\nScan QR (${attempt}/5):\n`);
2313
+ console.log(rendered);
2314
+ },
2315
+ });
2316
+ await qrHandler(qr);
2317
+ }
2318
+ });
2319
+ ```
2320
+
2321
+ </details>
2322
+
2323
+ <details>
2324
+ <summary><h3>📁 Media Downloader</h3></summary>
2325
+
2326
+ Download semua media dari chat:
2327
+
2328
+ ```typescript
2329
+ import { createMediaDownloader, downloadAllMedia } from "baileys-joss";
2330
+
2331
+ const downloader = createMediaDownloader(async (key) =>
2332
+ store.loadMessage(key.remoteJid, key.id),
2333
+ );
2334
+
2335
+ // Download all media from chat
2336
+ const summary = await downloader.downloadFromChat(
2337
+ "6281234567890@s.whatsapp.net",
2338
+ chatMessages,
2339
+ {
2340
+ outputDir: "./downloads/john-doe",
2341
+ types: ["image", "video", "document"], // or ['all']
2342
+ createSubfolders: true, // images/, videos/, etc.
2343
+ skipExisting: true,
2344
+ maxFileSize: 50 * 1024 * 1024, // 50MB limit
2345
+ delay: 500, // Delay between downloads
2346
+ onProgress: (current, total, filename) => {
2347
+ console.log(`Downloading ${current}/${total}: ${filename}`);
2348
+ },
2349
+ onError: (msg, error) => {
2350
+ console.log(`Failed: ${error.message}`);
2351
+ },
2352
+ },
2353
+ );
2354
+
2355
+ console.log(`Downloaded: ${summary.successful}/${summary.total}`);
2356
+ console.log(`Total size: ${(summary.totalSize / 1024 / 1024).toFixed(2)} MB`);
2357
+ console.log(`Failed: ${summary.failed}`);
2358
+
2359
+ // Download single media
2360
+ const singleResult = await downloader.downloadSingle(
2361
+ mediaMessage,
2362
+ "./downloads",
2363
+ { filenameTemplate: "{type}_{date}_{id}{ext}" },
2364
+ );
2365
+
2366
+ // Quick download all (without manager)
2367
+ await downloadAllMedia(messages, "./downloads/all-media", {
2368
+ types: ["image"],
2369
+ onProgress: (curr, total) => console.log(`${curr}/${total}`),
2370
+ });
2371
+ ```
2372
+
2373
+ </details>
2374
+
2375
+ ---
2376
+
2377
+ ## 📋 API Reference
2378
+
2379
+ <details>
2380
+ <summary><b>🖱️ Interactive Messages</b></summary>
2381
+
2382
+ | Function | Description |
2383
+ | ------------------------------------ | ---------------------------------------------- |
2384
+ | `generateInteractiveButtonMessage()` | Buat button message dengan media header |
2385
+ | `generateInteractiveListMessage()` | Buat list message dengan sections |
2386
+ | `generateTemplateMessage()` | Buat template message (Quick Reply, URL, Call) |
2387
+ | `generateNativeFlowMessage()` | Buat native flow message (format terbaru) |
2388
+ | `generateCopyCodeButton()` | Button untuk copy code |
2389
+ | `generateUrlButtonMessage()` | Button dengan URL |
2390
+ | `generateQuickReplyButtons()` | Quick reply buttons |
2391
+ | `generateCombinedButtons()` | Gabungan berbagai jenis button |
2392
+
2393
+ </details>
2394
+
2395
+ <details>
2396
+ <summary><b>📍 JID Plotting</b></summary>
2397
+
2398
+ | Function | Description |
2399
+ | --------------------------- | ----------------------------------------- |
2400
+ | `parseJid()` | Parse JID dan extract info lengkap |
2401
+ | `getSenderPn()` | Get senderPn dari AuthenticationCreds |
2402
+ | `getCurrentSenderInfo()` | Get current sender info dari authState |
2403
+ | `isSelf()` | Check apakah JID adalah diri sendiri |
2404
+ | `plotJid()` | Plot JID (basic, tanpa LID mapping) |
2405
+ | `normalizePhoneToJid()` | Normalize nomor ke JID |
2406
+ | `extractPhoneNumber()` | Extract phone number dari JID |
2407
+ | `formatJidDisplay()` | Format JID untuk display |
2408
+ | `isSameUser()` | Compare dua JID |
2409
+ | `getJidVariants()` | Get semua variant JID dari nomor |
2410
+ | `constructJidWithDevice()` | Construct JID dengan device ID |
2411
+ | `getRemoteJidFromMessage()` | Get remoteJid dari message |
2412
+ | `createJidPlotter()` | Create plotter dengan LID mapping support |
2413
+
2414
+ </details>
2415
+
2416
+ <details>
2417
+ <summary><b>📢 Newsletter/Channel</b></summary>
2418
+
2419
+ | Function | Description |
2420
+ | ------------------------------- | ------------------------- |
2421
+ | `newsletterCreate()` | Buat channel baru |
2422
+ | `newsletterUpdateName()` | Update nama channel |
2423
+ | `newsletterUpdateDescription()` | Update deskripsi channel |
2424
+ | `newsletterUpdatePicture()` | Update foto channel |
2425
+ | `newsletterFollow()` | Follow channel |
2426
+ | `newsletterUnfollow()` | Unfollow channel |
2427
+ | `newsletterMute()` | Mute notifikasi channel |
2428
+ | `newsletterUnmute()` | Unmute notifikasi channel |
2429
+ | `newsletterReactMessage()` | React ke pesan channel |
2430
+ | `newsletterMetadata()` | Get metadata channel |
2431
+ | `newsletterAdminCount()` | Get jumlah admin |
2432
+ | `newsletterChangeOwner()` | Ganti owner channel |
2433
+ | `newsletterDemote()` | Demote admin channel |
2434
+ | `newsletterDelete()` | Hapus channel |
2435
+
2436
+ </details>
2437
+
2438
+ <details>
2439
+ <summary><b>👥 Group Management</b></summary>
2440
+
2441
+ | Function | Description |
2442
+ | --------------------------- | -------------------------------- |
2443
+ | `groupCreate()` | Buat grup baru |
2444
+ | `groupUpdateSubject()` | Update nama grup |
2445
+ | `groupUpdateDescription()` | Update deskripsi grup |
2446
+ | `groupParticipantsUpdate()` | Add/remove/promote/demote member |
2447
+ | `groupSettingUpdate()` | Update pengaturan grup |
2448
+ | `groupMetadata()` | Get metadata grup |
2449
+ | `groupLeave()` | Keluar dari grup |
2450
+ | `groupInviteCode()` | Get kode invite grup |
2451
+ | `groupAcceptInvite()` | Join grup via invite code |
2452
+
2453
+ </details>
2454
+
2455
+ <details>
2456
+ <summary><b>💬 Message Types</b></summary>
2457
+
2458
+ | Type | Description |
2459
+ | ---------- | ------------------------------------------------------- |
2460
+ | `text` | Pesan teks biasa |
2461
+ | `image` | Kirim gambar (dengan opsi `hd: true` untuk kualitas HD) |
2462
+ | `video` | Kirim video (dengan opsi `hd: true` untuk kualitas HD) |
2463
+ | `audio` | Kirim audio |
2464
+ | `document` | Kirim dokumen |
2465
+ | `sticker` | Kirim sticker |
2466
+ | `location` | Kirim lokasi |
2467
+ | `contacts` | Kirim kontak |
2468
+ | `poll` | Buat polling |
2469
+ | `album` | Kirim album (multiple media) |
2470
+ | `react` | React ke pesan |
2471
+ | `edit` | Edit pesan |
2472
+ | `delete` | Hapus pesan |
2473
+
2474
+ </details>
2475
+
2476
+ <details>
2477
+ <summary><b>📷 Profile Picture</b></summary>
2478
+
2479
+ | Function | Description |
2480
+ | -------------------------------- | ------------------------------------------------- |
2481
+ | `updateProfilePicture()` | Update foto profil (square crop) |
2482
+ | `updatePanoramaProfilePicture()` | Update foto profil panorama (wide, tidak di-crop) |
2483
+ | `removeProfilePicture()` | Hapus foto profil |
2484
+ | `profilePictureUrl()` | Get URL foto profil |
2485
+
2486
+ </details>
2487
+
2488
+ <details>
2489
+ <summary><b>🎮 Mini Games (UNTESTED ⚠️)</b></summary>
2490
+
2491
+ | Function | Description |
2492
+ | ------------------------------- | ----------------------------------- |
2493
+ | `MiniGamesManager` | Main class untuk manage semua games |
2494
+ | `games.startGuessNumber()` | Start guess number game |
2495
+ | `games.startQuiz()` | Start quiz game |
2496
+ | `games.startTicTacToe()` | Start TicTacToe (vs bot/player) |
2497
+ | `games.playRockPaperScissors()` | Play RPS game |
2498
+ | `games.rollDice()` | Roll dice (1-6) |
2499
+ | `games.flipCoin()` | Flip a coin |
2500
+ | `games.handleGuess()` | Handle guess input |
2501
+ | `games.handleMove()` | Handle TicTacToe move |
2502
+ | `games.getLeaderboard()` | Get player leaderboard |
2503
+
2504
+ </details>
2505
+
2506
+ <details>
2507
+ <summary><b>🔍 Content Detector (UNTESTED ⚠️)</b></summary>
2508
+
2509
+ | Function | Description |
2510
+ | -------------------- | ------------------------------- |
2511
+ | `ContentDetector` | Main class untuk detect content |
2512
+ | `detector.analyze()` | Full content analysis |
2513
+ | `hasLinks()` | Quick check for URLs |
2514
+ | `hasPhoneNumbers()` | Quick check for phone numbers |
2515
+ | `hasEmails()` | Quick check for emails |
2516
+ | `hasMediaContent()` | Check if message has media |
2517
+ | `isForwarded()` | Check if message is forwarded |
2518
+ | `ContentFilter` | Filter messages by criteria |
2519
+
2520
+ </details>
2521
+
2522
+ <details>
2523
+ <summary><b>🛡️ Anti-Spam (UNTESTED ⚠️)</b></summary>
2524
+
2525
+ | Function | Description |
2526
+ | ------------------------- | ------------------------ |
2527
+ | `AntiSpamManager` | Main anti-spam class |
2528
+ | `antispam.checkMessage()` | Check if message is spam |
2529
+ | `antispam.addRule()` | Add custom spam rule |
2530
+ | `antispam.muteUser()` | Mute user temporarily |
2531
+ | `antispam.banUser()` | Ban user permanently |
2532
+ | `antispam.whitelist()` | Add user to whitelist |
2533
+ | `antispam.getStats()` | Get spam statistics |
2534
+
2535
+ </details>
2536
+
2537
+ <details>
2538
+ <summary><b>🔗 Link Scanner (UNTESTED ⚠️)</b></summary>
2539
+
2540
+ | Function | Description |
2541
+ | ------------------------------ | --------------------------- |
2542
+ | `LinkScanner` | Main URL security scanner |
2543
+ | `scanner.scanUrl()` | Scan single URL |
2544
+ | `scanner.scanMessage()` | Scan all URLs in message |
2545
+ | `scanner.isUrlSafe()` | Quick safety check |
2546
+ | `scanner.addPhishingPattern()` | Add custom phishing pattern |
2547
+ | `scanner.addTrustedDomain()` | Add trusted domain |
2548
+
2549
+ </details>
2550
+
2551
+ <details>
2552
+ <summary><b>📝 Activity Logger (UNTESTED ⚠️)</b></summary>
2553
+
2554
+ | Function | Description |
2555
+ | ------------------------- | ----------------------- |
2556
+ | `ActivityLogger` | Main logging class |
2557
+ | `logger.log()` | Log custom activity |
2558
+ | `logger.logMessage()` | Log message activity |
2559
+ | `logger.logUserAction()` | Log user action |
2560
+ | `logger.logGroupAction()` | Log group action |
2561
+ | `logger.query()` | Query logs with filters |
2562
+ | `logger.getStats()` | Get activity statistics |
2563
+
2564
+ </details>
2565
+
2566
+ <details>
2567
+ <summary><b>🎭 Meme Generator (UNTESTED ⚠️)</b></summary>
2568
+
2569
+ | Function | Description |
2570
+ | ------------------------- | -------------------------- |
2571
+ | `MemeGenerator` | Main meme class |
2572
+ | `meme.generateTextMeme()` | Generate ASCII text meme |
2573
+ | `meme.generateSvgMeme()` | Generate SVG meme |
2574
+ | `meme.getTemplates()` | List available templates |
2575
+ | `drakeMeme()` | Quick Drake meme |
2576
+ | `expandingBrainMeme()` | Quick Expanding Brain meme |
2577
+ | `thisIsFineMeme()` | Quick "This is Fine" meme |
2578
+
2579
+ </details>
2580
+
2581
+ <details>
2582
+ <summary><b>🍅 Pomodoro Timer (UNTESTED ⚠️)</b></summary>
2583
+
2584
+ | Function | Description |
2585
+ | ------------------- | ------------------ |
2586
+ | `PomodoroManager` | Main timer class |
2587
+ | `pomodoro.start()` | Start work session |
2588
+ | `pomodoro.break()` | Start break |
2589
+ | `pomodoro.pause()` | Pause timer |
2590
+ | `pomodoro.resume()` | Resume timer |
2591
+ | `pomodoro.stop()` | Stop and reset |
2592
+ | `pomodoro.status()` | Get current status |
2593
+ | `pomodoro.stats()` | Get statistics |
2594
+
2595
+ </details>
2596
+
2597
+ <details>
2598
+ <summary><b>💬 Quote Generator (UNTESTED ⚠️)</b></summary>
2599
+
2600
+ | Function | Description |
2601
+ | ------------------------ | ---------------------- |
2602
+ | `QuoteManager` | Main quote class |
2603
+ | `getRandomQuote()` | Get random quote |
2604
+ | `getQuoteOfTheDay()` | Get daily quote |
2605
+ | `getMotivationalQuote()` | Get motivational quote |
2606
+ | `getIslamicQuote()` | Get Islamic quote |
2607
+ | `getFunnyQuote()` | Get funny quote |
2608
+ | `quoteCommand()` | Parse quote command |
2609
+
2610
+ </details>
2611
+
2612
+ <details>
2613
+ <summary><b>🌤️ Weather Bot (UNTESTED ⚠️)</b></summary>
2614
+
2615
+ | Function | Description |
2616
+ | ---------------------- | -------------------------- |
2617
+ | `WeatherBot` | Main weather class |
2618
+ | `weather.getWeather()` | Get weather for city |
2619
+ | `weather.setApiKey()` | Set OpenWeatherMap API key |
2620
+ | `getWeather()` | Quick weather helper |
2621
+ | `getSimpleWeather()` | Get simplified weather |
2622
+ | `weatherCommand()` | Parse weather command |
2623
+
2624
+ </details>
2625
+
2626
+ ---
2627
+
2628
+ ## 🔄 Changelog
2629
+
2630
+ <details open>
2631
+ <summary><b>v2.0.1</b> - Latest (BIG UPDATE! 🎉)</summary>
2632
+
2633
+ ### 🆕 New Features
2634
+
2635
+ **🎮 Mini Games**
2636
+
2637
+ - Guess Number game with hints
2638
+ - Quiz with multiple categories
2639
+ - TicTacToe (vs Bot or 2 players)
2640
+ - Rock Paper Scissors
2641
+ - Dice Roll & Coin Flip
2642
+ - Score tracking and leaderboards
2643
+
2644
+ **🔍 Content Detector**
2645
+
2646
+ - URL/Link detection with extraction
2647
+ - Phone number detection
2648
+ - Email detection
2649
+ - Media type detection (image, video, audio, document, sticker)
2650
+ - Mention and hashtag extraction
2651
+ - Emoji analysis
2652
+ - Sensitive content filtering
2653
+ - Forwarded message detection
2654
+
2655
+ **🛡️ Anti-Spam System**
2656
+
2657
+ - Rate limiting per user
2658
+ - Duplicate message detection
2659
+ - Flood protection
2660
+ - Pattern-based spam detection
2661
+ - User mute/ban functionality
2662
+ - Whitelist support
2663
+ - Customizable rules and thresholds
2664
+
2665
+ **🔗 Link Scanner**
2666
+
2667
+ - URL security analysis
2668
+ - Phishing detection with pattern matching
2669
+ - URL shortener expansion
2670
+ - Suspicious TLD detection
2671
+ - Domain reputation checking
2672
+ - Risk level scoring (safe/low/medium/high/critical)
2673
+ - Redirect following for shortened URLs
2674
+
2675
+ **📝 Activity Logger**
2676
+
2677
+ - Audit trail logging
2678
+ - Multiple log levels (debug, info, warn, error, critical)
2679
+ - Category-based logging (message, user, group, bot, security)
2680
+ - File logging with rotation support
2681
+ - Query and search logs
2682
+ - Statistics and analytics
2683
+ - Memory and file output options
2684
+
2685
+ **🎭 Meme Generator**
2686
+
2687
+ - Text-based meme templates
2688
+ - Popular meme formats (Drake, Expanding Brain, Two Buttons, etc.)
2689
+ - SVG/HTML output generation
2690
+ - Custom template support
2691
+ - Quick meme helpers
2692
+
2693
+ **🍅 Pomodoro Timer**
2694
+
2695
+ - Productivity timer with Pomodoro technique
2696
+ - Configurable work/break durations
2697
+ - Session tracking and statistics
2698
+ - Pause/resume functionality
2699
+ - Auto-start options
2700
+ - Streak tracking
2701
+
2702
+ **💬 Quote Generator**
2703
+
2704
+ - 40+ built-in quotes (English & Indonesian)
2705
+ - Multiple categories (motivational, inspirational, love, life, success, wisdom, funny, islamic, philosophy)
2706
+ - Quote of the day feature
2707
+ - Search quotes by text or author
2708
+ - Custom quote support
2709
+ - Formatted output styles
2710
+
2711
+ **🌤️ Weather Bot**
2712
+
2713
+ - Real-time weather information
2714
+ - OpenWeatherMap API integration
2715
+ - Mock data for offline use
2716
+ - Detailed weather display (temp, humidity, wind, etc.)
2717
+ - Weather advice based on conditions
2718
+ - Multiple city support
2719
+ - Caching for performance
2720
+
2721
+ **📅 Message Scheduling**
2722
+
2723
+ - Schedule messages for future delivery
2724
+ - Delay-based scheduling
2725
+ - Cancel/manage scheduled messages
2726
+
2727
+ **📨 Bulk Messaging**
2728
+
2729
+ - Send to multiple recipients with rate limiting
2730
+ - Progress tracking and callbacks
2731
+ - Retry mechanism with error handling
2732
+
2733
+ **🔄 Auto Reply System**
2734
+
2735
+ - Keyword-based auto replies
2736
+ - Regex pattern matching
2737
+ - Cooldown per user/rule
2738
+ - Group/private chat filters
2739
+
2740
+ **📇 Contact Card (vCard)**
2741
+
2742
+ - Full vCard generation with all fields
2743
+ - Multi-contact support
2744
+ - Quick contact helper
2745
+
2746
+ **📺 Status/Story Posting**
2747
+
2748
+ - Text status with backgrounds & fonts
2749
+ - Image/video status
2750
+ - Pre-defined backgrounds & styles
2751
+
2752
+ **📋 Message Templates**
2753
+
2754
+ - Pre-built templates (order, invoice, etc.)
2755
+ - Variable substitution
2756
+ - Export/import templates
2757
+
2758
+ **📡 Broadcast Manager**
2759
+
2760
+ - Create and manage broadcast lists
2761
+ - Send to multiple lists
2762
+ - Statistics and tracking
2763
+
2764
+ **⌨️ Typing Indicator**
2765
+
2766
+ - Start/stop typing simulation
2767
+ - Recording indicator for voice notes
2768
+ - Auto-pause functionality
2769
+
2770
+ **✅ Read Receipt Control**
2771
+
2772
+ - Enable/disable read receipts
2773
+ - Delay before marking read
2774
+ - Exclude specific JIDs
2775
+
2776
+ **🔍 Message Search**
2777
+
2778
+ - Full-text search in messages
2779
+ - Regex support
2780
+ - Filter by type, date, sender
2781
+
2782
+ **📊 Chat Analytics**
2783
+
2784
+ - Message statistics
2785
+ - Activity by hour/day
2786
+ - Top participants
2787
+ - Media/link/emoji counts
2788
+
2789
+ **💾 Chat Export**
2790
+
2791
+ - Export to JSON, HTML, TXT, CSV
2792
+ - Customizable templates
2793
+ - Date range filtering
2794
+
2795
+ **🔗 QR Code Generator**
2796
+
2797
+ - Multiple output formats (terminal, SVG, PNG, base64)
2798
+ - Customizable colors and styles
2799
+ - WhatsApp-styled QR
2800
+
2801
+ **📁 Media Downloader**
2802
+
2803
+ - Batch download media from chats
2804
+ - Progress tracking
2805
+ - Type filtering and size limits
2806
+
2807
+ ### ✨ Improvements
2808
+
2809
+ - ✨ **HD Images & Videos** - Send uncompressed media with `hd: true`
2810
+ - ✨ **Panorama Profile Picture** - Set wide/panoramic profile pictures without cropping
2811
+ - ✨ Higher quality thumbnails for HD mode
2812
+ - 📸 Better image quality preservation
2813
+ - 🎬 Video quality improvements
2814
+ - 📝 Added complete demo code example
2815
+ - 🎨 Improved documentation structure
2816
+ - 🐛 Bug fixes and performance improvements
2817
+
2818
+ </details>
2819
+
2820
+ <details>
2821
+ <summary><b>v1.0.1</b></summary>
2822
+
2823
+ - ✨ Added Album Messages support (carousel format)
2824
+ - ✨ Added AI Message Style (`ai: true`) - shows AI indicator on messages
2825
+ - ✨ Added Custom Pairing Code support
2826
+ - ✨ Enhanced Newsletter/Channel control
2827
+ - ✨ Enhanced Poll creation
2828
+ - 🔧 **Fixed Interactive Buttons** - Added `biz` node for proper button rendering
2829
+ - 🔧 Fixed List Messages delivery
2830
+ - 🎨 Improved documentation with collapsible sections
2831
+ - 🐛 Bug fixes and stability improvements
2832
+
2833
+ </details>
2834
+
2835
+ <details>
2836
+ <summary><b>v1.0.0</b></summary>
2837
+
2838
+ - 🎉 Initial release
2839
+ - ✨ Interactive Buttons support
2840
+ - ✨ List Messages support
2841
+ - ✨ Copy Code Button
2842
+ - ✨ URL Buttons
2843
+ - ✨ Combined Button Types
2844
+ - ✨ Native Flow Messages
2845
+ - ✨ LID/SenderPn Plotting utilities
2846
+
2847
+ </details>
2848
+
2849
+ ---
2850
+
2851
+ ## 🤝 Contributing
2852
+
2853
+ Kontribusi sangat diterima! Silahkan:
2854
+
2855
+ 1. 🍴 Fork repository ini
2856
+ 2. 🌿 Buat branch fitur (`git checkout -b feature/AmazingFeature`)
2857
+ 3. 💾 Commit changes (`git commit -m 'Add some AmazingFeature'`)
2858
+ 4. 📤 Push ke branch (`git push origin feature/AmazingFeature`)
2859
+ 5. 🔃 Buka Pull Request
2860
+
2861
+ ---
2862
+
2863
+ ## 💖 Support
2864
+
2865
+ Jika project ini membantu, berikan ⭐ di [GitHub](https://github.com/firdausmntp/Baileys-Joss)!
2866
+
2867
+ <p align="center">
2868
+ <a href="https://github.com/firdausmntp/Baileys-Joss/stargazers">
2869
+ <img src="https://img.shields.io/github/stars/firdausmntp/Baileys-Joss?style=social" alt="GitHub Stars"/>
2870
+ </a>
2871
+ </p>
2872
+
2873
+ ---
2874
+
2875
+ ## ⚠️ Disclaimer
2876
+
2877
+ > **Peringatan:** Proyek ini tidak berafiliasi dengan WhatsApp atau Meta. Gunakan dengan tanggung jawab dan sesuai dengan Terms of Service WhatsApp.
2878
+ >
2879
+ > ❌ **Jangan spam!**
2880
+ > ❌ **Jangan abuse API!**
2881
+ > ✅ **Gunakan untuk keperluan yang baik!**
2882
+
2883
+ ---
2884
+
2885
+ ## 📄 License
2886
+
2887
+ MIT License - Lihat file [LICENSE](LICENSE) untuk detail.
2888
+
2889
+ ---
2890
+
2891
+ ## 🙏 Credits
2892
+
2893
+ <table>
2894
+ <tr>
2895
+ <td align="center">
2896
+ <a href="https://github.com/WhiskeySockets/Baileys">
2897
+ <b>Baileys Original</b>
2898
+ </a>
2899
+ <br>Base library
2900
+ </td>
2901
+ <td align="center">
2902
+ <a href="https://github.com/WhiskeySockets">
2903
+ <b>WhiskeySockets</b>
2904
+ </a>
2905
+ <br>Maintainer Baileys
2906
+ </td>
2907
+ </tr>
2908
+ </table>
2909
+
2910
+ ---
2911
+
2912
+ <p align="center">
2913
+ <b>Made with ❤️ by <a href="https://github.com/firdausmntp">firdausmntp</a></b>
2914
+ </p>
2915
+
2916
+ <p align="center">
2917
+ <img src="https://img.shields.io/badge/TypeScript-007ACC?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript"/>
2918
+ <img src="https://img.shields.io/badge/Node.js-339933?style=for-the-badge&logo=nodedotjs&logoColor=white" alt="Node.js"/>
2919
+ <img src="https://img.shields.io/badge/WhatsApp-25D366?style=for-the-badge&logo=whatsapp&logoColor=white" alt="WhatsApp"/>
2920
+ </p>