@itsliaaa/baileys 0.1.33 β 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -58
- package/lib/Defaults/index.js +2 -0
- package/lib/Socket/chats.js +185 -52
- package/lib/Socket/groups.js +6 -0
- package/lib/Socket/messages-recv.js +230 -53
- package/lib/Socket/messages-send.js +78 -7
- package/lib/Utils/chat-utils.js +34 -7
- package/lib/Utils/decode-wa-message.js +14 -0
- package/lib/Utils/event-buffer.js +2 -0
- package/lib/Utils/generics.js +9 -0
- package/lib/Utils/history.js +11 -9
- package/lib/Utils/identity-change-handler.js +1 -0
- package/lib/Utils/messages-media.js +1 -1
- package/lib/Utils/messages.js +14 -0
- package/lib/Utils/process-message.js +53 -1
- package/lib/Utils/rich-message-utils.js +1 -1
- package/lib/Utils/sync-action-utils.js +1 -0
- package/lib/Utils/tc-token-utils.js +151 -5
- package/lib/Utils/use-single-file-auth-state.js +19 -26
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +26 -3
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +22 -0
- package/lib/WAUSync/Protocols/index.js +2 -1
- package/lib/WAUSync/USyncQuery.js +5 -1
- package/lib/WAUSync/USyncUser.js +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,30 +34,8 @@ This fork designed for production use with a focus on clarity and safety:
|
|
|
34
34
|
- π« No obfuscation. Easy to read and audit.
|
|
35
35
|
- π« No auto-follow channel (newsletter) behavior.
|
|
36
36
|
|
|
37
|
-
> [!IMPORTANT]
|
|
38
|
-
Hi everyone,
|
|
39
|
-
>
|
|
40
|
-
> I want to share something thatβs been weighing on me a bit.
|
|
41
|
-
>
|
|
42
|
-
> Recently, I found a few packages published on npm that are essentially just **renamed** versions of a fork I personally worked on:
|
|
43
|
-
>
|
|
44
|
-
> - ~[RESOLVED]~
|
|
45
|
-
> - ~[RESOLVED]~
|
|
46
|
-
> - ~[RESOLVED]~
|
|
47
|
-
> - [@zackmans](https://www.npmjs.com/package/@zackmans/baileys) **[STEALER]**
|
|
48
|
-
> - [@dnuzi](https://www.npmjs.com/package/@dnuzi/baileys) **[STEALER]**
|
|
49
|
-
> - ["Update rich.js"](https://github.com/gcamerator/mbaileys/commits/main/) **[STEALER]**
|
|
50
|
-
>
|
|
51
|
-
> To be clear, Iβm **not** the original maintainer of Baileys all respect goes to the amazing work behind [@whiskeysockets/baileys](https://github.com/WhiskeySockets/Baileys). I only created and maintained my own fork ([@itsliaaa/baileys](https://www.npmjs.com/package/@itsliaaa/baileys)) where I spent a **lot** of time improving and adapting things on my own.
|
|
52
|
-
>
|
|
53
|
-
> **Itβs honestly a bit disheartening to see my fork being republished under different names without any acknowledgment or credit.** I put a lot of late nights and personal effort into it, so seeing it circulate like this feelsβ¦ quietly painful.
|
|
54
|
-
>
|
|
55
|
-
> If you come across these packages, Iβd really appreciate it if you could take a closer look and, if appropriate, **help report them to npm.** More than anything, I just hope for a bit of fairness and proper recognition for the work that people put in.
|
|
56
|
-
>
|
|
57
|
-
> Thank you for taking the time to read this π€
|
|
58
|
-
|
|
59
37
|
> [!NOTE]
|
|
60
|
-
π This project is maintained with limited scope and is not intended to replace upstream Baileys.
|
|
38
|
+
> π This project is maintained with limited scope and is not intended to replace upstream Baileys.
|
|
61
39
|
>
|
|
62
40
|
> π And, really sorry for my bad english.
|
|
63
41
|
|
|
@@ -90,10 +68,15 @@ Hi everyone,
|
|
|
90
68
|
- π [`secureMetaServiceLabel`](#6%EF%B8%8Fβ£-secure-meta-service-label) - Secure meta service label on message **[NEW]**
|
|
91
69
|
- π [`raw`](#5%EF%B8%8Fβ£-raw) - Build your message manually **(DO NOT USE FOR EXPLOITATION)**
|
|
92
70
|
|
|
93
|
-
### π
|
|
71
|
+
### π Table of Contents
|
|
72
|
+
- [β¨ Highlights](#-highlights)
|
|
73
|
+
- [π οΈ Internal Adjustments](#%EF%B8%8F-internal-adjustments)
|
|
74
|
+
- [π¨ Messages Handling & Compatibility](#-highlights)
|
|
75
|
+
- [π§© Additional Message Options](#-additional-message-options)
|
|
94
76
|
- [π₯ Installation](#-installation)
|
|
95
77
|
- [π§© Import (ESM & CJS)](#-import-esm--cjs)
|
|
96
78
|
- [π Connect to WhatsApp (Quick Step)](#-connect-to-whatsapp-quick-step)
|
|
79
|
+
- [π Auth State](#-auth-state)
|
|
97
80
|
- [ποΈ Implementing Data Store](#%EF%B8%8F-implementing-data-store)
|
|
98
81
|
- [πͺͺ WhatsApp IDs Explain](#-whatsapp-ids-explain)
|
|
99
82
|
- [βοΈ Sending Messages](#%EF%B8%8F-sending-messages)
|
|
@@ -154,6 +137,7 @@ Hi everyone,
|
|
|
154
137
|
- [π€ Profile Management](#-profile-management)
|
|
155
138
|
- [π Privacy Management](#-privacy-management)
|
|
156
139
|
- [π‘ Events](#-events)
|
|
140
|
+
- [π Try the Bot](#-try-the-bot)
|
|
157
141
|
- [π¦ Fork Base](#-fork-base)
|
|
158
142
|
- [π£ Credits](#-credits)
|
|
159
143
|
|
|
@@ -249,10 +233,15 @@ const connectToWhatsApp = async () => {
|
|
|
249
233
|
connectToWhatsApp()
|
|
250
234
|
```
|
|
251
235
|
|
|
236
|
+
#### π Auth State
|
|
237
|
+
|
|
238
|
+
> [!NOTE]
|
|
239
|
+
> You can use the experimental `useSingleFileAuthState` as an alternative to `useMultiFileAuthState`. However, `useSingleFileAuthState` already includes an internal caching mechanism, so there is no need to wrap `state.keys` with `makeCacheableSignalKeyStore`.
|
|
240
|
+
|
|
252
241
|
### ποΈ Implementing Data Store
|
|
253
242
|
|
|
254
243
|
> [!CAUTION]
|
|
255
|
-
I highly recommend building your own data store, as keeping an entire chat history in memory can lead to excessive RAM usage.
|
|
244
|
+
> I highly recommend building your own data store, as keeping an entire chat history in memory can lead to excessive RAM usage.
|
|
256
245
|
|
|
257
246
|
```javascript
|
|
258
247
|
import { makeWASocket, makeInMemoryStore, delay, DisconnectReason, useMultiFileAuthState } from '@itsliaaa/baileys'
|
|
@@ -335,16 +324,68 @@ connectToWhatsApp()
|
|
|
335
324
|
### βοΈ Sending Messages
|
|
336
325
|
|
|
337
326
|
> [!NOTE]
|
|
338
|
-
You can get the `jid` from `message.key.remoteJid` in the first example.
|
|
327
|
+
> You can get the `jid` from `message.key.remoteJid` in the first example.
|
|
339
328
|
|
|
340
329
|
#### π Text
|
|
341
330
|
|
|
342
331
|
```javascript
|
|
332
|
+
// --- Send a regular text message
|
|
343
333
|
sock.sendMessage(jid, {
|
|
344
334
|
text: 'ππ» Hello'
|
|
345
335
|
}, {
|
|
346
336
|
quoted: message
|
|
347
337
|
})
|
|
338
|
+
|
|
339
|
+
// --- Send a text message with a link preview
|
|
340
|
+
const urlA = 'https://www.npmjs.com/package/@itsliaaa/baileys'
|
|
341
|
+
|
|
342
|
+
sock.sendMessage(jid, {
|
|
343
|
+
text: urlA + ' ππ» Check it out!',
|
|
344
|
+
linkPreview: {
|
|
345
|
+
'matched-text': urlA,
|
|
346
|
+
title: 'π± @itsliaaa/baileys',
|
|
347
|
+
description: 'Underrated Baileys Fork',
|
|
348
|
+
previewType: 0, // --- Use 1 for video playback in the link preview
|
|
349
|
+
jpegThumbnail: fs.readFileSync('./path/to/image.jpg')
|
|
350
|
+
}
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
// --- Send a text message with a large link preview and favicon
|
|
354
|
+
import { prepareWAMessageMedia } from '@itsliaaa/baileys'
|
|
355
|
+
|
|
356
|
+
const urlB = 'https://www.npmjs.com/package/@itsliaaa/baileys#readme'
|
|
357
|
+
|
|
358
|
+
const { imageMessage: image } = await prepareWAMessageMedia({
|
|
359
|
+
image: {
|
|
360
|
+
url: './path/to/image.jpg'
|
|
361
|
+
}
|
|
362
|
+
}, {
|
|
363
|
+
upload: sock.waUploadToServer,
|
|
364
|
+
mediaTypeOverride: 'thumbnail-link'
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
// --- Set the thumbnail display size
|
|
368
|
+
image.height = 720
|
|
369
|
+
image.width = 480
|
|
370
|
+
|
|
371
|
+
sock.sendMessage(jid, {
|
|
372
|
+
text: urlB + ' ππ» Check it out!',
|
|
373
|
+
linkPreview: {
|
|
374
|
+
'matched-text': urlB,
|
|
375
|
+
title: 'π± @itsliaaa/baileys',
|
|
376
|
+
description: 'Underrated Baileys Fork',
|
|
377
|
+
previewType: 0,
|
|
378
|
+
jpegThumbnail: fs.readFileSync('./path/to/image.jpg'),
|
|
379
|
+
highQualityThumbnail: image,
|
|
380
|
+
linkPreviewMetadata: {
|
|
381
|
+
linkMediaDuration: 0, // --- Duration in seconds (for video/audio content)
|
|
382
|
+
socialMediaPostType: 1, // --- Enum: 0 = NONE, 1 = REEL, 2 = LIVE_VIDEO, 3 = LONG_VIDEO, 4 = SINGLE_IMAGE, 5 = CAROUSEL
|
|
383
|
+
} // --- Additional metadata for large link preview
|
|
384
|
+
},
|
|
385
|
+
favicon: {
|
|
386
|
+
url: './path/to/tiny-image.jpg'
|
|
387
|
+
}
|
|
388
|
+
})
|
|
348
389
|
```
|
|
349
390
|
|
|
350
391
|
#### π Mention
|
|
@@ -625,10 +666,10 @@ sock.sendMessage(jid, {
|
|
|
625
666
|
#### β¨ Rich Response
|
|
626
667
|
|
|
627
668
|
> [!NOTE]
|
|
628
|
-
`richResponse[]` is a representation of [`submessages[]`](https://baileys.wiki/docs/api/namespaces/proto/interfaces/IAIRichResponseSubMessage) inside `richResponseMessage`.
|
|
669
|
+
> `richResponse[]` is a representation of [`submessages[]`](https://baileys.wiki/docs/api/namespaces/proto/interfaces/IAIRichResponseSubMessage) inside `richResponseMessage`.
|
|
629
670
|
|
|
630
671
|
> [!TIP]
|
|
631
|
-
You can still use the original [`submessages[]`](https://baileys.wiki/docs/api/namespaces/proto/interfaces/IAIRichResponseSubMessage) field directly.
|
|
672
|
+
> You can still use the original [`submessages[]`](https://baileys.wiki/docs/api/namespaces/proto/interfaces/IAIRichResponseSubMessage) field directly.
|
|
632
673
|
> The code example below is just an implementation using a helper, not a required structure.
|
|
633
674
|
|
|
634
675
|
```javascript
|
|
@@ -664,7 +705,7 @@ sock.sendMessage(jid, {
|
|
|
664
705
|
```
|
|
665
706
|
|
|
666
707
|
> [!TIP]
|
|
667
|
-
You can easily add syntax highlighting by importing `tokenizeCode` directly from Baileys.
|
|
708
|
+
> You can easily add syntax highlighting by importing `tokenizeCode` directly from Baileys.
|
|
668
709
|
|
|
669
710
|
```javascript
|
|
670
711
|
import { tokenizeCode } from '@itsliaaa/baileys'
|
|
@@ -687,7 +728,7 @@ sock.sendMessage(jid, {
|
|
|
687
728
|
#### π§Ύ Message with Code Block
|
|
688
729
|
|
|
689
730
|
> [!NOTE]
|
|
690
|
-
This feature already includes a built-in tokenizer.
|
|
731
|
+
> This feature already includes a built-in tokenizer.
|
|
691
732
|
|
|
692
733
|
```javascript
|
|
693
734
|
sock.sendMessage(jid, {
|
|
@@ -750,7 +791,7 @@ sock.sendMessage([jidA, jidB, jidC], {
|
|
|
750
791
|
### π Sending Media Messages
|
|
751
792
|
|
|
752
793
|
> [!NOTE]
|
|
753
|
-
For media messages, you can pass a `Buffer` directly, or an object with either `{ stream: Readable }` or `{ url: string }` (local file path or HTTP/HTTPS URL).
|
|
794
|
+
> For media messages, you can pass a `Buffer` directly, or an object with either `{ stream: Readable }` or `{ url: string }` (local file path or HTTP/HTTPS URL).
|
|
754
795
|
|
|
755
796
|
#### πΌοΈ Image
|
|
756
797
|
|
|
@@ -852,7 +893,7 @@ sock.sendMessage(jid, {
|
|
|
852
893
|
#### π¦ Sticker Pack
|
|
853
894
|
|
|
854
895
|
> [!IMPORTANT]
|
|
855
|
-
If `sharp` or `@napi-rs/image` is not installed, the `cover` and `stickers` must already be in WebP format.
|
|
896
|
+
> If `sharp` or `@napi-rs/image` is not installed, the `cover` and `stickers` must already be in WebP format.
|
|
856
897
|
|
|
857
898
|
```javascript
|
|
858
899
|
sock.sendMessage(jid, {
|
|
@@ -936,7 +977,7 @@ sock.sendMessage(jid, {
|
|
|
936
977
|
#### 2οΈβ£ List
|
|
937
978
|
|
|
938
979
|
> [!NOTE]
|
|
939
|
-
It only works in private chat (`@s.whatsapp.net`).
|
|
980
|
+
> It only works in private chat (`@s.whatsapp.net`).
|
|
940
981
|
|
|
941
982
|
```javascript
|
|
942
983
|
sock.sendMessage(jid, {
|
|
@@ -1114,7 +1155,7 @@ sock.sendMessage(jid, {
|
|
|
1114
1155
|
#### 2οΈβ£ Invoice
|
|
1115
1156
|
|
|
1116
1157
|
> [!NOTE]
|
|
1117
|
-
Invoice message are not supported yet.
|
|
1158
|
+
> Invoice message are not supported yet.
|
|
1118
1159
|
|
|
1119
1160
|
```javascript
|
|
1120
1161
|
sock.sendMessage(jid, {
|
|
@@ -1150,7 +1191,7 @@ sock.sendMessage(jid, {
|
|
|
1150
1191
|
#### 1οΈβ£ AI Icon
|
|
1151
1192
|
|
|
1152
1193
|
> [!NOTE]
|
|
1153
|
-
It only works in private chat (`@s.whatsapp.net`).
|
|
1194
|
+
> It only works in private chat (`@s.whatsapp.net`).
|
|
1154
1195
|
|
|
1155
1196
|
```javascript
|
|
1156
1197
|
sock.sendMessage(jid, {
|
|
@@ -1167,7 +1208,7 @@ sock.sendMessage(jid, {
|
|
|
1167
1208
|
#### 2οΈβ£ Ephemeral
|
|
1168
1209
|
|
|
1169
1210
|
> [!NOTE]
|
|
1170
|
-
Wrap message into `ephemeralMessage`
|
|
1211
|
+
> Wrap message into `ephemeralMessage`
|
|
1171
1212
|
|
|
1172
1213
|
```javascript
|
|
1173
1214
|
sock.sendMessage(jid, {
|
|
@@ -1182,7 +1223,7 @@ sock.sendMessage(jid, {
|
|
|
1182
1223
|
#### 3οΈβ£ External Ad Reply
|
|
1183
1224
|
|
|
1184
1225
|
> [!NOTE]
|
|
1185
|
-
Add an ad thumbnail to messages (may not be displayed on some WhatsApp versions).
|
|
1226
|
+
> Add an ad thumbnail to messages (may not be displayed on some WhatsApp versions).
|
|
1186
1227
|
|
|
1187
1228
|
```javascript
|
|
1188
1229
|
sock.sendMessage(jid, {
|
|
@@ -1202,7 +1243,7 @@ sock.sendMessage(jid, {
|
|
|
1202
1243
|
#### 4οΈβ£ Group Status
|
|
1203
1244
|
|
|
1204
1245
|
> [!NOTE]
|
|
1205
|
-
It only works in group chat (`@g.us`)
|
|
1246
|
+
> It only works in group chat (`@g.us`)
|
|
1206
1247
|
|
|
1207
1248
|
```javascript
|
|
1208
1249
|
sock.sendMessage(jid, {
|
|
@@ -1248,7 +1289,7 @@ sock.sendMessage(jid, {
|
|
|
1248
1289
|
#### 7οΈβ£ View Once
|
|
1249
1290
|
|
|
1250
1291
|
> [!NOTE]
|
|
1251
|
-
Wrap message into `viewOnceMessage`
|
|
1292
|
+
> Wrap message into `viewOnceMessage`
|
|
1252
1293
|
|
|
1253
1294
|
```javascript
|
|
1254
1295
|
sock.sendMessage(jid, {
|
|
@@ -1263,7 +1304,7 @@ sock.sendMessage(jid, {
|
|
|
1263
1304
|
#### 8οΈβ£ View Once V2
|
|
1264
1305
|
|
|
1265
1306
|
> [!NOTE]
|
|
1266
|
-
Wrap message into `viewOnceMessageV2`
|
|
1307
|
+
> Wrap message into `viewOnceMessageV2`
|
|
1267
1308
|
|
|
1268
1309
|
```javascript
|
|
1269
1310
|
sock.sendMessage(jid, {
|
|
@@ -1278,7 +1319,7 @@ sock.sendMessage(jid, {
|
|
|
1278
1319
|
#### 9οΈβ£ View Once V2 Extension
|
|
1279
1320
|
|
|
1280
1321
|
> [!NOTE]
|
|
1281
|
-
Wrap message into `viewOnceMessageV2Extension`
|
|
1322
|
+
> Wrap message into `viewOnceMessageV2Extension`
|
|
1282
1323
|
|
|
1283
1324
|
```javascript
|
|
1284
1325
|
sock.sendMessage(jid, {
|
|
@@ -1321,7 +1362,7 @@ sock.sendMessage(jid, {
|
|
|
1321
1362
|
#### π·οΈ Find User ID (JID|PN/LID)
|
|
1322
1363
|
|
|
1323
1364
|
> [!NOTE]
|
|
1324
|
-
The ID must contain numbers only (no +, (), or -) and must include the country code with WhatsApp ID format.
|
|
1365
|
+
> The ID must contain numbers only (no +, (), or -) and must include the country code with WhatsApp ID format.
|
|
1325
1366
|
|
|
1326
1367
|
```javascript
|
|
1327
1368
|
// --- PN (Phone Number)
|
|
@@ -1354,7 +1395,7 @@ console.log('π·οΈ Got user ID', ':', ids)
|
|
|
1354
1395
|
#### π Request Custom Pairing Code
|
|
1355
1396
|
|
|
1356
1397
|
> [!NOTE]
|
|
1357
|
-
The phone number must contain numbers only (no +, (), or -) and must include the country code.
|
|
1398
|
+
> The phone number must contain numbers only (no +, (), or -) and must include the country code.
|
|
1358
1399
|
|
|
1359
1400
|
```javascript
|
|
1360
1401
|
const phoneNumber = '6281111111111'
|
|
@@ -1368,7 +1409,7 @@ console.log('π Pairing code', ':', customPairingCode)
|
|
|
1368
1409
|
#### πΌοΈ Image Processing
|
|
1369
1410
|
|
|
1370
1411
|
> [!NOTE]
|
|
1371
|
-
Automatically use available image processing library: `sharp`, `@napi-rs/image`, or `jimp`
|
|
1412
|
+
> Automatically use available image processing library: `sharp`, `@napi-rs/image`, or `jimp`
|
|
1372
1413
|
|
|
1373
1414
|
```javascript
|
|
1374
1415
|
import { getImageProcessingLibrary } from '@itsliaaa/baileys'
|
|
@@ -1725,19 +1766,29 @@ sock.ev.on('newsletter-settings.update', (update) => {})
|
|
|
1725
1766
|
sock.ev.on('settings.update', (update) => {})
|
|
1726
1767
|
```
|
|
1727
1768
|
|
|
1728
|
-
|
|
1729
|
-
|
|
1769
|
+
### π Try the Bot
|
|
1770
|
+
|
|
1771
|
+
A fast, lightweight, and modular WhatsApp bot built with [@itsliaaa/baileys](https://www.npmjs.com/package/@itsliaaa/baileys).
|
|
1772
|
+
Perfect for managing groups, moderating chats, and adding fun with quiz games and handy tools.
|
|
1773
|
+
|
|
1774
|
+
ππ» [@itsliaaa/starseed](https://github.com/itsliaaa/starseed#readme)
|
|
1775
|
+
|
|
1776
|
+
### π¦ Fork Base
|
|
1777
|
+
|
|
1730
1778
|
This fork is based on [Baileys (GitHub)](https://github.com/WhiskeySockets/Baileys)
|
|
1731
1779
|
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
This
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1780
|
+
### π£ Credits
|
|
1781
|
+
|
|
1782
|
+
This project utilizes Protocol Buffer definitions maintained by [WPPConnect](https://github.com/wppconnect-team) through the [`wa-proto`](https://github.com/wppconnect-team/wa-proto) repository.
|
|
1783
|
+
|
|
1784
|
+
Full credit is attributed to the original maintainers and contributors of Baileys:
|
|
1785
|
+
- [purpshell](https://github.com/purpshell)
|
|
1786
|
+
- [jlucaso1](https://github.com/jlucaso1)
|
|
1787
|
+
- [adiwajshing](https://github.com/adiwajshing)
|
|
1788
|
+
|
|
1789
|
+
This fork includes additional enhancements and modifications by [Lia Wynn](https://github.com/itsliaaa)
|
|
1790
|
+
|
|
1791
|
+
Special thanks to [itsreimau](https://github.com/itsreimau) for the fix to the `updateBlockStatus` implementation.
|
|
1792
|
+
|
|
1793
|
+
> [!CAUTION]
|
|
1794
|
+
> β οΈ **Modification, removal, or misrepresentation of these credits is strictly prohibited. Any redistribution or fork must preserve this section in its original form without exception.**
|
package/lib/Defaults/index.js
CHANGED
|
@@ -126,6 +126,8 @@ export const MEDIA_HKDF_KEY_MAPPING = {
|
|
|
126
126
|
'biz-cover-photo': 'Image'
|
|
127
127
|
};
|
|
128
128
|
export const MEDIA_KEYS = Object.keys(MEDIA_PATH_MAP);
|
|
129
|
+
/** 120s timeout for history sync stall detection, same as WA Web's handleChunkProgress / restartPausedTimer (g = 120) */
|
|
130
|
+
export const HISTORY_SYNC_PAUSED_TIMEOUT_MS = 120_000;
|
|
129
131
|
export const MIN_PREKEY_COUNT = 5;
|
|
130
132
|
export const INITIAL_PREKEY_COUNT = 812;
|
|
131
133
|
export const UPLOAD_TIMEOUT = 30000; // 30 seconds
|