@whanext/core 0.17.1 → 0.18.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.
package/dist/index.js CHANGED
@@ -2297,96 +2297,36 @@ var Browser = /* @__PURE__ */ ((Browser2) => {
2297
2297
  return Browser2;
2298
2298
  })(Browser || {});
2299
2299
 
2300
- // src/provider/baileys/baileys-provider.ts
2300
+ // src/provider/zapo/zapo-provider.ts
2301
+ import { mkdir } from "fs/promises";
2302
+ import { join } from "path";
2303
+ import { createMediaProcessor } from "@zapo-js/media-utils";
2304
+ import { createSqliteStore } from "@zapo-js/store-sqlite";
2301
2305
  import {
2302
- Browsers,
2303
- DisconnectReason,
2304
- downloadMediaMessage,
2305
- generateWAMessageFromContent,
2306
- isJidGroup,
2307
- makeWASocket,
2308
- proto as proto2,
2309
- useMultiFileAuthState
2310
- } from "@whiskeysockets/baileys";
2311
-
2312
- // src/provider/baileys/baileys-logger.ts
2313
- function createBaileysLogger(logger) {
2314
- return {
2315
- level: logger.level,
2316
- child(context) {
2317
- const name = typeof context.class === "string" ? context.class : "internal";
2318
- return createBaileysLogger(logger.child(name));
2319
- },
2320
- trace(value, message) {
2321
- logger.debug(resolveMessage(value, message, "Provider trace"), safeContext(value));
2322
- },
2323
- debug(value, message) {
2324
- logger.debug(resolveMessage(value, message, "Provider debug"), safeContext(value));
2325
- },
2326
- info(value, message) {
2327
- logger.debug(resolveMessage(value, message, "Provider info"), safeContext(value));
2328
- },
2329
- warn(value, message) {
2330
- logger.warn(resolveMessage(value, message, "Provider warning"), safeContext(value));
2331
- },
2332
- error(value, message) {
2333
- logger.error(resolveMessage(value, message, "Provider error"), safeContext(value));
2334
- }
2335
- };
2336
- }
2337
- function resolveMessage(value, message, fallback) {
2338
- if (message) {
2339
- return message;
2340
- }
2341
- return typeof value === "string" ? value : fallback;
2342
- }
2343
- function safeContext(value) {
2344
- if (value instanceof Error) {
2345
- return { error: value };
2346
- }
2347
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
2348
- return {};
2349
- }
2350
- const source = value;
2351
- const allowed = [
2352
- "location",
2353
- "reason",
2354
- "status",
2355
- "statusCode",
2356
- "type"
2357
- ];
2358
- return Object.fromEntries(
2359
- allowed.filter((key) => source[key] !== void 0).map((key) => [key, source[key]])
2360
- );
2361
- }
2306
+ WaClient,
2307
+ createStore,
2308
+ proto
2309
+ } from "zapo-js";
2362
2310
 
2363
- // src/provider/baileys/normalize-message.ts
2364
- import {
2365
- extractMessageContent,
2366
- getContentType,
2367
- normalizeMessageContent
2368
- } from "@whiskeysockets/baileys";
2369
- function unwrapMessageContent(input) {
2311
+ // src/provider/zapo/normalize-message.ts
2312
+ function unwrapZapoMessageContent(input) {
2370
2313
  if (!input) return void 0;
2371
- const normalized = normalizeMessageContent(input);
2372
- const content = extractMessageContent(normalized);
2373
- if (!content) return void 0;
2374
- const nested = content.ephemeralMessage?.message ?? content.viewOnceMessage?.message ?? content.viewOnceMessageV2?.message ?? content.viewOnceMessageV2Extension?.message;
2375
- return nested ? unwrapMessageContent(nested) : content;
2314
+ const nested = input.ephemeralMessage?.message ?? input.viewOnceMessage?.message ?? input.viewOnceMessageV2?.message ?? input.deviceSentMessage?.message ?? input.documentWithCaptionMessage?.message;
2315
+ return nested ? unwrapZapoMessageContent(nested) : input;
2376
2316
  }
2377
- function isViewOnceContent(input) {
2317
+ function isZapoViewOnceContent(input) {
2378
2318
  if (!input) return false;
2379
- if (input.viewOnceMessage?.message || input.viewOnceMessageV2?.message || input.viewOnceMessageV2Extension?.message) {
2319
+ if (input.viewOnceMessage?.message || input.viewOnceMessageV2?.message) {
2380
2320
  return true;
2381
2321
  }
2382
- return isViewOnceContent(input.ephemeralMessage?.message);
2322
+ return isZapoViewOnceContent(input.ephemeralMessage?.message);
2383
2323
  }
2384
- function extractQuotedBaileysMessage(input) {
2385
- const chatId = input.key.remoteJid;
2386
- const content = unwrapMessageContent(input.message);
2324
+ function extractQuotedZapoMessage(input) {
2325
+ const event = input;
2326
+ const chatId = event.key.remoteJid;
2327
+ const content = unwrapZapoMessageContent(event.message);
2387
2328
  if (!chatId || !content) return void 0;
2388
- const type = getContentType(content);
2389
- const node = type ? content[type] : void 0;
2329
+ const node = contentNode(content);
2390
2330
  const context = getContextInfo(node);
2391
2331
  if (!context?.stanzaId || !context.quotedMessage) return void 0;
2392
2332
  return {
@@ -2399,26 +2339,25 @@ function extractQuotedBaileysMessage(input) {
2399
2339
  message: context.quotedMessage
2400
2340
  };
2401
2341
  }
2402
- function normalizeBaileysMessage(input) {
2403
- const chatId = input.key.remoteJid;
2404
- const id = input.key.id;
2405
- if (!chatId || !id || !input.message) {
2342
+ function normalizeZapoMessage(input) {
2343
+ const event = input;
2344
+ const chatId = event.key.remoteJid;
2345
+ const id = event.key.id;
2346
+ if (!chatId || !id || !event.message) {
2406
2347
  return void 0;
2407
2348
  }
2408
- const content = unwrapMessageContent(input.message);
2349
+ const content = unwrapZapoMessageContent(event.message);
2409
2350
  if (!content) {
2410
2351
  return void 0;
2411
2352
  }
2412
- const type = getContentType(content);
2413
- const node = type ? content[type] : void 0;
2353
+ const { type, node } = contentNode(content);
2414
2354
  const context = getContextInfo(node);
2415
- const isPrivateIncoming = !chatId.endsWith("@g.us") && input.key.fromMe !== true;
2416
- const remoteJidAlt = input.key.remoteJidAlt;
2355
+ const isPrivateIncoming = !chatId.endsWith("@g.us") && event.key.fromMe !== true;
2417
2356
  const senderIds = uniqueIdentities([
2418
- input.key.participant,
2419
- input.key.participantAlt,
2420
- input.key.participantUsername?.includes("@") ? input.key.participantUsername : void 0,
2421
- isPrivateIncoming ? remoteJidAlt : void 0,
2357
+ event.key.participant,
2358
+ event.key.participantAlt,
2359
+ event.key.senderUsername?.includes("@") ? event.key.senderUsername : void 0,
2360
+ isPrivateIncoming ? event.key.remoteJidAlt : void 0,
2422
2361
  isPrivateIncoming ? chatId : void 0
2423
2362
  ]);
2424
2363
  const senderJid = senderIds.find((identity) => identity.endsWith("@s.whatsapp.net") || identity.endsWith("@c.us"));
@@ -2427,10 +2366,11 @@ function normalizeBaileysMessage(input) {
2427
2366
  const sender = new User({
2428
2367
  id: senderId,
2429
2368
  identities: senderIds.length > 0 ? senderIds : [senderId],
2430
- ...input.pushName ? { name: input.pushName } : {}
2369
+ ...event.pushName ? { name: event.pushName } : {}
2431
2370
  });
2432
- const mentionedUsers = (context?.mentionedJid ?? []).map((identity) => User.fromIdentities([identity]));
2433
- const viewOnce = Boolean(input.key.isViewOnce) || isViewOnceContent(input.message);
2371
+ const mentionedIds = [...context?.mentionedJid ?? []];
2372
+ const mentionedUsers = mentionedIds.map((identity) => User.fromIdentities([identity]));
2373
+ const viewOnce = isZapoViewOnceContent(event.message);
2434
2374
  const media = getMedia(type, node, viewOnce);
2435
2375
  const contentKind = getContentKind(type);
2436
2376
  const text = getText(content);
@@ -2443,10 +2383,10 @@ function normalizeBaileysMessage(input) {
2443
2383
  senderId,
2444
2384
  senderIds: senderIds.length > 0 ? senderIds : [senderId],
2445
2385
  sender,
2446
- keys: normalizeKey(input.key),
2447
- mentions: [...context?.mentionedJid ?? []],
2386
+ keys: normalizeZapoKey(event.key),
2387
+ mentions: mentionedIds,
2448
2388
  mentionedUsers,
2449
- timestamp: toDate(input.messageTimestamp),
2389
+ timestamp: toDate(event.timestampSeconds),
2450
2390
  isGroup: chatId.endsWith("@g.us"),
2451
2391
  isReply: quoted !== void 0,
2452
2392
  isViewOnce: media?.viewOnce ?? false,
@@ -2464,6 +2404,45 @@ function normalizeBaileysMessage(input) {
2464
2404
  if (quoted !== void 0) message.quoted = quoted;
2465
2405
  return message;
2466
2406
  }
2407
+ function normalizeZapoKey(key) {
2408
+ const normalized = {
2409
+ id: key.id ?? "",
2410
+ chatId: key.remoteJid ?? "",
2411
+ fromMe: key.fromMe ?? false
2412
+ };
2413
+ const participantId = key.participant ?? key.participantAlt;
2414
+ if (participantId !== null && participantId !== void 0) {
2415
+ normalized.participantId = participantId;
2416
+ }
2417
+ return normalized;
2418
+ }
2419
+ function contentNode(content) {
2420
+ const order = [
2421
+ "conversation",
2422
+ "extendedTextMessage",
2423
+ "imageMessage",
2424
+ "videoMessage",
2425
+ "audioMessage",
2426
+ "documentMessage",
2427
+ "stickerMessage",
2428
+ "locationMessage",
2429
+ "liveLocationMessage",
2430
+ "contactMessage",
2431
+ "contactsArrayMessage",
2432
+ "buttonsResponseMessage",
2433
+ "listResponseMessage",
2434
+ "templateButtonReplyMessage",
2435
+ "pollCreationMessage",
2436
+ "pollCreationMessageV2",
2437
+ "pollCreationMessageV3",
2438
+ "pollCreationMessageV5",
2439
+ "productMessage",
2440
+ "orderMessage",
2441
+ "interactiveResponseMessage"
2442
+ ];
2443
+ const type = order.find((key) => content[key] !== null && content[key] !== void 0);
2444
+ return { type, node: type ? content[type] : void 0 };
2445
+ }
2467
2446
  function getContentKind(type) {
2468
2447
  switch (String(type ?? "")) {
2469
2448
  case "conversation":
@@ -2471,6 +2450,7 @@ function getContentKind(type) {
2471
2450
  case "buttonsResponseMessage":
2472
2451
  case "listResponseMessage":
2473
2452
  case "templateButtonReplyMessage":
2453
+ case "interactiveResponseMessage":
2474
2454
  return "text";
2475
2455
  case "imageMessage":
2476
2456
  return "image";
@@ -2479,7 +2459,6 @@ function getContentKind(type) {
2479
2459
  case "audioMessage":
2480
2460
  return "audio";
2481
2461
  case "documentMessage":
2482
- case "documentWithCaptionMessage":
2483
2462
  return "document";
2484
2463
  case "stickerMessage":
2485
2464
  return "sticker";
@@ -2492,6 +2471,7 @@ function getContentKind(type) {
2492
2471
  case "pollCreationMessage":
2493
2472
  case "pollCreationMessageV2":
2494
2473
  case "pollCreationMessageV3":
2474
+ case "pollCreationMessageV5":
2495
2475
  return "poll";
2496
2476
  case "productMessage":
2497
2477
  case "orderMessage":
@@ -2500,20 +2480,8 @@ function getContentKind(type) {
2500
2480
  return "unknown";
2501
2481
  }
2502
2482
  }
2503
- function normalizeKey(key) {
2504
- const normalized = {
2505
- id: key.id ?? "",
2506
- chatId: key.remoteJid ?? "",
2507
- fromMe: key.fromMe ?? false
2508
- };
2509
- const participantId = key.participant ?? key.participantAlt;
2510
- if (participantId !== null && participantId !== void 0) {
2511
- normalized.participantId = participantId;
2512
- }
2513
- return normalized;
2514
- }
2515
2483
  function getText(content) {
2516
- return content.conversation ?? content.extendedTextMessage?.text ?? content.buttonsResponseMessage?.selectedDisplayText ?? content.listResponseMessage?.title ?? void 0;
2484
+ return content.conversation ?? content.extendedTextMessage?.text ?? content.buttonsResponseMessage?.selectedDisplayText ?? content.listResponseMessage?.title ?? content.templateButtonReplyMessage?.selectedDisplayText ?? void 0;
2517
2485
  }
2518
2486
  function getCaption(content) {
2519
2487
  return content.imageMessage?.caption ?? content.videoMessage?.caption ?? content.documentMessage?.caption ?? void 0;
@@ -2522,9 +2490,9 @@ function getContextInfo(node) {
2522
2490
  if (typeof node !== "object" || node === null || !("contextInfo" in node)) {
2523
2491
  return void 0;
2524
2492
  }
2525
- return node.contextInfo;
2493
+ return node.contextInfo ?? void 0;
2526
2494
  }
2527
- function getMedia(type, node, keyViewOnce) {
2495
+ function getMedia(type, node, wrapperViewOnce) {
2528
2496
  const mapping = {
2529
2497
  imageMessage: "image",
2530
2498
  videoMessage: "video",
@@ -2539,307 +2507,112 @@ function getMedia(type, node, keyViewOnce) {
2539
2507
  const value = node;
2540
2508
  const media = {
2541
2509
  kind,
2542
- viewOnce: keyViewOnce || value.viewOnce === true
2510
+ viewOnce: wrapperViewOnce || value.viewOnce === true
2543
2511
  };
2544
2512
  if (value.mimetype) media.mimetype = value.mimetype;
2545
2513
  if (value.fileName) media.fileName = value.fileName;
2546
- if (value.seconds !== void 0 && value.seconds !== null) media.seconds = Number(value.seconds);
2514
+ const seconds = toNumber(value.seconds);
2515
+ if (seconds !== void 0) media.seconds = seconds;
2547
2516
  return media;
2548
2517
  }
2549
2518
  function getQuoted(context, chatId) {
2550
- if (!context?.stanzaId) {
2551
- return void 0;
2552
- }
2553
- const quoted = context.quotedMessage;
2554
- const quotedIsViewOnce = isViewOnceContent(quoted);
2555
- const content = unwrapMessageContent(quoted);
2556
- const type = content ? getContentType(content) : void 0;
2557
- const node = type && content ? content[type] : void 0;
2558
- const media = getMedia(type, node, quotedIsViewOnce);
2559
- const result = {
2519
+ if (!context?.stanzaId || !context.quotedMessage) return void 0;
2520
+ const content = unwrapZapoMessageContent(context.quotedMessage);
2521
+ if (!content) return void 0;
2522
+ const { type, node } = contentNode(content);
2523
+ const media = getMedia(type, node, isZapoViewOnceContent(context.quotedMessage));
2524
+ const senderId = context.participant ?? void 0;
2525
+ const quoted = {
2560
2526
  key: {
2561
2527
  id: context.stanzaId,
2562
2528
  chatId: context.remoteJid ?? chatId,
2563
- fromMe: false
2529
+ fromMe: false,
2530
+ ...senderId ? { participantId: senderId } : {}
2564
2531
  },
2565
2532
  hasMedia: media !== void 0,
2566
- isViewOnce: media?.viewOnce ?? quotedIsViewOnce
2533
+ isViewOnce: media?.viewOnce ?? false,
2534
+ contentKind: getContentKind(type)
2567
2535
  };
2568
- if (content) {
2569
- result.contentKind = getContentKind(type);
2570
- }
2571
- if (media) {
2572
- result.media = media;
2573
- }
2574
- if (context.participant) {
2575
- result.senderId = context.participant;
2576
- result.sender = User.fromIdentities([context.participant]);
2577
- result.key.participantId = context.participant;
2578
- }
2579
- if (content) {
2580
- const text = getText(content) ?? getCaption(content);
2581
- if (text !== void 0) result.text = text;
2582
- }
2583
- return result;
2536
+ const text = getText(content) ?? getCaption(content);
2537
+ if (text !== void 0) quoted.text = text;
2538
+ if (senderId !== void 0) {
2539
+ quoted.senderId = senderId;
2540
+ quoted.sender = User.fromIdentities([senderId]);
2541
+ }
2542
+ if (media !== void 0) quoted.media = media;
2543
+ return quoted;
2584
2544
  }
2585
2545
  function toDate(value) {
2586
- const seconds = value === null || value === void 0 ? Date.now() / 1e3 : Number(value);
2546
+ const seconds = toNumber(value) ?? Math.floor(Date.now() / 1e3);
2587
2547
  return new Date(seconds * 1e3);
2588
2548
  }
2589
-
2590
- // src/provider/baileys/secret-edit.ts
2591
- import { createDecipheriv, hkdfSync } from "crypto";
2592
- import {
2593
- jidNormalizedUser,
2594
- normalizeMessageContent as normalizeMessageContent2,
2595
- proto
2596
- } from "@whiskeysockets/baileys";
2597
- function isSecretEncryptedEdit(message) {
2598
- const content = normalizeMessageContent2(message.message);
2599
- return content?.secretEncryptedMessage?.secretEncType === proto.Message.SecretEncryptedMessage.SecretEncType.MESSAGE_EDIT;
2600
- }
2601
- function secretEditTargetKey(message) {
2602
- const content = normalizeMessageContent2(message.message);
2603
- const secret = content?.secretEncryptedMessage;
2604
- if (secret?.secretEncType !== proto.Message.SecretEncryptedMessage.SecretEncType.MESSAGE_EDIT || !secret.targetMessageKey?.id) {
2605
- return void 0;
2606
- }
2607
- return secret.targetMessageKey;
2608
- }
2609
- function decryptSecretEncryptedEdit(envelope, original, meId, meLid) {
2610
- const content = normalizeMessageContent2(envelope.message);
2611
- const secret = content?.secretEncryptedMessage;
2612
- const targetKey = secret?.targetMessageKey;
2613
- if (secret?.secretEncType !== proto.Message.SecretEncryptedMessage.SecretEncType.MESSAGE_EDIT || !targetKey?.id || !secret.encPayload?.length || secret.encIv?.length !== 12) {
2614
- return void 0;
2615
- }
2616
- const messageSecret = findMessageSecret(original.message);
2617
- if (messageSecret?.length !== 32) {
2618
- return void 0;
2619
- }
2620
- const editorCandidates = envelope.key.fromMe ? uniqueUserJids([meId, meLid]) : uniqueUserJids([
2621
- envelope.key.participant,
2622
- envelope.key.participantAlt,
2623
- envelope.key.remoteJid,
2624
- envelope.key.remoteJidAlt
2625
- ]);
2626
- const originalSenderCandidates = targetKey.fromMe ? editorCandidates : isUserJid(envelope.key.remoteJid) ? uniqueUserJids([
2627
- targetKey.remoteJid,
2628
- targetKey.remoteJidAlt,
2629
- original.key.remoteJid,
2630
- original.key.remoteJidAlt
2631
- ]) : uniqueUserJids([
2632
- targetKey.participant,
2633
- targetKey.participantAlt,
2634
- original.key.participant,
2635
- original.key.participantAlt
2636
- ]);
2637
- const fallbackOriginalCandidates = original.key.fromMe ? uniqueUserJids([meId, meLid]) : uniqueUserJids([
2638
- original.key.participant,
2639
- original.key.participantAlt,
2640
- original.key.remoteJid,
2641
- original.key.remoteJidAlt
2642
- ]);
2643
- const senders = uniqueUserJids([
2644
- ...originalSenderCandidates,
2645
- ...fallbackOriginalCandidates
2646
- ]);
2647
- if (!editorCandidates.length || !senders.length) {
2648
- return void 0;
2649
- }
2650
- let decoded;
2651
- for (const originalSender of senders) {
2652
- for (const editor of editorCandidates) {
2653
- try {
2654
- decoded = decryptPayload(
2655
- targetKey.id,
2656
- originalSender,
2657
- editor,
2658
- messageSecret,
2659
- secret.encIv,
2660
- secret.encPayload
2661
- );
2662
- break;
2663
- } catch {
2664
- continue;
2665
- }
2666
- }
2667
- if (decoded) {
2668
- break;
2669
- }
2670
- }
2671
- const protocol = decoded?.protocolMessage;
2672
- if (protocol?.type !== proto.Message.ProtocolMessage.Type.MESSAGE_EDIT || !protocol.editedMessage || protocol.key?.id && protocol.key.id !== targetKey.id) {
2673
- return void 0;
2674
- }
2675
- const edited = protocol.editedMessage;
2676
- if (!edited.messageContextInfo?.messageSecret?.length) {
2677
- edited.messageContextInfo = {
2678
- ...edited.messageContextInfo,
2679
- messageSecret
2680
- };
2681
- }
2682
- return {
2683
- message: edited,
2684
- ...protocol.timestampMs ? { timestamp: Math.floor(numberValue(protocol.timestampMs) / 1e3) } : {}
2685
- };
2686
- }
2687
- function decryptPayload(messageId, originalSender, editor, messageSecret, iv, payload) {
2688
- const info = Buffer.concat([
2689
- Buffer.from(messageId, "utf8"),
2690
- Buffer.from(originalSender, "utf8"),
2691
- Buffer.from(editor, "utf8"),
2692
- Buffer.from("Message Edit", "utf8")
2693
- ]);
2694
- const key = Buffer.from(hkdfSync(
2695
- "sha256",
2696
- Buffer.from(messageSecret),
2697
- Buffer.alloc(32),
2698
- info,
2699
- 32
2700
- ));
2701
- const encrypted = Buffer.from(payload);
2702
- if (encrypted.length <= 16) {
2703
- throw new Error("INVALID_EDIT_PAYLOAD");
2704
- }
2705
- const decipher = createDecipheriv("aes-256-gcm", key, Buffer.from(iv));
2706
- decipher.setAAD(Buffer.alloc(0));
2707
- decipher.setAuthTag(encrypted.subarray(encrypted.length - 16));
2708
- return proto.Message.decode(Buffer.concat([
2709
- decipher.update(encrypted.subarray(0, encrypted.length - 16)),
2710
- decipher.final()
2711
- ]));
2712
- }
2713
- function findMessageSecret(message) {
2714
- if (!message) {
2715
- return void 0;
2716
- }
2717
- const direct = message.messageContextInfo?.messageSecret;
2718
- if (direct?.length) {
2719
- return direct;
2720
- }
2721
- const normalized = normalizeMessageContent2(message);
2722
- const normalizedSecret = normalized?.messageContextInfo?.messageSecret;
2723
- if (normalizedSecret?.length) {
2724
- return normalizedSecret;
2725
- }
2726
- const deviceSecret = message.deviceSentMessage?.message?.messageContextInfo?.messageSecret;
2727
- if (deviceSecret?.length) {
2728
- return deviceSecret;
2729
- }
2549
+ function toNumber(value) {
2550
+ if (typeof value === "number") return value;
2551
+ if (value?.toNumber) return value.toNumber();
2552
+ if (typeof value?.low === "number") return value.low;
2730
2553
  return void 0;
2731
2554
  }
2732
- function uniqueUserJids(values) {
2733
- return [...new Set(
2734
- values.map(normalizeUserJid).filter((value) => value !== void 0)
2735
- )];
2736
- }
2737
- function normalizeUserJid(value) {
2738
- if (!isUserJid(value)) {
2739
- return void 0;
2740
- }
2741
- return jidNormalizedUser(value);
2742
- }
2743
- function isUserJid(value) {
2744
- if (!value) {
2745
- return false;
2746
- }
2747
- const normalized = jidNormalizedUser(value);
2748
- return normalized.endsWith("@s.whatsapp.net") || normalized.endsWith("@lid") || normalized.endsWith("@hosted") || normalized.endsWith("@hosted.lid");
2749
- }
2750
- function numberValue(value) {
2751
- if (typeof value === "number") {
2752
- return value;
2753
- }
2754
- if (typeof value === "bigint") {
2755
- return Number(value);
2756
- }
2757
- if (value && typeof value === "object" && "toString" in value) {
2758
- return Number(String(value));
2759
- }
2760
- return Number(value);
2761
- }
2762
2555
 
2763
- // src/provider/baileys/baileys-provider.ts
2764
- var BaileysProvider = class {
2556
+ // src/provider/zapo/zapo-provider.ts
2557
+ var ZapoProvider = class {
2765
2558
  #options;
2766
2559
  #events = new TypedEventEmitter();
2767
2560
  #logger;
2768
2561
  #messageStore = /* @__PURE__ */ new Map();
2769
2562
  #messageCacheSize;
2770
- #groupMetadataCache = /* @__PURE__ */ new Map();
2771
- #groupMetadataRequests = /* @__PURE__ */ new Map();
2772
- #groupMetadataGenerations = /* @__PURE__ */ new Map();
2773
- #groupMetadataCacheEnabled;
2774
- #groupMetadataCacheTtlMs;
2775
- #groupMetadataCacheSize;
2776
- #socket;
2777
- #saveCredentials;
2778
- #saveQueue = Promise.resolve();
2563
+ #client;
2564
+ #voip;
2779
2565
  #intentionalClose = false;
2780
2566
  #reconnectAttempt = 0;
2781
2567
  #reconnectTimer;
2782
- #registered = false;
2568
+ #connectPromise;
2569
+ #pairingRequired = false;
2570
+ #connected = false;
2571
+ #connectedAtSeconds = 0;
2572
+ #pairingReady = Promise.resolve();
2573
+ #resolvePairingReady;
2783
2574
  constructor(options) {
2784
2575
  this.#options = options;
2785
2576
  this.#logger = options.logger ?? new Logger("silent");
2786
2577
  this.#messageCacheSize = Math.max(1, options.messageCacheSize ?? 1e3);
2787
- this.#groupMetadataCacheEnabled = options.groupMetadataCache?.enabled !== false;
2788
- this.#groupMetadataCacheTtlMs = Math.max(1, options.groupMetadataCache?.ttlMs ?? 3e5);
2789
- this.#groupMetadataCacheSize = Math.max(1, options.groupMetadataCache?.maxEntries ?? 1e3);
2790
2578
  }
2791
2579
  on(event, listener) {
2792
2580
  return this.#events.on(event, listener);
2793
2581
  }
2794
2582
  async connect() {
2795
- if (this.#socket) {
2583
+ this.#intentionalClose = false;
2584
+ const client = await this.#ensureClient();
2585
+ if (this.#connected || this.#connectPromise) {
2796
2586
  return;
2797
2587
  }
2798
- this.#intentionalClose = false;
2588
+ this.#preparePairingGate();
2799
2589
  await this.#events.emit("connection", {
2800
2590
  state: this.#reconnectAttempt > 0 ? "reconnecting" : "connecting",
2801
2591
  attempt: this.#reconnectAttempt
2802
2592
  });
2803
- try {
2804
- const { state, saveCreds } = await useMultiFileAuthState(this.#options.auth);
2805
- this.#registered = state.creds.registered;
2806
- this.#saveCredentials = saveCreds;
2807
- const socket = makeWASocket({
2808
- auth: state,
2809
- browser: this.#browserDescription(),
2810
- logger: createBaileysLogger(this.#logger.child("baileys")),
2811
- markOnlineOnConnect: false,
2812
- enableAutoSessionRecreation: true,
2813
- enableRecentMessageCache: true,
2814
- cachedGroupMetadata: async (jid) => this.#getGroupMetadata(jid),
2815
- getMessage: async (key) => this.#messageStore.get(this.#messageStoreKey(key))?.message ?? void 0
2816
- });
2817
- this.#socket = socket;
2818
- this.#bind(socket);
2819
- } catch (error) {
2820
- this.#socket = void 0;
2821
- throw new WhaNextError("CONNECTION_FAILED", "Could not start the WhatsApp connection.", {
2822
- cause: error,
2823
- recoverable: true
2824
- });
2825
- }
2593
+ this.#startConnect(client);
2826
2594
  }
2827
2595
  async disconnect() {
2828
2596
  this.#intentionalClose = true;
2829
- if (this.#reconnectTimer) clearTimeout(this.#reconnectTimer);
2830
- const socket = this.#socket;
2831
- this.#socket = void 0;
2832
- if (socket) {
2833
- await socket.end(void 0);
2597
+ if (this.#reconnectTimer) {
2598
+ clearTimeout(this.#reconnectTimer);
2599
+ this.#reconnectTimer = void 0;
2600
+ }
2601
+ const client = this.#client;
2602
+ this.#connectPromise = void 0;
2603
+ if (client) {
2604
+ await client.disconnect();
2605
+ } else {
2606
+ await this.#events.emit("connection", { state: "closed" });
2834
2607
  }
2835
- await this.#events.emit("connection", { state: "closed" });
2608
+ this.#connected = false;
2836
2609
  }
2837
2610
  getCurrentUserIds() {
2838
- const user = this.#socket?.user;
2839
- if (!user) {
2840
- return [];
2841
- }
2842
- return [user.id, user.lid, user.phoneNumber].filter((id) => Boolean(id)).filter((id, index, ids) => ids.indexOf(id) === index);
2611
+ const credentials = this.#client?.getCredentials();
2612
+ return uniqueIdentities([
2613
+ credentials?.meJid,
2614
+ credentials?.meLid
2615
+ ]);
2843
2616
  }
2844
2617
  async requestPairingCode(phone) {
2845
2618
  const normalized = phone.replace(/\D/g, "");
@@ -2849,34 +2622,28 @@ var BaileysProvider = class {
2849
2622
  "The phone number must include its country code."
2850
2623
  );
2851
2624
  }
2852
- if (this.#registered) {
2625
+ const client = await this.#ensureClient();
2626
+ if (client.getCredentials()?.meJid) {
2853
2627
  return "";
2854
2628
  }
2855
- const socket = this.#requireSocket();
2629
+ if (!this.#connectPromise && !this.#connected) {
2630
+ await this.connect();
2631
+ }
2856
2632
  try {
2857
- await socket.waitForConnectionUpdate(async (update) => Boolean(update.qr), 6e4);
2858
- if (socket !== this.#socket) {
2859
- throw new WhaNextError(
2860
- "CONNECTION_CLOSED",
2861
- "The WhatsApp socket changed while preparing the pairing code.",
2862
- {
2863
- recoverable: true
2864
- }
2633
+ if (!this.#pairingRequired) {
2634
+ await this.#withTimeout(
2635
+ this.#pairingReady,
2636
+ 6e4,
2637
+ "The WhatsApp pairing challenge was not received in time."
2865
2638
  );
2866
2639
  }
2867
- return await socket.requestPairingCode(normalized);
2640
+ return await client.auth.requestPairingCode(normalized);
2868
2641
  } catch (error) {
2869
- if (error instanceof WhaNextError) {
2870
- throw error;
2871
- }
2642
+ if (error instanceof WhaNextError) throw error;
2872
2643
  throw new WhaNextError(
2873
2644
  "CONNECTION_FAILED",
2874
2645
  "Could not request the pairing code after the authentication challenge.",
2875
- {
2876
- cause: error,
2877
- context: { statusCode: this.#statusCode(error) },
2878
- recoverable: this.#statusCode(error) === DisconnectReason.connectionClosed
2879
- }
2646
+ { cause: error, recoverable: true }
2880
2647
  );
2881
2648
  }
2882
2649
  }
@@ -2884,18 +2651,17 @@ var BaileysProvider = class {
2884
2651
  if ("buttons" in content) {
2885
2652
  return this.#sendButtons(chatId, content, replyTo);
2886
2653
  }
2887
- const socket = this.#requireSocket();
2888
- const options = replyTo ? {
2889
- quoted: {
2890
- key: this.#toWaKey(replyTo),
2891
- message: { conversation: "" }
2892
- }
2893
- } : void 0;
2894
- const result = await socket.sendMessage(chatId, this.#toContent(content), options);
2895
- return this.#sent(result);
2654
+ const client = this.#requireClient();
2655
+ const { value, mentions, viewOnce } = await this.#toContent(content);
2656
+ const result = await client.message.send(chatId, value, {
2657
+ ...replyTo ? { quote: this.#toZapoKey(replyTo) } : {},
2658
+ ...mentions.length > 0 ? { mentions } : {},
2659
+ ...viewOnce !== void 0 ? { viewOnce } : {}
2660
+ });
2661
+ return this.#sent(result, chatId);
2896
2662
  }
2897
2663
  async repostMessage(source, chatId, options = {}) {
2898
- const original = this.#messageStore.get(this.#messageStoreKey(source));
2664
+ const original = this.#findStoredMessage(this.#toZapoKey(source));
2899
2665
  if (!original?.message) {
2900
2666
  throw new WhaNextError(
2901
2667
  "MESSAGE_NOT_FOUND",
@@ -2906,24 +2672,26 @@ var BaileysProvider = class {
2906
2672
  }
2907
2673
  );
2908
2674
  }
2909
- const content = {
2910
- forward: original,
2911
- ...options.mentions && options.mentions.length > 0 ? { mentions: this.#mentions(options.mentions) } : {}
2912
- };
2913
- const result = await this.#requireSocket().sendMessage(chatId, content);
2914
- return this.#sent(result);
2675
+ const result = await this.#requireClient().message.send(
2676
+ chatId,
2677
+ original.message,
2678
+ {
2679
+ forward: true,
2680
+ ...options.mentions && options.mentions.length > 0 ? { mentions: this.#mentions(options.mentions) } : {}
2681
+ }
2682
+ );
2683
+ return this.#sent(result, chatId);
2915
2684
  }
2916
2685
  async reactToMessage(key, emoji) {
2917
- const result = await this.#requireSocket().sendMessage(key.chatId, {
2918
- react: {
2919
- text: emoji ?? "",
2920
- key: this.#toWaKey(key)
2921
- }
2686
+ const result = await this.#requireClient().message.send(key.chatId, {
2687
+ type: "reaction",
2688
+ emoji: emoji ?? "",
2689
+ target: this.#toZapoKey(key)
2922
2690
  });
2923
- return this.#sent(result);
2691
+ return this.#sent(result, key.chatId);
2924
2692
  }
2925
2693
  async downloadMedia(key) {
2926
- const message = this.#messageStore.get(this.#messageStoreKey(key));
2694
+ const message = this.#findStoredMessage(this.#toZapoKey(key));
2927
2695
  if (!message?.message) {
2928
2696
  throw new WhaNextError(
2929
2697
  "MEDIA_NOT_AVAILABLE",
@@ -2931,54 +2699,75 @@ var BaileysProvider = class {
2931
2699
  { recoverable: true }
2932
2700
  );
2933
2701
  }
2934
- const data = await downloadMediaMessage(message, "buffer", {}, {
2935
- reuploadRequest: (current) => this.#requireSocket().updateMediaMessage(current),
2936
- logger: createBaileysLogger(this.#logger.child("media"))
2937
- });
2938
- const media = normalizeBaileysMessage(message)?.media;
2939
- if (!media) {
2940
- throw new WhaNextError("MEDIA_NOT_AVAILABLE", "The selected message does not contain media.");
2702
+ const normalized = normalizeZapoMessage(message);
2703
+ if (!normalized?.media) {
2704
+ throw new WhaNextError(
2705
+ "MEDIA_NOT_AVAILABLE",
2706
+ "The selected message does not contain media."
2707
+ );
2708
+ }
2709
+ try {
2710
+ const bytes = await this.#requireClient().message.downloadBytes(
2711
+ message
2712
+ );
2713
+ return {
2714
+ data: Buffer.from(bytes),
2715
+ kind: normalized.media.kind,
2716
+ ...normalized.media.mimetype ? { mimetype: normalized.media.mimetype } : {},
2717
+ ...normalized.media.fileName ? { fileName: normalized.media.fileName } : {}
2718
+ };
2719
+ } catch (error) {
2720
+ throw new WhaNextError(
2721
+ "MEDIA_NOT_AVAILABLE",
2722
+ "WhatsApp could not download the selected media.",
2723
+ { cause: error, recoverable: true }
2724
+ );
2941
2725
  }
2942
- return {
2943
- data,
2944
- kind: media.kind,
2945
- ...media.mimetype ? { mimetype: media.mimetype } : {},
2946
- ...media.fileName ? { fileName: media.fileName } : {}
2947
- };
2948
2726
  }
2949
2727
  async editMessage(key, content) {
2950
- const result = await this.#requireSocket().sendMessage(key.chatId, {
2951
- text: content,
2952
- edit: this.#toWaKey(key)
2953
- });
2954
- return this.#sent(result);
2728
+ const result = await this.#requireClient().message.send(
2729
+ key.chatId,
2730
+ content,
2731
+ { editKey: this.#toZapoKey(key) }
2732
+ );
2733
+ return this.#sent(result, key.chatId);
2955
2734
  }
2956
2735
  async deleteMessage(key) {
2957
- await this.#requireSocket().sendMessage(key.chatId, { delete: this.#toWaKey(key) });
2736
+ await this.#requireClient().message.send(key.chatId, {
2737
+ type: "revoke",
2738
+ target: this.#toZapoKey(key)
2739
+ });
2958
2740
  }
2959
2741
  async getGroup(groupId) {
2960
- const metadata = await this.#getGroupMetadata(groupId);
2742
+ const metadata = await this.#requireClient().group.queryGroupMetadata(groupId);
2743
+ const participants = [...metadata.participants ?? []];
2744
+ const addressingMode = metadata.addressingMode === "lid" || participants.some((participant) => (participant.jid ?? participant.id ?? "").endsWith("@lid")) ? "lid" : "pn";
2961
2745
  return {
2962
- id: metadata.id,
2963
- subject: metadata.subject,
2746
+ id: metadata.jid ?? metadata.id ?? groupId,
2747
+ subject: metadata.subject ?? "",
2964
2748
  access: metadata.announce ? "closed" : "open",
2965
- addressingMode: metadata.addressingMode === "lid" ? "lid" : "pn",
2749
+ addressingMode,
2966
2750
  fetchedAt: /* @__PURE__ */ new Date(),
2967
- participants: metadata.participants.map((participant) => ({
2968
- id: participant.id,
2969
- ...participant.lid ? { lid: participant.lid } : {},
2970
- ...participant.phoneNumber ? { phoneNumber: participant.phoneNumber } : {},
2971
- role: participant.admin === "superadmin" || participant.isSuperAdmin ? "owner" : participant.admin === "admin" || participant.isAdmin ? "admin" : "member"
2972
- }))
2751
+ participants: participants.map((participant) => {
2752
+ const id = participant.jid ?? participant.id ?? participant.lid ?? participant.phoneNumber ?? "";
2753
+ return {
2754
+ id,
2755
+ ...participant.lid ? { lid: participant.lid } : {},
2756
+ ...participant.phoneNumber ? { phoneNumber: participant.phoneNumber } : {},
2757
+ role: participant.isSuperAdmin || participant.admin === "superadmin" ? "owner" : participant.isAdmin || participant.admin === "admin" ? "admin" : "member"
2758
+ };
2759
+ })
2973
2760
  };
2974
2761
  }
2975
2762
  async setGroupAccess(groupId, access) {
2976
- const setting = access === "closed" ? "announcement" : "not_announcement";
2977
- await this.#requireSocket().groupSettingUpdate(groupId, setting);
2978
- this.#invalidateGroupMetadata(groupId);
2763
+ await this.#requireClient().group.setSetting(
2764
+ groupId,
2765
+ "announcement",
2766
+ access === "closed"
2767
+ );
2979
2768
  }
2980
2769
  async getGroupInviteCode(groupId) {
2981
- const code = await this.#requireSocket().groupInviteCode(groupId);
2770
+ const code = await this.#requireClient().group.queryInviteCode(groupId);
2982
2771
  if (!code) {
2983
2772
  throw new WhaNextError(
2984
2773
  "PROVIDER_ERROR",
@@ -2988,139 +2777,300 @@ var BaileysProvider = class {
2988
2777
  return code;
2989
2778
  }
2990
2779
  async revokeGroupInvite(groupId) {
2991
- const code = await this.#requireSocket().groupRevokeInvite(groupId);
2992
- if (!code) {
2780
+ const result = await this.#requireClient().group.revokeInvite(groupId);
2781
+ if (!result.code) {
2993
2782
  throw new WhaNextError(
2994
2783
  "PROVIDER_ERROR",
2995
2784
  "WhatsApp did not return a new group invite code."
2996
2785
  );
2997
2786
  }
2998
- return code;
2787
+ return result.code;
2999
2788
  }
3000
2789
  async setMessagePin(groupId, key, pinned) {
3001
- await this.#requireSocket().sendMessage(groupId, {
3002
- pin: this.#toWaKey(key),
3003
- type: pinned ? proto2.PinInChat.Type.PIN_FOR_ALL : proto2.PinInChat.Type.UNPIN_FOR_ALL,
3004
- ...pinned ? { time: 604800 } : {}
2790
+ await this.#requireClient().message.send(groupId, pinned ? {
2791
+ type: "pin",
2792
+ target: this.#toZapoKey(key),
2793
+ durationSecs: 604800
2794
+ } : {
2795
+ type: "unpin",
2796
+ target: this.#toZapoKey(key)
3005
2797
  });
3006
2798
  }
3007
2799
  async updateParticipant(groupId, memberId, action) {
3008
- const [result] = await this.#requireSocket().groupParticipantsUpdate(groupId, [memberId], action);
3009
- this.#invalidateGroupMetadata(groupId);
3010
- const status = result?.status ?? "unknown";
2800
+ const group = this.#requireClient().group;
2801
+ const results = action === "remove" ? await group.removeParticipants(groupId, [memberId]) : action === "promote" ? await group.promoteParticipants(groupId, [memberId]) : await group.demoteParticipants(groupId, [memberId]);
2802
+ const result = results[0];
2803
+ const success = result?.status === "ok";
2804
+ const status = success ? "200" : String(result?.code ?? result?.status ?? "unknown");
3011
2805
  return {
3012
- success: status === "200",
2806
+ success,
3013
2807
  status,
3014
2808
  ...result?.jid ? { memberId: result.jid } : {}
3015
2809
  };
3016
2810
  }
3017
2811
  async setPresence(chatId, state) {
3018
- const socket = this.#requireSocket();
3019
- const presence = state === "typing" ? "composing" : state === "recording" ? "recording" : "paused";
3020
- await socket.sendPresenceUpdate(presence, chatId);
3021
- }
3022
- async rejectCall(callId, from) {
3023
- await this.#requireSocket().rejectCall(callId, from);
2812
+ const value = state === "typing" ? "composing" : state === "recording" ? "recording" : "paused";
2813
+ const presence = this.#requireClient().presence;
2814
+ await presence.sendChatstate(chatId, { state: value });
3024
2815
  }
3025
- #bind(socket) {
3026
- socket.ev.on("creds.update", (update) => {
3027
- if (update.registered !== void 0) {
3028
- this.#registered = update.registered;
2816
+ async rejectCall(callId, _from) {
2817
+ this.#requireClient();
2818
+ if (!this.#voip) {
2819
+ throw new WhaNextError(
2820
+ "PROVIDER_ERROR",
2821
+ "WhatsApp call support is unavailable because the Zapo VoIP plugin could not be loaded.",
2822
+ { recoverable: true }
2823
+ );
2824
+ }
2825
+ await this.#voip.rejectCall(callId);
2826
+ }
2827
+ async #ensureClient() {
2828
+ if (this.#client) return this.#client;
2829
+ await mkdir(this.#options.auth, { recursive: true });
2830
+ const store = createStore({
2831
+ backends: {
2832
+ sqlite: createSqliteStore({
2833
+ path: join(this.#options.auth, "state.sqlite"),
2834
+ driver: "auto"
2835
+ })
2836
+ },
2837
+ providers: {
2838
+ auth: "sqlite",
2839
+ signal: "sqlite",
2840
+ preKey: "sqlite",
2841
+ session: "sqlite",
2842
+ identity: "sqlite",
2843
+ senderKey: "sqlite",
2844
+ appState: "sqlite",
2845
+ privacyToken: "sqlite",
2846
+ messages: "none",
2847
+ threads: "none",
2848
+ contacts: "none"
2849
+ },
2850
+ cacheProviders: {
2851
+ messageSecret: "sqlite"
3029
2852
  }
3030
- this.#saveQueue = this.#saveQueue.then(() => this.#saveCredentials?.()).then(() => void 0);
3031
2853
  });
3032
- socket.ev.on("messages.upsert", ({ messages, type }) => {
3033
- if (type !== "notify") return;
3034
- for (const raw of messages) {
3035
- if (this.#handleSecretEncryptedEdit(raw)) continue;
3036
- if (raw.key.id && raw.message) this.#remember(raw);
3037
- const quoted = extractQuotedBaileysMessage(raw);
3038
- if (quoted?.key.id && quoted.message) this.#remember(quoted);
3039
- const message = normalizeBaileysMessage(raw);
3040
- if (message) void this.#events.emit("message", message);
3041
- }
2854
+ const plugins = [];
2855
+ try {
2856
+ const { voipPlugin } = await import("@zapo-js/voip");
2857
+ plugins.push(voipPlugin({ logLevel: "warn" }));
2858
+ } catch (error) {
2859
+ this.#logger.warn("Zapo VoIP support is unavailable; call events and rejection are disabled.", {
2860
+ error: error instanceof Error ? error.message : String(error)
2861
+ });
2862
+ }
2863
+ const client = new WaClient({
2864
+ store,
2865
+ sessionId: this.#options.sessionId ?? "default",
2866
+ markOnlineOnConnect: false,
2867
+ deviceBrowser: this.#deviceBrowser(),
2868
+ deviceOsDisplayName: this.#deviceOsDisplayName(),
2869
+ history: { enabled: false },
2870
+ addons: {
2871
+ autoDecrypt: true,
2872
+ persistAllSecrets: true
2873
+ },
2874
+ media: { processor: createMediaProcessor() },
2875
+ plugins
2876
+ }, new WhaNextZapoLogger(this.#logger));
2877
+ this.#client = client;
2878
+ this.#voip = client.voip;
2879
+ this.#bind(client);
2880
+ return client;
2881
+ }
2882
+ #bind(client) {
2883
+ client.on("auth_pairing_required", () => {
2884
+ this.#markPairingReady();
3042
2885
  });
3043
- socket.ev.on("messages.update", (updates) => {
3044
- for (const { key, update } of updates) {
3045
- if (!key.id || !key.remoteJid) continue;
3046
- const stored = this.#messageStore.get(this.#messageStoreKey(key));
3047
- if (update.message === null) {
3048
- const message = stored ? normalizeBaileysMessage(stored) : void 0;
3049
- const deletionKey = update.key;
3050
- const deletedByMe = deletionKey?.fromMe === true;
3051
- const deletedById = deletionKey?.participant ?? deletionKey?.participantAlt ?? (deletedByMe ? this.#socket?.user?.id : deletionKey?.remoteJid ?? void 0);
3052
- void this.#events.emit("messageDeleted", {
3053
- key: message?.keys ?? normalizeKey(key),
3054
- ...message ? { message } : {},
3055
- deletedByMe,
3056
- ...deletedById ? { deletedById } : {},
3057
- deletedAt: /* @__PURE__ */ new Date()
3058
- });
3059
- continue;
3060
- }
3061
- if (!update.message?.editedMessage?.message) continue;
3062
- const editedRaw = {
3063
- ...stored ?? {},
3064
- key: {
3065
- ...stored?.key ?? {},
3066
- ...key
3067
- },
3068
- message: update.message,
3069
- messageTimestamp: update.messageTimestamp ?? stored?.messageTimestamp ?? Math.floor(Date.now() / 1e3)
3070
- };
3071
- this.#emitMessageEdited(key, editedRaw, stored);
3072
- }
2886
+ client.on("auth_qr", () => {
2887
+ this.#markPairingReady();
3073
2888
  });
3074
- socket.ev.on("groups.update", (groups) => {
3075
- for (const group of groups) {
3076
- if (group.id) {
3077
- this.#invalidateGroupMetadata(group.id);
3078
- void this.#events.emit("groupChanged", { groupId: group.id });
3079
- }
3080
- }
2889
+ client.on("auth_paired", () => {
2890
+ this.#pairingRequired = false;
3081
2891
  });
3082
- socket.ev.on("group-participants.update", (update) => {
3083
- this.#invalidateGroupMetadata(update.id);
3084
- const change = this.#groupParticipantsChanged(update);
3085
- void this.#events.emit("groupParticipantsChanged", change);
3086
- const { id } = update;
3087
- void this.#events.emit("groupChanged", { groupId: id });
2892
+ client.on("message", (event) => {
2893
+ this.#handleMessage(event);
3088
2894
  });
3089
- socket.ev.on("call", (calls) => {
3090
- for (const call of calls) {
3091
- void this.#events.emit("call", this.#normalizeCall(call));
3092
- }
2895
+ client.on("message_send", (event) => {
2896
+ if (!event.id || !event.message) return;
2897
+ this.#remember({
2898
+ key: {
2899
+ id: event.id,
2900
+ remoteJid: event.to,
2901
+ fromMe: true
2902
+ },
2903
+ message: event.message,
2904
+ timestampSeconds: Math.floor(Date.now() / 1e3)
2905
+ });
2906
+ });
2907
+ client.on("message_protocol", (event) => {
2908
+ this.#handleProtocolEvent(event);
2909
+ });
2910
+ client.on("group", (event) => {
2911
+ this.#handleGroupEvent(event);
2912
+ });
2913
+ if (this.#voip) {
2914
+ const voipClient = client;
2915
+ voipClient.on("voip_call_incoming", (event) => {
2916
+ const call = this.#normalizeVoipCall(event, "offer");
2917
+ if (call) void this.#events.emit("call", call);
2918
+ });
2919
+ voipClient.on("voip_call_state", (event) => {
2920
+ if (event.stateData?.state === "ended") return;
2921
+ const call = this.#normalizeVoipCall(event);
2922
+ if (call && call.status !== "offer") void this.#events.emit("call", call);
2923
+ });
2924
+ voipClient.on("voip_call_ended", (event) => {
2925
+ const call = this.#normalizeVoipCall(
2926
+ event,
2927
+ this.#callEndStatus(event.stateData?.endReason)
2928
+ );
2929
+ if (call) void this.#events.emit("call", call);
2930
+ });
2931
+ }
2932
+ client.on("connection", (event) => {
2933
+ void this.#handleConnectionEvent(client, event);
3093
2934
  });
3094
- socket.ev.on("connection.update", (update) => {
3095
- void this.#handleConnectionUpdate(socket, update.connection, update.lastDisconnect?.error);
2935
+ }
2936
+ #handleMessage(event) {
2937
+ const stored = event;
2938
+ if (this.#isOfflineMessage(stored)) {
2939
+ this.#logger.debug("Ignored message queued before the current connection.", {
2940
+ messageId: stored.key.id ?? void 0,
2941
+ chatId: stored.key.remoteJid ?? void 0,
2942
+ timestampSeconds: stored.timestampSeconds ?? void 0
2943
+ });
2944
+ return;
2945
+ }
2946
+ if (stored.key?.id && stored.message) this.#remember(stored);
2947
+ const quoted = extractQuotedZapoMessage(event);
2948
+ if (quoted) {
2949
+ const quotedStored = quoted;
2950
+ if (quotedStored.key?.id && quotedStored.message) this.#remember(quotedStored);
2951
+ }
2952
+ const message = normalizeZapoMessage(event);
2953
+ if (message) void this.#events.emit("message", message);
2954
+ }
2955
+ #handleProtocolEvent(event) {
2956
+ const protocol = event.protocolMessage ?? event.message?.protocolMessage;
2957
+ if (!protocol) return;
2958
+ const protocolKey = protocol.key;
2959
+ if (!protocolKey?.id) return;
2960
+ const remoteJid = protocolKey.remoteJid ?? event.key.remoteJid;
2961
+ const participant = protocolKey.participant ?? event.key.participant;
2962
+ const participantAlt = protocolKey.participantAlt ?? event.key.participantAlt;
2963
+ const target = {
2964
+ ...protocolKey,
2965
+ ...remoteJid !== void 0 ? { remoteJid } : {},
2966
+ ...participant !== void 0 ? { participant } : {},
2967
+ ...participantAlt !== void 0 ? { participantAlt } : {}
2968
+ };
2969
+ if (!target.remoteJid) return;
2970
+ const stored = this.#findStoredMessage(target);
2971
+ const type = protocol?.type;
2972
+ if (type === proto.Message.ProtocolMessage.Type.REVOKE) {
2973
+ const previous2 = stored ? normalizeZapoMessage(stored) : void 0;
2974
+ const deletedByMe = event.key.fromMe === true;
2975
+ const deletedById = event.key.participant ?? event.key.participantAlt ?? (deletedByMe ? this.getCurrentUserIds()[0] : event.key.remoteJid ?? void 0);
2976
+ void this.#events.emit("messageDeleted", {
2977
+ key: previous2?.keys ?? normalizeZapoKey(target),
2978
+ ...previous2 ? { message: previous2 } : {},
2979
+ deletedByMe,
2980
+ ...deletedById ? { deletedById } : {},
2981
+ deletedAt: /* @__PURE__ */ new Date()
2982
+ });
2983
+ return;
2984
+ }
2985
+ if (type !== proto.Message.ProtocolMessage.Type.MESSAGE_EDIT || !protocol.editedMessage) {
2986
+ return;
2987
+ }
2988
+ const pushName = event.pushName ?? stored?.pushName;
2989
+ const edited = {
2990
+ ...stored ?? {},
2991
+ key: {
2992
+ ...stored?.key ?? {},
2993
+ ...target
2994
+ },
2995
+ message: protocol.editedMessage,
2996
+ timestampSeconds: toSeconds(protocol.timestampMs) ?? event.timestampSeconds ?? stored?.timestampSeconds ?? Math.floor(Date.now() / 1e3),
2997
+ ...pushName !== void 0 ? { pushName } : {}
2998
+ };
2999
+ const message = normalizeZapoMessage(edited);
3000
+ if (!message) return;
3001
+ const previous = stored ? normalizeZapoMessage(stored) : void 0;
3002
+ this.#remember(edited);
3003
+ const editedByMe = event.key.fromMe === true;
3004
+ const editedById = event.key.participant ?? event.key.participantAlt ?? (editedByMe ? this.getCurrentUserIds()[0] : event.key.remoteJid ?? void 0);
3005
+ void this.#events.emit("messageEdited", {
3006
+ key: message.keys,
3007
+ ...previous ? { previous } : {},
3008
+ message,
3009
+ editedByMe,
3010
+ ...editedById ? { editedById } : {},
3011
+ editedAt: message.timestamp
3096
3012
  });
3097
3013
  }
3098
- async #handleConnectionUpdate(socket, connection, error) {
3099
- if (socket !== this.#socket || !connection) return;
3100
- if (connection === "open") {
3014
+ #handleGroupEvent(event) {
3015
+ const groupId = event.groupJid;
3016
+ if (!groupId) return;
3017
+ const action = this.#groupAction(event.action);
3018
+ const participantIds = this.#groupParticipantIds(event);
3019
+ if (action && participantIds.length > 0) {
3020
+ const authorId = event.authorJid;
3021
+ const change = {
3022
+ groupId,
3023
+ action,
3024
+ participantIds,
3025
+ ...authorId ? { authorId } : {}
3026
+ };
3027
+ void this.#events.emit("groupParticipantsChanged", change);
3028
+ }
3029
+ void this.#events.emit("groupChanged", { groupId });
3030
+ }
3031
+ async #handleConnectionEvent(client, event) {
3032
+ if (client !== this.#client) return;
3033
+ if (event.status === "open") {
3034
+ this.#connectPromise = void 0;
3035
+ this.#connected = true;
3036
+ this.#connectedAtSeconds = Math.floor(Date.now() / 1e3);
3101
3037
  this.#reconnectAttempt = 0;
3102
3038
  await this.#events.emit("connection", { state: "connected" });
3103
3039
  return;
3104
3040
  }
3105
- if (connection === "connecting") {
3041
+ this.#connectPromise = void 0;
3042
+ this.#connected = false;
3043
+ const error = event.reason instanceof Error ? event.reason : event.reason ? new Error(String(event.reason)) : void 0;
3044
+ if (this.#intentionalClose || event.isLogout) {
3106
3045
  await this.#events.emit("connection", {
3107
- state: "connecting",
3108
- attempt: this.#reconnectAttempt
3046
+ state: "closed",
3047
+ ...error ? { error } : {}
3109
3048
  });
3110
3049
  return;
3111
3050
  }
3112
- this.#socket = void 0;
3113
- if (this.#intentionalClose || this.#isTerminal(error)) {
3114
- await this.#events.emit("connection", { state: "closed", ...error ? { error } : {} });
3115
- return;
3116
- }
3117
3051
  await this.#scheduleReconnect(error);
3118
3052
  }
3053
+ #startConnect(client) {
3054
+ const promise = client.connect();
3055
+ this.#connectPromise = promise;
3056
+ void promise.catch(async (error) => {
3057
+ if (this.#connectPromise !== promise) return;
3058
+ this.#connectPromise = void 0;
3059
+ if (this.#intentionalClose) return;
3060
+ const normalized = error instanceof Error ? error : new Error(String(error));
3061
+ this.#logger.warn("WhatsApp connection attempt failed.", { error: normalized });
3062
+ await this.#scheduleReconnect(normalized);
3063
+ });
3064
+ }
3119
3065
  async #scheduleReconnect(error) {
3066
+ if (this.#reconnectTimer) return;
3120
3067
  const options = this.#options.reconnect;
3121
3068
  const maxAttempts = options?.maxAttempts ?? 10;
3122
3069
  if (options?.enabled === false || this.#reconnectAttempt >= maxAttempts) {
3123
- await this.#events.emit("connection", { state: "closed", ...error ? { error } : {} });
3070
+ await this.#events.emit("connection", {
3071
+ state: "closed",
3072
+ ...error ? { error } : {}
3073
+ });
3124
3074
  return;
3125
3075
  }
3126
3076
  this.#reconnectAttempt += 1;
@@ -3132,184 +3082,120 @@ var BaileysProvider = class {
3132
3082
  const initial = options?.initialDelayMs ?? 1e3;
3133
3083
  const maximum = options?.maxDelayMs ?? 3e4;
3134
3084
  const delay = Math.min(maximum, initial * 2 ** (this.#reconnectAttempt - 1));
3135
- this.#reconnectTimer = setTimeout(
3136
- () => void this.connect(),
3137
- delay + Math.floor(Math.random() * 250)
3138
- );
3139
- }
3140
- #browserDescription() {
3141
- if (this.#options.browser === "macos" /* MacOS */) return Browsers.macOS("Safari");
3142
- if (this.#options.browser === "ubuntu" /* Ubuntu */) return Browsers.ubuntu("Chrome");
3143
- return Browsers.windows("Brave");
3144
- }
3145
- #isTerminal(error) {
3146
- const statusCode = this.#statusCode(error);
3147
- return statusCode === DisconnectReason.loggedOut || statusCode === DisconnectReason.badSession || statusCode === DisconnectReason.connectionReplaced;
3148
- }
3149
- #statusCode(error) {
3150
- return error?.output?.statusCode;
3151
- }
3152
- #requireSocket() {
3153
- if (!this.#socket) {
3154
- throw new WhaNextError(
3155
- "CONNECTION_CLOSED",
3156
- "WhatsApp is not connected.",
3157
- { recoverable: true }
3158
- );
3159
- }
3160
- return this.#socket;
3085
+ this.#reconnectTimer = setTimeout(() => {
3086
+ this.#reconnectTimer = void 0;
3087
+ void this.connect();
3088
+ }, delay + Math.floor(Math.random() * 250));
3161
3089
  }
3162
3090
  async #sendButtons(chatId, content, replyTo) {
3163
- const socket = this.#requireSocket();
3164
- const userJid = socket.user?.id;
3165
- if (!userJid) {
3166
- throw new WhaNextError(
3167
- "PROVIDER_ERROR",
3168
- "WhatsApp did not expose the current account identity for the interactive message."
3169
- );
3170
- }
3171
- const interactiveMessage = proto2.Message.InteractiveMessage.create({
3172
- ...content.title !== void 0 ? {
3173
- header: {
3174
- title: content.title,
3175
- hasMediaAttachment: false
3176
- }
3177
- } : {},
3178
- body: { text: content.text },
3179
- ...content.footer !== void 0 ? { footer: { text: content.footer } } : {},
3180
- ...content.mentions && content.mentions.length > 0 ? {
3181
- contextInfo: {
3182
- mentionedJid: this.#mentions(content.mentions)
3183
- }
3184
- } : {},
3185
- nativeFlowMessage: {
3186
- buttons: content.buttons.map((button) => {
3187
- if (button.type === "copy") {
3188
- return {
3189
- name: "cta_copy",
3190
- buttonParamsJson: JSON.stringify({
3191
- display_text: button.label,
3192
- copy_code: button.code
3193
- })
3194
- };
3091
+ const mentions = content.mentions ? this.#mentions(content.mentions) : [];
3092
+ const raw = {
3093
+ interactiveMessage: {
3094
+ ...content.title !== void 0 ? {
3095
+ header: {
3096
+ title: content.title,
3097
+ hasMediaAttachment: false
3195
3098
  }
3196
- return {
3099
+ } : {},
3100
+ body: { text: content.text },
3101
+ ...content.footer !== void 0 ? { footer: { text: content.footer } } : {},
3102
+ ...mentions.length > 0 ? { contextInfo: { mentionedJid: mentions } } : {},
3103
+ nativeFlowMessage: {
3104
+ buttons: content.buttons.map((button) => button.type === "copy" ? {
3105
+ name: "cta_copy",
3106
+ buttonParamsJson: JSON.stringify({
3107
+ display_text: button.label,
3108
+ copy_code: button.code
3109
+ })
3110
+ } : {
3197
3111
  name: "cta_url",
3198
3112
  buttonParamsJson: JSON.stringify({
3199
3113
  display_text: button.label,
3200
3114
  url: button.url,
3201
3115
  merchant_url: button.url
3202
3116
  })
3203
- };
3204
- }),
3205
- messageParamsJson: "{}",
3206
- messageVersion: 1
3207
- }
3208
- });
3209
- const quoted = replyTo ? {
3210
- key: this.#toWaKey(replyTo),
3211
- message: { conversation: "" }
3212
- } : void 0;
3213
- const generated = generateWAMessageFromContent(
3214
- chatId,
3215
- { interactiveMessage },
3216
- {
3217
- userJid,
3218
- ...quoted ? { quoted } : {}
3219
- }
3220
- );
3221
- const messageId = generated.key.id;
3222
- if (!generated.message || !messageId) {
3223
- throw new WhaNextError(
3224
- "PROVIDER_ERROR",
3225
- "WhatsApp could not generate the interactive message."
3226
- );
3227
- }
3228
- await socket.relayMessage(chatId, generated.message, {
3229
- messageId,
3230
- additionalNodes: this.#interactiveRelayNodes(chatId)
3231
- });
3232
- return this.#sent(generated);
3233
- }
3234
- #interactiveRelayNodes(chatId) {
3235
- const bizNode = {
3236
- tag: "biz",
3237
- attrs: {
3238
- actual_actors: "2",
3239
- host_storage: "2",
3240
- privacy_mode_ts: (Math.floor(Date.now() / 1e3) - 77980457).toString()
3241
- },
3242
- content: [
3243
- {
3244
- tag: "interactive",
3245
- attrs: { type: "native_flow", v: "1" },
3246
- content: [
3247
- {
3248
- tag: "native_flow",
3249
- attrs: { v: "9", name: "mixed" }
3250
- }
3251
- ]
3252
- },
3253
- {
3254
- tag: "quality_control",
3255
- attrs: { source_type: "third_party" }
3117
+ }),
3118
+ messageParamsJson: "{}",
3119
+ messageVersion: 1
3256
3120
  }
3257
- ]
3121
+ }
3258
3122
  };
3259
- if (isJidGroup(chatId)) {
3260
- return [bizNode];
3261
- }
3262
- return [
3263
- { tag: "bot", attrs: { biz_bot: "1" } },
3264
- bizNode
3265
- ];
3123
+ const result = await this.#requireClient().message.send(chatId, raw, {
3124
+ ...replyTo ? { quote: this.#toZapoKey(replyTo) } : {},
3125
+ ...mentions.length > 0 ? { mentions } : {}
3126
+ });
3127
+ return this.#sent(result, chatId);
3266
3128
  }
3267
- #toContent(content) {
3129
+ async #toContent(content) {
3268
3130
  if ("text" in content) {
3269
3131
  return {
3270
- text: content.text,
3271
- ...content.mentions ? { mentions: this.#mentions(content.mentions) } : {}
3132
+ value: { type: "text", text: content.text },
3133
+ mentions: content.mentions ? this.#mentions(content.mentions) : []
3272
3134
  };
3273
3135
  }
3274
3136
  if ("image" in content) {
3275
3137
  return {
3276
- image: this.#media(content.image),
3277
- ...content.caption !== void 0 ? { caption: content.caption } : {},
3278
- ...content.mentions ? { mentions: this.#mentions(content.mentions) } : {},
3138
+ value: {
3139
+ type: "image",
3140
+ media: await this.#media(content.image),
3141
+ ...content.caption !== void 0 ? { caption: content.caption } : {}
3142
+ },
3143
+ mentions: content.mentions ? this.#mentions(content.mentions) : [],
3279
3144
  ...content.viewOnce !== void 0 ? { viewOnce: content.viewOnce } : {}
3280
3145
  };
3281
3146
  }
3282
3147
  if ("video" in content) {
3283
3148
  return {
3284
- video: this.#media(content.video),
3285
- ...content.caption !== void 0 ? { caption: content.caption } : {},
3286
- ...content.mentions ? { mentions: this.#mentions(content.mentions) } : {},
3287
- ...content.viewOnce !== void 0 ? { viewOnce: content.viewOnce } : {},
3288
- ...content.gif !== void 0 ? { gifPlayback: content.gif } : {}
3149
+ value: {
3150
+ type: "video",
3151
+ media: await this.#media(content.video),
3152
+ ...content.caption !== void 0 ? { caption: content.caption } : {},
3153
+ ...content.gif !== void 0 ? { gifPlayback: content.gif } : {}
3154
+ },
3155
+ mentions: content.mentions ? this.#mentions(content.mentions) : [],
3156
+ ...content.viewOnce !== void 0 ? { viewOnce: content.viewOnce } : {}
3289
3157
  };
3290
3158
  }
3291
3159
  if ("sticker" in content) {
3292
- return { sticker: this.#media(content.sticker) };
3160
+ return {
3161
+ value: {
3162
+ type: "sticker",
3163
+ media: await this.#media(content.sticker),
3164
+ mimetype: "image/webp"
3165
+ },
3166
+ mentions: []
3167
+ };
3293
3168
  }
3294
3169
  return {
3295
- audio: this.#media(content.audio),
3296
- ...content.mimetype ? { mimetype: content.mimetype } : {},
3297
- ...content.voice !== void 0 ? { ptt: content.voice } : {}
3170
+ value: {
3171
+ type: "audio",
3172
+ media: await this.#media(content.audio),
3173
+ ...content.mimetype ? { mimetype: content.mimetype } : {},
3174
+ ...content.voice !== void 0 ? { ptt: content.voice } : {}
3175
+ },
3176
+ mentions: []
3298
3177
  };
3299
3178
  }
3300
- #media(source) {
3301
- if (source instanceof Uint8Array) {
3302
- return Buffer.from(source);
3303
- }
3304
- if ("url" in source) {
3305
- return { url: source.url };
3179
+ async #media(source) {
3180
+ if (source instanceof Uint8Array) return source;
3181
+ if ("path" in source) return source.path;
3182
+ const response = await fetch(source.url);
3183
+ if (!response.ok) {
3184
+ throw new WhaNextError(
3185
+ "PROVIDER_ERROR",
3186
+ "Could not download the remote media source.",
3187
+ {
3188
+ context: { status: response.status },
3189
+ recoverable: response.status >= 500
3190
+ }
3191
+ );
3306
3192
  }
3307
- return { url: source.path };
3193
+ return new Uint8Array(await response.arrayBuffer());
3308
3194
  }
3309
3195
  #mentions(mentions) {
3310
3196
  return mentions.map((mention) => typeof mention === "string" ? mention : mention.mentionId);
3311
3197
  }
3312
- #toWaKey(key) {
3198
+ #toZapoKey(key) {
3313
3199
  return {
3314
3200
  id: key.id,
3315
3201
  remoteJid: key.chatId,
@@ -3317,101 +3203,78 @@ var BaileysProvider = class {
3317
3203
  ...key.participantId ? { participant: key.participantId } : {}
3318
3204
  };
3319
3205
  }
3320
- #sent(message) {
3321
- if (!message?.key.id || !message.key.remoteJid) {
3322
- throw new WhaNextError("PROVIDER_ERROR", "WhatsApp did not confirm the sent message.");
3206
+ #sent(result, chatId) {
3207
+ if (!result.id) {
3208
+ throw new WhaNextError(
3209
+ "PROVIDER_ERROR",
3210
+ "WhatsApp did not confirm the sent message."
3211
+ );
3323
3212
  }
3324
- if (message.message) this.#remember(message);
3325
3213
  return {
3326
- id: message.key.id,
3327
- chatId: message.key.remoteJid,
3328
- keys: normalizeKey(message.key),
3214
+ id: result.id,
3215
+ chatId,
3216
+ keys: {
3217
+ id: result.id,
3218
+ chatId,
3219
+ fromMe: true
3220
+ },
3329
3221
  timestamp: /* @__PURE__ */ new Date()
3330
3222
  };
3331
3223
  }
3332
- #normalizeCall(call) {
3224
+ #normalizeVoipCall(event, forcedStatus) {
3225
+ const id = event.callId;
3226
+ const from = event.callerPn ?? event.peerJid ?? event.callCreator;
3227
+ const chatId = event.groupJid ?? event.peerJid ?? from;
3228
+ if (!id || !from || !chatId) return void 0;
3333
3229
  return {
3334
- id: call.id,
3335
- chatId: call.chatId,
3336
- from: call.from,
3337
- status: this.#callStatus(call.status),
3338
- isVideo: Boolean(call.isVideo),
3339
- isGroup: Boolean(call.isGroup),
3340
- date: call.date ?? /* @__PURE__ */ new Date()
3341
- };
3342
- }
3343
- #groupParticipantsChanged(change) {
3344
- const participantIds = change.participants.map((participant) => participant.id).filter((id) => Boolean(id));
3345
- return {
3346
- groupId: change.id,
3347
- action: change.action,
3348
- participantIds,
3349
- ...change.author ? { authorId: change.author } : {}
3230
+ id,
3231
+ chatId,
3232
+ from,
3233
+ status: forcedStatus ?? this.#callStatus(event.stateData?.state),
3234
+ isVideo: event.mediaType === "video",
3235
+ isGroup: Boolean(event.groupJid),
3236
+ date: event.createdAt ?? /* @__PURE__ */ new Date()
3350
3237
  };
3351
3238
  }
3352
3239
  #callStatus(status) {
3353
- const known = [
3354
- "offer",
3355
- "ringing",
3356
- "preaccept",
3357
- "timeout",
3358
- "reject",
3359
- "accept"
3360
- ];
3361
- return known.find((value) => value === status) ?? "timeout";
3362
- }
3363
- #handleSecretEncryptedEdit(raw) {
3364
- if (!isSecretEncryptedEdit(raw)) return false;
3365
- const targetKey = secretEditTargetKey(raw);
3366
- if (!targetKey?.id) return true;
3367
- const stored = this.#findStoredMessage(targetKey);
3368
- if (!stored) return true;
3369
- try {
3370
- const decrypted = decryptSecretEncryptedEdit(
3371
- raw,
3372
- stored,
3373
- this.#socket?.user?.id,
3374
- this.#socket?.user?.lid
3375
- );
3376
- if (!decrypted) return true;
3377
- const key = {
3378
- ...stored.key,
3379
- id: targetKey.id
3380
- };
3381
- const editedRaw = {
3382
- ...stored,
3383
- key,
3384
- message: {
3385
- editedMessage: {
3386
- message: decrypted.message
3387
- }
3388
- },
3389
- messageTimestamp: decrypted.timestamp ?? raw.messageTimestamp ?? stored.messageTimestamp ?? Math.floor(Date.now() / 1e3)
3390
- };
3391
- this.#emitMessageEdited(key, editedRaw, stored);
3392
- } catch (error) {
3393
- this.#logger.warn("Could not decrypt the encrypted message edit.", {
3394
- messageId: targetKey.id,
3395
- error: error instanceof Error ? error.message : String(error)
3396
- });
3397
- }
3398
- return true;
3240
+ switch (status?.toLowerCase()) {
3241
+ case "offer":
3242
+ case "initiating":
3243
+ return "offer";
3244
+ case "ringing":
3245
+ case "incoming_ringing":
3246
+ return "ringing";
3247
+ case "preaccept":
3248
+ case "connecting":
3249
+ return "preaccept";
3250
+ case "accept":
3251
+ case "accepted":
3252
+ case "active":
3253
+ return "accept";
3254
+ case "reject":
3255
+ case "rejected":
3256
+ case "terminate":
3257
+ case "terminated":
3258
+ case "ended":
3259
+ return "reject";
3260
+ default:
3261
+ return "timeout";
3262
+ }
3263
+ }
3264
+ #callEndStatus(reason) {
3265
+ return reason?.toLowerCase() === "timeout" ? "timeout" : "reject";
3266
+ }
3267
+ #groupAction(action) {
3268
+ const value = action?.toLowerCase() ?? "";
3269
+ if (value.includes("promote")) return "promote";
3270
+ if (value.includes("demote")) return "demote";
3271
+ if (value.includes("remove") || value.includes("leave")) return "remove";
3272
+ if (value.includes("add") || value.includes("join")) return "add";
3273
+ if (value.includes("participant") || value.includes("modify")) return "modify";
3274
+ return void 0;
3399
3275
  }
3400
- #emitMessageEdited(key, editedRaw, stored) {
3401
- const previous = stored ? normalizeBaileysMessage(stored) : void 0;
3402
- const message = normalizeBaileysMessage(editedRaw);
3403
- if (!message) return;
3404
- this.#remember(editedRaw);
3405
- const editedByMe = key.fromMe === true;
3406
- const editedById = key.participant ?? key.participantAlt ?? (editedByMe ? this.#socket?.user?.id : key.remoteJid ?? void 0);
3407
- void this.#events.emit("messageEdited", {
3408
- key: message.keys,
3409
- ...previous ? { previous } : {},
3410
- message,
3411
- editedByMe,
3412
- ...editedById ? { editedById } : {},
3413
- editedAt: message.timestamp
3414
- });
3276
+ #groupParticipantIds(event) {
3277
+ return uniqueIdentities((event.participants ?? []).map((participant) => participant.jid ?? participant.lidJid ?? participant.phoneJid));
3415
3278
  }
3416
3279
  #findStoredMessage(key) {
3417
3280
  const direct = this.#messageStore.get(this.#messageStoreKey(key));
@@ -3431,70 +3294,117 @@ var BaileysProvider = class {
3431
3294
  if (oldest) this.#messageStore.delete(oldest);
3432
3295
  }
3433
3296
  }
3434
- async #getGroupMetadata(groupId) {
3435
- if (this.#groupMetadataCacheEnabled) {
3436
- const cached = this.#groupMetadataCache.get(groupId);
3437
- if (cached && cached.expiresAt > Date.now()) {
3438
- this.#groupMetadataCache.delete(groupId);
3439
- this.#groupMetadataCache.set(groupId, cached);
3440
- return cached.value;
3441
- }
3442
- if (cached) this.#groupMetadataCache.delete(groupId);
3443
- }
3444
- const generation = this.#groupMetadataGenerations.get(groupId) ?? 0;
3445
- const pending = this.#groupMetadataRequests.get(groupId);
3446
- if (pending?.generation === generation) return pending.promise;
3447
- const request = this.#requireSocket().groupMetadata(groupId);
3448
- const requestEntry = { generation, promise: request };
3449
- this.#groupMetadataRequests.set(groupId, requestEntry);
3297
+ #messageStoreKey(key) {
3298
+ return `${key.remoteJid ?? ""}:${key.id ?? ""}:${key.participant ?? key.participantAlt ?? ""}`;
3299
+ }
3300
+ #isOfflineMessage(message) {
3301
+ if (this.#options.processOfflineMessages === true) return false;
3302
+ if (message.offline === true) return true;
3303
+ if (!this.#connectedAtSeconds || message.timestampSeconds == null) return false;
3304
+ const timestamp = toSeconds(message.timestampSeconds);
3305
+ if (timestamp === void 0) return false;
3306
+ return timestamp < this.#connectedAtSeconds - 3;
3307
+ }
3308
+ #markPairingReady() {
3309
+ this.#pairingRequired = true;
3310
+ this.#resolvePairingReady?.();
3311
+ this.#resolvePairingReady = void 0;
3312
+ }
3313
+ #preparePairingGate() {
3314
+ this.#pairingRequired = false;
3315
+ this.#pairingReady = new Promise((resolve3) => {
3316
+ this.#resolvePairingReady = resolve3;
3317
+ });
3318
+ }
3319
+ #deviceBrowser() {
3320
+ return this.#options.browser === "macos" /* MacOS */ ? "safari" : "chrome";
3321
+ }
3322
+ #deviceOsDisplayName() {
3323
+ if (this.#options.browser === "macos" /* MacOS */) return "macOS";
3324
+ if (this.#options.browser === "ubuntu" /* Ubuntu */) return "Ubuntu";
3325
+ return "Windows";
3326
+ }
3327
+ #requireClient() {
3328
+ const client = this.#client;
3329
+ if (!client || !this.#connected) {
3330
+ throw new WhaNextError(
3331
+ "CONNECTION_CLOSED",
3332
+ "WhatsApp is not connected.",
3333
+ { recoverable: true }
3334
+ );
3335
+ }
3336
+ return client;
3337
+ }
3338
+ async #withTimeout(promise, timeoutMs, message) {
3339
+ let timer;
3450
3340
  try {
3451
- const metadata = await request;
3452
- if ((this.#groupMetadataGenerations.get(groupId) ?? 0) === generation) {
3453
- this.#rememberGroupMetadata(groupId, metadata);
3454
- }
3455
- return metadata;
3341
+ return await Promise.race([
3342
+ promise,
3343
+ new Promise((_, reject) => {
3344
+ timer = setTimeout(() => {
3345
+ reject(new WhaNextError(
3346
+ "CONNECTION_FAILED",
3347
+ message,
3348
+ { recoverable: true }
3349
+ ));
3350
+ }, timeoutMs);
3351
+ })
3352
+ ]);
3456
3353
  } finally {
3457
- if (this.#groupMetadataRequests.get(groupId) === requestEntry) {
3458
- this.#groupMetadataRequests.delete(groupId);
3459
- }
3354
+ if (timer) clearTimeout(timer);
3460
3355
  }
3461
3356
  }
3462
- #rememberGroupMetadata(groupId, metadata) {
3463
- if (!this.#groupMetadataCacheEnabled) return;
3464
- this.#groupMetadataCache.delete(groupId);
3465
- this.#groupMetadataCache.set(groupId, {
3466
- value: metadata,
3467
- expiresAt: Date.now() + this.#groupMetadataCacheTtlMs
3468
- });
3469
- while (this.#groupMetadataCache.size > this.#groupMetadataCacheSize) {
3470
- const oldest = this.#groupMetadataCache.keys().next().value;
3471
- if (oldest === void 0) return;
3472
- this.#groupMetadataCache.delete(oldest);
3473
- }
3357
+ };
3358
+ var WhaNextZapoLogger = class _WhaNextZapoLogger {
3359
+ level;
3360
+ #logger;
3361
+ #context;
3362
+ constructor(logger, context = {}) {
3363
+ this.#logger = logger;
3364
+ this.#context = context;
3365
+ this.level = logger.level === "debug" ? "debug" : logger.level === "warn" ? "warn" : logger.level === "error" || logger.level === "silent" ? "error" : "info";
3474
3366
  }
3475
- #invalidateGroupMetadata(groupId) {
3476
- this.#groupMetadataCache.delete(groupId);
3477
- const generation = this.#groupMetadataGenerations.get(groupId) ?? 0;
3478
- this.#groupMetadataGenerations.set(groupId, generation + 1);
3367
+ trace(message, context) {
3368
+ this.#logger.debug(message, this.#merge(context));
3479
3369
  }
3480
- #messageStoreKey(key) {
3481
- const chatId = "chatId" in key ? key.chatId : key.remoteJid;
3482
- return `${chatId ?? ""}:${key.id ?? ""}`;
3370
+ debug(message, context) {
3371
+ this.#logger.debug(message, this.#merge(context));
3372
+ }
3373
+ info(message, context) {
3374
+ this.#logger.info(message, this.#merge(context));
3375
+ }
3376
+ warn(message, context) {
3377
+ this.#logger.warn(message, this.#merge(context));
3378
+ }
3379
+ error(message, context) {
3380
+ this.#logger.error(message, this.#merge(context));
3381
+ }
3382
+ child(bindings) {
3383
+ return new _WhaNextZapoLogger(this.#logger, {
3384
+ ...this.#context,
3385
+ ...bindings
3386
+ });
3387
+ }
3388
+ #merge(context) {
3389
+ return context ? { ...this.#context, ...context } : this.#context;
3483
3390
  }
3484
3391
  };
3392
+ function toSeconds(value) {
3393
+ const raw = typeof value === "number" ? value : value?.toNumber ? value.toNumber() : value?.low;
3394
+ if (raw === void 0) return void 0;
3395
+ return raw > 1e10 ? Math.floor(raw / 1e3) : raw;
3396
+ }
3485
3397
 
3486
3398
  // src/app/create.ts
3487
3399
  async function create(options = {}) {
3488
3400
  const logger = new Logger(options.logger);
3489
- const provider = options.provider ?? new BaileysProvider({
3401
+ const provider = options.provider ?? new ZapoProvider({
3490
3402
  auth: options.auth ?? "./session",
3491
3403
  browser: options.browser ?? "windows" /* Windows */,
3492
3404
  logger: logger.child("provider"),
3405
+ ...options.accountId ? { sessionId: options.accountId } : {},
3493
3406
  ...options.messageCacheSize !== void 0 ? { messageCacheSize: options.messageCacheSize } : {},
3494
- groupMetadataCache: {
3495
- ...options.cache?.groupTtlMs !== void 0 ? { ttlMs: options.cache.groupTtlMs } : {},
3496
- ...options.cache?.memoryMaxEntries !== void 0 ? { maxEntries: options.cache.memoryMaxEntries } : {}
3497
- },
3407
+ ...options.processOfflineMessages !== void 0 ? { processOfflineMessages: options.processOfflineMessages } : {},
3498
3408
  ...options.reconnect ? { reconnect: options.reconnect } : {}
3499
3409
  });
3500
3410
  return new WhaNextApp(provider, {
@@ -3510,7 +3420,7 @@ async function create(options = {}) {
3510
3420
 
3511
3421
  // src/app/multi-app.ts
3512
3422
  import {
3513
- join,
3423
+ join as join2,
3514
3424
  resolve as resolve2
3515
3425
  } from "path";
3516
3426
  var MultiCommandRouter = class {
@@ -3656,7 +3566,7 @@ async function createMulti(options) {
3656
3566
  }
3657
3567
  normalizedIds.add(normalizedId);
3658
3568
  if (account.provider === void 0) {
3659
- const authPath = resolve2(account.auth ?? join(authRoot, account.id));
3569
+ const authPath = resolve2(account.auth ?? join2(authRoot, account.id));
3660
3570
  if (authPaths.has(authPath)) {
3661
3571
  throw new WhaNextError(
3662
3572
  "ARGUMENT_INVALID",
@@ -3677,13 +3587,13 @@ async function createMulti(options) {
3677
3587
  if (merged.mute?.enabled === true && merged.mute.store === void 0 && merged.mute.database === void 0) {
3678
3588
  merged.mute = {
3679
3589
  ...merged.mute,
3680
- database: join("./data", `whanext-${id}.sqlite`)
3590
+ database: join2("./data", `whanext-${id}.sqlite`)
3681
3591
  };
3682
3592
  }
3683
3593
  const app = await create({
3684
3594
  ...merged,
3685
3595
  accountId: id,
3686
- auth: overrides.auth ?? join(authRoot, id)
3596
+ auth: overrides.auth ?? join2(authRoot, id)
3687
3597
  });
3688
3598
  apps.set(id, app);
3689
3599
  }