@kangwifi-pro/waliwa 1.0.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/LICENSE +21 -0
- package/README.md +600 -0
- package/dist/advanced/features.d.ts +211 -0
- package/dist/advanced/features.d.ts.map +1 -0
- package/dist/advanced/features.js +671 -0
- package/dist/advanced/features.js.map +1 -0
- package/dist/auth/auth-state.d.ts +89 -0
- package/dist/auth/auth-state.d.ts.map +1 -0
- package/dist/auth/auth-state.js +330 -0
- package/dist/auth/auth-state.js.map +1 -0
- package/dist/auth/pairing-code.d.ts +78 -0
- package/dist/auth/pairing-code.d.ts.map +1 -0
- package/dist/auth/pairing-code.js +191 -0
- package/dist/auth/pairing-code.js.map +1 -0
- package/dist/auth/qr-code.d.ts +60 -0
- package/dist/auth/qr-code.d.ts.map +1 -0
- package/dist/auth/qr-code.js +151 -0
- package/dist/auth/qr-code.js.map +1 -0
- package/dist/calls/handler.d.ts +61 -0
- package/dist/calls/handler.d.ts.map +1 -0
- package/dist/calls/handler.js +117 -0
- package/dist/calls/handler.js.map +1 -0
- package/dist/core/binary.d.ts +74 -0
- package/dist/core/binary.d.ts.map +1 -0
- package/dist/core/binary.js +448 -0
- package/dist/core/binary.js.map +1 -0
- package/dist/core/crypto.d.ts +60 -0
- package/dist/core/crypto.d.ts.map +1 -0
- package/dist/core/crypto.js +170 -0
- package/dist/core/crypto.js.map +1 -0
- package/dist/core/noise.d.ts +106 -0
- package/dist/core/noise.d.ts.map +1 -0
- package/dist/core/noise.js +307 -0
- package/dist/core/noise.js.map +1 -0
- package/dist/events/emitter.d.ts +44 -0
- package/dist/events/emitter.d.ts.map +1 -0
- package/dist/events/emitter.js +79 -0
- package/dist/events/emitter.js.map +1 -0
- package/dist/features/index.d.ts +342 -0
- package/dist/features/index.d.ts.map +1 -0
- package/dist/features/index.js +755 -0
- package/dist/features/index.js.map +1 -0
- package/dist/fixes/index.d.ts +333 -0
- package/dist/fixes/index.d.ts.map +1 -0
- package/dist/fixes/index.js +762 -0
- package/dist/fixes/index.js.map +1 -0
- package/dist/groups/management.d.ts +86 -0
- package/dist/groups/management.d.ts.map +1 -0
- package/dist/groups/management.js +443 -0
- package/dist/groups/management.js.map +1 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +236 -0
- package/dist/index.js.map +1 -0
- package/dist/messages/media.d.ts +93 -0
- package/dist/messages/media.d.ts.map +1 -0
- package/dist/messages/media.js +252 -0
- package/dist/messages/media.js.map +1 -0
- package/dist/messages/message-encoder.d.ts +70 -0
- package/dist/messages/message-encoder.d.ts.map +1 -0
- package/dist/messages/message-encoder.js +453 -0
- package/dist/messages/message-encoder.js.map +1 -0
- package/dist/messages/send.d.ts +101 -0
- package/dist/messages/send.d.ts.map +1 -0
- package/dist/messages/send.js +409 -0
- package/dist/messages/send.js.map +1 -0
- package/dist/recovery/index.d.ts +125 -0
- package/dist/recovery/index.d.ts.map +1 -0
- package/dist/recovery/index.js +584 -0
- package/dist/recovery/index.js.map +1 -0
- package/dist/skdm/index.d.ts +220 -0
- package/dist/skdm/index.d.ts.map +1 -0
- package/dist/skdm/index.js +600 -0
- package/dist/skdm/index.js.map +1 -0
- package/dist/socket/hybrid.d.ts +118 -0
- package/dist/socket/hybrid.d.ts.map +1 -0
- package/dist/socket/hybrid.js +352 -0
- package/dist/socket/hybrid.js.map +1 -0
- package/dist/socket/wa-socket.d.ts +300 -0
- package/dist/socket/wa-socket.d.ts.map +1 -0
- package/dist/socket/wa-socket.js +1094 -0
- package/dist/socket/wa-socket.js.map +1 -0
- package/dist/socket/ws-socket.d.ts +96 -0
- package/dist/socket/ws-socket.d.ts.map +1 -0
- package/dist/socket/ws-socket.js +302 -0
- package/dist/socket/ws-socket.js.map +1 -0
- package/dist/types/index.d.ts +444 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/jid.d.ts +46 -0
- package/dist/utils/jid.d.ts.map +1 -0
- package/dist/utils/jid.js +152 -0
- package/dist/utils/jid.js.map +1 -0
- package/dist/utils/logger.d.ts +45 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +79 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/retry.d.ts +43 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +175 -0
- package/dist/utils/retry.js.map +1 -0
- package/docs/API.md +745 -0
- package/docs/ARCHITECTURE.md +307 -0
- package/docs/BAILEYS_FIXES.md +360 -0
- package/docs/FEATURES.md +532 -0
- package/docs/PROTOCOL.md +489 -0
- package/docs/RECOVERY.md +409 -0
- package/docs/SKDM.md +233 -0
- package/examples/ai-bot/index.ts +479 -0
- package/examples/ai-bot/package.json +17 -0
- package/examples/echo-bot/index.ts +171 -0
- package/examples/echo-bot/package.json +18 -0
- package/examples/group-bot/index.ts +557 -0
- package/examples/group-bot/package.json +17 -0
- package/examples/rest-gateway/index.ts +499 -0
- package/examples/rest-gateway/package.json +19 -0
- package/package.json +75 -0
- package/src/advanced/features.ts +817 -0
- package/src/auth/auth-state.ts +342 -0
- package/src/auth/pairing-code.ts +246 -0
- package/src/auth/qr-code.ts +191 -0
- package/src/calls/handler.ts +153 -0
- package/src/core/binary.ts +464 -0
- package/src/core/crypto.ts +189 -0
- package/src/core/noise.ts +406 -0
- package/src/events/emitter.ts +88 -0
- package/src/features/index.ts +921 -0
- package/src/fixes/index.ts +882 -0
- package/src/groups/management.ts +497 -0
- package/src/index.ts +274 -0
- package/src/messages/media.ts +372 -0
- package/src/messages/message-encoder.ts +520 -0
- package/src/messages/send.ts +521 -0
- package/src/recovery/index.ts +704 -0
- package/src/skdm/index.ts +693 -0
- package/src/socket/hybrid.ts +414 -0
- package/src/socket/wa-socket.ts +1347 -0
- package/src/socket/ws-socket.ts +355 -0
- package/src/types/index.ts +457 -0
- package/src/utils/jid.ts +156 -0
- package/src/utils/logger.ts +84 -0
- package/src/utils/retry.ts +205 -0
- package/tsconfig.json +30 -0
package/docs/PROTOCOL.md
ADDED
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
# WhatsApp Protocol Reverse Engineering
|
|
2
|
+
|
|
3
|
+
This document describes the WhatsApp protocols implemented in Waliwa, based on publicly documented reverse engineering efforts.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
1. [WhatsApp Web Multi-Device Protocol](#whatsapp-web-multi-device-protocol)
|
|
8
|
+
2. [Mobile API Pairing Protocol](#mobile-api-pairing-protocol)
|
|
9
|
+
3. [Binary Protocol Format](#binary-protocol-format)
|
|
10
|
+
4. [Noise Protocol Handshake](#noise-protocol-handshake)
|
|
11
|
+
5. [Media Encryption](#media-encryption)
|
|
12
|
+
6. [Message Types](#message-types)
|
|
13
|
+
|
|
14
|
+
## WhatsApp Web Multi-Device Protocol
|
|
15
|
+
|
|
16
|
+
### Connection
|
|
17
|
+
|
|
18
|
+
WhatsApp Web MD connects to `wss://web.whatsapp.com/ws/chat` via WebSocket with custom headers:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
Origin: https://web.whatsapp.com
|
|
22
|
+
User-Agent: Mozilla/5.0 ...
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
WebSocket compression is disabled to reduce CPU overhead.
|
|
26
|
+
|
|
27
|
+
### Frame Format
|
|
28
|
+
|
|
29
|
+
Every frame follows this structure:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
┌─────────┬──────────┬───────────┬─────────────────────┐
|
|
33
|
+
│ WA (2B) │ Ver (2B) │ Len (3B) │ Encrypted Payload │
|
|
34
|
+
└─────────┴──────────┴───────────┴─────────────────────┘
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
- **WA (2 bytes)**: Magic header `0x57 0x41` ("WA")
|
|
38
|
+
- **Version (2 bytes)**: Protocol version, typically `0x05 0x2d`
|
|
39
|
+
- **Length (3 bytes)**: Big-endian length of payload (max 16MB)
|
|
40
|
+
- **Payload**: AES-256-GCM encrypted with traffic keys
|
|
41
|
+
|
|
42
|
+
### Traffic Encryption
|
|
43
|
+
|
|
44
|
+
After Noise handshake completes, two traffic keys are derived:
|
|
45
|
+
- `sendKey` (32 bytes) - For encrypting outgoing frames
|
|
46
|
+
- `recvKey` (32 bytes) - For decrypting incoming frames
|
|
47
|
+
|
|
48
|
+
Each frame uses counter-based nonce:
|
|
49
|
+
```
|
|
50
|
+
Nonce (12 bytes):
|
|
51
|
+
┌───────────────┬───────────────┐
|
|
52
|
+
│ 4 bytes zeros │ 8 bytes counter│
|
|
53
|
+
└───────────────┴───────────────┘
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Counter increments per message (separate counters for send/recv).
|
|
57
|
+
|
|
58
|
+
### Keep-Alive
|
|
59
|
+
|
|
60
|
+
Every 20 seconds, client sends ping:
|
|
61
|
+
```xml
|
|
62
|
+
<iq type="get" id="ping-123">
|
|
63
|
+
<ping/>
|
|
64
|
+
</iq>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Server responds with `<iq type="result" id="ping-123"/>`.
|
|
68
|
+
|
|
69
|
+
## Mobile API Pairing Protocol
|
|
70
|
+
|
|
71
|
+
### Pairing Code Generation
|
|
72
|
+
|
|
73
|
+
Pairing code is 8 alphanumeric characters derived via HKDF:
|
|
74
|
+
|
|
75
|
+
1. Inputs:
|
|
76
|
+
- Phone number (target, digits only)
|
|
77
|
+
- Pairing static key (Curve25519 private key, 32 bytes)
|
|
78
|
+
|
|
79
|
+
2. Derivation:
|
|
80
|
+
```
|
|
81
|
+
derived = HKDF-SHA256(
|
|
82
|
+
ikm = pairingKey.private,
|
|
83
|
+
salt = phoneNumber_bytes,
|
|
84
|
+
info = "pairing-code-v1",
|
|
85
|
+
length = 4 bytes
|
|
86
|
+
)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
3. Encoding (4 bytes → 8 chars):
|
|
90
|
+
- Each nibble (4 bits) → 1 char from "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
|
|
91
|
+
- Ambiguous characters (0/O, 1/I) excluded
|
|
92
|
+
- Format: `XXXX-XXXX` (dash separator at position 4)
|
|
93
|
+
|
|
94
|
+
### Pairing Request Flow
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
Client Server
|
|
98
|
+
│ │
|
|
99
|
+
│ WebSocket + Noise handshake │
|
|
100
|
+
│ ─────────────────────────────────→ │
|
|
101
|
+
│ │
|
|
102
|
+
│ Pairing request: │
|
|
103
|
+
│ <iq type="set" to="s.whatsapp.net">│
|
|
104
|
+
│ <pair code="AB12CD34" │
|
|
105
|
+
│ phone="6281234567890" │
|
|
106
|
+
│ pubkey="<base64>" │
|
|
107
|
+
│ platform="Waliwa"/> │
|
|
108
|
+
│ </iq> │
|
|
109
|
+
│ ─────────────────────────────────→ │
|
|
110
|
+
│ │
|
|
111
|
+
│ User inputs code in mobile app │
|
|
112
|
+
│ │
|
|
113
|
+
│ Pairing result: │
|
|
114
|
+
│ <iq type="result"> │
|
|
115
|
+
│ <pair jid="6281234567890@..." │
|
|
116
|
+
│ platform="android" │
|
|
117
|
+
│ identity="<base64>"/> │
|
|
118
|
+
│ </iq> │
|
|
119
|
+
│ ←─────────────────────────────────│
|
|
120
|
+
│ │
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Pairing Code Expiry
|
|
124
|
+
|
|
125
|
+
- Code expires after 65 seconds
|
|
126
|
+
- Client must refresh and request new code if expired
|
|
127
|
+
- Server rejects expired codes with `<error code="410">`
|
|
128
|
+
|
|
129
|
+
## Binary Protocol Format
|
|
130
|
+
|
|
131
|
+
WhatsApp uses a custom binary format for encoding XML-like nodes.
|
|
132
|
+
|
|
133
|
+
### Node Structure
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
Node = Tag + Attributes + Content
|
|
137
|
+
|
|
138
|
+
Tag: string (dictionary-encoded)
|
|
139
|
+
Attributes: { key: value, ... }
|
|
140
|
+
Content: string | bytes | Node[] | undefined
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Encoding Tags
|
|
144
|
+
|
|
145
|
+
| Tag Value | Meaning | Description |
|
|
146
|
+
|-----------|----------------------|------------------------------------------|
|
|
147
|
+
| 0x00 | EMPTY | Empty string or no content |
|
|
148
|
+
| 0xF8 | LIST_8 | List with 8-bit count |
|
|
149
|
+
| 0xF9 | LIST_16 | List with 16-bit count |
|
|
150
|
+
| 0xFA | JID_PAIR | JID user@server format |
|
|
151
|
+
| 0xFB | PACKED_NIBBLE | Numeric string (4 bits per char) |
|
|
152
|
+
| 0xFC | BINARY_8 / PACKED_HEX| Binary 8-bit length / Hex string |
|
|
153
|
+
| 0xFD | BINARY_20 | Binary 20-bit length |
|
|
154
|
+
| 0xFE | BINARY_32 | Binary 32-bit length |
|
|
155
|
+
| 0xFF | ADJACENT | Adjacent node marker |
|
|
156
|
+
|
|
157
|
+
### Dictionary Encoding
|
|
158
|
+
|
|
159
|
+
Common strings are stored in 4 dictionaries (240+ entries each). Encoding:
|
|
160
|
+
- Look up string in dictionary
|
|
161
|
+
- If found: emit `0xFC + dict_num` byte, then index byte (1 byte total)
|
|
162
|
+
- If not found: emit as raw bytes with length prefix
|
|
163
|
+
|
|
164
|
+
### Packed Nibble Encoding
|
|
165
|
+
|
|
166
|
+
For numeric strings (phone numbers, IDs), use nibble packing:
|
|
167
|
+
- 2 characters per byte (4 bits each)
|
|
168
|
+
- Append `0xF` terminator if odd length
|
|
169
|
+
|
|
170
|
+
Example: `"12345"` → `[0x12, 0x34, 0x5F]`
|
|
171
|
+
|
|
172
|
+
### Packed Hex Encoding
|
|
173
|
+
|
|
174
|
+
For hex strings (hashes, IDs), use hex packing:
|
|
175
|
+
- Same as nibble but supports `a-f` characters
|
|
176
|
+
- Append `0xF` terminator if odd length
|
|
177
|
+
|
|
178
|
+
### Binary Encoding
|
|
179
|
+
|
|
180
|
+
For arbitrary bytes (media hashes, encrypted payloads):
|
|
181
|
+
- **BINARY_8**: 1-byte length prefix (max 255 bytes)
|
|
182
|
+
- **BINARY_20**: 3-byte length prefix (max 1MB)
|
|
183
|
+
- **BINARY_32**: 4-byte length prefix (max 4GB)
|
|
184
|
+
|
|
185
|
+
## Noise Protocol Handshake
|
|
186
|
+
|
|
187
|
+
WhatsApp uses Noise Protocol Framework with **XX pattern**:
|
|
188
|
+
|
|
189
|
+
### Protocol Name
|
|
190
|
+
```
|
|
191
|
+
Noise_XX_25519_AESGCM_SHA256
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Handshake Flow
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
Client Server
|
|
198
|
+
│ │
|
|
199
|
+
│ -> e │ (ephemeral key, 32 bytes)
|
|
200
|
+
│ ─────────────────────────────────→ │
|
|
201
|
+
│ │
|
|
202
|
+
│ <- e, ee, s, es │
|
|
203
|
+
│ │
|
|
204
|
+
│ (server ephemeral + encrypted │
|
|
205
|
+
│ server static + DH computations) │
|
|
206
|
+
│ ←─────────────────────────────────│
|
|
207
|
+
│ │
|
|
208
|
+
│ -> s, se │
|
|
209
|
+
│ │
|
|
210
|
+
│ (client static, encrypted with │
|
|
211
|
+
│ current handshake key) │
|
|
212
|
+
│ ─────────────────────────────────→ │
|
|
213
|
+
│ │
|
|
214
|
+
│ Handshake complete │
|
|
215
|
+
│ Traffic keys derived │
|
|
216
|
+
│ │
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Key Derivation
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
Initial state:
|
|
223
|
+
ck = SHA256("Noise_XX_25519_AESGCM_SHA256")
|
|
224
|
+
h = SHA256("Noise_XX_25519_AESGCM_SHA256")
|
|
225
|
+
|
|
226
|
+
Step 1: Client sends ephemeral
|
|
227
|
+
h = SHA256(h || client_ephemeral_pub)
|
|
228
|
+
ck, k = HKDF(ck, client_ephemeral_pub, info=empty, 64 bytes)
|
|
229
|
+
(split: ck = first 32, k = next 32)
|
|
230
|
+
|
|
231
|
+
Step 2: Server sends ephemeral + encrypted static
|
|
232
|
+
h = SHA256(h || server_ephemeral_pub)
|
|
233
|
+
ck, k = HKDF(ck, server_ephemeral_pub, info=empty, 64 bytes)
|
|
234
|
+
|
|
235
|
+
ee = X25519(client_ephemeral_priv, server_ephemeral_pub)
|
|
236
|
+
ck, k = HKDF(ck, ee, info=empty, 64 bytes)
|
|
237
|
+
|
|
238
|
+
server_static_encrypted = AES-GCM-Encrypt(k, h, server_static_pub)
|
|
239
|
+
h = SHA256(h || server_static_pub)
|
|
240
|
+
|
|
241
|
+
es = X25519(client_ephemeral_priv, server_static_pub)
|
|
242
|
+
ck, k = HKDF(ck, es, info=empty, 64 bytes)
|
|
243
|
+
|
|
244
|
+
Step 3: Client sends encrypted static
|
|
245
|
+
client_static_encrypted = AES-GCM-Encrypt(k, h, client_static_pub)
|
|
246
|
+
h = SHA256(h || client_static_pub)
|
|
247
|
+
|
|
248
|
+
se = X25519(client_static_priv, server_ephemeral_pub)
|
|
249
|
+
ck, k = HKDF(ck, se, info=empty, 64 bytes)
|
|
250
|
+
|
|
251
|
+
Final: Traffic keys
|
|
252
|
+
prk = HKDF(ck, empty_input, "Noise_XX_..._handshake_done", 64 bytes)
|
|
253
|
+
sendKey = prk[:32]
|
|
254
|
+
recvKey = prk[32:64]
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### AEAD Construction
|
|
258
|
+
|
|
259
|
+
AES-256-GCM with:
|
|
260
|
+
- Key: 32 bytes (derived from HKDF)
|
|
261
|
+
- Nonce: 12 bytes (4 bytes zeros + 8 bytes counter, big-endian)
|
|
262
|
+
- AAD: Current handshake hash `h`
|
|
263
|
+
- Plaintext: Message payload
|
|
264
|
+
- Output: Ciphertext + 16-byte tag
|
|
265
|
+
|
|
266
|
+
Counter starts at 0 and increments per message (separate for send/recv).
|
|
267
|
+
|
|
268
|
+
## Media Encryption
|
|
269
|
+
|
|
270
|
+
### Media Key Derivation
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
mediaKey = random 32 bytes
|
|
274
|
+
derived = HKDF-SHA256(
|
|
275
|
+
ikm = mediaKey,
|
|
276
|
+
salt = [mediaType_byte],
|
|
277
|
+
info = "WhatsApp ${mediaType} Keys",
|
|
278
|
+
length = 112 bytes
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
iv = derived[0:16]
|
|
282
|
+
cipherKey = derived[16:48]
|
|
283
|
+
macKey = derived[48:80]
|
|
284
|
+
refKey = derived[80:112]
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Media type bytes:
|
|
288
|
+
- 1 = image
|
|
289
|
+
- 2 = video
|
|
290
|
+
- 3 = audio
|
|
291
|
+
- 4 = document
|
|
292
|
+
- 5 = sticker
|
|
293
|
+
- etc.
|
|
294
|
+
|
|
295
|
+
### Encryption Process
|
|
296
|
+
|
|
297
|
+
```
|
|
298
|
+
1. fileSha256 = SHA256(plaintext)
|
|
299
|
+
2. ciphertext = AES-256-CBC-Encrypt(cipherKey, iv, plaintext + PKCS7_PAD)
|
|
300
|
+
3. mac = HMAC-SHA256(macKey, iv + ciphertext)[0:10]
|
|
301
|
+
4. encFileSha256 = SHA256(ciphertext + mac)
|
|
302
|
+
5. Upload (ciphertext + mac) to CDN
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Note: MAC is truncated to 10 bytes (not full 32) for bandwidth efficiency.
|
|
306
|
+
|
|
307
|
+
### Decryption Process
|
|
308
|
+
|
|
309
|
+
```
|
|
310
|
+
1. Split: ciphertext = data[:-10], mac = data[-10:]
|
|
311
|
+
2. expectedMac = HMAC-SHA256(macKey, iv + ciphertext)[0:10]
|
|
312
|
+
3. Verify mac == expectedMac (constant-time comparison)
|
|
313
|
+
4. plaintext = AES-256-CBC-Decrypt(cipherKey, iv, ciphertext)
|
|
314
|
+
5. Strip PKCS7 padding
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## Message Types
|
|
318
|
+
|
|
319
|
+
### Text Message
|
|
320
|
+
|
|
321
|
+
```xml
|
|
322
|
+
<message to="jid" type="text" id="msgId">
|
|
323
|
+
<conversation>Text content</conversation>
|
|
324
|
+
</message>
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Image Message
|
|
328
|
+
|
|
329
|
+
```xml
|
|
330
|
+
<message to="jid" type="media" id="msgId">
|
|
331
|
+
<imageMessage
|
|
332
|
+
url="https://mmg.whatsapp.net/..."
|
|
333
|
+
mimetype="image/jpeg"
|
|
334
|
+
fileSha256="<base64>"
|
|
335
|
+
fileLength="12345"
|
|
336
|
+
mediaKey="<base64>"
|
|
337
|
+
encFileSha256="<base64>">
|
|
338
|
+
<caption>Optional caption</caption>
|
|
339
|
+
</imageMessage>
|
|
340
|
+
</message>
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Reply (Quoted) Message
|
|
344
|
+
|
|
345
|
+
```xml
|
|
346
|
+
<message to="jid" type="text" id="msgId">
|
|
347
|
+
<conversation>Reply text</conversation>
|
|
348
|
+
<contextInfo>
|
|
349
|
+
<stanzaId>original_msg_id</stanzaId>
|
|
350
|
+
<participant>original_sender@s.whatsapp.net</participant>
|
|
351
|
+
<quotedMessage>Original text</quotedMessage>
|
|
352
|
+
<mentionedJidList>
|
|
353
|
+
<user>mentioned_user@s.whatsapp.net</user>
|
|
354
|
+
</mentionedJidList>
|
|
355
|
+
</contextInfo>
|
|
356
|
+
</message>
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Reaction
|
|
360
|
+
|
|
361
|
+
```xml
|
|
362
|
+
<message to="jid" type="reaction" id="msgId">
|
|
363
|
+
<reactionMessage
|
|
364
|
+
key='{"id":"original_id","fromMe":false,"remoteJid":"..."}'
|
|
365
|
+
text="👍"
|
|
366
|
+
timestamp="1234567890"/>
|
|
367
|
+
</message>
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Poll
|
|
371
|
+
|
|
372
|
+
```xml
|
|
373
|
+
<message to="jid" type="poll" id="msgId">
|
|
374
|
+
<pollCreationMessage name="Question?" selectableOptionsCount="[1]">
|
|
375
|
+
<option index="0" name="Option 1"/>
|
|
376
|
+
<option index="1" name="Option 2"/>
|
|
377
|
+
</pollCreationMessage>
|
|
378
|
+
</message>
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
## Receipts
|
|
382
|
+
|
|
383
|
+
### Read Receipt
|
|
384
|
+
|
|
385
|
+
```xml
|
|
386
|
+
<receipt id="msgId" type="read" to="jid@s.whatsapp.net" t="1234567890">
|
|
387
|
+
<receipt id="msgId1"/>
|
|
388
|
+
<receipt id="msgId2"/>
|
|
389
|
+
</receipt>
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### Delivery Receipt
|
|
393
|
+
|
|
394
|
+
```xml
|
|
395
|
+
<receipt id="msgId" type="delivered" to="jid" t="1234567890"/>
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
## Presence
|
|
399
|
+
|
|
400
|
+
### Subscribe to User Presence
|
|
401
|
+
|
|
402
|
+
```xml
|
|
403
|
+
<iq type="set" to="s.whatsapp.net" id="sub-1">
|
|
404
|
+
<user jid="target@s.whatsapp.net"/>
|
|
405
|
+
</iq>
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### Presence Update
|
|
409
|
+
|
|
410
|
+
```xml
|
|
411
|
+
<presence from="user@s.whatsapp.net" type="available" t="1234567890"/>
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Typing Indicator
|
|
415
|
+
|
|
416
|
+
```xml
|
|
417
|
+
<chat to="jid" type="composing"/>
|
|
418
|
+
<chat to="jid" type="recording"/>
|
|
419
|
+
<chat to="jid" type="paused"/>
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## Group Operations
|
|
423
|
+
|
|
424
|
+
### Create Group
|
|
425
|
+
|
|
426
|
+
```xml
|
|
427
|
+
<iq type="set" to="g.us" id="create-1">
|
|
428
|
+
<create subject="Group Name">
|
|
429
|
+
<participant jid="user1@s.whatsapp.net"/>
|
|
430
|
+
<participant jid="user2@s.whatsapp.net"/>
|
|
431
|
+
</create>
|
|
432
|
+
</iq>
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### Add/Remove Participants
|
|
436
|
+
|
|
437
|
+
```xml
|
|
438
|
+
<iq type="set" to="group@g.us" id="add-1">
|
|
439
|
+
<add>
|
|
440
|
+
<participant jid="newuser@s.whatsapp.net"/>
|
|
441
|
+
</add>
|
|
442
|
+
</iq>
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### Group Settings
|
|
446
|
+
|
|
447
|
+
```xml
|
|
448
|
+
<!-- Restrict (only admins can send) -->
|
|
449
|
+
<iq type="set" to="group@g.us" id="restrict-1">
|
|
450
|
+
<announce type="restrict"/>
|
|
451
|
+
</iq>
|
|
452
|
+
|
|
453
|
+
<!-- Announce (only admins can edit info) -->
|
|
454
|
+
<iq type="set" to="group@g.us" id="announce-1">
|
|
455
|
+
<info type="announce"/>
|
|
456
|
+
</iq>
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
## Call Events
|
|
460
|
+
|
|
461
|
+
### Incoming Call
|
|
462
|
+
|
|
463
|
+
```xml
|
|
464
|
+
<call
|
|
465
|
+
from="caller@s.whatsapp.net"
|
|
466
|
+
id="call-123"
|
|
467
|
+
to="me@s.whatsapp.net"
|
|
468
|
+
video="false"
|
|
469
|
+
status="ringing"/>
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### Reject Call
|
|
473
|
+
|
|
474
|
+
```xml
|
|
475
|
+
<call
|
|
476
|
+
id="call-123"
|
|
477
|
+
to="caller@s.whatsapp.net"
|
|
478
|
+
action="reject"
|
|
479
|
+
type="reject"/>
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
## References
|
|
483
|
+
|
|
484
|
+
- [Noise Protocol Framework Specification](https://noiseprotocol.org/)
|
|
485
|
+
- [Signal Protocol Documentation](https://signal.org/docs/)
|
|
486
|
+
- [HKDF (RFC 5869)](https://tools.ietf.org/html/rfc5869)
|
|
487
|
+
- [WhatsApp Multi-Device Documentation](https://faq.whatsapp.com/384073008)
|
|
488
|
+
|
|
489
|
+
*Note: Protocol details are based on publicly documented reverse engineering efforts. WhatsApp may change protocol without notice.*
|