@fer2809fl/baileys 7.0.4 → 7.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +347 -50
  2. package/lib/Defaults/index.js +1 -1
  3. package/lib/Modded/message_builder.js +2356 -0
  4. package/lib/Socket/chats.d.ts +14 -0
  5. package/lib/Socket/chats.js +48 -0
  6. package/lib/Socket/index.d.ts +17 -0
  7. package/lib/Socket/messages-send.d.ts +18 -0
  8. package/lib/Socket/messages-send.js +50 -2
  9. package/lib/Utils/anti-ban.d.ts +41 -0
  10. package/lib/Utils/anti-ban.js +182 -0
  11. package/lib/Utils/banner.d.ts +8 -0
  12. package/lib/Utils/banner.js +76 -0
  13. package/lib/Utils/bot-utils.d.ts +57 -0
  14. package/lib/Utils/bot-utils.js +241 -0
  15. package/lib/Utils/enhanced-cache.d.ts +40 -0
  16. package/lib/Utils/enhanced-cache.js +242 -0
  17. package/lib/Utils/enhanced-logger.d.ts +41 -0
  18. package/lib/Utils/enhanced-logger.js +185 -0
  19. package/lib/Utils/index.d.ts +14 -0
  20. package/lib/Utils/index.js +13 -0
  21. package/lib/Utils/lid-utils.d.ts +139 -0
  22. package/lib/Utils/lid-utils.js +503 -0
  23. package/lib/Utils/message-queue.d.ts +47 -0
  24. package/lib/Utils/message-queue.js +226 -0
  25. package/lib/Utils/rich-message-utils.d.ts +21 -0
  26. package/lib/Utils/rich-message-utils.js +229 -0
  27. package/lib/Utils/rich-messages.d.ts +52 -0
  28. package/lib/Utils/rich-messages.js +185 -0
  29. package/lib/Utils/scheduled-messages.d.ts +122 -0
  30. package/lib/Utils/scheduled-messages.js +289 -0
  31. package/lib/Utils/smart-reconnect.d.ts +48 -0
  32. package/lib/Utils/smart-reconnect.js +207 -0
  33. package/lib/Utils/use-sqlite-auth-state.d.ts +11 -0
  34. package/lib/Utils/use-sqlite-auth-state.js +95 -0
  35. package/lib/VoIP/audio-feeder.d.ts +15 -0
  36. package/lib/VoIP/audio-feeder.js +132 -0
  37. package/lib/VoIP/index.js +277 -0
  38. package/lib/VoIP/relay-transport.d.ts +43 -0
  39. package/lib/VoIP/relay-transport.js +559 -0
  40. package/lib/VoIP/signaling.js +594 -0
  41. package/lib/VoIP/types.d.ts +69 -0
  42. package/lib/VoIP/types.js +17 -0
  43. package/lib/VoIP/wasm-engine.d.ts +103 -0
  44. package/lib/VoIP/wasm-engine.js +1214 -0
  45. package/lib/VoIP/worker-bootstrap.js +1042 -0
  46. package/lib/assets/wasm/loader.js +5 -0
  47. package/lib/assets/wasm/whatsapp.wasm +0 -0
  48. package/lib/assets/wasm/worker-modules.js +273 -0
  49. package/lib/index.d.ts +41 -0
  50. package/lib/index.js +3 -0
  51. package/package.json +10 -2
@@ -0,0 +1,277 @@
1
+ import { EventEmitter } from "node:events";
2
+ import { randomBytes, createHmac } from "node:crypto";
3
+
4
+ import { WasmEngine } from "./wasm-engine.js";
5
+ import { RelayRtcTransport } from "./relay-transport.js";
6
+ import { SignalingBridge } from "./signaling.js";
7
+ import { AudioFeeder } from "./audio-feeder.js";
8
+ import { CallState } from "./types.js";
9
+
10
+ export { CallState } from "./types.js";
11
+
12
+ const SHA256_LEN = 32;
13
+
14
+ const toBareJid = (jid) => {
15
+ if (!jid) return jid;
16
+ const at = jid.indexOf("@");
17
+ if (at < 0) return jid;
18
+ const user = jid.slice(0, at).split(":")[0];
19
+ return `${user}@${jid.slice(at + 1)}`;
20
+ };
21
+
22
+ const computeHkdf = (key, salt, info, length) => {
23
+ const effectiveSalt = salt && salt.length > 0 ? Buffer.from(salt) : Buffer.alloc(SHA256_LEN, 0);
24
+ const prk = createHmac("sha256", effectiveSalt).update(key).digest();
25
+ const blocks = Math.ceil(length / SHA256_LEN);
26
+ const okm = Buffer.alloc(blocks * SHA256_LEN);
27
+ let prev = Buffer.alloc(0);
28
+ for (let i = 1; i <= blocks; i += 1) {
29
+ prev = createHmac("sha256", prk)
30
+ .update(prev)
31
+ .update(info)
32
+ .update(Buffer.from([i]))
33
+ .digest();
34
+ prev.copy(okm, (i - 1) * SHA256_LEN);
35
+ }
36
+ return new Uint8Array(okm.buffer, okm.byteOffset, length);
37
+ };
38
+
39
+ const computeHmacSha256 = (data, key) => {
40
+ const result = createHmac("sha256", Buffer.from(key)).update(data).digest();
41
+ return new Uint8Array(result.buffer, result.byteOffset, result.byteLength);
42
+ };
43
+
44
+ const isCallReceiptNode = (node) => {
45
+ if (node?.tag !== "receipt") return false;
46
+ const child = Array.isArray(node.content) ? node.content[0] : null;
47
+ return !!(child?.attrs?.["call-id"] || child?.attrs?.call_id);
48
+ };
49
+
50
+ export class ActiveCall extends EventEmitter {
51
+ #state = CallState.Idle;
52
+ #endResolver;
53
+ #endPromise;
54
+ #endTimer = null;
55
+ #ended = false;
56
+ _audioSource = "silence";
57
+
58
+ constructor(callId, engine, durationMs) {
59
+ super();
60
+ this.callId = callId;
61
+ this.engine = engine;
62
+ this.#endPromise = new Promise((res) => { this.#endResolver = res; });
63
+ if (durationMs > 0) {
64
+ this.#endTimer = setTimeout(() => this.end(), durationMs);
65
+ }
66
+ }
67
+
68
+ get state() { return this.#state; }
69
+
70
+ end = () => {
71
+ if (this.#ended) return;
72
+ this.#ended = true;
73
+ if (this.#endTimer) { clearTimeout(this.#endTimer); this.#endTimer = null; }
74
+ try { this.engine.endCall(0, true); } catch {}
75
+ };
76
+
77
+ mute = (muted) => {
78
+ try { this.engine.setMute(muted); } catch {}
79
+ };
80
+
81
+ waitForEnd = () => this.#endPromise;
82
+
83
+ _updateState = (state) => {
84
+ this.#state = state;
85
+ if (state === CallState.PreacceptReceived) this.emit("ringing");
86
+ else if (state === CallState.Active) this.emit("connected");
87
+ else if (state === CallState.Idle || state === CallState.Ending) {
88
+ this._forceEnd("ended");
89
+ }
90
+ };
91
+
92
+ _emitAudio = (pcm) => { this.emit("audio", pcm); };
93
+
94
+ _forceEnd = (reason) => {
95
+ if (this.#ended) return;
96
+ this.#ended = true;
97
+ if (this.#endTimer) { clearTimeout(this.#endTimer); this.#endTimer = null; }
98
+ this.emit("ended", reason);
99
+ this.#endResolver(reason);
100
+ };
101
+ }
102
+
103
+ export class VoipClient {
104
+ #config;
105
+ #engine = null;
106
+ #relay = null;
107
+ #signaling = null;
108
+ #sock = null;
109
+ #activeCall = null;
110
+ #capturePtr = 0;
111
+ #captureChunkBytes = 0;
112
+ #captureSampleRate = 16000;
113
+ #captureChannels = 1;
114
+ #captureFramesPerChunk = 320;
115
+ #feeder = null;
116
+
117
+ constructor(config = {}) {
118
+ this.#config = config;
119
+ }
120
+
121
+ connectWithSocket = async (existingSock) => {
122
+ this.#sock = existingSock;
123
+ await this.#initVoipStack();
124
+ };
125
+
126
+ #initVoipStack = async () => {
127
+ this.#signaling = new SignalingBridge({ sock: this.#sock });
128
+ await this.#signaling.init();
129
+
130
+ this.#relay = new RelayRtcTransport({
131
+ onTransportMessage: (data, ip, port) => this.#engine?.handleOnTransportMessage(data, ip, port),
132
+ onIceRtt: (rttMs, ip, port) => this.#engine?.updateIceRtt(rttMs, ip, port),
133
+ });
134
+
135
+ this.#engine = new WasmEngine({
136
+ resourcesPath: this.#config.resourcesPath,
137
+ callbacks: {
138
+ onSignalingXmpp: (peerJid, callId, xmlPayload) =>
139
+ this.#signaling.sendSignaling(peerJid, callId, xmlPayload),
140
+ onCallEvent: (eventType, eventData) => this.#handleCallEvent(eventType, eventData),
141
+ sendDataToRelay: (data, ip, port) => this.#relay.send(data, ip, port),
142
+ onAudioCaptureInit: (config) => this.#handleAudioCaptureInit(config),
143
+ onAudioCaptureStart: () => this.#handleAudioCaptureStart(),
144
+ onAudioCaptureStop: () => this.#handleAudioCaptureStop(),
145
+ onAudioPlaybackData: (audioData) => this.#activeCall?._emitAudio(audioData),
146
+ cryptoHkdf: computeHkdf,
147
+ hmacSha256: computeHmacSha256,
148
+ },
149
+ });
150
+
151
+ await this.#engine.initialize();
152
+ this.#signaling.attachEngine(this.#engine);
153
+
154
+ const selfPnJid = this.#sock.authState.creds.me?.id;
155
+ const selfLidJid = this.#sock.authState.creds.me?.lid;
156
+ this.#engine.initVoipStack(selfPnJid, toBareJid(selfPnJid), selfLidJid);
157
+ await this.#engine.waitForVoipStackReady();
158
+ try { this.#engine.updateNetworkMedium(2, 0); } catch {}
159
+
160
+ this.#sock.ws.on("CB:call", (node) => {
161
+ this.#signaling.processIncomingCall(node, this.#engine, this.#activeCall?.callId ?? "");
162
+ });
163
+ this.#sock.ws.on("CB:receipt", (node) => {
164
+ if (!isCallReceiptNode(node)) return;
165
+ this.#signaling.processIncomingReceipt(node, this.#engine, this.#activeCall?.callId ?? "");
166
+ });
167
+ };
168
+
169
+ call = async (phoneNumber, opts = {}) => {
170
+ if (!this.#engine || !this.#signaling) throw new Error("Not connected. Call connectWithSocket() first.");
171
+ if (this.#activeCall) throw new Error("A call is already active.");
172
+
173
+ const targetNumber = phoneNumber.replace(/\D/g, "");
174
+ const targetPnJid = `${targetNumber}@s.whatsapp.net`;
175
+ const durationMs = opts.durationMs ?? 120_000;
176
+ const audioSource = opts.audioSource ?? "silence";
177
+
178
+ const peerLid = await this.#signaling.resolveLid(targetPnJid);
179
+ if (!peerLid) throw new Error(`Could not resolve LID for ${targetPnJid}`);
180
+
181
+ for (const jid of [targetPnJid, peerLid]) {
182
+ try { await this.#sock.presenceSubscribe(jid); } catch {}
183
+ }
184
+ await new Promise((r) => setTimeout(r, 750));
185
+
186
+ const peerDeviceJids = await this.#signaling.discoverPeerDevices(peerLid);
187
+ const deviceList = peerDeviceJids.length ? peerDeviceJids : [toBareJid(peerLid)];
188
+
189
+ await this.#signaling.ensureSessionsForPeers(deviceList);
190
+
191
+ await new Promise((r) => setTimeout(r, 500));
192
+ await this.#signaling.issueTcToken(peerLid);
193
+ const tcToken = await this.#signaling.ensureTcToken(peerLid, targetPnJid);
194
+
195
+ const callId = ("00" + randomBytes(16).toString("hex").slice(2)).toUpperCase();
196
+
197
+ const call = new ActiveCall(callId, this.#engine, durationMs);
198
+ call._audioSource = audioSource;
199
+ this.#activeCall = call;
200
+
201
+ this.#engine.startCall({
202
+ peerJid: peerLid,
203
+ peerPn: targetPnJid,
204
+ peerList: deviceList,
205
+ callId,
206
+ isVideo: false,
207
+ isLidCall: true,
208
+ isFromDialer: false,
209
+ extraData: tcToken,
210
+ });
211
+
212
+ return call;
213
+ };
214
+
215
+ disconnect = () => {
216
+ this.#activeCall?._forceEnd("disconnect");
217
+ this.#activeCall = null;
218
+ this.#relay?.closeAll();
219
+ this.#engine?.destroy();
220
+ this.#engine = null;
221
+ this.#relay = null;
222
+ this.#signaling = null;
223
+ this.#sock = null;
224
+ };
225
+
226
+ #handleCallEvent = (eventType, eventData) => {
227
+ if (eventType === 16 && eventData) {
228
+ try {
229
+ const parsed = JSON.parse(eventData);
230
+ const info = parsed.call_info ?? parsed.callInfo ?? {};
231
+ const callState = Number(info.call_state ?? info.callState ?? 0);
232
+ this.#activeCall?._updateState(callState);
233
+ } catch {}
234
+ } else if (eventType === 156 && eventData) {
235
+ try {
236
+ const update = JSON.parse(eventData);
237
+ this.#relay?.updateRelayList(update);
238
+ } catch {}
239
+ } else if (eventType === 2) {
240
+ this.#activeCall?._forceEnd("remote_end");
241
+ }
242
+ };
243
+
244
+ #handleAudioCaptureInit = (config) => {
245
+ if (!this.#engine) return;
246
+ this.#captureSampleRate = config.sampleRate || 16000;
247
+ this.#captureChannels = config.channels || 1;
248
+ this.#captureFramesPerChunk = config.framesPerChunk || 320;
249
+ const chunkSamples = this.#captureFramesPerChunk * this.#captureChannels;
250
+ this.#captureChunkBytes = chunkSamples * Float32Array.BYTES_PER_ELEMENT;
251
+ this.#capturePtr = this.#engine.malloc(this.#captureChunkBytes);
252
+ };
253
+
254
+ #handleAudioCaptureStart = () => {
255
+ if (!this.#engine || !this.#capturePtr) return;
256
+ const audioSource = this.#activeCall?._audioSource ?? "silence";
257
+ this.#feeder = new AudioFeeder(
258
+ this.#captureSampleRate,
259
+ this.#captureChannels,
260
+ this.#captureFramesPerChunk,
261
+ (chunk) => {
262
+ if (this.#engine && this.#capturePtr) this.#engine.sendAudioData(chunk, this.#capturePtr);
263
+ },
264
+ audioSource,
265
+ );
266
+ this.#feeder.start();
267
+ };
268
+
269
+ #handleAudioCaptureStop = () => {
270
+ this.#feeder?.stop();
271
+ this.#feeder = null;
272
+ if (this.#engine && this.#capturePtr) {
273
+ try { this.#engine.free(this.#capturePtr); } catch {}
274
+ this.#capturePtr = 0;
275
+ }
276
+ };
277
+ }
@@ -0,0 +1,43 @@
1
+ type RelayAddress = {
2
+ protocol: number;
3
+ ipv4?: string;
4
+ ipv6?: string;
5
+ port?: number;
6
+ port_v6?: number;
7
+ };
8
+ type RelayDescriptor = {
9
+ relay_id: number;
10
+ relay_name: string;
11
+ token_id: number;
12
+ auth_token_id?: number;
13
+ addresses: RelayAddress[];
14
+ };
15
+ export type RelayListUpdatePayload = {
16
+ relay_key: string;
17
+ relay_tokens: string[];
18
+ auth_tokens?: string[];
19
+ enable_edgeray_dtls_active_mode?: boolean;
20
+ relays: RelayDescriptor[];
21
+ };
22
+ export type RelayTransportStats = {
23
+ sentPackets: number;
24
+ receivedPackets: number;
25
+ sentBytes: number;
26
+ receivedBytes: number;
27
+ droppedPackets: number;
28
+ openConnections: number;
29
+ };
30
+ export type RelayTransportConfig = {
31
+ onTransportMessage: (data: Uint8Array, ip: string, port: number) => void;
32
+ onIceRtt?: (rttMs: number, ip: string, port: number) => void;
33
+ };
34
+ export declare class RelayRtcTransport {
35
+ #private;
36
+ private readonly config;
37
+ constructor(config: RelayTransportConfig);
38
+ updateRelayList: (update: RelayListUpdatePayload) => void;
39
+ send: (packet: Uint8Array | Buffer, ip: string, port: number) => number;
40
+ getStats: () => RelayTransportStats;
41
+ closeAll: () => Promise<void>;
42
+ }
43
+ export {};