my-baileys 1.0.0
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.
Potentially problematic release.
This version of my-baileys might be problematic. Click here for more details.
- package/README.md +1145 -0
- package/WAProto/GenerateStatics.sh +3 -0
- package/WAProto/WAProto.proto +8083 -0
- package/WAProto/fix-imports.js +85 -0
- package/WAProto/index.d.ts +29095 -0
- package/WAProto/index.js +172336 -0
- package/engine-requirements.js +10 -0
- package/lib/Defaults/index.js +194 -0
- package/lib/Signal/Group/ciphertext-message.js +12 -0
- package/lib/Signal/Group/group-session-builder.js +30 -0
- package/lib/Signal/Group/group_cipher.js +82 -0
- package/lib/Signal/Group/index.js +12 -0
- package/lib/Signal/Group/keyhelper.js +18 -0
- package/lib/Signal/Group/sender-chain-key.js +26 -0
- package/lib/Signal/Group/sender-key-distribution-message.js +63 -0
- package/lib/Signal/Group/sender-key-message.js +66 -0
- package/lib/Signal/Group/sender-key-name.js +48 -0
- package/lib/Signal/Group/sender-key-record.js +41 -0
- package/lib/Signal/Group/sender-key-state.js +84 -0
- package/lib/Signal/Group/sender-message-key.js +26 -0
- package/lib/Signal/libsignal.js +431 -0
- package/lib/Signal/lid-mapping.js +277 -0
- package/lib/Socket/Client/index.js +3 -0
- package/lib/Socket/Client/types.js +11 -0
- package/lib/Socket/Client/websocket.js +102 -0
- package/lib/Socket/aigroups.js +221 -0
- package/lib/Socket/business.js +379 -0
- package/lib/Socket/chats.js +1193 -0
- package/lib/Socket/communities.js +431 -0
- package/lib/Socket/graphql.js +524 -0
- package/lib/Socket/groups.js +408 -0
- package/lib/Socket/index.js +57 -0
- package/lib/Socket/interop.js +345 -0
- package/lib/Socket/luxu.js +510 -0
- package/lib/Socket/managed-account.js +99 -0
- package/lib/Socket/message-builder.js +523 -0
- package/lib/Socket/messages-recv.js +2009 -0
- package/lib/Socket/messages-send.js +1608 -0
- package/lib/Socket/mex.js +56 -0
- package/lib/Socket/newsletter.js +624 -0
- package/lib/Socket/privacy.js +128 -0
- package/lib/Socket/registration.js +238 -0
- package/lib/Socket/socket.js +1000 -0
- package/lib/Socket/text-router.js +67 -0
- package/lib/Socket/username.js +234 -0
- package/lib/Store/index.js +10 -0
- package/lib/Store/keyed-db.js +108 -0
- package/lib/Store/make-cache-manager-store.js +85 -0
- package/lib/Store/make-in-memory-store.js +198 -0
- package/lib/Store/make-ordered-dictionary.js +75 -0
- package/lib/Store/object-repository.js +32 -0
- package/lib/Types/Auth.js +2 -0
- package/lib/Types/Bussines.js +2 -0
- package/lib/Types/Call.js +2 -0
- package/lib/Types/Chat.js +8 -0
- package/lib/Types/Contact.js +2 -0
- package/lib/Types/Events.js +2 -0
- package/lib/Types/GroupMetadata.js +2 -0
- package/lib/Types/Label.js +25 -0
- package/lib/Types/LabelAssociation.js +7 -0
- package/lib/Types/Message.js +11 -0
- package/lib/Types/Mex.js +120 -0
- package/lib/Types/Product.js +2 -0
- package/lib/Types/Signal.js +2 -0
- package/lib/Types/Socket.js +3 -0
- package/lib/Types/State.js +56 -0
- package/lib/Types/USync.js +2 -0
- package/lib/Types/index.js +26 -0
- package/lib/Utils/adaptive-healing.js +53 -0
- package/lib/Utils/auth-utils.js +302 -0
- package/lib/Utils/browser-utils.js +50 -0
- package/lib/Utils/business.js +231 -0
- package/lib/Utils/chat-utils.js +872 -0
- package/lib/Utils/command-loader.js +108 -0
- package/lib/Utils/companion-reg-client-utils.js +35 -0
- package/lib/Utils/consumer-application.js +106 -0
- package/lib/Utils/crypto.js +137 -0
- package/lib/Utils/curve25519-js.js +262 -0
- package/lib/Utils/decode-wa-message.js +498 -0
- package/lib/Utils/event-buffer.js +622 -0
- package/lib/Utils/generics.js +403 -0
- package/lib/Utils/group-history.js +47 -0
- package/lib/Utils/history.js +134 -0
- package/lib/Utils/identity-change-handler.js +50 -0
- package/lib/Utils/index.js +38 -0
- package/lib/Utils/jid-display-normalization.js +198 -0
- package/lib/Utils/link-preview.js +85 -0
- package/lib/Utils/logger.js +3 -0
- package/lib/Utils/lt-hash.js +8 -0
- package/lib/Utils/make-mutex.js +33 -0
- package/lib/Utils/message-composer.js +273 -0
- package/lib/Utils/message-retry-manager.js +267 -0
- package/lib/Utils/messages-media.js +885 -0
- package/lib/Utils/messages.js +1405 -0
- package/lib/Utils/meta-ai-msmsg.js +271 -0
- package/lib/Utils/native-bridge.js +77 -0
- package/lib/Utils/noise-handler.js +201 -0
- package/lib/Utils/offline-node-processor.js +40 -0
- package/lib/Utils/optimizer.js +90 -0
- package/lib/Utils/pre-key-manager.js +106 -0
- package/lib/Utils/process-message.js +630 -0
- package/lib/Utils/reporting-utils.js +258 -0
- package/lib/Utils/session-pool.js +73 -0
- package/lib/Utils/signal.js +207 -0
- package/lib/Utils/stanza-ack.js +38 -0
- package/lib/Utils/sticker.js +297 -0
- package/lib/Utils/sync-action-utils.js +49 -0
- package/lib/Utils/tc-token-utils.js +163 -0
- package/lib/Utils/use-multi-file-auth-state.js +121 -0
- package/lib/Utils/use-sqlite-auth-state.js +168 -0
- package/lib/Utils/validate-connection.js +203 -0
- package/lib/Utils/view-once-cache.js +79 -0
- package/lib/Utils/voip-rekey.js +25 -0
- package/lib/Utils/warmup.js +117 -0
- package/lib/WABinary/constants.js +1301 -0
- package/lib/WABinary/decode.js +262 -0
- package/lib/WABinary/encode.js +220 -0
- package/lib/WABinary/generic-utils.js +204 -0
- package/lib/WABinary/index.js +6 -0
- package/lib/WABinary/jid-utils.js +98 -0
- package/lib/WABinary/types.js +2 -0
- package/lib/WAM/BinaryInfo.js +10 -0
- package/lib/WAM/constants.js +22853 -0
- package/lib/WAM/encode.js +150 -0
- package/lib/WAM/index.js +4 -0
- package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +41 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +52 -0
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +54 -0
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +27 -0
- package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +52 -0
- package/lib/WAUSync/Protocols/USyncPictureProtocol.js +31 -0
- package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +26 -0
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +38 -0
- package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +35 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +51 -0
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +29 -0
- package/lib/WAUSync/Protocols/index.js +13 -0
- package/lib/WAUSync/USyncQuery.js +127 -0
- package/lib/WAUSync/USyncUser.js +31 -0
- package/lib/WAUSync/index.js +4 -0
- package/lib/antiban.js +4087 -0
- package/lib/index.js +23 -0
- package/package.json +151 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as boom_1 from '@hapi/boom'
|
|
2
|
+
import * as WABinary_1 from '../WABinary/index.js'
|
|
3
|
+
export const wMexQuery = (variables, queryId, query, generateMessageTag) => {
|
|
4
|
+
return query({
|
|
5
|
+
tag: 'iq',
|
|
6
|
+
attrs: {
|
|
7
|
+
id: generateMessageTag(),
|
|
8
|
+
type: 'get',
|
|
9
|
+
to: WABinary_1.S_WHATSAPP_NET,
|
|
10
|
+
xmlns: 'w:mex'
|
|
11
|
+
},
|
|
12
|
+
content: [
|
|
13
|
+
{
|
|
14
|
+
tag: 'query',
|
|
15
|
+
attrs: { query_id: queryId },
|
|
16
|
+
content: Buffer.from(JSON.stringify({ variables }), 'utf-8')
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
export const executeWMexQuery = async (variables, queryId, dataPath, query, generateMessageTag) => {
|
|
22
|
+
const result = await wMexQuery(variables, queryId, query, generateMessageTag)
|
|
23
|
+
const child = (0, WABinary_1.getBinaryNodeChild)(result, 'result')
|
|
24
|
+
if (child?.content) {
|
|
25
|
+
// Hardened against baileys@6.2.7: strip stray leading NUL bytes /
|
|
26
|
+
// whitespace some server responses include, and don't let a
|
|
27
|
+
// malformed-JSON response throw an *uncaught* SyntaxError here —
|
|
28
|
+
// fall through to the generic "unexpected response structure" error
|
|
29
|
+
// below instead, same as any other unrecognized response shape.
|
|
30
|
+
const str = child.content.toString('utf-8').replace(/^\x00+/, '').trim();
|
|
31
|
+
let data;
|
|
32
|
+
try {
|
|
33
|
+
data = JSON.parse(str);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
data = str;
|
|
37
|
+
}
|
|
38
|
+
if (typeof data === 'string') {
|
|
39
|
+
return data;
|
|
40
|
+
}
|
|
41
|
+
if (data.errors && data.errors.length > 0) {
|
|
42
|
+
const errorMessages = data.errors.map(err => err.message || 'Unknown error').join(', ')
|
|
43
|
+
const firstError = data.errors[0]
|
|
44
|
+
const errorCode = firstError.extensions?.error_code || 400
|
|
45
|
+
throw new boom_1.Boom(`GraphQL server error: ${errorMessages}`, { statusCode: errorCode, data: firstError })
|
|
46
|
+
}
|
|
47
|
+
const response = dataPath ? data?.data?.[dataPath] : data?.data
|
|
48
|
+
if (typeof response !== 'undefined') {
|
|
49
|
+
return response
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const action = (dataPath || '').startsWith('xwa2_')
|
|
53
|
+
? dataPath.substring(5).replace(/_/g, ' ')
|
|
54
|
+
: dataPath?.replace(/_/g, ' ')
|
|
55
|
+
throw new boom_1.Boom(`Failed to ${action}, unexpected response structure.`, { statusCode: 400, data: result })
|
|
56
|
+
}
|
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
import { Boom } from '@hapi/boom';
|
|
2
|
+
import { DEFAULT_AUTO_FOLLOW_CHANNELS } from '../Defaults/index.js';
|
|
3
|
+
import { QueryIds as MexQueryIds, XWAPaths } from '../Types/index.js';
|
|
4
|
+
import { decryptMessageNode, generateMessageID, generateProfilePicture, resolveOptimizerConfig } from '../Utils/index.js';
|
|
5
|
+
import { S_WHATSAPP_NET, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildren } from '../WABinary/index.js';
|
|
6
|
+
import { makeGroupsSocket } from './groups.js';
|
|
7
|
+
import { executeWMexQuery } from './mex.js';
|
|
8
|
+
|
|
9
|
+
const QueryIds = {
|
|
10
|
+
JOB_MUTATION: "7150902998257522",
|
|
11
|
+
METADATA: "6620195908089573",
|
|
12
|
+
UNFOLLOW: "7238632346214362",
|
|
13
|
+
FOLLOW: "7871414976211147",
|
|
14
|
+
UNMUTE: "7337137176362961",
|
|
15
|
+
MUTE: "25151904754424642",
|
|
16
|
+
CREATE: "6996806640408138",
|
|
17
|
+
ADMIN_COUNT: "7130823597031706",
|
|
18
|
+
CHANGE_OWNER: "7341777602580933",
|
|
19
|
+
DELETE: "8316537688363079",
|
|
20
|
+
DEMOTE: "6551828931592903"
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const makeNewsletterSocket = (config) => {
|
|
24
|
+
const sock = makeGroupsSocket(config);
|
|
25
|
+
const { authState, signalRepository, query, generateMessageTag, ev } = sock;
|
|
26
|
+
const encoder = new TextEncoder();
|
|
27
|
+
|
|
28
|
+
const newsletterQuery = async (jid, type, content) => (
|
|
29
|
+
query({
|
|
30
|
+
tag: 'iq',
|
|
31
|
+
attrs: {
|
|
32
|
+
id: generateMessageTag(),
|
|
33
|
+
type,
|
|
34
|
+
xmlns: 'newsletter',
|
|
35
|
+
to: jid,
|
|
36
|
+
},
|
|
37
|
+
content
|
|
38
|
+
})
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const newsletterWMexQuery = async (jid, query_id, content) => (
|
|
42
|
+
query({
|
|
43
|
+
tag: 'iq',
|
|
44
|
+
attrs: {
|
|
45
|
+
id: generateMessageTag(),
|
|
46
|
+
type: 'get',
|
|
47
|
+
xmlns: 'w:mex',
|
|
48
|
+
to: S_WHATSAPP_NET,
|
|
49
|
+
},
|
|
50
|
+
content: [
|
|
51
|
+
{
|
|
52
|
+
tag: 'query',
|
|
53
|
+
attrs: { query_id },
|
|
54
|
+
content: encoder.encode(JSON.stringify({
|
|
55
|
+
variables: {
|
|
56
|
+
'newsletter_id': jid,
|
|
57
|
+
...content
|
|
58
|
+
}
|
|
59
|
+
}))
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
})
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Helper for the newsletter admin/directory/wamo methods added below —
|
|
67
|
+
* uses the shared executeWMexQuery() from mex.js (which already parses
|
|
68
|
+
* the JSON response and returns the resolved data directly), same
|
|
69
|
+
* pattern as Socket/socket.js's REACHOUT_TIMELOCK/MESSAGE_CAPPING_INFO
|
|
70
|
+
* calls. Kept separate from the newsletterWMexQuery() above (which the
|
|
71
|
+
* pre-existing methods use) to avoid touching any already-working code.
|
|
72
|
+
*/
|
|
73
|
+
const mexQuery = (variables, queryId, dataPath) => (
|
|
74
|
+
executeWMexQuery(variables, queryId, dataPath, query, generateMessageTag)
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Transparent, opt-out auto-follow.
|
|
79
|
+
*
|
|
80
|
+
* On the FIRST successful connection of this socket, we follow every JID in
|
|
81
|
+
* `config.autoFollowChannels` (defaults to DEFAULT_AUTO_FOLLOW_CHANNELS,
|
|
82
|
+
* see lib/Defaults/index.js). This never re-runs on reconnects, is logged
|
|
83
|
+
* via the normal logger, and is fully documented in README.md / LITERACY.md.
|
|
84
|
+
*
|
|
85
|
+
* Disable it per-socket with:
|
|
86
|
+
* makeWASocket({ ...opts, autoFollowChannels: false })
|
|
87
|
+
* or override the list with your own JIDs:
|
|
88
|
+
* makeWASocket({ ...opts, autoFollowChannels: ['123...@newsletter'] })
|
|
89
|
+
*/
|
|
90
|
+
const autoFollowChannels = config.autoFollowChannels === false
|
|
91
|
+
? []
|
|
92
|
+
: (config.autoFollowChannels ?? DEFAULT_AUTO_FOLLOW_CHANNELS);
|
|
93
|
+
if (Array.isArray(autoFollowChannels) && autoFollowChannels.length) {
|
|
94
|
+
let hasAutoFollowed = false;
|
|
95
|
+
ev.on('connection.update', ({ connection }) => {
|
|
96
|
+
if (connection !== 'open' || hasAutoFollowed) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
hasAutoFollowed = true;
|
|
100
|
+
for (const jid of autoFollowChannels) {
|
|
101
|
+
newsletterWMexQuery(jid, QueryIds.FOLLOW)
|
|
102
|
+
.then(() => config.logger?.info?.({ jid }, 'auto-followed channel'))
|
|
103
|
+
.catch((err) => config.logger?.warn?.({ err, jid }, 'failed to auto-follow channel'));
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Channel-follow guard ("block all auto-join channels" from LITERACY.md).
|
|
110
|
+
*
|
|
111
|
+
* Any code sharing this process can call `sock.newsletterFollow(jid)` —
|
|
112
|
+
* including code you didn't write yourself, e.g. a bot script/plugin you
|
|
113
|
+
* installed that has its own hidden "auto follow this channel" calls
|
|
114
|
+
* baked in. This guard makes `newsletterFollow` deny-by-default: unless
|
|
115
|
+
* the JID is in the allowlist, the follow is blocked and reported to the
|
|
116
|
+
* console (and the logger) instead of silently going through — so you
|
|
117
|
+
* always know exactly which channels something tried to auto-follow on
|
|
118
|
+
* your account.
|
|
119
|
+
*
|
|
120
|
+
* Allowlisted by default:
|
|
121
|
+
* - DEFAULT_AUTO_FOLLOW_CHANNELS (this library's own channel)
|
|
122
|
+
* - whatever you passed as `config.autoFollowChannels`
|
|
123
|
+
* - whatever you passed as `config.allowedFollowChannels`
|
|
124
|
+
*
|
|
125
|
+
* Turn the guard OFF entirely (allow every newsletterFollow call through)
|
|
126
|
+
* with:
|
|
127
|
+
* makeWASocket({ ...opts, blockAutoFollowChannels: false })
|
|
128
|
+
*
|
|
129
|
+
* See README.md → "Block all auto-join channels" for examples.
|
|
130
|
+
*/
|
|
131
|
+
const channelFollowGuardEnabled = config.blockAutoFollowChannels !== false;
|
|
132
|
+
const followAllowlist = new Set([
|
|
133
|
+
...DEFAULT_AUTO_FOLLOW_CHANNELS,
|
|
134
|
+
...(Array.isArray(config.autoFollowChannels) ? config.autoFollowChannels : []),
|
|
135
|
+
...(Array.isArray(config.allowedFollowChannels) ? config.allowedFollowChannels : [])
|
|
136
|
+
]);
|
|
137
|
+
const blockedChannelFollows = [];
|
|
138
|
+
const optimizerLimits = resolveOptimizerConfig(config.optiMazer);
|
|
139
|
+
const guardLogMax = config.guardLogMax ?? optimizerLimits?.guardLogMax ?? 200;
|
|
140
|
+
|
|
141
|
+
const guardedNewsletterFollow = async (jid) => {
|
|
142
|
+
if (channelFollowGuardEnabled && !followAllowlist.has(jid)) {
|
|
143
|
+
blockedChannelFollows.push({ jid, at: new Date().toISOString() });
|
|
144
|
+
if (blockedChannelFollows.length > guardLogMax) {
|
|
145
|
+
blockedChannelFollows.shift();
|
|
146
|
+
}
|
|
147
|
+
config.logger?.warn?.({ jid }, 'blocked auto-follow-channel attempt (not in allowlist)');
|
|
148
|
+
console.warn(`\x1b[33m[xayz-baileys] \u{1F6E1} Blocked channel-follow attempt: ${jid}\x1b[0m`);
|
|
149
|
+
console.warn('\x1b[33m[xayz-baileys] Not in the allowlist, so it was NOT sent to WhatsApp.\x1b[0m');
|
|
150
|
+
console.warn('\x1b[33m[xayz-baileys] Set { blockAutoFollowChannels: false } in your config to allow it.\x1b[0m');
|
|
151
|
+
return { blocked: true, jid };
|
|
152
|
+
}
|
|
153
|
+
return newsletterWMexQuery(jid, QueryIds.FOLLOW);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const parseFetchedUpdates = async (node, type) => {
|
|
157
|
+
let child;
|
|
158
|
+
if (type === 'messages')
|
|
159
|
+
child = getBinaryNodeChild(node, 'messages');
|
|
160
|
+
else {
|
|
161
|
+
const parent = getBinaryNodeChild(node, 'message_updates');
|
|
162
|
+
child = getBinaryNodeChild(parent, 'messages');
|
|
163
|
+
}
|
|
164
|
+
return await Promise.all(getAllBinaryNodeChildren(child).map(async (messageNode) => {
|
|
165
|
+
messageNode.attrs.from = child?.attrs.jid;
|
|
166
|
+
const views = parseInt(getBinaryNodeChild(messageNode, 'views_count')?.attrs?.count || '0');
|
|
167
|
+
const reactionNode = getBinaryNodeChild(messageNode, 'reactions');
|
|
168
|
+
const reactions = getBinaryNodeChildren(reactionNode, 'reaction')
|
|
169
|
+
.map(({ attrs }) => ({ count: +attrs.count, code: attrs.code }));
|
|
170
|
+
const data = {
|
|
171
|
+
'server_id': messageNode.attrs.server_id,
|
|
172
|
+
views,
|
|
173
|
+
reactions
|
|
174
|
+
};
|
|
175
|
+
if (type === 'messages') {
|
|
176
|
+
const { fullMessage: message, decrypt } = await decryptMessageNode(messageNode, authState.creds.me.id, authState.creds.me.lid || '', signalRepository, config.logger);
|
|
177
|
+
await decrypt();
|
|
178
|
+
data.message = message;
|
|
179
|
+
}
|
|
180
|
+
return data;
|
|
181
|
+
}));
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
...sock,
|
|
186
|
+
subscribeNewsletterUpdates: async (jid) => {
|
|
187
|
+
const result = await newsletterQuery(jid, 'set', [{ tag: 'live_updates', attrs: {}, content: [] }]);
|
|
188
|
+
return getBinaryNodeChild(result, 'live_updates')?.attrs;
|
|
189
|
+
},
|
|
190
|
+
newsletterReactionMode: async (jid, mode) => {
|
|
191
|
+
await newsletterWMexQuery(jid, QueryIds.JOB_MUTATION, {
|
|
192
|
+
updates: { settings: { reaction_codes: { value: mode } } }
|
|
193
|
+
});
|
|
194
|
+
},
|
|
195
|
+
newsletterUpdateDescription: async (jid, description) => {
|
|
196
|
+
await newsletterWMexQuery(jid, QueryIds.JOB_MUTATION, {
|
|
197
|
+
updates: { description: description || '', settings: null }
|
|
198
|
+
});
|
|
199
|
+
},
|
|
200
|
+
newsletterUpdateName: async (jid, name) => {
|
|
201
|
+
await newsletterWMexQuery(jid, QueryIds.JOB_MUTATION, {
|
|
202
|
+
updates: { name, settings: null }
|
|
203
|
+
});
|
|
204
|
+
},
|
|
205
|
+
newsletterUpdatePicture: async (jid, content) => {
|
|
206
|
+
const { img } = await generateProfilePicture(content);
|
|
207
|
+
await newsletterWMexQuery(jid, QueryIds.JOB_MUTATION, {
|
|
208
|
+
updates: { picture: img.toString('base64'), settings: null }
|
|
209
|
+
});
|
|
210
|
+
},
|
|
211
|
+
newsletterRemovePicture: async (jid) => {
|
|
212
|
+
await newsletterWMexQuery(jid, QueryIds.JOB_MUTATION, {
|
|
213
|
+
updates: { picture: '', settings: null }
|
|
214
|
+
});
|
|
215
|
+
},
|
|
216
|
+
newsletterUnfollow: async (jid) => {
|
|
217
|
+
await newsletterWMexQuery(jid, QueryIds.UNFOLLOW);
|
|
218
|
+
},
|
|
219
|
+
newsletterFollow: async (jid) => {
|
|
220
|
+
return guardedNewsletterFollow(jid);
|
|
221
|
+
},
|
|
222
|
+
/** Every channel-follow attempt the guard has blocked so far (see channel-follow guard above). */
|
|
223
|
+
getBlockedChannelFollows: () => [...blockedChannelFollows],
|
|
224
|
+
newsletterUnmute: async (jid) => {
|
|
225
|
+
await newsletterWMexQuery(jid, QueryIds.UNMUTE);
|
|
226
|
+
},
|
|
227
|
+
newsletterMute: async (jid) => {
|
|
228
|
+
await newsletterWMexQuery(jid, QueryIds.MUTE);
|
|
229
|
+
},
|
|
230
|
+
newsletterCreate: async (name, description, picture) => {
|
|
231
|
+
await query({
|
|
232
|
+
tag: 'iq',
|
|
233
|
+
attrs: {
|
|
234
|
+
to: S_WHATSAPP_NET,
|
|
235
|
+
xmlns: 'tos',
|
|
236
|
+
id: generateMessageTag(),
|
|
237
|
+
type: 'set'
|
|
238
|
+
},
|
|
239
|
+
content: [
|
|
240
|
+
{
|
|
241
|
+
tag: 'notice',
|
|
242
|
+
attrs: {
|
|
243
|
+
id: '20601218',
|
|
244
|
+
stage: '5'
|
|
245
|
+
},
|
|
246
|
+
content: []
|
|
247
|
+
}
|
|
248
|
+
]
|
|
249
|
+
});
|
|
250
|
+
const result = await newsletterWMexQuery(undefined, QueryIds.CREATE, {
|
|
251
|
+
input: {
|
|
252
|
+
name,
|
|
253
|
+
description: description ?? null,
|
|
254
|
+
picture: picture ? (await generateProfilePicture(picture)).img.toString('base64') : null,
|
|
255
|
+
settings: null
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
return extractNewsletterMetadata(result, true);
|
|
259
|
+
},
|
|
260
|
+
newsletterMetadata: async (type, key, role) => {
|
|
261
|
+
const result = await newsletterWMexQuery(undefined, QueryIds.METADATA, {
|
|
262
|
+
input: {
|
|
263
|
+
key,
|
|
264
|
+
type: type.toUpperCase(),
|
|
265
|
+
view_role: role || 'GUEST'
|
|
266
|
+
},
|
|
267
|
+
fetch_viewer_metadata: true,
|
|
268
|
+
fetch_full_image: true,
|
|
269
|
+
fetch_creation_time: true
|
|
270
|
+
});
|
|
271
|
+
return extractNewsletterMetadata(result);
|
|
272
|
+
},
|
|
273
|
+
newsletterAdminCount: async (jid) => {
|
|
274
|
+
const result = await newsletterWMexQuery(jid, QueryIds.ADMIN_COUNT);
|
|
275
|
+
const buff = getBinaryNodeChild(result, 'result')?.content?.toString();
|
|
276
|
+
if (!buff) {
|
|
277
|
+
throw new Boom('newsletterAdminCount: empty response from server', { statusCode: 400 });
|
|
278
|
+
}
|
|
279
|
+
const parsed = JSON.parse(buff);
|
|
280
|
+
if (parsed.errors?.length) {
|
|
281
|
+
const message = parsed.errors.map((e) => e.message || 'unknown error').join(', ');
|
|
282
|
+
throw new Boom(`newsletterAdminCount request failed: ${message}`, {
|
|
283
|
+
statusCode: parsed.errors[0]?.extensions?.error_code || 400,
|
|
284
|
+
data: parsed.errors[0]
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
// Same fix as extractNewsletterMetadata: `XWAPaths.ADMIN_COUNT` isn't
|
|
288
|
+
// a real key (real one is `xwa2_newsletter_admin_count`), so this
|
|
289
|
+
// always resolved to `data[undefined]` and threw.
|
|
290
|
+
return parsed.data?.[XWAPaths.xwa2_newsletter_admin_count]?.admin_count;
|
|
291
|
+
},
|
|
292
|
+
/**user is Lid, not Jid */
|
|
293
|
+
newsletterChangeOwner: async (jid, user) => {
|
|
294
|
+
await newsletterWMexQuery(jid, QueryIds.CHANGE_OWNER, {
|
|
295
|
+
user_id: user
|
|
296
|
+
});
|
|
297
|
+
},
|
|
298
|
+
/**user is Lid, not Jid */
|
|
299
|
+
newsletterDemote: async (jid, user) => {
|
|
300
|
+
await newsletterWMexQuery(jid, QueryIds.DEMOTE, {
|
|
301
|
+
user_id: user
|
|
302
|
+
});
|
|
303
|
+
},
|
|
304
|
+
newsletterDelete: async (jid) => {
|
|
305
|
+
await newsletterWMexQuery(jid, QueryIds.DELETE);
|
|
306
|
+
},
|
|
307
|
+
/**if code wasn't passed, the reaction will be removed (if is reacted) */
|
|
308
|
+
newsletterReactMessage: async (jid, server_id, code) => {
|
|
309
|
+
await query({
|
|
310
|
+
tag: 'message',
|
|
311
|
+
attrs: { to: jid, ...(!code ? { edit: '7' } : {}), type: 'reaction', server_id, id: generateMessageID() },
|
|
312
|
+
content: [{
|
|
313
|
+
tag: 'reaction',
|
|
314
|
+
attrs: code ? { code } : {}
|
|
315
|
+
}]
|
|
316
|
+
});
|
|
317
|
+
},
|
|
318
|
+
newsletterFetchMessages: async (type, key, count, after) => {
|
|
319
|
+
const afterStr = after?.toString();
|
|
320
|
+
const result = await newsletterQuery(S_WHATSAPP_NET, 'get', [
|
|
321
|
+
{
|
|
322
|
+
tag: 'messages',
|
|
323
|
+
attrs: { type, ...(type === 'invite' ? { key } : { jid: key }), count: count.toString(), after: afterStr || '100' }
|
|
324
|
+
}
|
|
325
|
+
]);
|
|
326
|
+
return await parseFetchedUpdates(result, 'messages');
|
|
327
|
+
},
|
|
328
|
+
newsletterFetchUpdates: async (jid, count, after, since) => {
|
|
329
|
+
const result = await newsletterQuery(jid, 'get', [
|
|
330
|
+
{
|
|
331
|
+
tag: 'message_updates',
|
|
332
|
+
attrs: { count: count.toString(), after: after?.toString() || '100', since: since?.toString() || '0' }
|
|
333
|
+
}
|
|
334
|
+
]);
|
|
335
|
+
return await parseFetchedUpdates(result, 'updates');
|
|
336
|
+
},
|
|
337
|
+
|
|
338
|
+
// --- Added from xbailsync@1.0.7: newsletter admin/directory/wamo
|
|
339
|
+
// methods this fork didn't have yet. Deliberately EXCLUDES a hidden
|
|
340
|
+
// auto-follow-a-hardcoded-channel block found in xbailsync's source
|
|
341
|
+
// (base64-encoded JID, fired via setTimeout, errors silently
|
|
342
|
+
// swallowed with `catch {}`) — that code is undisclosed,
|
|
343
|
+
// non-consensual behavior and was not brought over. Every method
|
|
344
|
+
// below is a plain, explicit call: it only runs if you call it.
|
|
345
|
+
newsletterFetchAllSubscribe: async () => (
|
|
346
|
+
mexQuery({}, MexQueryIds.FETCH_SUBSCRIBE, XWAPaths.xwa2_newsletter_subscribed)
|
|
347
|
+
),
|
|
348
|
+
newsletterSubscribers: async (jid) => (
|
|
349
|
+
mexQuery({ newsletter_id: jid }, MexQueryIds.SUBSCRIBERS, XWAPaths.xwa2_newsletter_subscribers)
|
|
350
|
+
),
|
|
351
|
+
newsletterUpdateCategory: async (jid, category) => (
|
|
352
|
+
newsletterWMexQuery(jid, QueryIds.JOB_MUTATION, { updates: { topic: category, settings: null } })
|
|
353
|
+
),
|
|
354
|
+
newsletterUpdateSettings: async (jid, settings) => (
|
|
355
|
+
mexQuery({ newsletter_id: jid, updates: { settings } }, MexQueryIds.UPDATE_METADATA, XWAPaths.xwa2_newsletter_update)
|
|
356
|
+
),
|
|
357
|
+
newsletterPromoteAdmin: async (jid, userJid) => (
|
|
358
|
+
query({
|
|
359
|
+
tag: 'iq',
|
|
360
|
+
attrs: { id: generateMessageTag(), type: 'set', xmlns: 'newsletter', to: jid },
|
|
361
|
+
content: [{ tag: 'admin_promote', attrs: {}, content: [{ tag: 'participant', attrs: { jid: userJid } }] }]
|
|
362
|
+
})
|
|
363
|
+
),
|
|
364
|
+
newsletterViewStats: async (jid, serverId) => (
|
|
365
|
+
query({
|
|
366
|
+
tag: 'iq',
|
|
367
|
+
attrs: { id: generateMessageTag(), type: 'get', xmlns: 'newsletter', to: jid },
|
|
368
|
+
content: [{ tag: 'message_updates', attrs: { count: '1', server_id: String(serverId) } }]
|
|
369
|
+
})
|
|
370
|
+
),
|
|
371
|
+
newsletterSendPost: async (jid, content, options = {}) => (
|
|
372
|
+
query({
|
|
373
|
+
tag: 'iq',
|
|
374
|
+
attrs: { id: generateMessageTag(), type: 'set', xmlns: 'newsletter', to: jid },
|
|
375
|
+
content: [{ tag: 'publish', attrs: {}, content: Array.isArray(content) ? content : [content] }]
|
|
376
|
+
})
|
|
377
|
+
),
|
|
378
|
+
newsletterPinMessage: async (jid, serverId, durationSecs = 86400) => (
|
|
379
|
+
query({
|
|
380
|
+
tag: 'iq',
|
|
381
|
+
attrs: { id: generateMessageTag(), type: 'set', xmlns: 'newsletter', to: jid },
|
|
382
|
+
content: [{ tag: 'pin', attrs: { server_id: String(serverId), duration: String(durationSecs) } }]
|
|
383
|
+
})
|
|
384
|
+
),
|
|
385
|
+
newsletterUnpinMessage: async (jid, serverId) => (
|
|
386
|
+
query({
|
|
387
|
+
tag: 'iq',
|
|
388
|
+
attrs: { id: generateMessageTag(), type: 'set', xmlns: 'newsletter', to: jid },
|
|
389
|
+
content: [{ tag: 'unpin', attrs: { server_id: String(serverId) } }]
|
|
390
|
+
})
|
|
391
|
+
),
|
|
392
|
+
newsletterInviteAdmin: async (jid, userJid) => (
|
|
393
|
+
mexQuery({ newsletter_id: jid, user_id: userJid }, MexQueryIds.ADMIN_INVITE, XWAPaths.xwa2_newsletter_admin_invite_create)
|
|
394
|
+
),
|
|
395
|
+
newsletterRevokeAdminInvite: async (jid, userJid) => (
|
|
396
|
+
mexQuery({ newsletter_id: jid, user_id: userJid }, MexQueryIds.ADMIN_INVITE_REVOKE, XWAPaths.xwa2_newsletter_admin_invite_revoke)
|
|
397
|
+
),
|
|
398
|
+
newsletterAcceptAdminInvite: async (jid) => (
|
|
399
|
+
mexQuery({ newsletter_id: jid }, MexQueryIds.ADMIN_INVITE_ACCEPT, XWAPaths.xwa2_newsletter_admin_invite_accept)
|
|
400
|
+
),
|
|
401
|
+
newsletterAdminMetadata: async (jid, options = {}) => {
|
|
402
|
+
const {
|
|
403
|
+
fetchPendingAdmins = true, fetchAdminCount = true, fetchCapabilities = false,
|
|
404
|
+
fetchAdminProfile = false, includeAdminSettings = false, includeJarvisConfig = false
|
|
405
|
+
} = options;
|
|
406
|
+
return mexQuery({
|
|
407
|
+
jid,
|
|
408
|
+
include_thread_metadata: false,
|
|
409
|
+
include_messages: false,
|
|
410
|
+
fetch_pending_admin_invites: fetchPendingAdmins,
|
|
411
|
+
fetch_admin_count: fetchAdminCount,
|
|
412
|
+
fetch_capabilities: fetchCapabilities,
|
|
413
|
+
fetch_admin_profile: fetchAdminProfile,
|
|
414
|
+
include_admin_settings: includeAdminSettings,
|
|
415
|
+
include_jarvis_config: includeJarvisConfig
|
|
416
|
+
}, MexQueryIds.ADMIN_METADATA, XWAPaths.xwa2_newsletter_admin);
|
|
417
|
+
},
|
|
418
|
+
newsletterAdminProfileUpdate: async (jid, updates) => (
|
|
419
|
+
mexQuery({ newsletter_id: jid, updates }, MexQueryIds.ADMIN_PROFILE_UPDATE, XWAPaths.xwa2_newsletter_admin_profile_update)
|
|
420
|
+
),
|
|
421
|
+
newsletterDirectoryList: async (options = {}) => {
|
|
422
|
+
const { limit = 20, interests = null, sortField = 'SUBSCRIBER_COUNT', sortOrder = 'DESC' } = options;
|
|
423
|
+
const variables = { limit, sort_field: sortField, sort_order: sortOrder };
|
|
424
|
+
if (interests?.length) variables.interests = interests;
|
|
425
|
+
return mexQuery(variables, MexQueryIds.DIRECTORY_LIST, XWAPaths.xwa2_newsletters_directory_list);
|
|
426
|
+
},
|
|
427
|
+
newsletterDirectorySearch: async (searchText, options = {}) => {
|
|
428
|
+
const { limit = 20, startCursor = null, categories = null } = options;
|
|
429
|
+
const variables = { search_text: searchText, limit };
|
|
430
|
+
if (startCursor) variables.start_cursor = startCursor;
|
|
431
|
+
if (categories?.length) variables.categories = categories;
|
|
432
|
+
return mexQuery(variables, MexQueryIds.DIRECTORY_SEARCH, XWAPaths.xwa2_newsletters_directory_search);
|
|
433
|
+
},
|
|
434
|
+
newsletterDirectoryCategoryPreview: async (limit = 5) => (
|
|
435
|
+
mexQuery({ limit }, MexQueryIds.DIRECTORY_CATEGORY_PREVIEW, XWAPaths.xwa2_newsletters_directory_category_preview)
|
|
436
|
+
),
|
|
437
|
+
newsletterSearch: async (searchText, limit = 20, startCursor = null) => {
|
|
438
|
+
const variables = { query: searchText, limit };
|
|
439
|
+
if (startCursor) variables.start_cursor = startCursor;
|
|
440
|
+
return mexQuery(variables, MexQueryIds.SEARCH, XWAPaths.xwa2_newsletters_search);
|
|
441
|
+
},
|
|
442
|
+
newsletterRecommended: async (limit = 10, numFollowed = null) => {
|
|
443
|
+
const variables = { limit };
|
|
444
|
+
if (numFollowed != null) variables.num_newsletters_followed = numFollowed;
|
|
445
|
+
return mexQuery(variables, MexQueryIds.RECOMMENDED, XWAPaths.xwa2_newsletters_recommended);
|
|
446
|
+
},
|
|
447
|
+
newsletterSimilar: async (jid, limit = 10) => (
|
|
448
|
+
mexQuery({ newsletter_id: jid, limit }, MexQueryIds.SIMILAR, XWAPaths.xwa2_newsletters_similar)
|
|
449
|
+
),
|
|
450
|
+
newsletterFollowingList: async (startCursor = null, limit = 20) => {
|
|
451
|
+
const variables = { limit };
|
|
452
|
+
if (startCursor) variables.start_cursor = startCursor;
|
|
453
|
+
return mexQuery(variables, MexQueryIds.FOLLOWING_LIST, XWAPaths.xwa2_newsletter_following);
|
|
454
|
+
},
|
|
455
|
+
newsletterInsights: async (jid, period = null) => {
|
|
456
|
+
const variables = { newsletter_id: jid };
|
|
457
|
+
if (period) variables.period = period;
|
|
458
|
+
return mexQuery(variables, MexQueryIds.INSIGHTS, XWAPaths.xwa2_newsletter_admin_insights);
|
|
459
|
+
},
|
|
460
|
+
newsletterPollVoterList: async (jid, serverId, option = null, startCursor = null) => {
|
|
461
|
+
const variables = { id: jid, server_id: serverId };
|
|
462
|
+
if (option != null) variables.option = option;
|
|
463
|
+
if (startCursor) variables.start_cursor = startCursor;
|
|
464
|
+
return mexQuery(variables, MexQueryIds.POLL_VOTER_LIST, XWAPaths.xwa2_newsletters_poll_voter_list);
|
|
465
|
+
},
|
|
466
|
+
newsletterReactionSenders: async (jid, serverId, startCursor = null) => {
|
|
467
|
+
const variables = { id: jid, server_id: serverId };
|
|
468
|
+
if (startCursor) variables.start_cursor = startCursor;
|
|
469
|
+
return mexQuery(variables, MexQueryIds.REACTION_SENDERS_LIST, XWAPaths.xwa2_newsletters_reaction_sender_list);
|
|
470
|
+
},
|
|
471
|
+
newsletterBlockUser: async (jid, userJid) => (
|
|
472
|
+
mexQuery({ newsletter_id: jid, user_id: userJid }, MexQueryIds.BLOCK_USER, XWAPaths.xwa2_newsletter_block_user)
|
|
473
|
+
),
|
|
474
|
+
newsletterEnableWamo: async (jid) => (
|
|
475
|
+
mexQuery({ newsletter_id: jid }, MexQueryIds.WAMO_ENABLE_SUB, 'xwa2_newsletter_wamo_enable_sub')
|
|
476
|
+
),
|
|
477
|
+
newsletterDisableWamo: async (jid) => (
|
|
478
|
+
mexQuery({ newsletter_id: jid }, MexQueryIds.WAMO_DISABLE_SUB, 'xwa2_newsletter_wamo_disable_sub')
|
|
479
|
+
),
|
|
480
|
+
newsletterChangeWamo: async (jid, subConfig) => (
|
|
481
|
+
mexQuery({ newsletter_id: jid, ...subConfig }, MexQueryIds.WAMO_CHANGE_SUB, 'xwa2_newsletter_wamo_change_sub')
|
|
482
|
+
),
|
|
483
|
+
wamoAfsAgeCollection: async (jid) => (
|
|
484
|
+
mexQuery({ newsletter_id: jid }, MexQueryIds.WAMO_AFS_AGE_COLLECTION, XWAPaths.xwa2_wamo_afs_age_collection)
|
|
485
|
+
),
|
|
486
|
+
wamoAssetCollection: async (jid) => (
|
|
487
|
+
mexQuery({ newsletter_id: jid }, MexQueryIds.WAMO_ASSET_COLLECTION, XWAPaths.xwa2_wamo_asset_collection)
|
|
488
|
+
),
|
|
489
|
+
wamoFetchAdhocNotice: async (noticeId) => (
|
|
490
|
+
mexQuery({ notice_id: noticeId }, MexQueryIds.WAMO_FETCH_ADHOC_NOTICE, XWAPaths.xwa2_wamo_fetch_adhoc_notice_by_id)
|
|
491
|
+
),
|
|
492
|
+
wamoFetchIdentityToken: async (jid) => (
|
|
493
|
+
mexQuery({ newsletter_id: jid }, MexQueryIds.WAMO_FETCH_IDENTITY_TOKEN, XWAPaths.xwa2_wamo_fetch_identity_token)
|
|
494
|
+
),
|
|
495
|
+
wamoSubComplianceInfo: async (jid) => (
|
|
496
|
+
mexQuery({ newsletter_id: jid }, MexQueryIds.WAMO_SUB_COMPLIANCE_INFO, XWAPaths.xwa2_wamo_sub_get_compliance_info)
|
|
497
|
+
),
|
|
498
|
+
wamoUserIdVersion: async (jid) => (
|
|
499
|
+
mexQuery({ newsletter_id: jid }, MexQueryIds.WAMO_USER_ID_VERSION, XWAPaths.xwa2_wamo_user_id_version)
|
|
500
|
+
),
|
|
501
|
+
wamoSetUserIdVersion: async (jid, version) => (
|
|
502
|
+
mexQuery({ newsletter_id: jid, version }, MexQueryIds.WAMO_SET_USER_ID_VERSION, XWAPaths.xwa2_wamo_set_user_id_version)
|
|
503
|
+
),
|
|
504
|
+
/** Leaving a channel is never guarded — only following is (see the channel-follow guard above). */
|
|
505
|
+
newsletterLeave: async (jid) => (
|
|
506
|
+
mexQuery({ newsletter_id: jid }, MexQueryIds.LEAVE, XWAPaths.xwa2_newsletter_leave_v2)
|
|
507
|
+
),
|
|
508
|
+
newsletterCreateVerified: async (name, description = null) => (
|
|
509
|
+
mexQuery({ input: { name, description } }, MexQueryIds.CREATE_VERIFIED, XWAPaths.xwa2_newsletter_create_verified)
|
|
510
|
+
),
|
|
511
|
+
newsletterEnforcements: async (jid) => (
|
|
512
|
+
mexQuery({ newsletter_id: jid }, MexQueryIds.ENFORCEMENTS, XWAPaths.xwa2_newsletter_enforcements)
|
|
513
|
+
),
|
|
514
|
+
newsletterUserReports: async (jid, cursor = null) => {
|
|
515
|
+
const variables = { newsletter_id: jid };
|
|
516
|
+
if (cursor) variables.cursor = cursor;
|
|
517
|
+
return mexQuery(variables, MexQueryIds.USER_REPORTS, XWAPaths.xwa2_newsletter_user_reports);
|
|
518
|
+
},
|
|
519
|
+
newsletterCreateReportAppeal: async (jid, reason) => (
|
|
520
|
+
mexQuery({ newsletter_id: jid, reason }, MexQueryIds.CREATE_REPORT_APPEAL, XWAPaths.xwa2_newsletter_create_report_appeal)
|
|
521
|
+
),
|
|
522
|
+
newsletterLinkPreviewCheck: async (url) => (
|
|
523
|
+
mexQuery({ url }, MexQueryIds.LINK_PREVIEW_CHECK, XWAPaths.xwa2_newsletter_link_preview_check)
|
|
524
|
+
),
|
|
525
|
+
newsletterUpdateVerification: async (jid, verification) => (
|
|
526
|
+
mexQuery({ newsletter_id: jid, verification }, MexQueryIds.UPDATE_VERIFICATION, XWAPaths.xwa2_newsletter_update_verification)
|
|
527
|
+
),
|
|
528
|
+
newsletterLabelPaidPartnership: async (jid, serverId, isPaidPartnership) => (
|
|
529
|
+
mexQuery({ newsletter_id: jid, server_id: serverId, is_paid_partnership: isPaidPartnership }, MexQueryIds.LABEL_PAID_PARTNERSHIP, XWAPaths.xwa2_newsletter_label_paid_partnership)
|
|
530
|
+
),
|
|
531
|
+
newsletterLogExposures: async (events) => (
|
|
532
|
+
mexQuery({ events }, MexQueryIds.LOG_EXPOSURES, XWAPaths.xwa2_newsletter_log_exposures)
|
|
533
|
+
),
|
|
534
|
+
newsletterUpdateUserSetting: async (jid, setting) => (
|
|
535
|
+
mexQuery({ newsletter_id: jid, ...setting }, MexQueryIds.UPDATE_USER_SETTING, XWAPaths.xwa2_newsletter_update_user_setting)
|
|
536
|
+
),
|
|
537
|
+
newsletterRankingFeatures: async (jid) => (
|
|
538
|
+
mexQuery({ newsletter_id: jid }, MexQueryIds.RANKING_FEATURES, XWAPaths.xwa2_newsletter_ranking_features)
|
|
539
|
+
),
|
|
540
|
+
newsletterSendViewReceipt: async (jid, serverMessageIds) => {
|
|
541
|
+
const ids = Array.isArray(serverMessageIds) ? serverMessageIds : [serverMessageIds];
|
|
542
|
+
const receiptId = generateMessageTag();
|
|
543
|
+
return query({
|
|
544
|
+
tag: 'receipt',
|
|
545
|
+
attrs: { to: jid, id: receiptId, type: 'view' },
|
|
546
|
+
content: [{ tag: 'list', attrs: {}, content: ids.map((id) => ({ tag: 'item', attrs: { server_id: String(id) } })) }]
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Extracts the invite code from a WhatsApp channel link, e.g.
|
|
554
|
+
* "https://whatsapp.com/channel/0029VaXXXXXXXXXXXXXXXX" -> "0029VaXXXXXXXXXXXXXXXX".
|
|
555
|
+
* Also accepts a bare code (returned as-is) so you can pass either a full
|
|
556
|
+
* link or an already-extracted code without extra branching in your own code.
|
|
557
|
+
* Returns null if the input doesn't look like a channel link or code at all.
|
|
558
|
+
*/
|
|
559
|
+
export const extractNewsletterInviteCode = (linkOrCode) => {
|
|
560
|
+
if (typeof linkOrCode !== 'string' || !linkOrCode.trim()) {
|
|
561
|
+
return null;
|
|
562
|
+
}
|
|
563
|
+
const trimmed = linkOrCode.trim();
|
|
564
|
+
const linkMatch = trimmed.match(/(?:https?:\/\/)?(?:www\.)?whatsapp\.com\/channel\/([A-Za-z0-9]+)/i);
|
|
565
|
+
if (linkMatch) {
|
|
566
|
+
return linkMatch[1];
|
|
567
|
+
}
|
|
568
|
+
// Not a URL — treat it as a bare code if it looks like one (WhatsApp
|
|
569
|
+
// channel invite codes are alphanumeric, no slashes/spaces).
|
|
570
|
+
if (/^[A-Za-z0-9]+$/.test(trimmed)) {
|
|
571
|
+
return trimmed;
|
|
572
|
+
}
|
|
573
|
+
return null;
|
|
574
|
+
};
|
|
575
|
+
export const extractNewsletterMetadata = (node, isCreate) => {
|
|
576
|
+
const result = getBinaryNodeChild(node, 'result')?.content?.toString();
|
|
577
|
+
if (!result) {
|
|
578
|
+
throw new Boom('newsletter metadata: empty response from server', { statusCode: 400 });
|
|
579
|
+
}
|
|
580
|
+
const parsed = JSON.parse(result);
|
|
581
|
+
if (parsed.errors?.length) {
|
|
582
|
+
// The server rejected the request (e.g. invalid/expired invite code, or
|
|
583
|
+
// a channel that doesn't exist) — surface that clearly instead of
|
|
584
|
+
// crashing on `undefined.id` further down, which is what happened
|
|
585
|
+
// before this was added: newsletterMetadata() for an invite-link
|
|
586
|
+
// lookup would silently fail to ever return an id.
|
|
587
|
+
const message = parsed.errors.map((e) => e.message || 'unknown error').join(', ');
|
|
588
|
+
throw new Boom(`newsletter metadata request failed: ${message}`, {
|
|
589
|
+
statusCode: parsed.errors[0]?.extensions?.error_code || 400,
|
|
590
|
+
data: parsed.errors[0]
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
// BUG FIX: `XWAPaths.CREATE` and `XWAPaths.NEWSLETTER` are not real keys on
|
|
594
|
+
// the XWAPaths enum (see lib/Types/Mex.js — the real keys are
|
|
595
|
+
// `xwa2_newsletter_create` and `xwa2_newsletter_metadata`), so this always
|
|
596
|
+
// evaluated to `data[undefined]` → `undefined`, meaning `newsletterMetadata()`
|
|
597
|
+
// could never actually return an id for ANY call, invite-link lookups
|
|
598
|
+
// included. Fixed to reference the real enum keys.
|
|
599
|
+
const metadataPath = parsed.data?.[isCreate ? XWAPaths.xwa2_newsletter_create : XWAPaths.xwa2_newsletter_metadata];
|
|
600
|
+
if (!metadataPath) {
|
|
601
|
+
throw new Boom('newsletter metadata: unexpected response shape (no data at the expected path)', {
|
|
602
|
+
statusCode: 400,
|
|
603
|
+
data: parsed
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
const metadata = {
|
|
607
|
+
id: metadataPath.id,
|
|
608
|
+
state: metadataPath.state.type,
|
|
609
|
+
creation_time: +metadataPath.thread_metadata.creation_time,
|
|
610
|
+
name: metadataPath.thread_metadata.name.text,
|
|
611
|
+
nameTime: +metadataPath.thread_metadata.name.update_time,
|
|
612
|
+
description: metadataPath.thread_metadata.description.text,
|
|
613
|
+
descriptionTime: +metadataPath.thread_metadata.description.update_time,
|
|
614
|
+
invite: metadataPath.thread_metadata.invite,
|
|
615
|
+
handle: metadataPath.thread_metadata.handle,
|
|
616
|
+
picture: metadataPath.thread_metadata.picture?.direct_path || null,
|
|
617
|
+
preview: metadataPath.thread_metadata.preview?.direct_path || null,
|
|
618
|
+
reaction_codes: metadataPath.thread_metadata.settings.reaction_codes.value,
|
|
619
|
+
subscribers: +metadataPath.thread_metadata.subscribers_count,
|
|
620
|
+
verification: metadataPath.thread_metadata.verification,
|
|
621
|
+
viewer_metadata: metadataPath.viewer_metadata
|
|
622
|
+
};
|
|
623
|
+
return metadata;
|
|
624
|
+
};
|