Compare commits

...

28 Commits

Author SHA1 Message Date
Renato Alcara 3dc7238dfa test(binary): drain AwaitingInitialSync timer & fix stale comment
Addresses 2 review comments on PR #384:

- Codex P2: the fresh-pair test ended the socket immediately after the
  buffer assertion, leaving the 2s setTimeout in chats.ts:1522 alive.
  Its callback fired post-teardown and emitted "Cannot log after tests
  are done" via baileys-logger, making the suite noisy and trippable
  by --detectOpenHandles. Drain the timer with a 2.1s wait before
  cleanup; bump the per-test timeout to 10s to accommodate it.

- Copilot: the file header said "live messages are not held for up to
  4 seconds", but the chats.ts AwaitingInitialSync timeout is 2s.
  Replace the stale literal with "the initial-sync window" so the
  comment doesn't drift again when the timeout is tuned.

Skipped intentionally (per PR review):
- CodeRabbit Major (try/finally cleanup): diverges from the pattern
  used by the only sibling test (connection-deadlock.test.ts).
- CodeRabbit Nitpick (replace 50ms sleeps with deterministic waits):
  not flaky in practice; sibling test uses the same pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 14:13:52 -03:00
Renato Alcara 7428528d7b test(binary): cover reconnection sync skip for both signals
Ports the upstream reconnection-sync-skip test from Baileys PR #2350
and adapts it to cover InfiniteAPI's dual-signal reconnect detection
in chats.ts:

1. accountSyncCounter > 0 (a previous full sync completed)
2. socketSkippedOfflineBuffer (forwarded from socket.ts when
   hadStaleRoutingInfo is true, e.g. routingInfo discarded on start)

Both are required because they cover different reconnect scenarios
and the absence of the second branch causes a buffer mismatch where
socket.ts skips the offline buffer while chats.ts still waits in
AwaitingInitialSync — stalling live messages for up to 4 seconds.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 13:45:12 -03:00
Renato Alcara c179c82cca perf(messages-recv): early-ignore JIDs before buffer/queue (#383)
* perf(messages-recv): early-ignore JIDs before buffer/queue

Aligns with Baileys upstream PR #2352. Moves the shouldIgnoreJid check
out of handleMessage/handleReceipt/handleNotification and into
processNode so ignored stanzas are acked and dropped before entering
ev.buffer(), the offline queue, or the keyed mutexes. Skips the LID->PN
resolution work in handleReceipt for ignored receipts as a side effect.

Diverges from upstream by excluding type='call' from the early-ignore
check — InfiniteAPI's handleCall has never used shouldIgnoreJid, so
keeping calls outside this filter preserves existing behavior for
integrators that filter messages but still want call events.

Carousel, buttons, LID/PN normalization, Bad MAC fix and retry manager
untouched.

Co-Authored-By: Renato Alcara
2026-04-25 13:16:22 -03:00
Renato Alcara d08a09c35f feat: add inbound username support and USync username protocol (#382)
* feat: add inbound username support and USync username protocol

Aligns with Baileys upstream PR #2480. WhatsApp is rolling out an
inbound username field that maps to the user's LID. This change is
purely additive — it captures and propagates the new optional field
through types, decoders, USync queries, and group/contact events.

Skipped intentionally to preserve InfiniteAPI's LID/PN customization:
- handleGroupNotification in messages-recv.ts (custom LID->PN flow on
  groups.upsert / participants.map). Username for these specific
  events still flows via process-message's emitParticipantsUpdate
  (reads message.key.participantUsername added in decode-wa-message).

Carousel/buttons code (messages.ts, messages-send.ts) untouched.

Co-Authored-By: Renato Alcara
2026-04-25 12:39:31 -03:00
Renato Alcara cffaad9e5a fix(chats): use abt xmlns and protocol
fix(chats): use abt xmlns and protocol
2026-04-25 11:08:47 -03:00
Renato Alcara 104d2c47f5 chore: update WhatsApp Web version to v2.3000.1038147544 (#380)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-25 06:17:10 -03:00
Renato Alcara 924c54f1f8 chore: update WhatsApp Web version to v2.3000.1038063777 (#378)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-24 06:31:31 -03:00
github-actions[bot] e34ef5479d chore: update proto/version to v2.3000.1038024963 (#377)
Co-authored-by: rsalcara <rsalcara@users.noreply.github.com>
2026-04-24 01:03:36 -03:00
Renato Alcara 5e8d30d709 chore: update WhatsApp Web version to v2.3000.1037968143 (#376)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-23 06:31:41 -03:00
github-actions[bot] ac93080595 chore: update proto/version to v2.3000.1037953056 (#375)
Co-authored-by: rsalcara <rsalcara@users.noreply.github.com>
2026-04-23 01:01:23 -03:00
Renato Alcara 37d1bdfb76 chore: update WhatsApp Web version to v2.3000.1037878879 (#374)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-22 06:29:59 -03:00
github-actions[bot] 76b2007f27 chore: update proto/version to v2.3000.1037847281 (#373)
Co-authored-by: rsalcara <rsalcara@users.noreply.github.com>
2026-04-22 01:00:13 -03:00
Renato Alcara 676912c295 Fix/remove vo raw debug logs (#372)
* chore: remove temporary [VO_RAW] debug logs from handleMessage

* chore: remove temporary VO_TRACE debug logger.info from handleMessage
2026-04-21 15:09:00 -03:00
Renato Alcara 06d9f35ca9 chore: remove temporary [VO_RAW] debug logs from handleMessage (#371)
chore: remove temporary [VO_RAW] debug logs from handleMessage (#371)
2026-04-21 15:00:00 -03:00
Renato Alcara 4cadcd1c57 chore: update WhatsApp Web version to v2.3000.1037786138 (#370)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-21 06:30:57 -03:00
github-actions[bot] 937b26edcd chore: update proto/version to v2.3000.1037753511 (#368)
Co-authored-by: rsalcara <rsalcara@users.noreply.github.com>
2026-04-21 01:01:09 -03:00
Renato Alcara acad0c06dc chore: update WhatsApp Web version to v2.3000.1037679412 (#369)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-20 06:33:26 -03:00
Renato Alcara 98995aae89 chore: update WhatsApp Web version to v2.3000.1037659536 (#367)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-19 06:15:22 -03:00
github-actions[bot] 7a7d92e3f8 chore: update proto/version to v2.3000.1037654574 (#366)
Co-authored-by: rsalcara <rsalcara@users.noreply.github.com>
2026-04-19 01:01:54 -03:00
Renato Alcara 1fbaa284f9 chore: update WhatsApp Web version to v2.3000.1037639827 (#365)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-18 06:15:03 -03:00
github-actions[bot] 9c7dd0460c chore: update proto/version to v2.3000.1037622359 (#364)
Co-authored-by: rsalcara <rsalcara@users.noreply.github.com>
2026-04-18 00:55:26 -03:00
Renato Alcara 579277cb29 chore: update WhatsApp Web version to v2.3000.1037554587 (#363)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-17 06:29:43 -03:00
github-actions[bot] bb0cb90260 chore: update proto/version to v2.3000.1037539916 (#358)
Co-authored-by: rsalcara <rsalcara@users.noreply.github.com>
2026-04-17 01:01:19 -03:00
Renato Alcara 56955e12f5 chore: update WhatsApp Web version to v2.3000.1037471135 (#362)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-16 06:29:30 -03:00
Renato Alcara b7d9f2b866 chore: update WhatsApp Web version to v2.3000.1037376462 (#361)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-15 06:30:13 -03:00
Renato Alcara daaf0bc813 chore: update WhatsApp Web version to v2.3000.1037280436 (#360)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-14 06:29:53 -03:00
Renato Alcara bf95867089 Merge branch 'master' of https://github.com/rsalcara/InfiniteAPI 2026-04-13 07:20:06 -03:00
Renato Alcara 230e08037b chore: update WhatsApp Web version to v2.3000.1037186195 (#359)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-13 06:33:07 -03:00
22 changed files with 765 additions and 46 deletions
+24 -1
View File
@@ -1,7 +1,7 @@
syntax = "proto3"; syntax = "proto3";
package proto; package proto;
/// WhatsApp Version: 2.3000.1037162330 /// WhatsApp Version: 2.3000.1038024963
message ADVDeviceIdentity { message ADVDeviceIdentity {
optional uint32 rawId = 1; optional uint32 rawId = 1;
@@ -4221,6 +4221,8 @@ enum MutationProps {
CUSTOMER_DATA_ACTION = 83; CUSTOMER_DATA_ACTION = 83;
SUBSCRIPTIONS_SYNC_V2_ACTION = 84; SUBSCRIPTIONS_SYNC_V2_ACTION = 84;
THREAD_PIN_ACTION = 85; THREAD_PIN_ACTION = 85;
AUTO_ORGANIZE_BUSINESS_CHAT_SETTING = 86;
BIZ_AI_SETTINGS_NUDGE_ACTION = 87;
SHARE_OWN_PN = 10001; SHARE_OWN_PN = 10001;
BUSINESS_BROADCAST_ACTION = 10002; BUSINESS_BROADCAST_ACTION = 10002;
AI_THREAD_DELETE_ACTION = 10003; AI_THREAD_DELETE_ACTION = 10003;
@@ -4866,6 +4868,8 @@ message SyncActionValue {
optional CustomerDataAction customerDataAction = 83; optional CustomerDataAction customerDataAction = 83;
optional SubscriptionsSyncV2Action subscriptionsSyncV2Action = 84; optional SubscriptionsSyncV2Action subscriptionsSyncV2Action = 84;
optional ThreadPinAction threadPinAction = 85; optional ThreadPinAction threadPinAction = 85;
optional AutoOrganizeBusinessChatSetting autoOrganizeBusinessChatSetting = 86;
optional BizAISettingsNudgeAction bizAiSettingsNudgeAction = 87;
message AgentAction { message AgentAction {
optional string name = 1; optional string name = 1;
optional int32 deviceID = 2; optional int32 deviceID = 2;
@@ -4885,6 +4889,10 @@ message SyncActionValue {
optional SyncActionValue.SyncActionMessageRange messageRange = 2; optional SyncActionValue.SyncActionMessageRange messageRange = 2;
} }
message AutoOrganizeBusinessChatSetting {
optional bool autoOrganize = 1;
}
message AvatarUpdatedAction { message AvatarUpdatedAction {
optional AvatarEventType eventType = 1; optional AvatarEventType eventType = 1;
repeated SyncActionValue.StickerAction recentAvatarStickers = 2; repeated SyncActionValue.StickerAction recentAvatarStickers = 2;
@@ -4895,6 +4903,20 @@ message SyncActionValue {
} }
} }
message BizAISettingsNudgeAction {
optional BizAISettingsCategory category = 1;
optional int64 version = 2;
optional int64 updatedAtMs = 3;
enum BizAISettingsCategory {
UNKNOWN = 0;
INSTRUCTIONS = 1;
RESPONSE_SETTINGS = 2;
EXAMPLE_RESPONSES = 3;
KNOWLEDGE = 4;
LEAD_GEN = 5;
}
}
message BotWelcomeRequestAction { message BotWelcomeRequestAction {
optional bool isSent = 1; optional bool isSent = 1;
} }
@@ -5358,6 +5380,7 @@ message SyncActionValue {
optional bool shareToFB = 3; optional bool shareToFB = 3;
optional bool shareToIG = 4; optional bool shareToIG = 4;
repeated CustomList customLists = 5; repeated CustomList customLists = 5;
repeated StatusDistributionMode modes = 6;
message CustomList { message CustomList {
optional string listId = 1; optional string listId = 1;
optional string name = 2; optional string name = 2;
+56
View File
@@ -10692,6 +10692,8 @@ export namespace proto {
CUSTOMER_DATA_ACTION = 83, CUSTOMER_DATA_ACTION = 83,
SUBSCRIPTIONS_SYNC_V2_ACTION = 84, SUBSCRIPTIONS_SYNC_V2_ACTION = 84,
THREAD_PIN_ACTION = 85, THREAD_PIN_ACTION = 85,
AUTO_ORGANIZE_BUSINESS_CHAT_SETTING = 86,
BIZ_AI_SETTINGS_NUDGE_ACTION = 87,
SHARE_OWN_PN = 10001, SHARE_OWN_PN = 10001,
BUSINESS_BROADCAST_ACTION = 10002, BUSINESS_BROADCAST_ACTION = 10002,
AI_THREAD_DELETE_ACTION = 10003 AI_THREAD_DELETE_ACTION = 10003
@@ -12309,6 +12311,8 @@ export namespace proto {
customerDataAction?: (proto.SyncActionValue.ICustomerDataAction|null); customerDataAction?: (proto.SyncActionValue.ICustomerDataAction|null);
subscriptionsSyncV2Action?: (proto.SyncActionValue.ISubscriptionsSyncV2Action|null); subscriptionsSyncV2Action?: (proto.SyncActionValue.ISubscriptionsSyncV2Action|null);
threadPinAction?: (proto.SyncActionValue.IThreadPinAction|null); threadPinAction?: (proto.SyncActionValue.IThreadPinAction|null);
autoOrganizeBusinessChatSetting?: (proto.SyncActionValue.IAutoOrganizeBusinessChatSetting|null);
bizAiSettingsNudgeAction?: (proto.SyncActionValue.IBizAISettingsNudgeAction|null);
} }
class SyncActionValue implements ISyncActionValue { class SyncActionValue implements ISyncActionValue {
@@ -12389,6 +12393,8 @@ export namespace proto {
public customerDataAction?: (proto.SyncActionValue.ICustomerDataAction|null); public customerDataAction?: (proto.SyncActionValue.ICustomerDataAction|null);
public subscriptionsSyncV2Action?: (proto.SyncActionValue.ISubscriptionsSyncV2Action|null); public subscriptionsSyncV2Action?: (proto.SyncActionValue.ISubscriptionsSyncV2Action|null);
public threadPinAction?: (proto.SyncActionValue.IThreadPinAction|null); public threadPinAction?: (proto.SyncActionValue.IThreadPinAction|null);
public autoOrganizeBusinessChatSetting?: (proto.SyncActionValue.IAutoOrganizeBusinessChatSetting|null);
public bizAiSettingsNudgeAction?: (proto.SyncActionValue.IBizAISettingsNudgeAction|null);
public static create(properties?: proto.ISyncActionValue): proto.SyncActionValue; public static create(properties?: proto.ISyncActionValue): proto.SyncActionValue;
public static encode(m: proto.ISyncActionValue, w?: $protobuf.Writer): $protobuf.Writer; public static encode(m: proto.ISyncActionValue, w?: $protobuf.Writer): $protobuf.Writer;
public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): proto.SyncActionValue; public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): proto.SyncActionValue;
@@ -12470,6 +12476,22 @@ export namespace proto {
public static getTypeUrl(typeUrlPrefix?: string): string; public static getTypeUrl(typeUrlPrefix?: string): string;
} }
interface IAutoOrganizeBusinessChatSetting {
autoOrganize?: (boolean|null);
}
class AutoOrganizeBusinessChatSetting implements IAutoOrganizeBusinessChatSetting {
constructor(p?: proto.SyncActionValue.IAutoOrganizeBusinessChatSetting);
public autoOrganize?: (boolean|null);
public static create(properties?: proto.SyncActionValue.IAutoOrganizeBusinessChatSetting): proto.SyncActionValue.AutoOrganizeBusinessChatSetting;
public static encode(m: proto.SyncActionValue.IAutoOrganizeBusinessChatSetting, w?: $protobuf.Writer): $protobuf.Writer;
public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): proto.SyncActionValue.AutoOrganizeBusinessChatSetting;
public static fromObject(d: { [k: string]: any }): proto.SyncActionValue.AutoOrganizeBusinessChatSetting;
public static toObject(m: proto.SyncActionValue.AutoOrganizeBusinessChatSetting, o?: $protobuf.IConversionOptions): { [k: string]: any };
public toJSON(): { [k: string]: any };
public static getTypeUrl(typeUrlPrefix?: string): string;
}
interface IAvatarUpdatedAction { interface IAvatarUpdatedAction {
eventType?: (proto.SyncActionValue.AvatarUpdatedAction.AvatarEventType|null); eventType?: (proto.SyncActionValue.AvatarUpdatedAction.AvatarEventType|null);
recentAvatarStickers?: (proto.SyncActionValue.IStickerAction[]|null); recentAvatarStickers?: (proto.SyncActionValue.IStickerAction[]|null);
@@ -12497,6 +12519,38 @@ export namespace proto {
} }
} }
interface IBizAISettingsNudgeAction {
category?: (proto.SyncActionValue.BizAISettingsNudgeAction.BizAISettingsCategory|null);
version?: (number|Long|null);
updatedAtMs?: (number|Long|null);
}
class BizAISettingsNudgeAction implements IBizAISettingsNudgeAction {
constructor(p?: proto.SyncActionValue.IBizAISettingsNudgeAction);
public category?: (proto.SyncActionValue.BizAISettingsNudgeAction.BizAISettingsCategory|null);
public version?: (number|Long|null);
public updatedAtMs?: (number|Long|null);
public static create(properties?: proto.SyncActionValue.IBizAISettingsNudgeAction): proto.SyncActionValue.BizAISettingsNudgeAction;
public static encode(m: proto.SyncActionValue.IBizAISettingsNudgeAction, w?: $protobuf.Writer): $protobuf.Writer;
public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): proto.SyncActionValue.BizAISettingsNudgeAction;
public static fromObject(d: { [k: string]: any }): proto.SyncActionValue.BizAISettingsNudgeAction;
public static toObject(m: proto.SyncActionValue.BizAISettingsNudgeAction, o?: $protobuf.IConversionOptions): { [k: string]: any };
public toJSON(): { [k: string]: any };
public static getTypeUrl(typeUrlPrefix?: string): string;
}
namespace BizAISettingsNudgeAction {
enum BizAISettingsCategory {
UNKNOWN = 0,
INSTRUCTIONS = 1,
RESPONSE_SETTINGS = 2,
EXAMPLE_RESPONSES = 3,
KNOWLEDGE = 4,
LEAD_GEN = 5
}
}
interface IBotWelcomeRequestAction { interface IBotWelcomeRequestAction {
isSent?: (boolean|null); isSent?: (boolean|null);
} }
@@ -13844,6 +13898,7 @@ export namespace proto {
shareToFB?: (boolean|null); shareToFB?: (boolean|null);
shareToIG?: (boolean|null); shareToIG?: (boolean|null);
customLists?: (proto.SyncActionValue.StatusPrivacyAction.ICustomList[]|null); customLists?: (proto.SyncActionValue.StatusPrivacyAction.ICustomList[]|null);
modes?: (proto.SyncActionValue.StatusPrivacyAction.StatusDistributionMode[]|null);
} }
class StatusPrivacyAction implements IStatusPrivacyAction { class StatusPrivacyAction implements IStatusPrivacyAction {
@@ -13853,6 +13908,7 @@ export namespace proto {
public shareToFB?: (boolean|null); public shareToFB?: (boolean|null);
public shareToIG?: (boolean|null); public shareToIG?: (boolean|null);
public customLists: proto.SyncActionValue.StatusPrivacyAction.ICustomList[]; public customLists: proto.SyncActionValue.StatusPrivacyAction.ICustomList[];
public modes: proto.SyncActionValue.StatusPrivacyAction.StatusDistributionMode[];
public static create(properties?: proto.SyncActionValue.IStatusPrivacyAction): proto.SyncActionValue.StatusPrivacyAction; public static create(properties?: proto.SyncActionValue.IStatusPrivacyAction): proto.SyncActionValue.StatusPrivacyAction;
public static encode(m: proto.SyncActionValue.IStatusPrivacyAction, w?: $protobuf.Writer): $protobuf.Writer; public static encode(m: proto.SyncActionValue.IStatusPrivacyAction, w?: $protobuf.Writer): $protobuf.Writer;
public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): proto.SyncActionValue.StatusPrivacyAction; public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): proto.SyncActionValue.StatusPrivacyAction;
+386
View File
@@ -75158,6 +75158,8 @@ export const proto = $root.proto = (() => {
values[valuesById[83] = "CUSTOMER_DATA_ACTION"] = 83; values[valuesById[83] = "CUSTOMER_DATA_ACTION"] = 83;
values[valuesById[84] = "SUBSCRIPTIONS_SYNC_V2_ACTION"] = 84; values[valuesById[84] = "SUBSCRIPTIONS_SYNC_V2_ACTION"] = 84;
values[valuesById[85] = "THREAD_PIN_ACTION"] = 85; values[valuesById[85] = "THREAD_PIN_ACTION"] = 85;
values[valuesById[86] = "AUTO_ORGANIZE_BUSINESS_CHAT_SETTING"] = 86;
values[valuesById[87] = "BIZ_AI_SETTINGS_NUDGE_ACTION"] = 87;
values[valuesById[10001] = "SHARE_OWN_PN"] = 10001; values[valuesById[10001] = "SHARE_OWN_PN"] = 10001;
values[valuesById[10002] = "BUSINESS_BROADCAST_ACTION"] = 10002; values[valuesById[10002] = "BUSINESS_BROADCAST_ACTION"] = 10002;
values[valuesById[10003] = "AI_THREAD_DELETE_ACTION"] = 10003; values[valuesById[10003] = "AI_THREAD_DELETE_ACTION"] = 10003;
@@ -85556,6 +85558,8 @@ export const proto = $root.proto = (() => {
SyncActionValue.prototype.customerDataAction = null; SyncActionValue.prototype.customerDataAction = null;
SyncActionValue.prototype.subscriptionsSyncV2Action = null; SyncActionValue.prototype.subscriptionsSyncV2Action = null;
SyncActionValue.prototype.threadPinAction = null; SyncActionValue.prototype.threadPinAction = null;
SyncActionValue.prototype.autoOrganizeBusinessChatSetting = null;
SyncActionValue.prototype.bizAiSettingsNudgeAction = null;
let $oneOfFields; let $oneOfFields;
@@ -86015,6 +86019,18 @@ export const proto = $root.proto = (() => {
set: $util.oneOfSetter($oneOfFields) set: $util.oneOfSetter($oneOfFields)
}); });
// Virtual OneOf for proto3 optional field
Object.defineProperty(SyncActionValue.prototype, "_autoOrganizeBusinessChatSetting", {
get: $util.oneOfGetter($oneOfFields = ["autoOrganizeBusinessChatSetting"]),
set: $util.oneOfSetter($oneOfFields)
});
// Virtual OneOf for proto3 optional field
Object.defineProperty(SyncActionValue.prototype, "_bizAiSettingsNudgeAction", {
get: $util.oneOfGetter($oneOfFields = ["bizAiSettingsNudgeAction"]),
set: $util.oneOfSetter($oneOfFields)
});
SyncActionValue.create = function create(properties) { SyncActionValue.create = function create(properties) {
return new SyncActionValue(properties); return new SyncActionValue(properties);
}; };
@@ -86174,6 +86190,10 @@ export const proto = $root.proto = (() => {
$root.proto.SyncActionValue.SubscriptionsSyncV2Action.encode(m.subscriptionsSyncV2Action, w.uint32(674).fork()).ldelim(); $root.proto.SyncActionValue.SubscriptionsSyncV2Action.encode(m.subscriptionsSyncV2Action, w.uint32(674).fork()).ldelim();
if (m.threadPinAction != null && Object.hasOwnProperty.call(m, "threadPinAction")) if (m.threadPinAction != null && Object.hasOwnProperty.call(m, "threadPinAction"))
$root.proto.SyncActionValue.ThreadPinAction.encode(m.threadPinAction, w.uint32(682).fork()).ldelim(); $root.proto.SyncActionValue.ThreadPinAction.encode(m.threadPinAction, w.uint32(682).fork()).ldelim();
if (m.autoOrganizeBusinessChatSetting != null && Object.hasOwnProperty.call(m, "autoOrganizeBusinessChatSetting"))
$root.proto.SyncActionValue.AutoOrganizeBusinessChatSetting.encode(m.autoOrganizeBusinessChatSetting, w.uint32(690).fork()).ldelim();
if (m.bizAiSettingsNudgeAction != null && Object.hasOwnProperty.call(m, "bizAiSettingsNudgeAction"))
$root.proto.SyncActionValue.BizAISettingsNudgeAction.encode(m.bizAiSettingsNudgeAction, w.uint32(698).fork()).ldelim();
return w; return w;
}; };
@@ -86490,6 +86510,14 @@ export const proto = $root.proto = (() => {
m.threadPinAction = $root.proto.SyncActionValue.ThreadPinAction.decode(r, r.uint32()); m.threadPinAction = $root.proto.SyncActionValue.ThreadPinAction.decode(r, r.uint32());
break; break;
} }
case 86: {
m.autoOrganizeBusinessChatSetting = $root.proto.SyncActionValue.AutoOrganizeBusinessChatSetting.decode(r, r.uint32());
break;
}
case 87: {
m.bizAiSettingsNudgeAction = $root.proto.SyncActionValue.BizAISettingsNudgeAction.decode(r, r.uint32());
break;
}
default: default:
r.skipType(t & 7); r.skipType(t & 7);
break; break;
@@ -86887,6 +86915,16 @@ export const proto = $root.proto = (() => {
throw TypeError(".proto.SyncActionValue.threadPinAction: object expected"); throw TypeError(".proto.SyncActionValue.threadPinAction: object expected");
m.threadPinAction = $root.proto.SyncActionValue.ThreadPinAction.fromObject(d.threadPinAction); m.threadPinAction = $root.proto.SyncActionValue.ThreadPinAction.fromObject(d.threadPinAction);
} }
if (d.autoOrganizeBusinessChatSetting != null) {
if (typeof d.autoOrganizeBusinessChatSetting !== "object")
throw TypeError(".proto.SyncActionValue.autoOrganizeBusinessChatSetting: object expected");
m.autoOrganizeBusinessChatSetting = $root.proto.SyncActionValue.AutoOrganizeBusinessChatSetting.fromObject(d.autoOrganizeBusinessChatSetting);
}
if (d.bizAiSettingsNudgeAction != null) {
if (typeof d.bizAiSettingsNudgeAction !== "object")
throw TypeError(".proto.SyncActionValue.bizAiSettingsNudgeAction: object expected");
m.bizAiSettingsNudgeAction = $root.proto.SyncActionValue.BizAISettingsNudgeAction.fromObject(d.bizAiSettingsNudgeAction);
}
return m; return m;
}; };
@@ -87277,6 +87315,16 @@ export const proto = $root.proto = (() => {
if (o.oneofs) if (o.oneofs)
d._threadPinAction = "threadPinAction"; d._threadPinAction = "threadPinAction";
} }
if (m.autoOrganizeBusinessChatSetting != null && m.hasOwnProperty("autoOrganizeBusinessChatSetting")) {
d.autoOrganizeBusinessChatSetting = $root.proto.SyncActionValue.AutoOrganizeBusinessChatSetting.toObject(m.autoOrganizeBusinessChatSetting, o);
if (o.oneofs)
d._autoOrganizeBusinessChatSetting = "autoOrganizeBusinessChatSetting";
}
if (m.bizAiSettingsNudgeAction != null && m.hasOwnProperty("bizAiSettingsNudgeAction")) {
d.bizAiSettingsNudgeAction = $root.proto.SyncActionValue.BizAISettingsNudgeAction.toObject(m.bizAiSettingsNudgeAction, o);
if (o.oneofs)
d._bizAiSettingsNudgeAction = "bizAiSettingsNudgeAction";
}
return d; return d;
}; };
@@ -87708,6 +87756,94 @@ export const proto = $root.proto = (() => {
return ArchiveChatAction; return ArchiveChatAction;
})(); })();
SyncActionValue.AutoOrganizeBusinessChatSetting = (function() {
function AutoOrganizeBusinessChatSetting(p) {
if (p)
for (var ks = Object.keys(p), i = 0; i < ks.length; ++i)
if (p[ks[i]] != null)
this[ks[i]] = p[ks[i]];
}
AutoOrganizeBusinessChatSetting.prototype.autoOrganize = null;
let $oneOfFields;
// Virtual OneOf for proto3 optional field
Object.defineProperty(AutoOrganizeBusinessChatSetting.prototype, "_autoOrganize", {
get: $util.oneOfGetter($oneOfFields = ["autoOrganize"]),
set: $util.oneOfSetter($oneOfFields)
});
AutoOrganizeBusinessChatSetting.create = function create(properties) {
return new AutoOrganizeBusinessChatSetting(properties);
};
AutoOrganizeBusinessChatSetting.encode = function encode(m, w) {
if (!w)
w = $Writer.create();
if (m.autoOrganize != null && Object.hasOwnProperty.call(m, "autoOrganize"))
w.uint32(8).bool(m.autoOrganize);
return w;
};
AutoOrganizeBusinessChatSetting.decode = function decode(r, l, e) {
if (!(r instanceof $Reader))
r = $Reader.create(r);
var c = l === undefined ? r.len : r.pos + l, m = new $root.proto.SyncActionValue.AutoOrganizeBusinessChatSetting();
while (r.pos < c) {
var t = r.uint32();
if (t === e)
break;
switch (t >>> 3) {
case 1: {
m.autoOrganize = r.bool();
break;
}
default:
r.skipType(t & 7);
break;
}
}
return m;
};
AutoOrganizeBusinessChatSetting.fromObject = function fromObject(d) {
if (d instanceof $root.proto.SyncActionValue.AutoOrganizeBusinessChatSetting)
return d;
var m = new $root.proto.SyncActionValue.AutoOrganizeBusinessChatSetting();
if (d.autoOrganize != null) {
m.autoOrganize = Boolean(d.autoOrganize);
}
return m;
};
AutoOrganizeBusinessChatSetting.toObject = function toObject(m, o) {
if (!o)
o = {};
var d = {};
if (m.autoOrganize != null && m.hasOwnProperty("autoOrganize")) {
d.autoOrganize = m.autoOrganize;
if (o.oneofs)
d._autoOrganize = "autoOrganize";
}
return d;
};
AutoOrganizeBusinessChatSetting.prototype.toJSON = function toJSON() {
return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
};
AutoOrganizeBusinessChatSetting.getTypeUrl = function getTypeUrl(typeUrlPrefix) {
if (typeUrlPrefix === undefined) {
typeUrlPrefix = "type.googleapis.com";
}
return typeUrlPrefix + "/proto.SyncActionValue.AutoOrganizeBusinessChatSetting";
};
return AutoOrganizeBusinessChatSetting;
})();
SyncActionValue.AvatarUpdatedAction = (function() { SyncActionValue.AvatarUpdatedAction = (function() {
function AvatarUpdatedAction(p) { function AvatarUpdatedAction(p) {
@@ -87852,6 +87988,196 @@ export const proto = $root.proto = (() => {
return AvatarUpdatedAction; return AvatarUpdatedAction;
})(); })();
SyncActionValue.BizAISettingsNudgeAction = (function() {
function BizAISettingsNudgeAction(p) {
if (p)
for (var ks = Object.keys(p), i = 0; i < ks.length; ++i)
if (p[ks[i]] != null)
this[ks[i]] = p[ks[i]];
}
BizAISettingsNudgeAction.prototype.category = null;
BizAISettingsNudgeAction.prototype.version = null;
BizAISettingsNudgeAction.prototype.updatedAtMs = null;
let $oneOfFields;
// Virtual OneOf for proto3 optional field
Object.defineProperty(BizAISettingsNudgeAction.prototype, "_category", {
get: $util.oneOfGetter($oneOfFields = ["category"]),
set: $util.oneOfSetter($oneOfFields)
});
// Virtual OneOf for proto3 optional field
Object.defineProperty(BizAISettingsNudgeAction.prototype, "_version", {
get: $util.oneOfGetter($oneOfFields = ["version"]),
set: $util.oneOfSetter($oneOfFields)
});
// Virtual OneOf for proto3 optional field
Object.defineProperty(BizAISettingsNudgeAction.prototype, "_updatedAtMs", {
get: $util.oneOfGetter($oneOfFields = ["updatedAtMs"]),
set: $util.oneOfSetter($oneOfFields)
});
BizAISettingsNudgeAction.create = function create(properties) {
return new BizAISettingsNudgeAction(properties);
};
BizAISettingsNudgeAction.encode = function encode(m, w) {
if (!w)
w = $Writer.create();
if (m.category != null && Object.hasOwnProperty.call(m, "category"))
w.uint32(8).int32(m.category);
if (m.version != null && Object.hasOwnProperty.call(m, "version"))
w.uint32(16).int64(m.version);
if (m.updatedAtMs != null && Object.hasOwnProperty.call(m, "updatedAtMs"))
w.uint32(24).int64(m.updatedAtMs);
return w;
};
BizAISettingsNudgeAction.decode = function decode(r, l, e) {
if (!(r instanceof $Reader))
r = $Reader.create(r);
var c = l === undefined ? r.len : r.pos + l, m = new $root.proto.SyncActionValue.BizAISettingsNudgeAction();
while (r.pos < c) {
var t = r.uint32();
if (t === e)
break;
switch (t >>> 3) {
case 1: {
m.category = r.int32();
break;
}
case 2: {
m.version = r.int64();
break;
}
case 3: {
m.updatedAtMs = r.int64();
break;
}
default:
r.skipType(t & 7);
break;
}
}
return m;
};
BizAISettingsNudgeAction.fromObject = function fromObject(d) {
if (d instanceof $root.proto.SyncActionValue.BizAISettingsNudgeAction)
return d;
var m = new $root.proto.SyncActionValue.BizAISettingsNudgeAction();
switch (d.category) {
default:
if (typeof d.category === "number") {
m.category = d.category;
break;
}
break;
case "UNKNOWN":
case 0:
m.category = 0;
break;
case "INSTRUCTIONS":
case 1:
m.category = 1;
break;
case "RESPONSE_SETTINGS":
case 2:
m.category = 2;
break;
case "EXAMPLE_RESPONSES":
case 3:
m.category = 3;
break;
case "KNOWLEDGE":
case 4:
m.category = 4;
break;
case "LEAD_GEN":
case 5:
m.category = 5;
break;
}
if (d.version != null) {
if ($util.Long)
(m.version = $util.Long.fromValue(d.version)).unsigned = false;
else if (typeof d.version === "string")
m.version = parseInt(d.version, 10);
else if (typeof d.version === "number")
m.version = d.version;
else if (typeof d.version === "object")
m.version = new $util.LongBits(d.version.low >>> 0, d.version.high >>> 0).toNumber();
}
if (d.updatedAtMs != null) {
if ($util.Long)
(m.updatedAtMs = $util.Long.fromValue(d.updatedAtMs)).unsigned = false;
else if (typeof d.updatedAtMs === "string")
m.updatedAtMs = parseInt(d.updatedAtMs, 10);
else if (typeof d.updatedAtMs === "number")
m.updatedAtMs = d.updatedAtMs;
else if (typeof d.updatedAtMs === "object")
m.updatedAtMs = new $util.LongBits(d.updatedAtMs.low >>> 0, d.updatedAtMs.high >>> 0).toNumber();
}
return m;
};
BizAISettingsNudgeAction.toObject = function toObject(m, o) {
if (!o)
o = {};
var d = {};
if (m.category != null && m.hasOwnProperty("category")) {
d.category = o.enums === String ? $root.proto.SyncActionValue.BizAISettingsNudgeAction.BizAISettingsCategory[m.category] === undefined ? m.category : $root.proto.SyncActionValue.BizAISettingsNudgeAction.BizAISettingsCategory[m.category] : m.category;
if (o.oneofs)
d._category = "category";
}
if (m.version != null && m.hasOwnProperty("version")) {
if (typeof m.version === "number")
d.version = o.longs === String ? String(m.version) : m.version;
else
d.version = o.longs === String ? longToString(m.version) : o.longs === Number ? longToNumber(m.version) : m.version;
if (o.oneofs)
d._version = "version";
}
if (m.updatedAtMs != null && m.hasOwnProperty("updatedAtMs")) {
if (typeof m.updatedAtMs === "number")
d.updatedAtMs = o.longs === String ? String(m.updatedAtMs) : m.updatedAtMs;
else
d.updatedAtMs = o.longs === String ? longToString(m.updatedAtMs) : o.longs === Number ? longToNumber(m.updatedAtMs) : m.updatedAtMs;
if (o.oneofs)
d._updatedAtMs = "updatedAtMs";
}
return d;
};
BizAISettingsNudgeAction.prototype.toJSON = function toJSON() {
return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
};
BizAISettingsNudgeAction.getTypeUrl = function getTypeUrl(typeUrlPrefix) {
if (typeUrlPrefix === undefined) {
typeUrlPrefix = "type.googleapis.com";
}
return typeUrlPrefix + "/proto.SyncActionValue.BizAISettingsNudgeAction";
};
BizAISettingsNudgeAction.BizAISettingsCategory = (function() {
const valuesById = {}, values = Object.create(valuesById);
values[valuesById[0] = "UNKNOWN"] = 0;
values[valuesById[1] = "INSTRUCTIONS"] = 1;
values[valuesById[2] = "RESPONSE_SETTINGS"] = 2;
values[valuesById[3] = "EXAMPLE_RESPONSES"] = 3;
values[valuesById[4] = "KNOWLEDGE"] = 4;
values[valuesById[5] = "LEAD_GEN"] = 5;
return values;
})();
return BizAISettingsNudgeAction;
})();
SyncActionValue.BotWelcomeRequestAction = (function() { SyncActionValue.BotWelcomeRequestAction = (function() {
function BotWelcomeRequestAction(p) { function BotWelcomeRequestAction(p) {
@@ -96080,6 +96406,7 @@ export const proto = $root.proto = (() => {
function StatusPrivacyAction(p) { function StatusPrivacyAction(p) {
this.userJid = []; this.userJid = [];
this.customLists = []; this.customLists = [];
this.modes = [];
if (p) if (p)
for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) for (var ks = Object.keys(p), i = 0; i < ks.length; ++i)
if (p[ks[i]] != null) if (p[ks[i]] != null)
@@ -96091,6 +96418,7 @@ export const proto = $root.proto = (() => {
StatusPrivacyAction.prototype.shareToFB = null; StatusPrivacyAction.prototype.shareToFB = null;
StatusPrivacyAction.prototype.shareToIG = null; StatusPrivacyAction.prototype.shareToIG = null;
StatusPrivacyAction.prototype.customLists = $util.emptyArray; StatusPrivacyAction.prototype.customLists = $util.emptyArray;
StatusPrivacyAction.prototype.modes = $util.emptyArray;
let $oneOfFields; let $oneOfFields;
@@ -96133,6 +96461,12 @@ export const proto = $root.proto = (() => {
for (var i = 0; i < m.customLists.length; ++i) for (var i = 0; i < m.customLists.length; ++i)
$root.proto.SyncActionValue.StatusPrivacyAction.CustomList.encode(m.customLists[i], w.uint32(42).fork()).ldelim(); $root.proto.SyncActionValue.StatusPrivacyAction.CustomList.encode(m.customLists[i], w.uint32(42).fork()).ldelim();
} }
if (m.modes != null && m.modes.length) {
w.uint32(50).fork();
for (var i = 0; i < m.modes.length; ++i)
w.int32(m.modes[i]);
w.ldelim();
}
return w; return w;
}; };
@@ -96169,6 +96503,17 @@ export const proto = $root.proto = (() => {
m.customLists.push($root.proto.SyncActionValue.StatusPrivacyAction.CustomList.decode(r, r.uint32())); m.customLists.push($root.proto.SyncActionValue.StatusPrivacyAction.CustomList.decode(r, r.uint32()));
break; break;
} }
case 6: {
if (!(m.modes && m.modes.length))
m.modes = [];
if ((t & 7) === 2) {
var c2 = r.uint32() + r.pos;
while (r.pos < c2)
m.modes.push(r.int32());
} else
m.modes.push(r.int32());
break;
}
default: default:
r.skipType(t & 7); r.skipType(t & 7);
break; break;
@@ -96233,6 +96578,40 @@ export const proto = $root.proto = (() => {
m.customLists[i] = $root.proto.SyncActionValue.StatusPrivacyAction.CustomList.fromObject(d.customLists[i]); m.customLists[i] = $root.proto.SyncActionValue.StatusPrivacyAction.CustomList.fromObject(d.customLists[i]);
} }
} }
if (d.modes) {
if (!Array.isArray(d.modes))
throw TypeError(".proto.SyncActionValue.StatusPrivacyAction.modes: array expected");
m.modes = [];
for (var i = 0; i < d.modes.length; ++i) {
switch (d.modes[i]) {
default:
if (typeof d.modes[i] === "number") {
m.modes[i] = d.modes[i];
break;
}
case "ALLOW_LIST":
case 0:
m.modes[i] = 0;
break;
case "DENY_LIST":
case 1:
m.modes[i] = 1;
break;
case "CONTACTS":
case 2:
m.modes[i] = 2;
break;
case "CLOSE_FRIENDS":
case 3:
m.modes[i] = 3;
break;
case "CUSTOM_LIST":
case 4:
m.modes[i] = 4;
break;
}
}
}
return m; return m;
}; };
@@ -96243,6 +96622,7 @@ export const proto = $root.proto = (() => {
if (o.arrays || o.defaults) { if (o.arrays || o.defaults) {
d.userJid = []; d.userJid = [];
d.customLists = []; d.customLists = [];
d.modes = [];
} }
if (m.mode != null && m.hasOwnProperty("mode")) { if (m.mode != null && m.hasOwnProperty("mode")) {
d.mode = o.enums === String ? $root.proto.SyncActionValue.StatusPrivacyAction.StatusDistributionMode[m.mode] === undefined ? m.mode : $root.proto.SyncActionValue.StatusPrivacyAction.StatusDistributionMode[m.mode] : m.mode; d.mode = o.enums === String ? $root.proto.SyncActionValue.StatusPrivacyAction.StatusDistributionMode[m.mode] === undefined ? m.mode : $root.proto.SyncActionValue.StatusPrivacyAction.StatusDistributionMode[m.mode] : m.mode;
@@ -96271,6 +96651,12 @@ export const proto = $root.proto = (() => {
d.customLists[j] = $root.proto.SyncActionValue.StatusPrivacyAction.CustomList.toObject(m.customLists[j], o); d.customLists[j] = $root.proto.SyncActionValue.StatusPrivacyAction.CustomList.toObject(m.customLists[j], o);
} }
} }
if (m.modes && m.modes.length) {
d.modes = [];
for (var j = 0; j < m.modes.length; ++j) {
d.modes[j] = o.enums === String ? $root.proto.SyncActionValue.StatusPrivacyAction.StatusDistributionMode[m.modes[j]] === undefined ? m.modes[j] : $root.proto.SyncActionValue.StatusPrivacyAction.StatusDistributionMode[m.modes[j]] : m.modes[j];
}
}
return d; return d;
}; };
+1 -1
View File
@@ -1 +1 @@
{"version":[2,3000,1037166441]} {"version":[2,3000,1038147544]}
+4 -5
View File
@@ -1060,22 +1060,21 @@ export const makeChatsSocket = (config: SocketConfig) => {
} }
} }
/** sending non-abt props may fix QR scan fail if server expects */ /** fetch AB props */
const fetchProps = async () => { const fetchProps = async () => {
//TODO: implement both protocol 1 and protocol 2 prop fetching, specially for abKey for WM
const resultNode = await query({ const resultNode = await query({
tag: 'iq', tag: 'iq',
attrs: { attrs: {
to: S_WHATSAPP_NET, to: S_WHATSAPP_NET,
xmlns: 'w', xmlns: 'abt',
type: 'get' type: 'get'
}, },
content: [ content: [
{ {
tag: 'props', tag: 'props',
attrs: { attrs: {
protocol: '2', protocol: '1',
hash: authState?.creds?.lastPropHash || '' ...(authState?.creds?.lastPropHash ? { hash: authState.creds.lastPropHash } : {})
} }
} }
] ]
+6
View File
@@ -343,11 +343,13 @@ export const extractGroupMetadata = (result: BinaryNode) => {
let descId: string | undefined let descId: string | undefined
let descOwner: string | undefined let descOwner: string | undefined
let descOwnerPn: string | undefined let descOwnerPn: string | undefined
let descOwnerUsername: string | undefined
let descTime: number | undefined let descTime: number | undefined
if (descChild) { if (descChild) {
desc = getBinaryNodeChildString(descChild, 'body') desc = getBinaryNodeChildString(descChild, 'body')
descOwner = descChild.attrs.participant ? jidNormalizedUser(descChild.attrs.participant) : undefined descOwner = descChild.attrs.participant ? jidNormalizedUser(descChild.attrs.participant) : undefined
descOwnerPn = descChild.attrs.participant_pn ? jidNormalizedUser(descChild.attrs.participant_pn) : undefined descOwnerPn = descChild.attrs.participant_pn ? jidNormalizedUser(descChild.attrs.participant_pn) : undefined
descOwnerUsername = descChild.attrs.participant_username || undefined
descTime = +descChild.attrs.t! descTime = +descChild.attrs.t!
descId = descChild.attrs.id descId = descChild.attrs.id
} }
@@ -362,16 +364,19 @@ export const extractGroupMetadata = (result: BinaryNode) => {
subject: group.attrs.subject!, subject: group.attrs.subject!,
subjectOwner: group.attrs.s_o, subjectOwner: group.attrs.s_o,
subjectOwnerPn: group.attrs.s_o_pn, subjectOwnerPn: group.attrs.s_o_pn,
subjectOwnerUsername: group.attrs.s_o_username,
subjectTime: +(group.attrs.s_t ?? '0'), subjectTime: +(group.attrs.s_t ?? '0'),
size: group.attrs.size ? +group.attrs.size : getBinaryNodeChildren(group, 'participant').length, size: group.attrs.size ? +group.attrs.size : getBinaryNodeChildren(group, 'participant').length,
creation: +(group.attrs.creation ?? '0'), creation: +(group.attrs.creation ?? '0'),
owner: group.attrs.creator ? jidNormalizedUser(group.attrs.creator) : undefined, owner: group.attrs.creator ? jidNormalizedUser(group.attrs.creator) : undefined,
ownerPn: group.attrs.creator_pn ? jidNormalizedUser(group.attrs.creator_pn) : undefined, ownerPn: group.attrs.creator_pn ? jidNormalizedUser(group.attrs.creator_pn) : undefined,
ownerUsername: group.attrs.creator_username || undefined,
owner_country_code: group.attrs.creator_country_code, owner_country_code: group.attrs.creator_country_code,
desc, desc,
descId, descId,
descOwner, descOwner,
descOwnerPn, descOwnerPn,
descOwnerUsername,
descTime, descTime,
linkedParent: getBinaryNodeChild(group, 'linked_parent')?.attrs.jid || undefined, linkedParent: getBinaryNodeChild(group, 'linked_parent')?.attrs.jid || undefined,
restrict: !!getBinaryNodeChild(group, 'locked'), restrict: !!getBinaryNodeChild(group, 'locked'),
@@ -386,6 +391,7 @@ export const extractGroupMetadata = (result: BinaryNode) => {
id: attrs.jid!, id: attrs.jid!,
phoneNumber: isLidUser(attrs.jid) && isPnUser(attrs.phone_number) ? attrs.phone_number : undefined, phoneNumber: isLidUser(attrs.jid) && isPnUser(attrs.phone_number) ? attrs.phone_number : undefined,
lid: isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined, lid: isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined,
username: attrs.participant_username || attrs.username || undefined,
admin: (attrs.type || null) as GroupParticipant['admin'] admin: (attrs.type || null) as GroupParticipant['admin']
} }
}), }),
+39 -34
View File
@@ -2124,12 +2124,6 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
if (resolvedRemoteJid) key.remoteJid = resolvedRemoteJid if (resolvedRemoteJid) key.remoteJid = resolvedRemoteJid
if (resolvedParticipant) key.participant = resolvedParticipant if (resolvedParticipant) key.participant = resolvedParticipant
if (shouldIgnoreJid(remoteJid!) && remoteJid !== S_WHATSAPP_NET) {
logger.trace({ remoteJid }, 'ignoring receipt from jid')
await sendMessageAck(node)
return
}
const ids = [attrs.id!] const ids = [attrs.id!]
if (Array.isArray(content)) { if (Array.isArray(content)) {
const items = getBinaryNodeChildren(content[0], 'item') const items = getBinaryNodeChildren(content[0], 'item')
@@ -2205,11 +2199,6 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
const handleNotification = async (node: BinaryNode) => { const handleNotification = async (node: BinaryNode) => {
const remoteJid = node.attrs.from const remoteJid = node.attrs.from
if (shouldIgnoreJid(remoteJid!) && remoteJid !== S_WHATSAPP_NET) {
logger.trace({ remoteJid }, 'ignored notification')
await sendMessageAck(node)
return
}
try { try {
await Promise.all([ await Promise.all([
@@ -2223,6 +2212,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
fromMe, fromMe,
participant: node.attrs.participant, participant: node.attrs.participant,
participantAlt, participantAlt,
participantUsername: node.attrs.participant_username,
addressingMode, addressingMode,
id: node.attrs.id, id: node.attrs.id,
...(msg.key || {}) ...(msg.key || {})
@@ -2231,6 +2221,10 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
msg.messageTimestamp = +node.attrs.t! msg.messageTimestamp = +node.attrs.t!
const fullMsg = proto.WebMessageInfo.fromObject(msg) as WAMessage const fullMsg = proto.WebMessageInfo.fromObject(msg) as WAMessage
// Preserve custom WAMessageKey fields (participantAlt, participantUsername,
// addressingMode) that proto.WebMessageInfo.fromObject strips because
// they aren't part of the proto.MessageKey schema.
fullMsg.key = msg.key
await upsertMessage(fullMsg, 'append') await upsertMessage(fullMsg, 'append')
} }
}) })
@@ -2241,32 +2235,9 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
} }
const handleMessage = async (node: BinaryNode) => { const handleMessage = async (node: BinaryNode) => {
const _encNode = getBinaryNodeChild(node, 'enc')
const _unavNode = getBinaryNodeChild(node, 'unavailable')
// VO_RAW: log BEFORE any filtering — captures every stanza that enters handleMessage
console.log(`[VO_RAW] id=${node.attrs.id} from=${node.attrs.from} type=${node.attrs.type} enc=${_encNode ? _encNode.attrs?.type : 'NO'} unavailable=${_unavNode ? _unavNode.attrs?.type : 'NO'} children=${Array.isArray(node.content) ? (node.content as BinaryNode[]).map(c => c.tag).join(',') : 'none'}`)
if (shouldIgnoreJid(node.attrs.from!) && node.attrs.from !== S_WHATSAPP_NET) {
console.log(`[VO_RAW] IGNORED by shouldIgnoreJid: ${node.attrs.from}`)
logger.trace({ from: node.attrs.from }, 'ignored message')
await sendMessageAck(node)
return
}
const encNode = getBinaryNodeChild(node, 'enc') const encNode = getBinaryNodeChild(node, 'enc')
const unavailableNode = getBinaryNodeChild(node, 'unavailable') const unavailableNode = getBinaryNodeChild(node, 'unavailable')
// DEBUG: log every incoming message stanza to trace view-once delivery
logger.info({
id: node.attrs.id,
from: node.attrs.from,
type: node.attrs.type,
hasEnc: !!encNode,
encType: encNode?.attrs?.type,
hasUnavailable: !!unavailableNode,
unavailableType: unavailableNode?.attrs?.type,
}, 'VO_TRACE: incoming message stanza')
// msmsg block removed — allow msmsg decryption (required for view-once on linked devices) // msmsg block removed — allow msmsg decryption (required for view-once on linked devices)
if (unavailableNode?.attrs?.type === 'view_once') { if (unavailableNode?.attrs?.type === 'view_once') {
const { fullMessage: voMsg } = decryptMessageNode( const { fullMessage: voMsg } = decryptMessageNode(
@@ -3117,6 +3088,40 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
identifier: string, identifier: string,
exec: (node: BinaryNode) => Promise<void> exec: (node: BinaryNode) => Promise<void>
) => { ) => {
// Fast path: ack and drop ignored JIDs before entering the buffer/queue.
// Skips type='call' so call events are never silently dropped via
// shouldIgnoreJid (preserves InfiniteAPI's pre-existing behavior).
// Wrapped in try/catch so a throw from shouldIgnoreJid (user callback)
// or sendMessageAck (e.g. websocket closed) is routed through
// onUnexpectedError instead of becoming an unhandled rejection —
// matches the protection processNodeWithBuffer provides.
if (type !== 'call') {
try {
const from = node.attrs.from
let ignoreJid = from
if (type === 'receipt' && from) {
const attrs = node.attrs
const isLid = attrs.from!.includes('lid')
const isNodeFromMe = areJidsSameUser(
attrs.participant || attrs.from,
isLid ? authState.creds.me?.lid : authState.creds.me?.id
)
ignoreJid = !isNodeFromMe || isJidGroup(attrs.from) ? attrs.from : attrs.recipient
}
if (ignoreJid && ignoreJid !== S_WHATSAPP_NET && shouldIgnoreJid(ignoreJid)) {
// Plain ack (no NACK error code) preserves InfiniteAPI's prior
// behavior — ignored stanzas are an intentional drop, not a
// processing failure, so we don't want server-side retries.
await sendMessageAck(node)
return
}
} catch (error) {
onUnexpectedError(error as Error, identifier)
return
}
}
const isOffline = !!node.attrs.offline const isOffline = !!node.attrs.offline
if (isOffline) { if (isOffline) {
+2
View File
@@ -9,6 +9,8 @@ export interface Contact {
name?: string name?: string
/** name of the contact, the contact has set on their own on WA */ /** name of the contact, the contact has set on their own on WA */
notify?: string notify?: string
/** username associated with this contact, when provided by WA */
username?: string
/** I have no idea */ /** I have no idea */
verifiedName?: string verifiedName?: string
// Baileys Added // Baileys Added
+2
View File
@@ -81,6 +81,7 @@ export type BaileysEventMap = {
id: string id: string
author: string author: string
authorPn?: string authorPn?: string
authorUsername?: string
participants: GroupParticipant[] participants: GroupParticipant[]
action: ParticipantAction action: ParticipantAction
} }
@@ -88,6 +89,7 @@ export type BaileysEventMap = {
id: string id: string
author: string author: string
authorPn?: string authorPn?: string
authorUsername?: string
participant: string participant: string
participantPn?: string participantPn?: string
action: RequestJoinAction action: RequestJoinAction
+4
View File
@@ -20,17 +20,20 @@ export interface GroupMetadata {
addressingMode?: WAMessageAddressingMode addressingMode?: WAMessageAddressingMode
owner: string | undefined owner: string | undefined
ownerPn?: string | undefined ownerPn?: string | undefined
ownerUsername?: string | undefined
owner_country_code?: string | undefined owner_country_code?: string | undefined
subject: string subject: string
/** group subject owner */ /** group subject owner */
subjectOwner?: string subjectOwner?: string
subjectOwnerPn?: string subjectOwnerPn?: string
subjectOwnerUsername?: string
/** group subject modification date */ /** group subject modification date */
subjectTime?: number subjectTime?: number
creation?: number creation?: number
desc?: string desc?: string
descOwner?: string descOwner?: string
descOwnerPn?: string descOwnerPn?: string
descOwnerUsername?: string
descId?: string descId?: string
descTime?: number descTime?: number
/** if this group is part of a community, it returns the jid of the community to which it belongs */ /** if this group is part of a community, it returns the jid of the community to which it belongs */
@@ -56,6 +59,7 @@ export interface GroupMetadata {
/** the person who added you to group or changed some setting in group */ /** the person who added you to group or changed some setting in group */
author?: string author?: string
authorPn?: string authorPn?: string
authorUsername?: string
} }
export interface WAGroupCreateResponse { export interface WAGroupCreateResponse {
+2
View File
@@ -19,7 +19,9 @@ export type WAContactMessage = proto.Message.IContactMessage
export type WAContactsArrayMessage = proto.Message.IContactsArrayMessage export type WAContactsArrayMessage = proto.Message.IContactsArrayMessage
export type WAMessageKey = proto.IMessageKey & { export type WAMessageKey = proto.IMessageKey & {
remoteJidAlt?: string remoteJidAlt?: string
remoteJidUsername?: string
participantAlt?: string participantAlt?: string
participantUsername?: string
server_id?: string server_id?: string
addressingMode?: string addressingMode?: string
isViewOnce?: boolean // TODO: remove out of the message key, place in WebMessageInfo isViewOnce?: boolean // TODO: remove out of the message key, place in WebMessageInfo
+1
View File
@@ -1083,6 +1083,7 @@ export const processSyncAction = (
action.lidContactAction.firstName || action.lidContactAction.firstName ||
action.lidContactAction.username || action.lidContactAction.username ||
undefined, undefined,
username: action.lidContactAction.username || undefined,
lid: id!, lid: id!,
phoneNumber: undefined phoneNumber: undefined
} }
+4
View File
@@ -242,10 +242,14 @@ export function decodeMessageNode(stanza: BinaryNode, meId: string, meLid: strin
const key: WAMessageKey = { const key: WAMessageKey = {
remoteJid: chatId, remoteJid: chatId,
remoteJidAlt: !isJidGroup(chatId) ? addressingContext.senderAlt : undefined, remoteJidAlt: !isJidGroup(chatId) ? addressingContext.senderAlt : undefined,
remoteJidUsername: !isJidGroup(chatId)
? stanza.attrs.peer_recipient_username || stanza.attrs.recipient_username
: undefined,
fromMe, fromMe,
id: msgId, id: msgId,
participant, participant,
participantAlt: isJidGroup(chatId) ? addressingContext.senderAlt : undefined, participantAlt: isJidGroup(chatId) ? addressingContext.senderAlt : undefined,
participantUsername: stanza.attrs.participant ? stanza.attrs.participant_username : undefined,
addressingMode: addressingContext.addressingMode, addressingMode: addressingContext.addressingMode,
...(msgType === 'newsletter' && stanza.attrs.server_id ? { server_id: stanza.attrs.server_id } : {}) ...(msgType === 'newsletter' && stanza.attrs.server_id ? { server_id: stanza.attrs.server_id } : {})
} }
+1
View File
@@ -315,6 +315,7 @@ export const processHistoryMessage = (item: proto.IHistorySync, logger?: ILogger
contacts.push({ contacts.push({
id: chatId, id: chatId,
name: chat.displayName || chat.name || chat.username || undefined, name: chat.displayName || chat.name || chat.username || undefined,
username: chat.username || undefined,
lid: chat.lidJid || chat.accountLid || undefined, lid: chat.lidJid || chat.accountLid || undefined,
phoneNumber: chat.pnJid || undefined phoneNumber: chat.pnJid || undefined
}) })
+9 -1
View File
@@ -763,12 +763,19 @@ const processMessage = async (
id: jid, id: jid,
author: message.key.participant!, author: message.key.participant!,
authorPn: message.key.participantAlt!, authorPn: message.key.participantAlt!,
authorUsername: message.key.participantUsername!,
participants, participants,
action action
}) })
const emitGroupUpdate = (update: Partial<GroupMetadata>) => { const emitGroupUpdate = (update: Partial<GroupMetadata>) => {
ev.emit('groups.update', [ ev.emit('groups.update', [
{ id: jid, ...update, author: message.key.participant ?? undefined, authorPn: message.key.participantAlt } {
id: jid,
...update,
author: message.key.participant ?? undefined,
authorPn: message.key.participantAlt,
authorUsername: message.key.participantUsername
}
]) ])
} }
@@ -777,6 +784,7 @@ const processMessage = async (
id: jid, id: jid,
author: message.key.participant!, author: message.key.participant!,
authorPn: message.key.participantAlt!, authorPn: message.key.participantAlt!,
authorUsername: message.key.participantUsername!,
participant: participant.lid, participant: participant.lid,
participantPn: participant.pn, participantPn: participant.pn,
action, action,
+1
View File
@@ -46,6 +46,7 @@ export const processContactAction = (
{ {
id, id,
name: action.fullName || action.firstName || action.username || undefined, name: action.fullName || action.firstName || action.username || undefined,
username: action.username || undefined,
lid: lidJid || undefined, lid: lidJid || undefined,
phoneNumber phoneNumber
} }
+29 -3
View File
@@ -13,11 +13,37 @@ export class USyncContactProtocol implements USyncQueryProtocol {
} }
getUserElement(user: USyncUser): BinaryNode { getUserElement(user: USyncUser): BinaryNode {
//TODO: Implement type / username fields (not yet supported) if (user.phone) {
return {
tag: 'contact',
attrs: {},
content: user.phone
}
}
if (user.username) {
return {
tag: 'contact',
attrs: {
username: user.username,
...(user.usernameKey ? { pin: user.usernameKey } : {}),
...(user.lid ? { lid: user.lid } : {})
}
}
}
if (user.type) {
return {
tag: 'contact',
attrs: {
type: user.type
}
}
}
return { return {
tag: 'contact', tag: 'contact',
attrs: {}, attrs: {}
content: user.phone
} }
} }
@@ -0,0 +1,28 @@
import type { USyncQueryProtocol } from '../../Types/USync'
import { assertNodeErrorFree, type BinaryNode } from '../../WABinary'
import { USyncUser } from '../USyncUser'
export class USyncUsernameProtocol implements USyncQueryProtocol {
name = 'username'
getQueryElement(): BinaryNode {
return {
tag: 'username',
attrs: {}
}
}
getUserElement(user: USyncUser): BinaryNode | null {
void user
return null
}
parser(node: BinaryNode): string | null {
if (node.tag === 'username') {
assertNodeErrorFree(node)
return typeof node.content === 'string' ? node.content : null
}
return null
}
}
+1
View File
@@ -2,3 +2,4 @@ export * from './USyncDeviceProtocol'
export * from './USyncContactProtocol' export * from './USyncContactProtocol'
export * from './USyncStatusProtocol' export * from './USyncStatusProtocol'
export * from './USyncDisappearingModeProtocol' export * from './USyncDisappearingModeProtocol'
export * from './USyncUsernameProtocol'
+7 -1
View File
@@ -6,7 +6,8 @@ import {
USyncContactProtocol, USyncContactProtocol,
USyncDeviceProtocol, USyncDeviceProtocol,
USyncDisappearingModeProtocol, USyncDisappearingModeProtocol,
USyncStatusProtocol USyncStatusProtocol,
USyncUsernameProtocol
} from './Protocols' } from './Protocols'
import { USyncUser } from './USyncUser' import { USyncUser } from './USyncUser'
@@ -137,4 +138,9 @@ export class USyncQuery {
this.protocols.push(new USyncLIDProtocol()) this.protocols.push(new USyncLIDProtocol())
return this return this
} }
withUsernameProtocol() {
this.protocols.push(new USyncUsernameProtocol())
return this
}
} }
+12
View File
@@ -2,6 +2,8 @@ export class USyncUser {
id?: string id?: string
lid?: string lid?: string
phone?: string phone?: string
username?: string
usernameKey?: string
type?: string type?: string
personaId?: string personaId?: string
@@ -20,6 +22,16 @@ export class USyncUser {
return this return this
} }
withUsername(username: string) {
this.username = username
return this
}
withUsernameKey(usernameKey: string) {
this.usernameKey = usernameKey
return this
}
withType(type: string) { withType(type: string) {
this.type = type this.type = type
return this return this
@@ -0,0 +1,146 @@
import { jest } from '@jest/globals'
import { proto, type WAMessage } from '../..'
import { DEFAULT_CONNECTION_CONFIG } from '../../Defaults'
import makeWASocket from '../../Socket'
import { makeSession, mockWebSocket } from '../TestUtils/session'
mockWebSocket()
// chats.ts treats a connection as a reconnection when EITHER signal is true:
// 1. authState.creds.accountSyncCounter > 0
// (at least one full history sync completed in a previous session)
// 2. socketSkippedOfflineBuffer (forwarded from socket.ts as `skipOfflineBuffer`)
// (socket.ts already decided to skip the offline buffer, e.g. because
// routingInfo was stale and was discarded on startup)
// On reconnection, AwaitingInitialSync is skipped and the buffer is flushed
// immediately so live messages are not held in the initial-sync window.
describe('Reconnection Sync Skip', () => {
it('should skip the history sync wait on reconnection (accountSyncCounter > 0)', async () => {
const { state, clear } = await makeSession()
state.creds.me = { id: '1234567890:1@s.whatsapp.net', name: 'Test User' }
// Simulate a session that has already synced before
state.creds.accountSyncCounter = 1
const sock = makeWASocket({
...DEFAULT_CONNECTION_CONFIG,
auth: state
})
const messageListener = jest.fn()
sock.ev.on('messages.upsert', messageListener)
// Simulate receiving pending notifications (triggers AwaitingInitialSync)
sock.ev.emit('connection.update', { receivedPendingNotifications: true })
// Emit a message immediately after — if the wait is skipped,
// the buffer should already be flushed and this message should be delivered.
const msg = proto.WebMessageInfo.fromObject({
key: { remoteJid: '1234567890@s.whatsapp.net', fromMe: false, id: 'MSG_AFTER_RECONNECT' },
messageTimestamp: Date.now() / 1000,
message: { conversation: 'Hello after reconnect' }
}) as WAMessage
sock.ev.emit('messages.upsert', { messages: [msg], type: 'notify' })
await new Promise(resolve => setTimeout(resolve, 50))
// Message should be delivered immediately, NOT buffered
expect(messageListener).toHaveBeenCalledTimes(1)
expect(messageListener).toHaveBeenCalledWith(
expect.objectContaining({
messages: expect.arrayContaining([expect.objectContaining({ key: msg.key })]),
type: 'notify'
})
)
await sock.end(new Error('Test completed'))
await clear()
})
it('should skip the history sync wait when socketSkippedOfflineBuffer is true (stale routingInfo, accountSyncCounter === 0)', async () => {
const { state, clear } = await makeSession()
state.creds.me = { id: '1234567890:1@s.whatsapp.net', name: 'Test User' }
// Fresh-looking session from the counter perspective
state.creds.accountSyncCounter = 0
// But routingInfo is present and we ask the socket to discard it on start.
// socket.ts will set hadStaleRoutingInfo=true → skipOfflineBuffer=true,
// which chats.ts forwards as socketSkippedOfflineBuffer. Without this
// branch, the buffers would be misaligned (offline buffer skipped while
// AwaitingInitialSync still waits) and live messages would stall.
state.creds.routingInfo = Buffer.from([1, 2, 3, 4])
const sock = makeWASocket({
...DEFAULT_CONNECTION_CONFIG,
auth: state,
clearRoutingInfoOnStart: true
})
const messageListener = jest.fn()
sock.ev.on('messages.upsert', messageListener)
sock.ev.emit('connection.update', { receivedPendingNotifications: true })
const msg = proto.WebMessageInfo.fromObject({
key: { remoteJid: '1234567890@s.whatsapp.net', fromMe: false, id: 'MSG_AFTER_STALE_ROUTING' },
messageTimestamp: Date.now() / 1000,
message: { conversation: 'Hello after stale routing reconnect' }
}) as WAMessage
sock.ev.emit('messages.upsert', { messages: [msg], type: 'notify' })
await new Promise(resolve => setTimeout(resolve, 50))
expect(messageListener).toHaveBeenCalledTimes(1)
expect(messageListener).toHaveBeenCalledWith(
expect.objectContaining({
messages: expect.arrayContaining([expect.objectContaining({ key: msg.key })]),
type: 'notify'
})
)
await sock.end(new Error('Test completed'))
await clear()
})
it('should still wait for history sync on fresh pairing (both signals false)', async () => {
const { state, clear } = await makeSession()
state.creds.me = { id: '1234567890:1@s.whatsapp.net', name: 'Test User' }
// Fresh pairing — both reconnect signals are false:
// accountSyncCounter is 0 (default) and no stale routingInfo to clear.
state.creds.accountSyncCounter = 0
const sock = makeWASocket({
...DEFAULT_CONNECTION_CONFIG,
auth: state
})
const messageListener = jest.fn()
sock.ev.on('messages.upsert', messageListener)
sock.ev.emit('connection.update', { receivedPendingNotifications: true })
const msg = proto.WebMessageInfo.fromObject({
key: { remoteJid: '1234567890@s.whatsapp.net', fromMe: false, id: 'MSG_DURING_INITIAL_SYNC' },
messageTimestamp: Date.now() / 1000,
message: { conversation: 'Hello during initial sync' }
}) as WAMessage
sock.ev.emit('messages.upsert', { messages: [msg], type: 'notify' })
await new Promise(resolve => setTimeout(resolve, 50))
// Message should be BUFFERED (not delivered) because we're waiting for history sync
expect(messageListener).toHaveBeenCalledTimes(0)
// Drain the 2s AwaitingInitialSync timeout (chats.ts:1522) so its callback
// doesn't fire after Jest considers the test done — otherwise we get a
// "Cannot log after tests are done" warning from the post-teardown flush
// and the suite is flagged with --detectOpenHandles. After the timer
// fires, syncState becomes Online and the buffer flushes; that delivery
// is post-assertion and irrelevant to the test goal.
await new Promise(resolve => setTimeout(resolve, 2_100))
await sock.end(new Error('Test completed'))
await clear()
}, 10_000)
})