@codexverified/baileys 2.7.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +233 -0
  3. package/WAProto/V +1 -0
  4. package/WAProto/WAProto.proto +8051 -0
  5. package/WAProto/index.js +289292 -0
  6. package/engine-requirements.js +13 -0
  7. package/lib/Defaults/index.js +158 -0
  8. package/lib/Signal/Group/ciphertext-message.js +11 -0
  9. package/lib/Signal/Group/group-session-builder.js +29 -0
  10. package/lib/Signal/Group/group_cipher.js +81 -0
  11. package/lib/Signal/Group/index.js +11 -0
  12. package/lib/Signal/Group/keyhelper.js +17 -0
  13. package/lib/Signal/Group/sender-chain-key.js +25 -0
  14. package/lib/Signal/Group/sender-key-distribution-message.js +62 -0
  15. package/lib/Signal/Group/sender-key-message.js +65 -0
  16. package/lib/Signal/Group/sender-key-name.js +47 -0
  17. package/lib/Signal/Group/sender-key-record.js +40 -0
  18. package/lib/Signal/Group/sender-key-state.js +83 -0
  19. package/lib/Signal/Group/sender-message-key.js +25 -0
  20. package/lib/Signal/libsignal.js +428 -0
  21. package/lib/Signal/lid-mapping.js +280 -0
  22. package/lib/Socket/Client/index.js +2 -0
  23. package/lib/Socket/Client/types.js +10 -0
  24. package/lib/Socket/Client/websocket.js +53 -0
  25. package/lib/Socket/aigroups.js +315 -0
  26. package/lib/Socket/business.js +407 -0
  27. package/lib/Socket/chats.js +1831 -0
  28. package/lib/Socket/communities.js +431 -0
  29. package/lib/Socket/graphql.js +899 -0
  30. package/lib/Socket/groups.js +549 -0
  31. package/lib/Socket/index.js +79 -0
  32. package/lib/Socket/interactive-handler.js +600 -0
  33. package/lib/Socket/interop.js +563 -0
  34. package/lib/Socket/managed-account.js +208 -0
  35. package/lib/Socket/messages-recv.js +2144 -0
  36. package/lib/Socket/messages-send.js +2030 -0
  37. package/lib/Socket/mex.js +41 -0
  38. package/lib/Socket/newsletter.js +226 -0
  39. package/lib/Socket/privacy.js +446 -0
  40. package/lib/Socket/registration.js +428 -0
  41. package/lib/Socket/socket.js +1017 -0
  42. package/lib/Socket/username.js +229 -0
  43. package/lib/Store/index.js +4 -0
  44. package/lib/Store/make-cache-manager-store.js +89 -0
  45. package/lib/Store/make-in-memory-store.js +420 -0
  46. package/lib/Store/make-ordered-dictionary.js +78 -0
  47. package/lib/Store/object-repository.js +23 -0
  48. package/lib/Types/Auth.js +1 -0
  49. package/lib/Types/Bussines.js +1 -0
  50. package/lib/Types/Call.js +1 -0
  51. package/lib/Types/Chat.js +7 -0
  52. package/lib/Types/Contact.js +1 -0
  53. package/lib/Types/Events.js +1 -0
  54. package/lib/Types/GroupMetadata.js +1 -0
  55. package/lib/Types/Label.js +24 -0
  56. package/lib/Types/LabelAssociation.js +6 -0
  57. package/lib/Types/Message.js +17 -0
  58. package/lib/Types/Mex.js +39 -0
  59. package/lib/Types/Product.js +1 -0
  60. package/lib/Types/RichType.js +22 -0
  61. package/lib/Types/Signal.js +1 -0
  62. package/lib/Types/Socket.js +2 -0
  63. package/lib/Types/State.js +48 -0
  64. package/lib/Types/USync.js +1 -0
  65. package/lib/Types/index.js +25 -0
  66. package/lib/Utils/auth-utils.js +301 -0
  67. package/lib/Utils/bot-planning-replay.js +169 -0
  68. package/lib/Utils/browser-utils.js +28 -0
  69. package/lib/Utils/business.js +230 -0
  70. package/lib/Utils/chat-utils.js +885 -0
  71. package/lib/Utils/companion-reg-client-utils.js +32 -0
  72. package/lib/Utils/consumer-application.js +102 -0
  73. package/lib/Utils/crypto.js +117 -0
  74. package/lib/Utils/decode-wa-message.js +484 -0
  75. package/lib/Utils/event-buffer.js +589 -0
  76. package/lib/Utils/generics.js +388 -0
  77. package/lib/Utils/group-history.js +56 -0
  78. package/lib/Utils/history.js +130 -0
  79. package/lib/Utils/identity-change-handler.js +48 -0
  80. package/lib/Utils/index.js +34 -0
  81. package/lib/Utils/jid-display-normalization.js +209 -0
  82. package/lib/Utils/link-preview.js +84 -0
  83. package/lib/Utils/logger.js +2 -0
  84. package/lib/Utils/lt-hash.js +7 -0
  85. package/lib/Utils/make-mutex.js +32 -0
  86. package/lib/Utils/message-composer.js +467 -0
  87. package/lib/Utils/message-inspect.js +67 -0
  88. package/lib/Utils/message-retry-manager.js +241 -0
  89. package/lib/Utils/messages-media.js +1006 -0
  90. package/lib/Utils/messages.js +1851 -0
  91. package/lib/Utils/meta-ai-msmsg.js +130 -0
  92. package/lib/Utils/meta-compositing.js +194 -0
  93. package/lib/Utils/noise-handler.js +200 -0
  94. package/lib/Utils/offline-node-processor.js +39 -0
  95. package/lib/Utils/pre-key-manager.js +105 -0
  96. package/lib/Utils/process-message.js +527 -0
  97. package/lib/Utils/reporting-utils.js +257 -0
  98. package/lib/Utils/rich-message-utils.js +439 -0
  99. package/lib/Utils/signal.js +200 -0
  100. package/lib/Utils/stanza-ack.js +37 -0
  101. package/lib/Utils/sync-action-utils.js +47 -0
  102. package/lib/Utils/tc-token-utils.js +162 -0
  103. package/lib/Utils/use-multi-file-auth-state.js +120 -0
  104. package/lib/Utils/use-single-file-auth-state.js +96 -0
  105. package/lib/Utils/use-sqlite-auth-state.js +108 -0
  106. package/lib/Utils/validate-connection.js +206 -0
  107. package/lib/Utils/voip-rekey.js +20 -0
  108. package/lib/Utils/welcome-flow.js +217 -0
  109. package/lib/WABinary/constants.js +1463 -0
  110. package/lib/WABinary/decode.js +261 -0
  111. package/lib/WABinary/encode.js +219 -0
  112. package/lib/WABinary/generic-utils.js +227 -0
  113. package/lib/WABinary/index.js +5 -0
  114. package/lib/WABinary/jid-utils.js +95 -0
  115. package/lib/WABinary/types.js +1 -0
  116. package/lib/WAM/BinaryInfo.js +9 -0
  117. package/lib/WAM/constants.js +22852 -0
  118. package/lib/WAM/encode.js +149 -0
  119. package/lib/WAM/index.js +3 -0
  120. package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +96 -0
  121. package/lib/WAUSync/Protocols/USyncContactProtocol.js +56 -0
  122. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +53 -0
  123. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +26 -0
  124. package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +70 -0
  125. package/lib/WAUSync/Protocols/USyncPictureProtocol.js +28 -0
  126. package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +25 -0
  127. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +37 -0
  128. package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +34 -0
  129. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +24 -0
  130. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +50 -0
  131. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +28 -0
  132. package/lib/WAUSync/Protocols/index.js +12 -0
  133. package/lib/WAUSync/USyncQuery.js +201 -0
  134. package/lib/WAUSync/USyncUser.js +54 -0
  135. package/lib/WAUSync/index.js +3 -0
  136. package/lib/antiban.cjs +4726 -0
  137. package/lib/index.d.ts +286 -0
  138. package/lib/index.js +18 -0
  139. package/package.json +121 -0
@@ -0,0 +1,149 @@
1
+ import { BinaryInfo } from './BinaryInfo.js';
2
+ import { FLAG_BYTE, FLAG_EVENT, FLAG_EXTENDED, FLAG_FIELD, FLAG_GLOBAL, WEB_EVENTS, WEB_GLOBALS } from './constants.js';
3
+ const getHeaderBitLength = (key) => (key < 256 ? 2 : 3);
4
+ export const encodeWAM = (binaryInfo) => {
5
+ binaryInfo.buffer = [];
6
+ encodeWAMHeader(binaryInfo);
7
+ encodeEvents(binaryInfo);
8
+ const totalSize = binaryInfo.buffer.map(a => a.length).reduce((a, b) => a + b);
9
+ const buffer = Buffer.alloc(totalSize);
10
+ let offset = 0;
11
+ for (const buffer_ of binaryInfo.buffer) {
12
+ buffer_.copy(buffer, offset);
13
+ offset += buffer_.length;
14
+ }
15
+ return buffer;
16
+ };
17
+ function encodeWAMHeader(binaryInfo) {
18
+ const headerBuffer = Buffer.alloc(8); // starting buffer
19
+ headerBuffer.write('WAM', 0, 'utf8');
20
+ headerBuffer.writeUInt8(binaryInfo.protocolVersion, 3);
21
+ headerBuffer.writeUInt8(1, 4); //random flag
22
+ headerBuffer.writeUInt16BE(binaryInfo.sequence, 5);
23
+ headerBuffer.writeUInt8(0, 7); // regular channel
24
+ binaryInfo.buffer.push(headerBuffer);
25
+ }
26
+ function encodeGlobalAttributes(binaryInfo, globals) {
27
+ for (const [key, _value] of Object.entries(globals)) {
28
+ const id = WEB_GLOBALS.find(a => a?.name === key).id;
29
+ let value = _value;
30
+ if (typeof value === 'boolean') {
31
+ value = value ? 1 : 0;
32
+ }
33
+ binaryInfo.buffer.push(serializeData(id, value, FLAG_GLOBAL));
34
+ }
35
+ }
36
+ function encodeEvents(binaryInfo) {
37
+ for (const [name, { props, globals }] of binaryInfo.events.map(a => Object.entries(a)[0])) {
38
+ encodeGlobalAttributes(binaryInfo, globals);
39
+ const event = WEB_EVENTS.find(a => a.name === name);
40
+ const props_ = Object.entries(props);
41
+ let extended = false;
42
+ for (const [, value] of props_) {
43
+ extended || (extended = value !== null);
44
+ }
45
+ const eventFlag = extended ? FLAG_EVENT : FLAG_EVENT | FLAG_EXTENDED;
46
+ binaryInfo.buffer.push(serializeData(event.id, -event.weight, eventFlag));
47
+ for (let i = 0; i < props_.length; i++) {
48
+ const [key, _value] = props_[i];
49
+ const id = event.props[key]?.[0];
50
+ extended = i < props_.length - 1;
51
+ let value = _value;
52
+ if (typeof value === 'boolean') {
53
+ value = value ? 1 : 0;
54
+ }
55
+ const fieldFlag = extended ? FLAG_EVENT : FLAG_FIELD | FLAG_EXTENDED;
56
+ binaryInfo.buffer.push(serializeData(id, value, fieldFlag));
57
+ }
58
+ }
59
+ }
60
+ function serializeData(key, value, flag) {
61
+ const bufferLength = getHeaderBitLength(key);
62
+ let buffer;
63
+ let offset = 0;
64
+ if (value === null) {
65
+ if (flag === FLAG_GLOBAL) {
66
+ buffer = Buffer.alloc(bufferLength);
67
+ offset = serializeHeader(buffer, offset, key, flag);
68
+ return buffer;
69
+ }
70
+ }
71
+ else if (typeof value === 'number' && Number.isInteger(value)) {
72
+ // is number
73
+ if (value === 0 || value === 1) {
74
+ buffer = Buffer.alloc(bufferLength);
75
+ offset = serializeHeader(buffer, offset, key, flag | ((value + 1) << 4));
76
+ return buffer;
77
+ }
78
+ else if (-128 <= value && value < 128) {
79
+ buffer = Buffer.alloc(bufferLength + 1);
80
+ offset = serializeHeader(buffer, offset, key, flag | (3 << 4));
81
+ buffer.writeInt8(value, offset);
82
+ return buffer;
83
+ }
84
+ else if (-32768 <= value && value < 32768) {
85
+ buffer = Buffer.alloc(bufferLength + 2);
86
+ offset = serializeHeader(buffer, offset, key, flag | (4 << 4));
87
+ buffer.writeInt16LE(value, offset);
88
+ return buffer;
89
+ }
90
+ else if (-2147483648 <= value && value < 2147483648) {
91
+ buffer = Buffer.alloc(bufferLength + 4);
92
+ offset = serializeHeader(buffer, offset, key, flag | (5 << 4));
93
+ buffer.writeInt32LE(value, offset);
94
+ return buffer;
95
+ }
96
+ else {
97
+ buffer = Buffer.alloc(bufferLength + 8);
98
+ offset = serializeHeader(buffer, offset, key, flag | (7 << 4));
99
+ buffer.writeDoubleLE(value, offset);
100
+ return buffer;
101
+ }
102
+ }
103
+ else if (typeof value === 'number') {
104
+ // is float
105
+ buffer = Buffer.alloc(bufferLength + 8);
106
+ offset = serializeHeader(buffer, offset, key, flag | (7 << 4));
107
+ buffer.writeDoubleLE(value, offset);
108
+ return buffer;
109
+ }
110
+ else if (typeof value === 'string') {
111
+ // is string
112
+ const utf8Bytes = Buffer.byteLength(value, 'utf8');
113
+ if (utf8Bytes < 256) {
114
+ buffer = Buffer.alloc(bufferLength + 1 + utf8Bytes);
115
+ offset = serializeHeader(buffer, offset, key, flag | (8 << 4));
116
+ buffer.writeUint8(utf8Bytes, offset++);
117
+ }
118
+ else if (utf8Bytes < 65536) {
119
+ buffer = Buffer.alloc(bufferLength + 2 + utf8Bytes);
120
+ offset = serializeHeader(buffer, offset, key, flag | (9 << 4));
121
+ buffer.writeUInt16LE(utf8Bytes, offset);
122
+ offset += 2;
123
+ }
124
+ else {
125
+ buffer = Buffer.alloc(bufferLength + 4 + utf8Bytes);
126
+ offset = serializeHeader(buffer, offset, key, flag | (10 << 4));
127
+ buffer.writeUInt32LE(utf8Bytes, offset);
128
+ offset += 4;
129
+ }
130
+ buffer.write(value, offset, 'utf8');
131
+ return buffer;
132
+ }
133
+ throw 'missing';
134
+ }
135
+ function serializeHeader(buffer, offset, key, flag) {
136
+ if (key < 256) {
137
+ buffer.writeUInt8(flag, offset);
138
+ offset += 1;
139
+ buffer.writeUInt8(key, offset);
140
+ offset += 1;
141
+ }
142
+ else {
143
+ buffer.writeUInt8(flag | FLAG_BYTE, offset);
144
+ offset += 1;
145
+ buffer.writeUInt16LE(key, offset);
146
+ offset += 2;
147
+ }
148
+ return offset;
149
+ }
@@ -0,0 +1,3 @@
1
+ export * from './constants.js';
2
+ export * from './encode.js';
3
+ export * from './BinaryInfo.js';
@@ -0,0 +1,96 @@
1
+ import { assertNodeErrorFree, getBinaryNodeChild, getBinaryNodeChildren } from '../../WABinary/index.js';
2
+ export class USyncBusinessProtocol {
3
+ constructor(profileVersion = '2') {
4
+ this.name = 'business';
5
+ this.profileVersion = profileVersion;
6
+ }
7
+ getQueryElement() {
8
+ return {
9
+ tag: 'business',
10
+ attrs: {},
11
+ content: [
12
+ { tag: 'verified_name', attrs: {} },
13
+ { tag: 'profile', attrs: { v: this.profileVersion } }
14
+ ]
15
+ };
16
+ }
17
+ getUserElement(user) {
18
+ const children = [];
19
+ if (user.verifiedNameSerial) {
20
+ children.push({ tag: 'verified_name', attrs: { serial: user.verifiedNameSerial } });
21
+ }
22
+ if (user.businessProfileTag) {
23
+ children.push({ tag: 'profile', attrs: { tag: user.businessProfileTag } });
24
+ }
25
+ return children.length > 0 ? { tag: 'business', attrs: {}, content: children } : null;
26
+ }
27
+ parser(node) {
28
+ if (node.tag !== 'business') return null;
29
+ assertNodeErrorFree(node);
30
+ const verifiedNameNode = getBinaryNodeChild(node, 'verified_name');
31
+ const profileNode = getBinaryNodeChild(node, 'profile');
32
+
33
+ // Business hours: profile may contain a business_hours child with business_hours_config children
34
+ let businessHours = undefined;
35
+ if (profileNode) {
36
+ const businessHoursNode = getBinaryNodeChild(profileNode, 'business_hours');
37
+ if (businessHoursNode) {
38
+ const configs = getBinaryNodeChildren(businessHoursNode, 'business_hours_config');
39
+ businessHours = {
40
+ timezone: businessHoursNode.attrs?.timezone ?? null,
41
+ config: configs.map(({ attrs }) => ({
42
+ dayOfWeek: attrs?.day_of_week ?? null,
43
+ openTime: attrs?.open_time ?? null,
44
+ closeTime: attrs?.close_time ?? null,
45
+ mode: attrs?.mode ?? null
46
+ }))
47
+ };
48
+ }
49
+ }
50
+
51
+ // Business address from profile child node
52
+ const addressNode = profileNode ? getBinaryNodeChild(profileNode, 'address') : null;
53
+ const businessAddress = addressNode?.content
54
+ ? Buffer.isBuffer(addressNode.content)
55
+ ? addressNode.content.toString()
56
+ : String(addressNode.content)
57
+ : (profileNode?.attrs?.address ?? null);
58
+
59
+ // Catalog status from profile attrs
60
+ const catalogStatus = profileNode
61
+ ? {
62
+ exists: profileNode.attrs?.catalog_exists === 'true' || profileNode.attrs?.catalog_exists === true,
63
+ sendAll: profileNode.attrs?.catalog_send_all === 'true' || profileNode.attrs?.catalog_send_all === true
64
+ }
65
+ : null;
66
+
67
+ // Cart flags from profile attrs
68
+ const cartEnabled =
69
+ profileNode?.attrs?.cart_enabled !== undefined
70
+ ? profileNode.attrs.cart_enabled === 'true' || profileNode.attrs.cart_enabled === true
71
+ : null;
72
+ const webCartEnabled =
73
+ profileNode?.attrs?.web_cart_enabled !== undefined
74
+ ? profileNode.attrs.web_cart_enabled === 'true' || profileNode.attrs.web_cart_enabled === true
75
+ : null;
76
+ const webCartOnOff = profileNode?.attrs?.web_cart_on_off ?? null;
77
+
78
+ // Commerce experience from profile attrs
79
+ const commerceExperience = profileNode?.attrs?.commerce_experience ?? null;
80
+
81
+ return {
82
+ verifiedName: verifiedNameNode?.content ?? null,
83
+ verifiedLevel: verifiedNameNode?.attrs?.verified_level ?? null,
84
+ verifiedNameLevel: verifiedNameNode?.attrs?.verified_level ?? null,
85
+ profileTag: profileNode?.attrs?.tag ?? null,
86
+ pnJid: node.attrs?.pn_jid ?? null,
87
+ businessHours: businessHours ?? null,
88
+ businessAddress,
89
+ catalogStatus,
90
+ cartEnabled,
91
+ webCartEnabled,
92
+ webCartOnOff,
93
+ commerceExperience
94
+ };
95
+ }
96
+ }
@@ -0,0 +1,56 @@
1
+ import { assertNodeErrorFree } from '../../WABinary/index.js';
2
+ export class USyncContactProtocol {
3
+ constructor() {
4
+ this.name = 'contact';
5
+ }
6
+ getQueryElement() {
7
+ return {
8
+ tag: 'contact',
9
+ attrs: {}
10
+ };
11
+ }
12
+ getUserElement(user) {
13
+ if (user.phone) {
14
+ return {
15
+ tag: 'contact',
16
+ attrs: {},
17
+ content: user.phone
18
+ };
19
+ }
20
+ if (user.username) {
21
+ return {
22
+ tag: 'contact',
23
+ attrs: {
24
+ username: user.username,
25
+ ...(user.usernameKey ? { pin: user.usernameKey } : {}),
26
+ ...(user.lid ? { lid: user.lid } : {})
27
+ }
28
+ };
29
+ }
30
+ if (user.type) {
31
+ return {
32
+ tag: 'contact',
33
+ attrs: {
34
+ type: user.type
35
+ }
36
+ };
37
+ }
38
+ return {
39
+ tag: 'contact',
40
+ attrs: {}
41
+ };
42
+ }
43
+ parser(node) {
44
+ if (node.tag === 'contact') {
45
+ assertNodeErrorFree(node);
46
+ const inWA = node?.attrs?.type === 'in';
47
+ const restrictionType = node?.attrs?.stella_addressbook_restriction_type;
48
+ if (restrictionType !== undefined) {
49
+ // Return object with extra metadata when restriction type is present
50
+ return { inWA, stellaAddressbookRestrictionType: restrictionType };
51
+ }
52
+ return inWA;
53
+ }
54
+ return false;
55
+ }
56
+ }
@@ -0,0 +1,53 @@
1
+ import { assertNodeErrorFree, getBinaryNodeChild } from '../../WABinary/index.js';
2
+ export class USyncDeviceProtocol {
3
+ constructor() {
4
+ this.name = 'devices';
5
+ }
6
+ getQueryElement() {
7
+ return {
8
+ tag: 'devices',
9
+ attrs: {
10
+ version: '2'
11
+ }
12
+ };
13
+ }
14
+ getUserElement( /* user: USyncUser */) {
15
+ //TODO: Implement device phashing, ts and expectedTs
16
+ //TODO: if all are not present, return null <- current behavior
17
+ //TODO: otherwise return a node w tag 'devices' w those as attrs
18
+ return null;
19
+ }
20
+ parser(node) {
21
+ const deviceList = [];
22
+ let keyIndex = undefined;
23
+ if (node.tag === 'devices') {
24
+ assertNodeErrorFree(node);
25
+ const deviceListNode = getBinaryNodeChild(node, 'device-list');
26
+ const keyIndexNode = getBinaryNodeChild(node, 'key-index-list');
27
+ if (Array.isArray(deviceListNode?.content)) {
28
+ for (const { tag, attrs } of deviceListNode.content) {
29
+ const id = +attrs.id;
30
+ const keyIndex = +attrs['key-index'];
31
+ if (tag === 'device') {
32
+ deviceList.push({
33
+ id,
34
+ keyIndex,
35
+ isHosted: !!(attrs['is_hosted'] && attrs['is_hosted'] === 'true')
36
+ });
37
+ }
38
+ }
39
+ }
40
+ if (keyIndexNode?.tag === 'key-index-list') {
41
+ keyIndex = {
42
+ timestamp: +keyIndexNode.attrs['ts'],
43
+ signedKeyIndex: keyIndexNode?.content,
44
+ expectedTimestamp: keyIndexNode.attrs['expected_ts'] ? +keyIndexNode.attrs['expected_ts'] : undefined
45
+ };
46
+ }
47
+ }
48
+ return {
49
+ deviceList,
50
+ keyIndex
51
+ };
52
+ }
53
+ }
@@ -0,0 +1,26 @@
1
+ import { assertNodeErrorFree } from '../../WABinary/index.js';
2
+ export class USyncDisappearingModeProtocol {
3
+ constructor() {
4
+ this.name = 'disappearing_mode';
5
+ }
6
+ getQueryElement() {
7
+ return {
8
+ tag: 'disappearing_mode',
9
+ attrs: {}
10
+ };
11
+ }
12
+ getUserElement() {
13
+ return null;
14
+ }
15
+ parser(node) {
16
+ if (node.tag === 'disappearing_mode') {
17
+ assertNodeErrorFree(node);
18
+ const duration = +node?.attrs.duration;
19
+ const setAt = new Date(+(node?.attrs.t || 0) * 1000);
20
+ return {
21
+ duration,
22
+ setAt
23
+ };
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,70 @@
1
+ import { getBinaryNodeChild } from '../../WABinary/index.js';
2
+ // Device feature flags that can be queried, ported from WhatsApp Web's
3
+ // WAWebUsyncFeature module.
4
+ export const USYNC_FEATURES = [
5
+ 'document',
6
+ 'encrypt',
7
+ 'encrypt_blist',
8
+ 'encrypt_contact',
9
+ 'encrypt_group_gen2',
10
+ 'encrypt_image',
11
+ 'encrypt_location',
12
+ 'encrypt_url',
13
+ 'encrypt_v2',
14
+ 'voip',
15
+ 'voip_legacy',
16
+ 'multi_agent',
17
+ 'bot_eligible'
18
+ ];
19
+ export class USyncFeatureProtocol {
20
+ /**
21
+ * @param {string[]} [features] feature names to query (defaults to all known)
22
+ */
23
+ constructor(features) {
24
+ this.name = 'feature';
25
+ this.features = features && features.length ? features : USYNC_FEATURES;
26
+ }
27
+ getQueryElement() {
28
+ return {
29
+ tag: 'feature',
30
+ attrs: {},
31
+ content: this.features.map(feature => ({ tag: feature, attrs: {} }))
32
+ };
33
+ }
34
+ getUserElement() {
35
+ return null;
36
+ }
37
+ parser(node) {
38
+ if (node.tag !== 'feature') {
39
+ return null;
40
+ }
41
+ const errorNode = getBinaryNodeChild(node, 'error');
42
+ if (errorNode) {
43
+ return {
44
+ errorCode: errorNode.attrs?.code ? +errorNode.attrs.code : undefined,
45
+ errorText: errorNode.attrs?.text
46
+ };
47
+ }
48
+ const CAMEL_MAP = {
49
+ encrypt_v2: 'encryptV2',
50
+ voip_legacy: 'voipLegacy',
51
+ multi_agent: 'multiAgent',
52
+ bot_eligible: 'botEligible',
53
+ encrypt_blist: 'encryptBlist',
54
+ encrypt_contact: 'encryptContact',
55
+ encrypt_group_gen2: 'encryptGroupGen2',
56
+ encrypt_image: 'encryptImage',
57
+ encrypt_location: 'encryptLocation',
58
+ encrypt_url: 'encryptUrl'
59
+ };
60
+ const features = {};
61
+ const children = Array.isArray(node.content) ? node.content : [];
62
+ for (const child of children) {
63
+ if (child?.attrs && child.attrs.value !== undefined) {
64
+ const key = CAMEL_MAP[child.tag] || child.tag;
65
+ features[key] = child.attrs.value;
66
+ }
67
+ }
68
+ return features;
69
+ }
70
+ }
@@ -0,0 +1,28 @@
1
+ import { assertNodeErrorFree } from '../../WABinary/index.js';
2
+ export class USyncPictureProtocol {
3
+ constructor(type = 'image') {
4
+ this.name = 'picture';
5
+ // "image" for full resolution, "preview" for thumbnail
6
+ this.type = type;
7
+ }
8
+ getQueryElement() {
9
+ return {
10
+ tag: 'picture',
11
+ attrs: { type: this.type }
12
+ };
13
+ }
14
+ getUserElement(user) {
15
+ const attrs = {};
16
+ if (user.pictureId != null) attrs.id = String(user.pictureId);
17
+ return Object.keys(attrs).length > 0 ? { tag: 'picture', attrs } : null;
18
+ }
19
+ parser(node) {
20
+ if (node.tag !== 'picture') return null;
21
+ assertNodeErrorFree(node);
22
+ const id = node.attrs?.id ?? null;
23
+ const directPath = node.attrs?.direct_path ?? null;
24
+ const hash = node.attrs?.hash ?? null;
25
+ if (!id && !directPath) return null;
26
+ return { id, directPath, hash };
27
+ }
28
+ }
@@ -0,0 +1,25 @@
1
+ import { assertNodeErrorFree } from '../../WABinary/index.js';
2
+ export class USyncSidelistProtocol {
3
+ constructor(useLidAddressing = true) {
4
+ this.name = 'sidelist';
5
+ // When true, sends addressing_mode="lid" required for interop/LID-mode contacts
6
+ this.useLidAddressing = useLidAddressing;
7
+ }
8
+ getQueryElement() {
9
+ const attrs = {};
10
+ if (this.useLidAddressing) attrs.addressing_mode = 'lid';
11
+ return { tag: 'sidelist', attrs };
12
+ }
13
+ getUserElement(user) {
14
+ if (user.sidelistDelete) {
15
+ return { tag: 'sidelist', attrs: { type: 'delete' } };
16
+ }
17
+ return null;
18
+ }
19
+ parser(node) {
20
+ if (node.tag !== 'sidelist' && node.tag !== 'side_list') return null;
21
+ assertNodeErrorFree(node);
22
+ // Returns minimal parsed data; the full side_list is parsed in USyncQuery
23
+ return { type: node.attrs?.type ?? null };
24
+ }
25
+ }
@@ -0,0 +1,37 @@
1
+ import { assertNodeErrorFree } from '../../WABinary/index.js';
2
+ export class USyncStatusProtocol {
3
+ constructor() {
4
+ this.name = 'status';
5
+ }
6
+ getQueryElement() {
7
+ return {
8
+ tag: 'status',
9
+ attrs: {}
10
+ };
11
+ }
12
+ getUserElement() {
13
+ return null;
14
+ }
15
+ parser(node) {
16
+ if (node.tag === 'status') {
17
+ assertNodeErrorFree(node);
18
+ let status = node?.content?.toString() ?? null;
19
+ const setAt = new Date(+(node?.attrs.t || 0) * 1000);
20
+ if (!status) {
21
+ if (node.attrs?.code && +node.attrs.code === 401) {
22
+ status = '';
23
+ }
24
+ else {
25
+ status = null;
26
+ }
27
+ }
28
+ else if (typeof status === 'string' && status.length === 0) {
29
+ status = null;
30
+ }
31
+ return {
32
+ status,
33
+ setAt
34
+ };
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,34 @@
1
+ import { assertNodeErrorFree, getBinaryNodeChild } from '../../WABinary/index.js';
2
+ export class USyncTextStatusProtocol {
3
+ constructor() {
4
+ this.name = 'text_status';
5
+ }
6
+ getQueryElement() {
7
+ return {
8
+ tag: 'text_status',
9
+ attrs: {}
10
+ };
11
+ }
12
+ getUserElement() {
13
+ return null;
14
+ }
15
+ parser(node) {
16
+ if (node.tag !== 'text_status') return null;
17
+ assertNodeErrorFree(node);
18
+ const lastUpdateTimeSec = node.attrs?.last_update_time ? +node.attrs.last_update_time : 0;
19
+ const ephemeralDurationSec = node.attrs?.ephemeral_duration_sec ? +node.attrs.ephemeral_duration_sec : 0;
20
+ const text = node.attrs?.text ?? null;
21
+ const emojiNode = getBinaryNodeChild(node, 'emoji');
22
+ const emoji = emojiNode?.attrs?.content ?? null;
23
+ // expiry is lastUpdateTime + ephemeralDuration (both in ms); 0 means no expiry
24
+ const expiresAt = ephemeralDurationSec > 0
25
+ ? new Date((lastUpdateTimeSec + ephemeralDurationSec) * 1000)
26
+ : null;
27
+ return {
28
+ text,
29
+ emoji,
30
+ setAt: new Date(lastUpdateTimeSec * 1000),
31
+ expiresAt
32
+ };
33
+ }
34
+ }
@@ -0,0 +1,24 @@
1
+ import { assertNodeErrorFree } from '../../WABinary/index.js';
2
+ export class USyncUsernameProtocol {
3
+ constructor() {
4
+ this.name = 'username';
5
+ }
6
+ getQueryElement() {
7
+ return {
8
+ tag: 'username',
9
+ attrs: {}
10
+ };
11
+ }
12
+ getUserElement(user) {
13
+ void user;
14
+ return null;
15
+ }
16
+ parser(node) {
17
+ if (node.tag === 'username') {
18
+ assertNodeErrorFree(node);
19
+ const username = node.content != null ? node.content.toString() : null;
20
+ return username && username.length > 0 ? username : null;
21
+ }
22
+ return null;
23
+ }
24
+ }
@@ -0,0 +1,50 @@
1
+ import { getBinaryNodeChild, getBinaryNodeChildren, getBinaryNodeChildString } from '../../WABinary/index.js';
2
+ import { USyncUser } from '../USyncUser.js';
3
+ export class USyncBotProfileProtocol {
4
+ constructor() {
5
+ this.name = 'bot';
6
+ }
7
+ getQueryElement() {
8
+ return {
9
+ tag: 'bot',
10
+ attrs: {},
11
+ content: [{ tag: 'profile', attrs: { v: '1' } }]
12
+ };
13
+ }
14
+ getUserElement(user) {
15
+ return {
16
+ tag: 'bot',
17
+ attrs: {},
18
+ content: [{ tag: 'profile', attrs: { persona_id: user.personaId } }]
19
+ };
20
+ }
21
+ parser(node) {
22
+ const botNode = getBinaryNodeChild(node, 'bot');
23
+ const profile = getBinaryNodeChild(botNode, 'profile');
24
+ const commandsNode = getBinaryNodeChild(profile, 'commands');
25
+ const promptsNode = getBinaryNodeChild(profile, 'prompts');
26
+ const commands = [];
27
+ const prompts = [];
28
+ for (const command of getBinaryNodeChildren(commandsNode, 'command')) {
29
+ commands.push({
30
+ name: getBinaryNodeChildString(command, 'name'),
31
+ description: getBinaryNodeChildString(command, 'description')
32
+ });
33
+ }
34
+ for (const prompt of getBinaryNodeChildren(promptsNode, 'prompt')) {
35
+ prompts.push(`${getBinaryNodeChildString(prompt, 'emoji')} ${getBinaryNodeChildString(prompt, 'text')}`);
36
+ }
37
+ return {
38
+ isDefault: !!getBinaryNodeChild(profile, 'default'),
39
+ jid: node.attrs.jid,
40
+ name: getBinaryNodeChildString(profile, 'name'),
41
+ attributes: getBinaryNodeChildString(profile, 'attributes'),
42
+ description: getBinaryNodeChildString(profile, 'description'),
43
+ category: getBinaryNodeChildString(profile, 'category'),
44
+ personaId: profile.attrs['persona_id'],
45
+ commandsDescription: getBinaryNodeChildString(commandsNode, 'description'),
46
+ commands,
47
+ prompts
48
+ };
49
+ }
50
+ }