@whanext/core 0.19.4 → 0.19.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.
package/CHANGELOG.md CHANGED
@@ -1,8 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.19.5
4
+
5
+ ### Fixed
6
+ - Zapo protocol mutations (`MESSAGE_EDIT` and `REVOKE`) no longer trust the undocumented `offline` envelope flag after the live connection is open; stale mutations are filtered by connection time instead.
7
+ - Protocol edits/revokes are recognized both from `message_protocol` and from a raw `message.protocolMessage` fallback, with existing deduplication preventing double delivery.
8
+ - Edited-message wrappers are unwrapped before normalization, preserving the previous/current payload expected by `messageEdited`.
9
+ - Decrypted `message_addon` edits now accept both the typed `message_edit` form and nested `protocolMessage` forms.
10
+ - Protocol target lookup remains ID-first so PN/LID/participant addressing differences do not prevent recovering the cached original.
11
+
12
+
3
13
  ## 0.19.4
4
14
  ### Corrigido
5
- - Corrigida a normalização de `timestampMs` dos eventos nativos de chamada; removida uma referência inválida ao helper privado `#number`.
6
15
  - Chamadas no provider Zapo agora usam o evento nativo `call`, sem depender do plugin completo de VoIP.
7
16
  - `rejectCall()` envia diretamente a sinalização mínima `<call><reject/></call>` pelo `client.lowlevel.sendNode()`, preservando `callId` e `callCreatorJid`.
8
17
  - Removidas as dependências `@zapo-js/voip`, `@roamhq/wrtc` e `libmlow-wasm` do WhaNext Core; detectar/rejeitar chamadas não exige WebRTC nem binário nativo.
package/dist/index.js CHANGED
@@ -2388,9 +2388,13 @@ import {
2388
2388
  // src/provider/zapo/normalize-message.ts
2389
2389
  function unwrapZapoMessageContent(input) {
2390
2390
  if (!input) return void 0;
2391
- const nested = input.ephemeralMessage?.message ?? input.viewOnceMessage?.message ?? input.viewOnceMessageV2?.message ?? viewOnceV2ExtensionMessage(input) ?? input.deviceSentMessage?.message ?? input.documentWithCaptionMessage?.message;
2391
+ const nested = input.ephemeralMessage?.message ?? input.viewOnceMessage?.message ?? input.viewOnceMessageV2?.message ?? viewOnceV2ExtensionMessage(input) ?? input.deviceSentMessage?.message ?? input.documentWithCaptionMessage?.message ?? editedWrapperMessage(input);
2392
2392
  return nested ? unwrapZapoMessageContent(nested) : input;
2393
2393
  }
2394
+ function editedWrapperMessage(input) {
2395
+ const edited = input.editedMessage;
2396
+ return edited?.message ?? void 0;
2397
+ }
2394
2398
  function viewOnceV2ExtensionMessage(input) {
2395
2399
  const extension = input.viewOnceMessageV2Extension;
2396
2400
  return extension?.message ?? void 0;
@@ -3042,6 +3046,14 @@ var ZapoProvider = class {
3042
3046
  this.#pairingRequired = false;
3043
3047
  });
3044
3048
  client.on("message", (event) => {
3049
+ const protocol = this.#protocolMessage(event);
3050
+ if (protocol && this.#isMessageMutationProtocol(protocol.type)) {
3051
+ this.#handleProtocolEvent({
3052
+ ...event,
3053
+ protocolMessage: protocol
3054
+ });
3055
+ return;
3056
+ }
3045
3057
  this.#handleMessage(event);
3046
3058
  });
3047
3059
  client.on("message_send", (event) => {
@@ -3109,26 +3121,56 @@ var ZapoProvider = class {
3109
3121
  void this.#events.emit("message", message);
3110
3122
  }
3111
3123
  #handleAddonEvent(event) {
3112
- if (event.kind !== "message_edit" || !event.targetMessageId) return;
3113
- const editedMessage = this.#addonEditedMessage(event.decrypted);
3124
+ const decrypted = this.#addonRecord(event.decrypted);
3125
+ const protocol = this.#addonProtocolMessage(decrypted);
3126
+ const kind = event.kind ?? this.#stringField(decrypted, "kind") ?? this.#stringField(decrypted, "type");
3127
+ const isEdit = kind === "message_edit" || protocol?.type === proto.Message.ProtocolMessage.Type.MESSAGE_EDIT;
3128
+ if (!isEdit) return;
3129
+ const targetMessageId = event.targetMessageId ?? this.#stringField(decrypted, "targetMessageId") ?? this.#stringField(decrypted, "targetMessageID") ?? protocol?.key?.id ?? void 0;
3130
+ if (!targetMessageId) return;
3131
+ const editedMessage = protocol?.editedMessage ?? this.#addonEditedMessage(event.decrypted);
3114
3132
  if (!editedMessage) return;
3133
+ const protocolKey = protocol?.key;
3115
3134
  const target = {
3116
- id: event.targetMessageId,
3117
- ...event.key.remoteJid !== void 0 ? { remoteJid: event.key.remoteJid } : {},
3118
- ...event.key.fromMe !== void 0 ? { fromMe: event.key.fromMe } : {},
3119
- ...event.key.participant !== void 0 ? { participant: event.key.participant } : {},
3120
- ...event.key.participantAlt !== void 0 ? { participantAlt: event.key.participantAlt } : {}
3135
+ ...protocolKey ?? {},
3136
+ id: targetMessageId,
3137
+ ...protocolKey?.remoteJid !== void 0 ? { remoteJid: protocolKey.remoteJid } : event.key.remoteJid !== void 0 ? { remoteJid: event.key.remoteJid } : {},
3138
+ ...protocolKey?.fromMe !== void 0 ? { fromMe: protocolKey.fromMe } : event.key.fromMe !== void 0 ? { fromMe: event.key.fromMe } : {},
3139
+ ...protocolKey?.participant !== void 0 ? { participant: protocolKey.participant } : event.key.participant !== void 0 ? { participant: event.key.participant } : {},
3140
+ ...protocolKey?.participantAlt !== void 0 ? { participantAlt: protocolKey.participantAlt } : event.key.participantAlt !== void 0 ? { participantAlt: event.key.participantAlt } : {}
3121
3141
  };
3122
3142
  this.#handleProtocolEvent({
3123
3143
  key: event.key,
3124
- ...event.offline !== void 0 ? { offline: event.offline } : {},
3144
+ ...event.timestampSeconds !== void 0 ? { timestampSeconds: event.timestampSeconds } : {},
3125
3145
  protocolMessage: {
3126
3146
  type: proto.Message.ProtocolMessage.Type.MESSAGE_EDIT,
3127
3147
  key: target,
3128
- editedMessage
3148
+ editedMessage,
3149
+ ...protocol?.timestampMs !== void 0 ? { timestampMs: protocol.timestampMs } : {}
3129
3150
  }
3130
3151
  });
3131
3152
  }
3153
+ #addonRecord(value) {
3154
+ return value && typeof value === "object" ? value : void 0;
3155
+ }
3156
+ #stringField(record, field) {
3157
+ const value = record?.[field];
3158
+ return typeof value === "string" && value.length > 0 ? value : void 0;
3159
+ }
3160
+ #addonProtocolMessage(record) {
3161
+ const direct = record?.protocolMessage;
3162
+ if (direct && typeof direct === "object") {
3163
+ return direct;
3164
+ }
3165
+ const message = record?.message;
3166
+ if (message && typeof message === "object") {
3167
+ const nested = message.protocolMessage;
3168
+ if (nested && typeof nested === "object") {
3169
+ return nested;
3170
+ }
3171
+ }
3172
+ return void 0;
3173
+ }
3132
3174
  #addonEditedMessage(value) {
3133
3175
  if (!value || typeof value !== "object") return void 0;
3134
3176
  const record = value;
@@ -3138,24 +3180,57 @@ var ZapoProvider = class {
3138
3180
  if (edited2 && typeof edited2 === "object") return edited2;
3139
3181
  }
3140
3182
  const edited = record.editedMessage;
3141
- if (edited && typeof edited === "object") return edited;
3183
+ if (edited && typeof edited === "object") {
3184
+ const nested = edited.message;
3185
+ return nested && typeof nested === "object" ? nested : edited;
3186
+ }
3142
3187
  const message = record.message;
3143
- if (message && typeof message === "object") return message;
3188
+ if (message && typeof message === "object") {
3189
+ const messageRecord = message;
3190
+ const nestedEdited = messageRecord.editedMessage;
3191
+ if (nestedEdited && typeof nestedEdited === "object") {
3192
+ const nested = nestedEdited.message;
3193
+ if (nested && typeof nested === "object") return nested;
3194
+ }
3195
+ return message;
3196
+ }
3144
3197
  return value;
3145
3198
  }
3199
+ #protocolMessage(event) {
3200
+ if (event.protocolMessage) return event.protocolMessage;
3201
+ const content = unwrapZapoMessageContent(event.message);
3202
+ return content?.protocolMessage;
3203
+ }
3204
+ #isMessageMutationProtocol(type) {
3205
+ return type === proto.Message.ProtocolMessage.Type.REVOKE || type === proto.Message.ProtocolMessage.Type.MESSAGE_EDIT;
3206
+ }
3207
+ #shouldIgnoreProtocolEvent(event) {
3208
+ if (this.#options.processOfflineMessages === true) return false;
3209
+ if (!this.#connected) return true;
3210
+ if (!this.#connectedAtSeconds || event.timestampSeconds == null) return false;
3211
+ const timestamp = toSeconds(event.timestampSeconds);
3212
+ if (timestamp === void 0) return false;
3213
+ return timestamp < this.#connectedAtSeconds - 3;
3214
+ }
3146
3215
  #handleProtocolEvent(event) {
3147
- if (this.#isOfflineMessage(event)) {
3148
- this.#logger.debug("Ignored protocol event queued before the current live connection.", {
3216
+ if (this.#shouldIgnoreProtocolEvent(event)) {
3217
+ this.#logger.debug("Ignored protocol mutation from before the current live connection.", {
3149
3218
  messageId: event.key.id ?? void 0,
3150
3219
  chatId: event.key.remoteJid ?? void 0,
3151
3220
  timestampSeconds: event.timestampSeconds ?? void 0
3152
3221
  });
3153
3222
  return;
3154
3223
  }
3155
- const protocol = event.protocolMessage ?? event.message?.protocolMessage;
3156
- if (!protocol) return;
3224
+ const protocol = this.#protocolMessage(event);
3225
+ if (!protocol || !this.#isMessageMutationProtocol(protocol.type)) return;
3157
3226
  const protocolKey = protocol.key;
3158
- if (!protocolKey?.id) return;
3227
+ if (!protocolKey?.id) {
3228
+ this.#logger.debug("Ignored Zapo protocol mutation without a target message id.", {
3229
+ messageId: event.key.id ?? void 0,
3230
+ protocolType: protocol.type ?? void 0
3231
+ });
3232
+ return;
3233
+ }
3159
3234
  const remoteJid = protocolKey.remoteJid ?? event.key.remoteJid;
3160
3235
  const participant = protocolKey.participant ?? event.key.participant;
3161
3236
  const participantAlt = protocolKey.participantAlt ?? event.key.participantAlt;
@@ -3165,9 +3240,10 @@ var ZapoProvider = class {
3165
3240
  ...participant !== void 0 ? { participant } : {},
3166
3241
  ...participantAlt !== void 0 ? { participantAlt } : {}
3167
3242
  };
3168
- if (!target.remoteJid) return;
3169
3243
  const stored = this.#findStoredMessage(target);
3170
- const type = protocol?.type;
3244
+ if (!target.remoteJid && stored?.key.remoteJid) target.remoteJid = stored.key.remoteJid;
3245
+ if (!target.remoteJid) return;
3246
+ const type = protocol.type;
3171
3247
  const mutationKey = this.#protocolDeliveryKey(event, target, type);
3172
3248
  if (mutationKey && this.#handledProtocolStore.has(mutationKey)) {
3173
3249
  this.#logger.debug("Ignored duplicate Zapo protocol event.", {
@@ -3181,6 +3257,12 @@ var ZapoProvider = class {
3181
3257
  const previous2 = stored ? normalizeZapoMessage(stored) : void 0;
3182
3258
  const deletedByMe = event.key.fromMe === true;
3183
3259
  const deletedById = event.key.participant ?? event.key.participantAlt ?? (deletedByMe ? this.getCurrentUserIds()[0] : event.key.remoteJid ?? void 0);
3260
+ if (!previous2) {
3261
+ this.#logger.debug("Zapo revoke target was not present in the recent message cache.", {
3262
+ targetMessageId: target.id ?? void 0,
3263
+ chatId: target.remoteJid ?? void 0
3264
+ });
3265
+ }
3184
3266
  void this.#events.emit("messageDeleted", {
3185
3267
  key: previous2?.keys ?? normalizeZapoKey(target),
3186
3268
  ...previous2 ? { message: previous2 } : {},
@@ -3190,9 +3272,8 @@ var ZapoProvider = class {
3190
3272
  });
3191
3273
  return;
3192
3274
  }
3193
- if (type !== proto.Message.ProtocolMessage.Type.MESSAGE_EDIT || !protocol.editedMessage) {
3194
- return;
3195
- }
3275
+ const editedContent = protocol.editedMessage ? this.#unwrapEditedContent(protocol.editedMessage) : void 0;
3276
+ if (!editedContent) return;
3196
3277
  const pushName = event.pushName ?? stored?.pushName;
3197
3278
  const edited = {
3198
3279
  ...stored ?? {},
@@ -3200,7 +3281,7 @@ var ZapoProvider = class {
3200
3281
  ...stored?.key ?? {},
3201
3282
  ...target
3202
3283
  },
3203
- message: protocol.editedMessage,
3284
+ message: editedContent,
3204
3285
  timestampSeconds: toSeconds(protocol.timestampMs) ?? event.timestampSeconds ?? stored?.timestampSeconds ?? Math.floor(Date.now() / 1e3),
3205
3286
  ...pushName !== void 0 ? { pushName } : {}
3206
3287
  };
@@ -3211,6 +3292,12 @@ var ZapoProvider = class {
3211
3292
  this.#remember(edited);
3212
3293
  const editedByMe = event.key.fromMe === true;
3213
3294
  const editedById = event.key.participant ?? event.key.participantAlt ?? (editedByMe ? this.getCurrentUserIds()[0] : event.key.remoteJid ?? void 0);
3295
+ if (!previous) {
3296
+ this.#logger.debug("Zapo edit target was not present in the recent message cache.", {
3297
+ targetMessageId: target.id ?? void 0,
3298
+ chatId: target.remoteJid ?? void 0
3299
+ });
3300
+ }
3214
3301
  void this.#events.emit("messageEdited", {
3215
3302
  key: message.keys,
3216
3303
  ...previous ? { previous } : {},
@@ -3220,6 +3307,10 @@ var ZapoProvider = class {
3220
3307
  editedAt: message.timestamp
3221
3308
  });
3222
3309
  }
3310
+ #unwrapEditedContent(message) {
3311
+ const wrapper = message.editedMessage;
3312
+ return wrapper?.message ?? message;
3313
+ }
3223
3314
  #handleGroupEvent(event) {
3224
3315
  const groupId = event.groupJid;
3225
3316
  if (!groupId) return;