kage-baileys 2.0.1 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/engine-requirements.js +1 -1
- package/lib/Utils/automation-tools.js +1 -2
- package/lib/Utils/generics.js +14 -28
- package/lib/Utils/status-tools.js +4 -46
- package/lib/index.d.ts +1 -4
- package/lib/index.js +1 -1
- package/package.json +5 -4
- package/test/installation-repair.test.cjs +43 -0
- package/test/revision.test.cjs +0 -27
- package/test/status-policy.test.cjs +40 -0
package/engine-requirements.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
const [major, minor] = process.versions.node.split('.').map(Number);
|
|
3
3
|
if (major < 22 || (major === 22 && minor < 12)) {
|
|
4
|
-
console.error('Kage Baileys
|
|
4
|
+
console.error('Kage Baileys necesita Node >=22.12.0 por interoperabilidad CommonJS/ESM. Selecciona Node 22 o 24 en el panel. No basta con borrar engines.');
|
|
5
5
|
process.exitCode = 1;
|
|
6
6
|
}
|
|
@@ -114,7 +114,6 @@ function installKageTools(conn, options = {}) {
|
|
|
114
114
|
const status = require('./status-tools');
|
|
115
115
|
const country = require('./country-info');
|
|
116
116
|
conn.getReceivedStatuses = (includeUnavailable = false) => status.listStatuses(conn, includeUnavailable);
|
|
117
|
-
conn.reactToReceivedStatus = (entry, emoji) => status.reactToStatus(conn, entry, emoji);
|
|
118
117
|
conn.getCountryForJid = jid => country.getCountryForJid(conn, jid);
|
|
119
118
|
conn.downloadNormalMedia = (raw, opts) => downloadNormalMedia(conn, raw, opts);
|
|
120
119
|
conn.saveNormalMedia = (raw, opts) => saveNormalMedia(conn, raw, opts);
|
|
@@ -150,7 +149,7 @@ function installKageTools(conn, options = {}) {
|
|
|
150
149
|
conn.enableStatusTracking = (settings = {}) => {
|
|
151
150
|
if (stop) return stop;
|
|
152
151
|
let active = true;
|
|
153
|
-
const scopedSettings = new Proxy(settings, { get(target, key) { return key === '
|
|
152
|
+
const scopedSettings = new Proxy(settings, { get(target, key) { return key === 'statusSave' ? active && target[key] : target[key]; } });
|
|
154
153
|
const upsert = event => { for (const raw of event?.messages || []) status.handleStatus(raw, conn, scopedSettings).catch(e => conn.logger?.warn?.({ error: e.message }, 'status tracking')); };
|
|
155
154
|
const deleted = event => { for (const key of event?.keys || []) status.markStatusDeleted(conn, key); };
|
|
156
155
|
const updated = items => { for (const item of items || []) if (item.key?.remoteJid === 'status@broadcast' && item.update?.message === null) status.markStatusDeleted(conn, item.key); };
|
package/lib/Utils/generics.js
CHANGED
|
@@ -46,7 +46,7 @@ const axios_1 = __importDefault(require("axios"));
|
|
|
46
46
|
const crypto_1 = require("crypto");
|
|
47
47
|
const os_1 = require("os");
|
|
48
48
|
const WAProto_1 = require("../../WAProto");
|
|
49
|
-
const
|
|
49
|
+
const kageVersionJson = require("../Defaults/wileys-version.json");
|
|
50
50
|
const Types_1 = require("../Types");
|
|
51
51
|
const WABinary_1 = require("../WABinary");
|
|
52
52
|
const COMPANION_PLATFORM_MAP = {
|
|
@@ -261,19 +261,22 @@ exports.printQRIfNecessaryListener = printQRIfNecessaryListener;
|
|
|
261
261
|
|
|
262
262
|
const fetchLatestKageVersion = async (options = {}) => {
|
|
263
263
|
try {
|
|
264
|
-
const { data } = await axios_1.default.get('https://registry.npmjs.org/kage-baileys', {
|
|
264
|
+
const { data } = await axios_1.default.get('https://registry.npmjs.org/kage-baileys/latest', {
|
|
265
|
+
timeout: 15000,
|
|
265
266
|
...options,
|
|
266
267
|
responseType: 'json'
|
|
267
268
|
});
|
|
268
|
-
const versionStr = data.version;
|
|
269
|
-
const
|
|
269
|
+
const versionStr = data.version;
|
|
270
|
+
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(versionStr || '');
|
|
271
|
+
if (!match) throw new Error('Invalid Kage package version');
|
|
272
|
+
const [major, minor, patch] = match.slice(1).map(Number);
|
|
270
273
|
return {
|
|
271
274
|
version: [major, minor, patch],
|
|
272
275
|
isLatest: true
|
|
273
276
|
};
|
|
274
277
|
} catch (error) {
|
|
275
278
|
return {
|
|
276
|
-
version:
|
|
279
|
+
version: require('../../package.json').version.split('-')[0].split('.').map(Number),
|
|
277
280
|
isLatest: false,
|
|
278
281
|
error
|
|
279
282
|
};
|
|
@@ -281,26 +284,8 @@ const fetchLatestKageVersion = async (options = {}) => {
|
|
|
281
284
|
};
|
|
282
285
|
exports.fetchLatestKageVersion = fetchLatestKageVersion;
|
|
283
286
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const { data } = await axios_1.default.get('https://registry.npmjs.org/kage-baileys', {
|
|
287
|
-
...options,
|
|
288
|
-
responseType: 'json'
|
|
289
|
-
});
|
|
290
|
-
const versionStr = data.version;
|
|
291
|
-
const [major, minor, patch] = versionStr.split('.').map(Number);
|
|
292
|
-
return {
|
|
293
|
-
version: [major, minor, patch],
|
|
294
|
-
isLatest: true
|
|
295
|
-
};
|
|
296
|
-
} catch (error) {
|
|
297
|
-
return {
|
|
298
|
-
version: kage-baileys_version_json_1.version,
|
|
299
|
-
isLatest: false,
|
|
300
|
-
error
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
};
|
|
287
|
+
// WhatsApp needs its WEB protocol version, not the npm library semver.
|
|
288
|
+
const fetchLatestBaileysVersion = async (options = {}) => fetchLatestWaWebVersion(options);
|
|
304
289
|
exports.fetchLatestBaileysVersion = fetchLatestBaileysVersion;
|
|
305
290
|
|
|
306
291
|
/**
|
|
@@ -310,14 +295,15 @@ exports.fetchLatestBaileysVersion = fetchLatestBaileysVersion;
|
|
|
310
295
|
const fetchLatestWaWebVersion = async (options) => {
|
|
311
296
|
try {
|
|
312
297
|
const { data } = await axios_1.default.get('https://web.whatsapp.com/sw.js', {
|
|
298
|
+
timeout: 15000,
|
|
313
299
|
...options,
|
|
314
|
-
responseType: '
|
|
300
|
+
responseType: 'text'
|
|
315
301
|
});
|
|
316
302
|
const regex = /\\?"client_revision\\?":\s*(\d+)/;
|
|
317
303
|
const match = data.match(regex);
|
|
318
304
|
if (!(match === null || match === void 0 ? void 0 : match[1])) {
|
|
319
305
|
return {
|
|
320
|
-
version:
|
|
306
|
+
version: kageVersionJson.version,
|
|
321
307
|
isLatest: false,
|
|
322
308
|
error: {
|
|
323
309
|
message: 'Could not find client revision in the fetched content'
|
|
@@ -332,7 +318,7 @@ const fetchLatestWaWebVersion = async (options) => {
|
|
|
332
318
|
}
|
|
333
319
|
catch (error) {
|
|
334
320
|
return {
|
|
335
|
-
version:
|
|
321
|
+
version: kageVersionJson.version,
|
|
336
322
|
isLatest: false,
|
|
337
323
|
error
|
|
338
324
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Estados recibidos por ESTA conexión. No consulta estados ocultos ni audiencias privadas.
|
|
3
3
|
const { mkdirSync, readFileSync, writeFileSync, renameSync, chmodSync } = require('node:fs')
|
|
4
4
|
const { resolve, join } = require('node:path')
|
|
5
|
-
const { createHash
|
|
5
|
+
const { createHash } = require('node:crypto')
|
|
6
6
|
const reportError = (area, error) => console.error(`[${area}]`, error?.message || error)
|
|
7
7
|
const caches = new WeakMap()
|
|
8
8
|
const DAY = 86_400_000
|
|
@@ -155,8 +155,9 @@ function captureStatus(raw, conn) {
|
|
|
155
155
|
viewOnce: !!(old?.viewOnce || info.wrappers.some(w => /viewOnce/.test(w))),
|
|
156
156
|
routes: [...new Set([...(old?.routes || []), raw.key.remoteJid])]
|
|
157
157
|
}
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
const stored = old ? Object.assign(old, entry) : entry
|
|
159
|
+
state.entries.set(identity, stored); prune(state); persist(state)
|
|
160
|
+
return stored
|
|
160
161
|
}
|
|
161
162
|
function listStatuses(conn, includeUnavailable = false) { const state = stateFor(conn); prune(state); return [...state.entries.values()].filter(e => includeUnavailable || !['deleted', 'expired'].includes(e.availability)).sort((a, b) => a.receivedAt - b.receivedAt) }
|
|
162
163
|
function findStatus(conn, selector = '') {
|
|
@@ -164,27 +165,6 @@ function findStatus(conn, selector = '') {
|
|
|
164
165
|
return listStatuses(conn, true).find(entry => entry.id === id || entry.sourceKey?.id === selector)
|
|
165
166
|
}
|
|
166
167
|
function clearStatuses(conn) { const state = stateFor(conn); const count = state.entries.size; for (const entry of state.entries.values()) retire(entry, 'expired', Date.now()); state.entries.clear(); persist(state); return count }
|
|
167
|
-
function isStatusEmoji(text) {
|
|
168
|
-
if (typeof text !== 'string' || text.length > 32 || !/\p{Extended_Pictographic}|\p{Regional_Indicator}|\u20e3/u.test(text)) return false
|
|
169
|
-
return [...new Intl.Segmenter('es', { granularity: 'grapheme' }).segment(text)].length === 1
|
|
170
|
-
}
|
|
171
|
-
async function reactToStatus(conn, entry, emoji, options = {}) {
|
|
172
|
-
if (!isStatusEmoji(emoji)) throw new Error('Escribe un solo emoji, por ejemplo ❤️, 👍🏽 o 👨👩👧👦.')
|
|
173
|
-
if (!entry || ['deleted', 'expired'].includes(entry.availability) || entry.expiresAt <= Date.now()) throw new Error('Ese estado ya no está disponible en la caché.')
|
|
174
|
-
if (!entry.sourceKey?.id || !/@(s\.whatsapp\.net|lid)$/.test(entry.author)) throw new Error('La notificación no incluye la clave y el autor originales; no es posible reaccionar con seguridad.')
|
|
175
|
-
const self = normalizeJid(conn.user?.jid || conn.user?.id)
|
|
176
|
-
if (entry.author === self || entry.author === normalizeJid(conn.user?.lid) || entry.sourceKey.fromMe) throw new Error('No se reacciona a los estados de la propia cuenta.')
|
|
177
|
-
const state = stateFor(conn)
|
|
178
|
-
if (state.busy) throw new Error('Hay otra reacción en curso; espera unos segundos.')
|
|
179
|
-
const interval = Math.max(1000, Number(options.intervalMs ?? global.safety?.statusReactionIntervalMs) || 5000)
|
|
180
|
-
if (Date.now() - state.last < interval) throw new Error('Espera unos segundos entre reacciones.')
|
|
181
|
-
state.busy = true; state.last = Date.now()
|
|
182
|
-
try {
|
|
183
|
-
if (typeof conn.readMessages === 'function') await conn.readMessages([entry.sourceKey])
|
|
184
|
-
await conn.sendMessage('status@broadcast', { react: { text: emoji, key: entry.sourceKey } }, { statusJidList: [entry.author] })
|
|
185
|
-
entry.reactedWith = emoji; entry.reactedAt = Date.now(); state.last = Date.now(); persist(state)
|
|
186
|
-
} finally { state.busy = false }
|
|
187
|
-
}
|
|
188
168
|
async function handleStatus(raw, conn, settings = {}) {
|
|
189
169
|
if (raw?.key?.remoteJid !== 'status@broadcast') return false
|
|
190
170
|
const entry = captureStatus(raw, conn)
|
|
@@ -192,27 +172,8 @@ async function handleStatus(raw, conn, settings = {}) {
|
|
|
192
172
|
try { await require('./status-archive').handleStatusArchive(conn, entry, settings, settings.archiveOptions || {}) }
|
|
193
173
|
catch (error) { reportError('status-archive', error) }
|
|
194
174
|
}
|
|
195
|
-
if (!entry || !settings.statusReaction || raw.key.fromMe || entry.reactedAt) return true
|
|
196
|
-
const state = stateFor(conn)
|
|
197
|
-
state.scheduled ||= new Set()
|
|
198
|
-
if (state.scheduled.has(entry.id) || state.scheduled.size >= 30) return true
|
|
199
|
-
state.scheduled.add(entry.id)
|
|
200
|
-
const task = (state.reactionTail || Promise.resolve()).then(async () => {
|
|
201
|
-
const gap = Math.max(1000, Number(settings.statusReactionIntervalMs ?? global.safety?.statusReactionIntervalMs) || 5000)
|
|
202
|
-
const wait = Math.max(0, state.last + gap - Date.now())
|
|
203
|
-
if (wait) await new Promise(resolve => setTimeout(resolve, wait))
|
|
204
|
-
if (!settings.statusReaction || entry.reactedAt || ['deleted', 'expired'].includes(entry.availability)) return
|
|
205
|
-
const pool = Array.isArray(settings.statusEmojis) ? settings.statusEmojis.filter(isStatusEmoji) : ['❤️', '🔥', '👍', '😊', '👏', '💜']
|
|
206
|
-
const emoji = settings.statusEmojiMode === 'random' ? pool.length ? pool[randomInt(pool.length)] : '❤️' : settings.statusEmoji || '❤️'
|
|
207
|
-
if (!isStatusEmoji(emoji)) return
|
|
208
|
-
const self = normalizeJid(conn.user?.jid || conn.user?.id)
|
|
209
|
-
if (!entry.author || entry.author === self || entry.author === normalizeJid(conn.user?.lid)) return
|
|
210
|
-
await reactToStatus(conn, entry, emoji, { intervalMs: gap })
|
|
211
|
-
})
|
|
212
|
-
state.reactionTail = task.catch(error => reportError('reacciones-estados', error)).finally(() => state.scheduled.delete(entry.id))
|
|
213
175
|
return true
|
|
214
176
|
}
|
|
215
|
-
async function waitStatusReactions(conn) { await stateFor(conn).reactionTail }
|
|
216
177
|
|
|
217
178
|
exports.markStatusDeleted = markStatusDeleted;
|
|
218
179
|
exports.flushStatuses = flushStatuses;
|
|
@@ -221,8 +182,5 @@ exports.captureStatus = captureStatus;
|
|
|
221
182
|
exports.listStatuses = listStatuses;
|
|
222
183
|
exports.findStatus = findStatus;
|
|
223
184
|
exports.clearStatuses = clearStatuses;
|
|
224
|
-
exports.isStatusEmoji = isStatusEmoji;
|
|
225
|
-
exports.reactToStatus = reactToStatus;
|
|
226
185
|
exports.handleStatus = handleStatus;
|
|
227
186
|
|
|
228
|
-
exports.waitStatusReactions = waitStatusReactions;
|
package/lib/index.d.ts
CHANGED
|
@@ -193,8 +193,8 @@ export declare const extractSyncdPatches: any;
|
|
|
193
193
|
export declare const extractUrlFromText: any;
|
|
194
194
|
export declare const extractVideoThumb: any;
|
|
195
195
|
export declare const fetchLatestBaileysVersion: any;
|
|
196
|
+
export declare const fetchLatestKageVersion: any;
|
|
196
197
|
export declare const fetchLatestWaWebVersion: any;
|
|
197
|
-
export declare const fetchLatestWileysVersion: any;
|
|
198
198
|
export declare const findStatus: any;
|
|
199
199
|
export declare const flushStatuses: any;
|
|
200
200
|
export declare const generateCodeBlockContent: any;
|
|
@@ -283,7 +283,6 @@ export declare const isJidUser: any;
|
|
|
283
283
|
export declare const isKnownStatusContact: any;
|
|
284
284
|
export declare const isLidUser: any;
|
|
285
285
|
export declare const isRealMessage: any;
|
|
286
|
-
export declare const isStatusEmoji: any;
|
|
287
286
|
export declare const isWABusinessPlatform: any;
|
|
288
287
|
export declare const jidDecode: any;
|
|
289
288
|
export declare const jidEncode: any;
|
|
@@ -313,7 +312,6 @@ export declare const processContactAction: any;
|
|
|
313
312
|
export declare const processHistoryMessage: any;
|
|
314
313
|
export declare const processSyncAction: any;
|
|
315
314
|
export declare const promiseTimeout: any;
|
|
316
|
-
export declare const reactToStatus: any;
|
|
317
315
|
export declare const readAndEmitEventStream: any;
|
|
318
316
|
export declare const reduceBinaryNodeToDictionary: any;
|
|
319
317
|
export declare const resendViewOnceMessage: any;
|
|
@@ -344,7 +342,6 @@ export declare const uploadFile: any;
|
|
|
344
342
|
export declare const useMultiFileAuthState: any;
|
|
345
343
|
export declare const version: any;
|
|
346
344
|
export declare const vid2jpg: any;
|
|
347
|
-
export declare const waitStatusReactions: any;
|
|
348
345
|
export declare const writeRandomPadMax16: any;
|
|
349
346
|
export declare const xmppPreKey: any;
|
|
350
347
|
export declare const xmppSignedPreKey: any;
|
package/lib/index.js
CHANGED
|
@@ -95,7 +95,7 @@ for (const __p of __kageExtraModules) {
|
|
|
95
95
|
try { __kageMergeSafe(require(__p)); } catch (__e) { /* opcional */ }
|
|
96
96
|
}
|
|
97
97
|
try { __kageMergeSafe(require("./VoIP/index.js")); } catch (__e) { /* VoIP opcional */ }
|
|
98
|
-
exports.KAGE_BUILD = Object.freeze({ name: "kage-baileys", version: "
|
|
98
|
+
exports.KAGE_BUILD = Object.freeze({ name: "kage-baileys", version: "3.0.1" });
|
|
99
99
|
|
|
100
100
|
__exportStar(require("./Utils/status-tools.js"), exports);
|
|
101
101
|
__exportStar(require("./Utils/country-info.js"), exports);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kage-baileys",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "Kage Baileys una libreria de WhatsApp.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kage-baileys",
|
|
@@ -38,8 +38,9 @@
|
|
|
38
38
|
"test": "node --test test/*.test.cjs",
|
|
39
39
|
"check": "npm run lint && npm test",
|
|
40
40
|
"example": "node Example/connection.cjs",
|
|
41
|
-
"
|
|
42
|
-
"
|
|
41
|
+
"gen:types": "node scripts/generate-public-types.cjs",
|
|
42
|
+
"check:engine": "node engine-requirements.js",
|
|
43
|
+
"prepublishOnly": "npm run gen:protobuf && npm run gen:types && npm run check"
|
|
43
44
|
},
|
|
44
45
|
"dependencies": {
|
|
45
46
|
"@adiwajshing/keyed-db": "^0.2.4",
|
|
@@ -71,7 +72,7 @@
|
|
|
71
72
|
},
|
|
72
73
|
"devDependencies": {
|
|
73
74
|
"@types/node": "^20.0.0",
|
|
74
|
-
"protobufjs-cli": "2.7.1",
|
|
75
|
+
"protobufjs-cli": "^2.7.1",
|
|
75
76
|
"typescript": "^5.8.2"
|
|
76
77
|
},
|
|
77
78
|
"peerDependencies": {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const test = require('node:test');
|
|
3
|
+
const assert = require('node:assert/strict');
|
|
4
|
+
const axios = require('axios');
|
|
5
|
+
const { fetchLatestBaileysVersion, fetchLatestKageVersion } = require('../lib/Utils/generics');
|
|
6
|
+
const fallback = require('../lib/Defaults/wileys-version.json').version;
|
|
7
|
+
async function mocked(get, fn) {
|
|
8
|
+
const old = axios.get;
|
|
9
|
+
axios.get = get;
|
|
10
|
+
try { await fn(); } finally { axios.get = old; }
|
|
11
|
+
}
|
|
12
|
+
test('connection version is WhatsApp WEB revision, not npm library semver', async () => {
|
|
13
|
+
await mocked(async (url, options) => {
|
|
14
|
+
assert.equal(url,'https://web.whatsapp.com/sw.js');
|
|
15
|
+
assert.equal(options.responseType,'text');
|
|
16
|
+
assert.equal(options.timeout,15000);
|
|
17
|
+
return {data:'self.config={"client_revision":1043857761}'};
|
|
18
|
+
}, async () => {
|
|
19
|
+
assert.deepEqual(await fetchLatestBaileysVersion(),{version:[2,3000,1043857761],isLatest:true});
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
test('network failure retains the included WhatsApp protocol version and original error', async () => {
|
|
23
|
+
const error = new Error('offline test');
|
|
24
|
+
await mocked(async () => {throw error}, async () => {
|
|
25
|
+
const result = await fetchLatestBaileysVersion();
|
|
26
|
+
assert.deepEqual(result.version,fallback);assert.equal(result.isLatest,false);assert.equal(result.error,error);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
test('malformed version response cannot pass an undefined or npm version to socket', async () => {
|
|
30
|
+
await mocked(async () => ({data:'<html>service unavailable</html>'}), async () => {
|
|
31
|
+
const result = await fetchLatestBaileysVersion();assert.deepEqual(result.version,fallback);assert.equal(result.isLatest,false);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
test('package update helper requests a real npm version document', async () => {
|
|
35
|
+
await mocked(async url => {assert.equal(url,'https://registry.npmjs.org/kage-baileys/latest');return {data:{version:'2.0.1'}}},async()=>{
|
|
36
|
+
const result=await fetchLatestKageVersion();assert.deepEqual(result.version,[2,0,1]);assert.equal(result.isLatest,true);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
test('package fallback is the installed library version, not a WhatsApp version', async () => {
|
|
40
|
+
await mocked(async()=>{throw new Error('offline')},async()=>{
|
|
41
|
+
const result=await fetchLatestKageVersion();assert.deepEqual(result.version,require('../package.json').version.split('.').map(Number));assert.equal(result.isLatest,false);
|
|
42
|
+
});
|
|
43
|
+
});
|
package/test/revision.test.cjs
CHANGED
|
@@ -11,8 +11,6 @@ const countries = require('../lib/Utils/countries.json');
|
|
|
11
11
|
const examples = require('libphonenumber-js/examples.mobile.json');
|
|
12
12
|
const raw = (id = 'S1', author = '51987654321@s.whatsapp.net', message = { conversation: 'Hola' }) => ({ key: { remoteJid: 'status@broadcast', participant: author, id, fromMe: false }, messageTimestamp: Math.floor(Date.now()/1000), message });
|
|
13
13
|
const socket = () => ({ user: { id: '34612345678:2@s.whatsapp.net' }, statusStorageDir: false, sendMessage: async () => ({}), readMessages: async () => {}, ev: new EventEmitter() });
|
|
14
|
-
for (const emoji of ['❤️','🔥','👍','👍🏽','👨👩👧👦','😊','🇵🇪','1️⃣','💜','👏','🌈','🏳️🌈','🧑🏽💻','🫶','☀️','⭐','🐕','💯','😀','🎉']) test(`emoji accepted: ${emoji}`, () => assert.equal(api.isStatusEmoji(emoji), true));
|
|
15
|
-
for (const value of ['', 'hola', '❤️🔥', 'a❤️', '123', null, 2, ' ', '\n', '👍 👍', 'x', '🇵🇪🇲🇽', '❤️'.repeat(30)]) test(`emoji rejected: ${JSON.stringify(value)}`, () => assert.equal(api.isStatusEmoji(value), false));
|
|
16
14
|
for (const code of ['PE','MX','ES','AR','CO','CL','EC','BO','BR','US','CA','GB','FR','DE','IT','JP','KR','AU','NZ','IN','ZA','EG','MA','UY','PY','VE','CR','PA','DO','GT']) test(`offline country lookup ${code}`, () => {
|
|
17
15
|
const info = api.getCountryInfo(require('libphonenumber-js').getExampleNumber(code, examples).number);
|
|
18
16
|
assert.ok(info, code); assert.equal(info.code, code); assert.equal(info.name, countries[code].name);
|
|
@@ -100,23 +98,6 @@ test('status cache bounded to 500 entries', () => {
|
|
|
100
98
|
const c = socket(); for (let i=0;i<510;i++) api.captureStatus(raw('id-'+i),c);
|
|
101
99
|
assert.equal(api.listStatuses(c).length,500); assert.equal(api.clearStatuses(c),500);
|
|
102
100
|
});
|
|
103
|
-
test('manual reaction uses status route, real message key and emoji', async () => {
|
|
104
|
-
const c = socket(); let sent; c.sendMessage = async (...args) => { sent=args; };
|
|
105
|
-
const e = api.captureStatus(raw(),c); await api.reactToStatus(c,e,'👍🏽');
|
|
106
|
-
assert.equal(sent[0],'status@broadcast'); assert.equal(sent[1].react.text,'👍🏽'); assert.deepEqual(sent[2].statusJidList,[e.author]);
|
|
107
|
-
});
|
|
108
|
-
test('cannot react to deleted or self statuses', async () => {
|
|
109
|
-
const c = socket(); const e = api.captureStatus(raw(),c); api.markStatusDeleted(c,e.sourceKey);
|
|
110
|
-
await assert.rejects(api.reactToStatus(c,e,'🔥'), /disponible/);
|
|
111
|
-
const self = api.captureStatus(raw('self','34612345678@s.whatsapp.net'),c);
|
|
112
|
-
await assert.rejects(api.reactToStatus(c,self,'🔥'), /propia cuenta/);
|
|
113
|
-
});
|
|
114
|
-
test('random reaction uses configured emoji pool and deduplicates delivery', async () => {
|
|
115
|
-
const c = socket(); const sent=[]; c.sendMessage=async (...args) => { sent.push(args); };
|
|
116
|
-
const settings = { statusReaction:true,statusEmojiMode:'random',statusEmojis:['🔥','💜'] };
|
|
117
|
-
await api.handleStatus(raw(),c,settings); await api.handleStatus(raw(),c,settings); await api.waitStatusReactions(c);
|
|
118
|
-
assert.equal(sent.length,1); assert.ok(settings.statusEmojis.includes(sent[0][1].react.text));
|
|
119
|
-
});
|
|
120
101
|
test('socket bindings install and detach status listeners without duplicates', async () => {
|
|
121
102
|
const c = socket(); api.installKageTools(c); api.installKageTools(c);
|
|
122
103
|
const stop = c.enableStatusTracking(); assert.equal(c.enableStatusTracking(),stop); assert.equal(c.ev.listenerCount('messages.upsert'),1);
|
|
@@ -140,14 +121,6 @@ test('browser presets support documented calls and missing browser', () => {
|
|
|
140
121
|
assert.equal(api.Browsers().length,3); assert.doesNotThrow(()=>api.getPlatformId());
|
|
141
122
|
});
|
|
142
123
|
|
|
143
|
-
test('automatic reaction queue processes a burst with configured pacing', async () => {
|
|
144
|
-
const c = socket(); const sent=[];
|
|
145
|
-
c.sendMessage = async (...args) => { sent.push({ time: Date.now(), args }); };
|
|
146
|
-
const settings = { statusReaction: true, statusEmoji: '🔥', statusReactionIntervalMs: 1000 };
|
|
147
|
-
await api.handleStatus(raw('first'), c, settings); await api.handleStatus(raw('second'), c, settings);
|
|
148
|
-
await api.waitStatusReactions(c);
|
|
149
|
-
assert.equal(sent.length,2); assert.ok(sent[1].time - sent[0].time >= 990);
|
|
150
|
-
});
|
|
151
124
|
test('detaching status tracking never overwrites persisted activation setting', () => {
|
|
152
125
|
const c = socket(); api.installKageTools(c); const settings = { statusReaction: true };
|
|
153
126
|
const stop = c.enableStatusTracking(settings); stop(); stop();
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const test = require('node:test');
|
|
3
|
+
const assert = require('node:assert/strict');
|
|
4
|
+
const { EventEmitter } = require('node:events');
|
|
5
|
+
const api = require('../lib');
|
|
6
|
+
const raw = id => ({ key: { remoteJid: 'status@broadcast', participant: '51987654321@s.whatsapp.net', id }, messageTimestamp: Math.floor(Date.now()/1000), message: { conversation: 'estado' } });
|
|
7
|
+
const socket = () => ({ user: { id: '34612345678@s.whatsapp.net' }, statusStorageDir: false, ev: new EventEmitter(), sent: [], async sendMessage(...args) { this.sent.push(args); } });
|
|
8
|
+
test('Baileys has no application-level status reaction exports or socket method', () => {
|
|
9
|
+
for (const name of ['reactToStatus', 'waitStatusReactions', 'isStatusEmoji']) assert.equal(api[name], undefined);
|
|
10
|
+
const conn = api.installKageTools(socket());
|
|
11
|
+
assert.equal(conn.reactToReceivedStatus, undefined);
|
|
12
|
+
});
|
|
13
|
+
test('statusReaction settings never send reactions from Baileys', async () => {
|
|
14
|
+
const conn = socket();
|
|
15
|
+
await api.handleStatus(raw('A'), conn, { statusReaction: true, statusEmoji: '🔥' });
|
|
16
|
+
await api.handleStatus(raw('A'), conn, { statusReaction: true });
|
|
17
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
18
|
+
assert.equal(conn.sent.length, 0);
|
|
19
|
+
assert.equal(api.listStatuses(conn).length, 1);
|
|
20
|
+
});
|
|
21
|
+
test('optional status tracking only records received statuses', async () => {
|
|
22
|
+
const conn = api.installKageTools(socket());
|
|
23
|
+
const stop = conn.enableStatusTracking({ statusReaction: true });
|
|
24
|
+
conn.ev.emit('messages.upsert', { type: 'notify', messages: [raw('B')] });
|
|
25
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
26
|
+
assert.equal(conn.sent.length, 0);
|
|
27
|
+
assert.equal(conn.getReceivedStatuses().length, 1);
|
|
28
|
+
stop();
|
|
29
|
+
assert.equal(conn.ev.listenerCount('messages.upsert'), 0);
|
|
30
|
+
});
|
|
31
|
+
test('official status reaction schema survives encoding and decoding', () => {
|
|
32
|
+
const source = { reactionMessage: { key: { remoteJid: 'status@broadcast', participant: '51987654321@s.whatsapp.net', id: 'ORIGINAL', fromMe: false }, text: '❤️', senderTimestampMs: '1770000000000' } };
|
|
33
|
+
const message = api.proto.Message.fromObject(source);
|
|
34
|
+
assert.equal(api.proto.Message.verify(message), null);
|
|
35
|
+
const decoded = api.proto.Message.decode(api.proto.Message.encode(message).finish());
|
|
36
|
+
assert.equal(decoded.reactionMessage.text, '❤️');
|
|
37
|
+
assert.equal(decoded.reactionMessage.key.id, 'ORIGINAL');
|
|
38
|
+
assert.equal(decoded.reactionMessage.key.remoteJid, 'status@broadcast');
|
|
39
|
+
assert.equal(api.proto.MessageAssociation.AssociationType.STATUS_REACTION, 18);
|
|
40
|
+
});
|