@whanext/core 0.19.2 → 0.19.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.19.3
4
+ ### Corrigido
5
+ - Carregamento lazy do plugin VoIP do Zapo para evitar importar o runtime WebRTC em consumidores e testes que não inicializam o provider.
6
+ - Ajustes de tipagem dos testes com `exactOptionalPropertyTypes`.
7
+ - Provider Zapo agora usa `@zapo-js/voip` como único caminho de chamadas: eventos `voip_*` e `client.voip.rejectCall()`, sem fallback silencioso.
8
+ - `messageEdited` também reconhece edições criptografadas descriptografadas pelo Zapo em `message_addon`, além de `message_protocol`.
9
+ - `messageDeleted` e `messageEdited` ignoram protocolo de backlog durante bootstrap quando `processOfflineMessages` está desativado.
10
+ - Mensagens recebidas antes de `connection: open` deixam de ser publicadas como mensagens ao vivo; ainda entram no cache limitado para permitir recuperação posterior.
11
+ - Eventos `message` duplicados no mesmo runtime são descartados antes de chegar aos consumidores, evitando downloads repetidos de mídia/ViewOnce.
12
+
13
+ ### Compatibilidade
14
+ - Mudança corretiva e aditiva; a API pública de `CallEvent`, `messageDeleted` e `messageEdited` permanece inalterada.
15
+
3
16
  ## 0.19.2
4
17
 
5
18
  - Corrigido o download real de mídias citadas no provider Zapo: `downloadMedia()` agora entrega o `Proto.IMessage` bruto diretamente ao `downloadBytes()`, como suportado pela API do Zapo.
package/dist/index.js CHANGED
@@ -2708,9 +2708,10 @@ var ZapoProvider = class {
2708
2708
  #logger;
2709
2709
  #messageStore = /* @__PURE__ */ new Map();
2710
2710
  #messageKeyStore = /* @__PURE__ */ new WeakMap();
2711
+ #deliveredMessageStore = /* @__PURE__ */ new Set();
2712
+ #handledProtocolStore = /* @__PURE__ */ new Set();
2711
2713
  #messageCacheSize;
2712
2714
  #client;
2713
- #voip;
2714
2715
  #intentionalClose = false;
2715
2716
  #reconnectAttempt = 0;
2716
2717
  #reconnectTimer;
@@ -2965,15 +2966,8 @@ var ZapoProvider = class {
2965
2966
  await presence.sendChatstate(chatId, { state: value });
2966
2967
  }
2967
2968
  async rejectCall(callId, _from) {
2968
- this.#requireClient();
2969
- if (!this.#voip) {
2970
- throw new WhaNextError(
2971
- "PROVIDER_ERROR",
2972
- "WhatsApp call support is unavailable because the Zapo VoIP plugin could not be loaded.",
2973
- { recoverable: true }
2974
- );
2975
- }
2976
- await this.#voip.rejectCall(callId);
2969
+ const client = this.#requireClient();
2970
+ await client.voip.rejectCall(callId);
2977
2971
  }
2978
2972
  async #ensureClient() {
2979
2973
  if (this.#client) return this.#client;
@@ -3002,15 +2996,8 @@ var ZapoProvider = class {
3002
2996
  messageSecret: "sqlite"
3003
2997
  }
3004
2998
  });
3005
- const plugins = [];
3006
- try {
3007
- const { voipPlugin } = await import("@zapo-js/voip");
3008
- plugins.push(voipPlugin({ logLevel: "warn" }));
3009
- } catch (error) {
3010
- this.#logger.warn("Zapo VoIP support is unavailable; call events and rejection are disabled.", {
3011
- error: error instanceof Error ? error.message : String(error)
3012
- });
3013
- }
2999
+ const { voipPlugin } = await import("@zapo-js/voip");
3000
+ const voip = voipPlugin({ logLevel: "warn" });
3014
3001
  const client = new WaClient({
3015
3002
  store,
3016
3003
  sessionId: this.#options.sessionId ?? "default",
@@ -3023,10 +3010,9 @@ var ZapoProvider = class {
3023
3010
  persistAllSecrets: true
3024
3011
  },
3025
3012
  media: { processor: createMediaProcessor() },
3026
- plugins
3013
+ plugins: [voip]
3027
3014
  }, new WhaNextZapoLogger(this.#logger));
3028
3015
  this.#client = client;
3029
- this.#voip = client.voip;
3030
3016
  this.#bind(client);
3031
3017
  return client;
3032
3018
  }
@@ -3058,49 +3044,60 @@ var ZapoProvider = class {
3058
3044
  client.on("message_protocol", (event) => {
3059
3045
  this.#handleProtocolEvent(event);
3060
3046
  });
3047
+ client.on("message_addon", (event) => {
3048
+ this.#handleAddonEvent(event);
3049
+ });
3061
3050
  client.on("group", (event) => {
3062
3051
  this.#handleGroupEvent(event);
3063
3052
  });
3064
- if (this.#voip) {
3065
- const voipClient = client;
3066
- voipClient.on("voip_call_incoming", (event) => {
3067
- const call = this.#normalizeVoipCall(event, "offer");
3068
- if (call) void this.#events.emit("call", call);
3069
- });
3070
- voipClient.on("voip_call_state", (event) => {
3071
- if (event.stateData?.state === "ended") return;
3072
- const call = this.#normalizeVoipCall(event);
3073
- if (call && call.status !== "offer") void this.#events.emit("call", call);
3074
- });
3075
- voipClient.on("voip_call_ended", (event) => {
3076
- const call = this.#normalizeVoipCall(
3077
- event,
3078
- this.#callEndStatus(event.stateData?.endReason)
3079
- );
3080
- if (call) void this.#events.emit("call", call);
3081
- });
3082
- }
3053
+ const voipClient = client;
3054
+ voipClient.on("voip_call_incoming", (event) => {
3055
+ const call = this.#normalizeVoipCall(event, "offer");
3056
+ if (call) void this.#events.emit("call", call);
3057
+ });
3058
+ voipClient.on("voip_call_state", (event) => {
3059
+ const state = event.stateData?.state;
3060
+ if (state === "incoming_ringing" || state === "ended") return;
3061
+ const call = this.#normalizeVoipCall(event);
3062
+ if (call) void this.#events.emit("call", call);
3063
+ });
3064
+ voipClient.on("voip_call_ended", (event) => {
3065
+ const call = this.#normalizeVoipCall(
3066
+ event,
3067
+ this.#callEndStatus(event.stateData?.endReason)
3068
+ );
3069
+ if (call) void this.#events.emit("call", call);
3070
+ });
3083
3071
  client.on("connection", (event) => {
3084
3072
  void this.#handleConnectionEvent(client, event);
3085
3073
  });
3086
3074
  }
3087
3075
  #handleMessage(event) {
3088
3076
  const stored = event;
3077
+ if (stored.key?.id && stored.message) this.#remember(stored);
3078
+ const quoted = extractQuotedZapoMessage(event);
3079
+ if (quoted?.key.id && quoted.message) {
3080
+ this.#remember(quoted);
3081
+ }
3089
3082
  if (this.#isOfflineMessage(stored)) {
3090
- this.#logger.debug("Ignored message queued before the current connection.", {
3083
+ this.#logger.debug("Ignored message queued before the current live connection.", {
3091
3084
  messageId: stored.key.id ?? void 0,
3092
3085
  chatId: stored.key.remoteJid ?? void 0,
3093
3086
  timestampSeconds: stored.timestampSeconds ?? void 0
3094
3087
  });
3095
3088
  return;
3096
3089
  }
3097
- if (stored.key?.id && stored.message) this.#remember(stored);
3098
- const quoted = extractQuotedZapoMessage(event);
3099
- if (quoted?.key.id && quoted.message) {
3100
- this.#remember(quoted);
3090
+ const deliveryKey = this.#messageDeliveryKey(stored.key);
3091
+ if (stored.key.id && this.#deliveredMessageStore.has(deliveryKey)) {
3092
+ this.#logger.debug("Ignored duplicate Zapo message event.", {
3093
+ messageId: stored.key.id,
3094
+ chatId: stored.key.remoteJid ?? void 0
3095
+ });
3096
+ return;
3101
3097
  }
3102
3098
  const message = normalizeZapoMessage(event);
3103
3099
  if (!message) return;
3100
+ if (stored.key.id) this.#rememberDeliveredMessage(deliveryKey);
3104
3101
  this.#messageKeyStore.set(message.keys, stored);
3105
3102
  if (message.quoted && quoted?.message) {
3106
3103
  this.#messageKeyStore.set(
@@ -3110,7 +3107,50 @@ var ZapoProvider = class {
3110
3107
  }
3111
3108
  void this.#events.emit("message", message);
3112
3109
  }
3110
+ #handleAddonEvent(event) {
3111
+ if (event.kind !== "message_edit" || !event.targetMessageId) return;
3112
+ const editedMessage = this.#addonEditedMessage(event.decrypted);
3113
+ if (!editedMessage) return;
3114
+ const target = {
3115
+ id: event.targetMessageId,
3116
+ ...event.key.remoteJid !== void 0 ? { remoteJid: event.key.remoteJid } : {},
3117
+ ...event.key.fromMe !== void 0 ? { fromMe: event.key.fromMe } : {},
3118
+ ...event.key.participant !== void 0 ? { participant: event.key.participant } : {},
3119
+ ...event.key.participantAlt !== void 0 ? { participantAlt: event.key.participantAlt } : {}
3120
+ };
3121
+ this.#handleProtocolEvent({
3122
+ key: event.key,
3123
+ ...event.offline !== void 0 ? { offline: event.offline } : {},
3124
+ protocolMessage: {
3125
+ type: proto.Message.ProtocolMessage.Type.MESSAGE_EDIT,
3126
+ key: target,
3127
+ editedMessage
3128
+ }
3129
+ });
3130
+ }
3131
+ #addonEditedMessage(value) {
3132
+ if (!value || typeof value !== "object") return void 0;
3133
+ const record = value;
3134
+ const protocol = record.protocolMessage;
3135
+ if (protocol && typeof protocol === "object") {
3136
+ const edited2 = protocol.editedMessage;
3137
+ if (edited2 && typeof edited2 === "object") return edited2;
3138
+ }
3139
+ const edited = record.editedMessage;
3140
+ if (edited && typeof edited === "object") return edited;
3141
+ const message = record.message;
3142
+ if (message && typeof message === "object") return message;
3143
+ return value;
3144
+ }
3113
3145
  #handleProtocolEvent(event) {
3146
+ if (this.#isOfflineMessage(event)) {
3147
+ this.#logger.debug("Ignored protocol event queued before the current live connection.", {
3148
+ messageId: event.key.id ?? void 0,
3149
+ chatId: event.key.remoteJid ?? void 0,
3150
+ timestampSeconds: event.timestampSeconds ?? void 0
3151
+ });
3152
+ return;
3153
+ }
3114
3154
  const protocol = event.protocolMessage ?? event.message?.protocolMessage;
3115
3155
  if (!protocol) return;
3116
3156
  const protocolKey = protocol.key;
@@ -3127,7 +3167,16 @@ var ZapoProvider = class {
3127
3167
  if (!target.remoteJid) return;
3128
3168
  const stored = this.#findStoredMessage(target);
3129
3169
  const type = protocol?.type;
3170
+ const mutationKey = this.#protocolDeliveryKey(event, target, type);
3171
+ if (mutationKey && this.#handledProtocolStore.has(mutationKey)) {
3172
+ this.#logger.debug("Ignored duplicate Zapo protocol event.", {
3173
+ messageId: event.key.id ?? void 0,
3174
+ targetMessageId: target.id ?? void 0
3175
+ });
3176
+ return;
3177
+ }
3130
3178
  if (type === proto.Message.ProtocolMessage.Type.REVOKE) {
3179
+ if (mutationKey) this.#rememberHandledProtocol(mutationKey);
3131
3180
  const previous2 = stored ? normalizeZapoMessage(stored) : void 0;
3132
3181
  const deletedByMe = event.key.fromMe === true;
3133
3182
  const deletedById = event.key.participant ?? event.key.participantAlt ?? (deletedByMe ? this.getCurrentUserIds()[0] : event.key.remoteJid ?? void 0);
@@ -3157,6 +3206,7 @@ var ZapoProvider = class {
3157
3206
  const message = normalizeZapoMessage(edited);
3158
3207
  if (!message) return;
3159
3208
  const previous = stored ? normalizeZapoMessage(stored) : void 0;
3209
+ if (mutationKey) this.#rememberHandledProtocol(mutationKey);
3160
3210
  this.#remember(edited);
3161
3211
  const editedByMe = event.key.fromMe === true;
3162
3212
  const editedById = event.key.participant ?? event.key.participantAlt ?? (editedByMe ? this.getCurrentUserIds()[0] : event.key.remoteJid ?? void 0);
@@ -3607,9 +3657,33 @@ var ZapoProvider = class {
3607
3657
  #messageStoreKey(key) {
3608
3658
  return `${key.remoteJid ?? ""}:${key.id ?? ""}:${key.participant ?? key.participantAlt ?? ""}`;
3609
3659
  }
3660
+ #messageDeliveryKey(key) {
3661
+ return `${key.remoteJid ?? ""}:${key.id ?? ""}`;
3662
+ }
3663
+ #protocolDeliveryKey(event, target, type) {
3664
+ if (type == null || !event.key.id || !target.id) return void 0;
3665
+ return `${type}:${event.key.remoteJid ?? target.remoteJid ?? ""}:${event.key.id}:${target.id}`;
3666
+ }
3667
+ #rememberHandledProtocol(key) {
3668
+ this.#handledProtocolStore.delete(key);
3669
+ this.#handledProtocolStore.add(key);
3670
+ while (this.#handledProtocolStore.size > this.#messageCacheSize) {
3671
+ const oldest = this.#handledProtocolStore.values().next().value;
3672
+ if (oldest) this.#handledProtocolStore.delete(oldest);
3673
+ }
3674
+ }
3675
+ #rememberDeliveredMessage(key) {
3676
+ this.#deliveredMessageStore.delete(key);
3677
+ this.#deliveredMessageStore.add(key);
3678
+ while (this.#deliveredMessageStore.size > this.#messageCacheSize) {
3679
+ const oldest = this.#deliveredMessageStore.values().next().value;
3680
+ if (oldest) this.#deliveredMessageStore.delete(oldest);
3681
+ }
3682
+ }
3610
3683
  #isOfflineMessage(message) {
3611
3684
  if (this.#options.processOfflineMessages === true) return false;
3612
3685
  if (message.offline === true) return true;
3686
+ if (!this.#connected) return true;
3613
3687
  if (!this.#connectedAtSeconds || message.timestampSeconds == null) return false;
3614
3688
  const timestamp = toSeconds(message.timestampSeconds);
3615
3689
  if (timestamp === void 0) return false;