kage-baileys 3.0.6 → 3.2.3
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/Example/connection.cjs +4 -0
- package/README.MD +4726 -48
- package/WAProto/WAProto.proto +3395 -415
- package/WAProto/compat.cjs +12 -0
- package/WAProto/index.d.ts +47104 -19888
- package/WAProto/index.js +131979 -57361
- package/lib/Addon/http-error.js +43 -0
- package/lib/Addon/index.js +32 -0
- package/lib/Addon/keyed-db.js +250 -0
- package/lib/Addon/link-preview.js +82 -0
- package/lib/Defaults/baileys-version.json +3 -0
- package/lib/Defaults/connection.js +110 -0
- package/lib/Defaults/constants.js +156 -0
- package/lib/Defaults/history.js +46 -0
- package/lib/Defaults/index.js +230 -0
- package/lib/Defaults/media.js +104 -0
- package/lib/Defaults/phonenumber-mcc.json +223 -0
- package/lib/Defaults/prefix.js +44 -0
- package/lib/Extensions/business-advanced.js +382 -0
- package/lib/Extensions/business.js +159 -0
- package/lib/Extensions/chats-advanced.js +1106 -0
- package/lib/Extensions/chats.js +349 -0
- package/lib/Extensions/communities-advanced.js +430 -0
- package/lib/Extensions/community.js +94 -0
- package/lib/Extensions/groups-advanced.js +343 -0
- package/lib/Extensions/index.js +30 -0
- package/lib/Extensions/messages-recv-advanced.js +1690 -0
- package/lib/Extensions/messages-send-advanced.js +622 -0
- package/lib/Extensions/messages-send.js +1380 -0
- package/lib/Extensions/newsletter-advanced.js +228 -0
- package/lib/Extensions/newsletter.js +72 -0
- package/lib/Extensions/socket.js +389 -0
- package/lib/Modded/message_builder.js +2084 -4
- package/lib/Signal/Group/ciphertext-message.js +54 -0
- package/lib/Signal/Group/group-session-builder.js +124 -0
- package/lib/Signal/Group/group_cipher.js +216 -0
- package/lib/Signal/Group/index.js +124 -0
- package/lib/Signal/Group/keyhelper.js +108 -0
- package/lib/Signal/Group/sender-chain-key.js +83 -0
- package/lib/Signal/Group/sender-key-distribution-message.js +157 -0
- package/lib/Signal/Group/sender-key-message.js +166 -0
- package/lib/Signal/Group/sender-key-name.js +130 -0
- package/lib/Signal/Group/sender-key-record.js +113 -0
- package/lib/Signal/Group/sender-key-state.js +208 -0
- package/lib/Signal/Group/sender-message-key.js +82 -0
- package/lib/Signal/libsignal.js +935 -0
- package/lib/Signal/lid-mapping.js +545 -1
- package/lib/Socket/Client/index.js +60 -0
- package/lib/Socket/Client/types.js +51 -0
- package/lib/Socket/Client/websocket.js +152 -0
- package/lib/Socket/business.js +831 -0
- package/lib/Socket/chats.js +2535 -49
- package/lib/Socket/communities.js +450 -1
- package/lib/Socket/community.js +1022 -0
- package/lib/Socket/dugong.js +1 -1
- package/lib/Socket/groups.js +747 -0
- package/lib/Socket/index.js +60 -2
- package/lib/Socket/messages-recv.js +3450 -5
- package/lib/Socket/messages-send.js +3123 -0
- package/lib/Socket/mex.js +126 -1
- package/lib/Socket/newsletter.js +465 -0
- package/lib/Socket/socket.js +2217 -1
- package/lib/Store/index.js +79 -0
- package/lib/Store/make-cache-manager-store.js +186 -0
- package/lib/Store/make-in-memory-store.js +878 -0
- package/lib/Store/make-ordered-dictionary.js +187 -0
- package/lib/Store/object-repository.js +78 -0
- package/lib/Types/Auth.js +25 -0
- package/lib/Types/Bussines.js +25 -1
- package/lib/Types/Call.js +25 -0
- package/lib/Types/Chat.js +39 -0
- package/lib/Types/Contact.js +25 -0
- package/lib/Types/Events.js +25 -0
- package/lib/Types/GroupMetadata.js +25 -0
- package/lib/Types/Label.js +76 -0
- package/lib/Types/LabelAssociation.js +40 -0
- package/lib/Types/Message.js +57 -0
- package/lib/Types/Mex.js +56 -1
- package/lib/Types/MexUpdates.js +46 -0
- package/lib/Types/Newsletter.js +56 -0
- package/lib/Types/Product.js +25 -0
- package/lib/Types/RichType.js +23 -0
- package/lib/Types/Signal.js +25 -0
- package/lib/Types/Socket.js +25 -0
- package/lib/Types/State.js +92 -0
- package/lib/Types/USync.js +25 -0
- package/lib/Types/index.js +133 -0
- package/lib/Utils/auth-utils.js +703 -0
- package/lib/Utils/automation-tools.js +51 -16
- package/lib/Utils/baileys-event-stream.js +146 -0
- package/lib/Utils/browser-utils.js +95 -0
- package/lib/Utils/business.js +525 -0
- package/lib/Utils/chat-utils.js +1861 -0
- package/lib/Utils/companion-reg-client-utils.js +55 -1
- package/lib/Utils/crypto.js +355 -0
- package/lib/Utils/decode-wa-message.js +709 -0
- package/lib/Utils/event-buffer.js +1323 -8
- package/lib/Utils/generics.js +945 -1
- package/lib/Utils/history.js +266 -0
- package/lib/Utils/identity-change-handler.js +62 -1
- package/lib/Utils/index.js +114 -0
- package/lib/Utils/link-preview.js +179 -0
- package/lib/Utils/logger.js +42 -0
- package/lib/Utils/lt-hash.js +73 -0
- package/lib/Utils/make-mutex.js +104 -0
- package/lib/Utils/message-inspector.js +110 -0
- package/lib/Utils/message-retry-manager.js +507 -2
- package/lib/Utils/messages-media.js +1931 -1
- package/lib/Utils/messages.js +3748 -0
- package/lib/Utils/noise-handler.js +423 -0
- package/lib/Utils/offline-node-processor.js +53 -1
- package/lib/Utils/pre-key-manager.js +255 -1
- package/lib/Utils/process-message.js +1592 -0
- package/lib/Utils/reporting-utils.js +272 -1
- package/lib/Utils/rich-message-utils.js +377 -0
- package/lib/Utils/rich-messages.js +1 -1
- package/lib/Utils/signal.js +437 -0
- package/lib/Utils/stanza-ack.js +51 -1
- package/lib/Utils/status-refresh.js +37 -0
- package/lib/Utils/status-tools.js +4 -2
- package/lib/Utils/sync-action-utils.js +63 -1
- package/lib/Utils/tc-token-utils.js +185 -1
- package/lib/Utils/terminal-banner.js +31 -0
- package/lib/Utils/use-multi-file-auth-state.js +271 -0
- package/lib/Utils/use-single-file-auth-state.js +108 -0
- package/lib/Utils/use-sqlite-auth-state.js +142 -0
- package/lib/Utils/validate-connection.js +497 -1
- package/lib/Utils/view-once-compat.js +13 -5
- package/lib/VoIP/audio-feeder.js +150 -0
- package/lib/VoIP/index.js +289 -0
- package/lib/VoIP/relay-transport.js +608 -0
- package/lib/VoIP/signaling.js +583 -0
- package/lib/VoIP/types.js +30 -0
- package/lib/VoIP/wasm-engine.js +1261 -2
- package/lib/VoIP/worker-bootstrap.js +1090 -2
- package/lib/WABinary/constants.js +2805 -0
- package/lib/WABinary/decode.js +649 -2
- package/lib/WABinary/encode.js +564 -2
- package/lib/WABinary/generic-utils.js +401 -0
- package/lib/WABinary/index.js +82 -0
- package/lib/WABinary/jid-utils.js +290 -0
- package/lib/WABinary/types.js +25 -0
- package/lib/WAM/BinaryInfo.js +50 -0
- package/lib/WAM/constants.js +45747 -0
- package/lib/WAM/encode.js +393 -0
- package/lib/WAM/index.js +78 -0
- package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +135 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +110 -0
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +142 -0
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +85 -0
- package/lib/WAUSync/Protocols/USyncLIDProtocol.js +69 -0
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +107 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +38 -1
- package/lib/WAUSync/Protocols/index.js +83 -0
- package/lib/WAUSync/USyncQuery.js +241 -2
- package/lib/WAUSync/USyncUser.js +86 -0
- package/lib/WAUSync/index.js +78 -0
- package/lib/assets/wasm/loader.js +1875 -1
- package/lib/assets/wasm/worker-modules.js +11047 -1
- package/lib/index.d.ts +126 -0
- package/lib/index.js +53 -21
- package/lib/index.mjs +437 -0
- package/licenses/upstream-MIT.txt +22 -0
- package/maintenance/api-coverage.json +1642 -0
- package/maintenance/archivos-sha256.json +207 -0
- package/maintenance/consumer-validation.log +3 -0
- package/maintenance/dependency-audit.json +4 -4
- package/maintenance/extension-inventory.json +57 -0
- package/maintenance/protocol-update.json +1101 -0
- package/maintenance/protocol-wire-audit.json +4 -0
- package/maintenance/reference-socket-surface.json +209 -0
- package/maintenance/resumen-validacion.json +50 -0
- package/maintenance/validation.log +2725 -0
- package/maintenance/verification.json +566 -0
- package/package.json +107 -9
- package/scripts/check-imports.cjs +10 -0
- package/scripts/check-package.cjs +40 -0
- package/scripts/check-syntax.cjs +1 -1
- package/scripts/generate-protobuf.cjs +6 -1
- package/scripts/generate-public-types.cjs +9 -0
- package/scripts/kage-base64-fix.cjs +45 -0
- package/scripts/maintenance.cjs +10 -0
- package/test/advanced.test.cjs +24 -0
- package/test/base64-fix.test.cjs +49 -0
- package/test/maintenance.test.cjs +1 -1
- package/test/npm-publication.test.cjs +141 -0
- package/test/recepcion-reparada.test.cjs +98 -0
- package/test/resilience-binary.test.cjs +69 -0
- package/test/resilience-transport.test.cjs +34 -0
- package/test/types.test.ts +5 -0
- package/test/upgrade.test.cjs +104 -0
- package/tsconfig.json +20 -0
- package/WAProto/p.html +0 -1
- package/lib/KageExtra/Modded/message_builder.js +0 -2330
- package/lib/KageExtra/VoIP/audio-feeder.js +0 -136
- package/lib/KageExtra/VoIP/index.js +0 -282
- package/lib/KageExtra/VoIP/relay-transport.js +0 -559
- package/lib/KageExtra/VoIP/signaling.js +0 -603
- package/lib/KageExtra/VoIP/types.js +0 -17
- package/lib/KageExtra/VoIP/wasm-engine.js +0 -1214
- package/lib/KageExtra/VoIP/worker-bootstrap.js +0 -1042
- package/lib/KageExtra/assets/wasm/loader.js +0 -5
- package/lib/KageExtra/assets/wasm/whatsapp.wasm +0 -0
- package/lib/KageExtra/assets/wasm/worker-modules.js +0 -273
- package/lib/KageExtra/index.js +0 -3
- package/lib/KageExtra/package.json +0 -1
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +0 -53
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +0 -24
- package/maintenance/revision-full-check.txt +0 -1097
- package/maintenance/revision-library-tests.tap +0 -1088
- package/maintenance/revision-protobuf.txt +0 -5
- package/maintenance/revision-syntax.txt +0 -5
- package/maintenance/schema-coverage.json +0 -23
- package/maintenance/schema-merge.json +0 -30
- package/maintenance/validation-node20.txt +0 -191
- package/maintenance/validation-node22.txt +0 -237
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const test=require('node:test');const assert=require('node:assert/strict');
|
|
3
|
+
const {EventEmitter}=require('node:events');const {Readable}=require('node:stream');
|
|
4
|
+
const fs=require('node:fs');const os=require('node:os');const path=require('node:path');
|
|
5
|
+
const api=require('../lib');
|
|
6
|
+
const chat='51900000001@s.whatsapp.net';
|
|
7
|
+
const socket=()=>({ev:new EventEmitter(),user:{id:'51900000002:1@s.whatsapp.net'},statusStorageDir:false,sent:[],async sendMessage(jid,body){const sent={jid,body};this.sent.push(sent);return sent;}});
|
|
8
|
+
const raw=(type='imageMessage',wrapper='viewOnceMessageV2',route=chat)=>({key:{remoteJid:route,participant:route.endsWith('@g.us')?chat:undefined,id:'fresh',fromMe:false},messageTimestamp:Math.floor(Date.now()/1000),message:{[wrapper]:{message:{[type]:{viewOnce:true,mediaKey:Buffer.from('secret'),directPath:'/delivered',mimetype:type==='audioMessage'?'audio/ogg':'application/octet-stream',ptt:type==='audioMessage'}}}}});
|
|
9
|
+
const download=async()=>Readable.from([Buffer.from('delivered-content')]);
|
|
10
|
+
for(const type of ['imageMessage','videoMessage','audioMessage','documentMessage','stickerMessage'])for(const wrapper of ['viewOnceMessage','viewOnceMessageV2','viewOnceMessageV2Extension']){
|
|
11
|
+
test(`inspección conserva ${wrapper}/${type}`,()=>{
|
|
12
|
+
const message=raw(type,wrapper),before=JSON.stringify(message),detail=api.inspectMessage(message);
|
|
13
|
+
assert.equal(detail.type,type);assert.equal(detail.viewOnce,true);assert.equal(detail.downloadable,true);assert.equal(JSON.stringify(message),before);assert.equal(JSON.stringify(detail).includes('secret'),false);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
for(const route of [chat,'123456@g.us'])for(const type of ['imageMessage','videoMessage','audioMessage'])test(`automático usa misma salida manual: ${route}/${type}`,async()=>{
|
|
17
|
+
const a=socket(),b=socket(),message=raw(type,'viewOnceMessageV2Extension',route);
|
|
18
|
+
await api.resendViewOnceMessage(a,message,{downloader:download});
|
|
19
|
+
await api.handleAutoViewOnce(message,b,{autoViewOnceChats:[route]},{downloader:download});
|
|
20
|
+
assert.deepEqual(b.sent,a.sent);assert.equal(b.sent.length,1);
|
|
21
|
+
});
|
|
22
|
+
test('deviceSentMessage y efímero no ocultan la vista única',async()=>{
|
|
23
|
+
const c=socket(),message=raw();message.message={deviceSentMessage:{message:{ephemeralMessage:{message:message.message}}}};
|
|
24
|
+
await api.handleAutoViewOnce(message,c,{autoViewOnceChats:[chat]},{downloader:download});assert.equal(c.sent.length,1);assert.equal(api.inspectMessage(message).viewOnce,true);
|
|
25
|
+
});
|
|
26
|
+
test('permiso de privado PN funciona con LID confirmado',async()=>{
|
|
27
|
+
const c=socket();c.signalRepository={lidMapping:{getPNForLID:async()=>chat}};
|
|
28
|
+
await api.handleAutoViewOnce(raw('imageMessage','viewOnceMessageV2','999@lid'),c,{autoViewOnceChats:[chat]},{downloader:download});assert.equal(c.sent.length,1);assert.equal(c.sent[0].jid,'999@lid');
|
|
29
|
+
});
|
|
30
|
+
test('un LID sin mapeo no se inventa como teléfono ni obtiene permiso',async()=>{
|
|
31
|
+
const c=socket();await api.handleAutoViewOnce(raw('imageMessage','viewOnceMessageV2','999@lid'),c,{autoViewOnceChats:['999@s.whatsapp.net']},{downloader:download});assert.equal(c.sent.length,0);assert.equal(api.inspectMessage(raw('imageMessage','viewOnceMessageV2','999@lid')).chat,'999@lid');
|
|
32
|
+
});
|
|
33
|
+
test('medios propios solo se copian mediante autorización explícita',async()=>{
|
|
34
|
+
const c=socket(),message=raw();message.key.fromMe=true;
|
|
35
|
+
await api.handleAutoViewOnce(message,c,{autoViewOnceChats:[chat]},{downloader:download});assert.equal(c.sent.length,0);
|
|
36
|
+
await api.handleAutoViewOnce(message,c,{autoViewOnceChats:[chat],autoViewOnceOwn:true},{downloader:download});assert.equal(c.sent.length,1);
|
|
37
|
+
const normal={...message,key:{...message.key,id:'normal-output'},message:{imageMessage:{}}};await api.handleAutoViewOnce(normal,c,{autoViewOnceChats:[chat],autoViewOnceOwn:true},{downloader:download});assert.equal(c.sent.length,1);
|
|
38
|
+
});
|
|
39
|
+
test('fallo no deja el ID bloqueado para la siguiente entrega',async()=>{
|
|
40
|
+
const c=socket(),message=raw(),settings={autoViewOnceChats:[chat]};
|
|
41
|
+
await assert.rejects(api.handleAutoViewOnce(message,c,settings,{downloader:async()=>{throw new Error('fallo controlado');}}),/fallo controlado/);
|
|
42
|
+
await api.handleAutoViewOnce(message,c,settings,{downloader:download});assert.equal(c.sent.length,1);
|
|
43
|
+
});
|
|
44
|
+
test('descarga renueva referencia 404 y reintenta una vez',async()=>{
|
|
45
|
+
const c=socket();let downloads=0,reuploads=0;
|
|
46
|
+
c.updateMediaMessage=async message=>{reuploads++;return message;};
|
|
47
|
+
const result=await api.downloadViewOnceMessage(c,raw(),{downloader:async()=>{if(!downloads++){const e=new Error('CDN 404');e.response={status:404};throw e;}return download();}});
|
|
48
|
+
assert.equal(result.buffer.toString(),'delivered-content');assert.equal(downloads,2);assert.equal(reuploads,1);
|
|
49
|
+
});
|
|
50
|
+
test('fallo persistente se detiene tras dos intentos',async()=>{
|
|
51
|
+
let calls=0;await assert.rejects(api.downloadViewOnceMessage(socket(),raw(),{downloader:async()=>{calls++;const e=new Error('CDN 410');e.statusCode=410;throw e;}}),/CDN 410/);assert.equal(calls,2);
|
|
52
|
+
});
|
|
53
|
+
test('vista única automática no procesa historial antiguo',async()=>{
|
|
54
|
+
const c=socket(),message=raw();message.messageTimestamp=1;
|
|
55
|
+
await api.handleAutoViewOnce(message,c,{autoViewOnceChats:[chat]},{downloader:download});assert.equal(c.sent.length,0);
|
|
56
|
+
});
|
|
57
|
+
test('aviso cifrado no se clasifica como medio disponible',()=>{
|
|
58
|
+
const info=api.inspectMessage({key:{remoteJid:chat,id:'pending'},messageStubType:2});assert.equal(info.type,'ciphertext');assert.equal(info.encrypted,true);assert.match(info.label,/pendiente/);
|
|
59
|
+
});
|
|
60
|
+
test('monitor detecta llamadas, presencia, reacciones y cierra sin listeners duplicados',()=>{
|
|
61
|
+
const c=api.installKageTools(socket()),stop=c.enableEventTracking();assert.equal(c.enableEventTracking(),stop);
|
|
62
|
+
c.ev.emit('call',[{id:'call1',from:chat,status:'offer',isVideo:true}]);
|
|
63
|
+
c.ev.emit('presence.update',{id:chat,presences:{[chat]:{lastKnownPresence:'composing'}}});
|
|
64
|
+
c.ev.emit('messages.reaction',[{key:{id:'reaction',remoteJid:chat},reaction:{text:'👍'}}]);
|
|
65
|
+
const diag=c.getMessageDiagnostics();assert.equal(diag.counts.call,1);assert.equal(diag.counts.presence,1);assert.equal(diag.counts.reaction,1);assert.equal(diag.recent[0].isVideo,true);
|
|
66
|
+
stop();assert.equal(c.ev.listenerCount('call'),0);assert.equal(c.ev.listenerCount('messages.upsert'),0);assert.equal(c.getMessageDiagnostics().active,false);
|
|
67
|
+
});
|
|
68
|
+
test('diagnóstico acotado no almacena claves de medios ni texto',()=>{
|
|
69
|
+
const c=api.installKageTools(socket());const stop=c.enableEventTracking();
|
|
70
|
+
for(let i=0;i<120;i++){const m=raw();m.key.id='m'+i;c.ev.emit('messages.upsert',{messages:[m],type:'notify'});}
|
|
71
|
+
const diag=c.getMessageDiagnostics();assert.equal(diag.recent.length,100);assert.equal(diag.messages.length,120);assert.equal(JSON.stringify(diag).includes('secret'),false);stop();
|
|
72
|
+
});
|
|
73
|
+
test('estado de historial y actualización descifrada se reindexa',async()=>{
|
|
74
|
+
const c=api.installKageTools(socket());const stop=c.enableStatusTracking();
|
|
75
|
+
const key={remoteJid:'status@broadcast',participant:chat,id:'status1'};const timestamp=Math.floor(Date.now()/1000);
|
|
76
|
+
c.ev.emit('messaging-history.set',{messages:[{key,messageTimestamp:timestamp,message:{conversation:'publicado'}}]});
|
|
77
|
+
assert.equal(c.getReceivedStatuses().length,1);
|
|
78
|
+
c.ev.emit('messages.update',[{key,update:{messageTimestamp:timestamp,message:{imageMessage:{mimetype:'image/jpeg'}}}}]);
|
|
79
|
+
await new Promise(r=>setImmediate(r));assert.equal(c.getReceivedStatuses()[0].type,'imageMessage');stop();
|
|
80
|
+
});
|
|
81
|
+
test('actualización de estados usa solo claves conocidas, sin consultar estados ocultos',async()=>{
|
|
82
|
+
const c=socket();api.captureStatus({key:{remoteJid:'status@broadcast',participant:chat,id:'missing'},messageTimestamp:Math.floor(Date.now()/1000),message:{}},c);
|
|
83
|
+
const requests=[];c.requestPlaceholderResend=async key=>requests.push(key);
|
|
84
|
+
const report=await api.refreshReceivedStatuses(c);assert.equal(report.requested,1);assert.equal(requests[0].id,'missing');
|
|
85
|
+
assert.equal((await api.refreshReceivedStatuses(c)).throttled,true);
|
|
86
|
+
});
|
|
87
|
+
test('caché consultada antes del login carga su archivo al conocerse la cuenta',()=>{
|
|
88
|
+
const dir=fs.mkdtempSync(path.join(os.tmpdir(),'status-login-'));
|
|
89
|
+
try{
|
|
90
|
+
const a=socket();a.statusStorageDir=dir;api.captureStatus({key:{remoteJid:'status@broadcast',participant:chat,id:'saved'},messageTimestamp:Math.floor(Date.now()/1000),message:{conversation:'persistido'}},a);
|
|
91
|
+
const b={statusStorageDir:dir};assert.equal(api.listStatuses(b).length,0);b.user=a.user;assert.equal(api.listStatuses(b).length,1);
|
|
92
|
+
}finally{fs.rmSync(dir,{recursive:true,force:true});}
|
|
93
|
+
});
|
|
94
|
+
test('banner admite fuentes y colores sin efectos al importar',()=>{
|
|
95
|
+
const lines=[];const stream={columns:60,isTTY:false,write:text=>lines.push(text)};
|
|
96
|
+
for(const font of ['simple','console','tiny','block'])api.say('kage baileys',{font,align:'center',gradient:['red','magenta'],stream});
|
|
97
|
+
assert.equal(lines.length,4);assert.match(lines[0],/KAGE BAILEYS/);assert.match(lines[3],/▰/);
|
|
98
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const test = require('node:test');
|
|
3
|
+
const assert = require('node:assert/strict');
|
|
4
|
+
const { deflateSync } = require('node:zlib');
|
|
5
|
+
const { randomBytes } = require('node:crypto');
|
|
6
|
+
const { decodeBinaryNode, decodeDecompressedBinaryNode, decompressingIfRequired } = require('../lib/WABinary/decode');
|
|
7
|
+
const { encodeBinaryNode } = require('../lib/WABinary/encode');
|
|
8
|
+
const constants = require('../lib/WABinary/constants');
|
|
9
|
+
const { inspectMessage } = require('../lib/Utils/message-inspector');
|
|
10
|
+
const { TAGS } = constants;
|
|
11
|
+
|
|
12
|
+
test('round trip de Unicode y contenido binario sin alteración', async () => {
|
|
13
|
+
const node = { tag:'message', attrs:{ id:'日本語-😺', to:'51900000001@s.whatsapp.net' }, content:[{tag:'body',attrs:{},content:Buffer.from([0,255,128,1,2])}] };
|
|
14
|
+
const decoded = await decodeBinaryNode(encodeBinaryNode(node));
|
|
15
|
+
assert.deepEqual(decoded,node);
|
|
16
|
+
});
|
|
17
|
+
test('paquetes vacíos, corruptos y truncados se rechazan', async () => {
|
|
18
|
+
await assert.rejects(decodeBinaryNode(Buffer.alloc(0)),/BINARY_SIZE_LIMIT/);
|
|
19
|
+
await assert.rejects(decodeBinaryNode(Buffer.from([2,255,0,13])));
|
|
20
|
+
const buffer = encodeBinaryNode({ tag:'message',attrs:{id:'test'},content:Buffer.from('test') });
|
|
21
|
+
for (let i=1;i<buffer.length;i++) await assert.rejects(decodeBinaryNode(buffer.subarray(0,i)));
|
|
22
|
+
});
|
|
23
|
+
test('inflado tiene límite contra paquetes comprimidos desproporcionados', async () => {
|
|
24
|
+
const compressed = deflateSync(Buffer.alloc(33*1024*1024));
|
|
25
|
+
await assert.rejects(decompressingIfRequired(Buffer.concat([Buffer.from([2]),compressed])), /larger|limit|length|buffer/i);
|
|
26
|
+
});
|
|
27
|
+
test('BINARY_32 no acepta longitudes negativas por desbordamiento de signo', () => {
|
|
28
|
+
const head = [TAGS.LIST_8,2,TAGS.BINARY_8,1,120,TAGS.BINARY_32,255,255,255,255];
|
|
29
|
+
assert.throws(() => decodeDecompressedBinaryNode(Buffer.from(head),constants),/end of stream/);
|
|
30
|
+
});
|
|
31
|
+
test('árbol binario profundo se detiene antes de agotar la pila', async () => {
|
|
32
|
+
const parts = [Buffer.from([0])];
|
|
33
|
+
for(let i=0;i<80;i++) parts.push(Buffer.from([TAGS.LIST_8,2,TAGS.BINARY_8,1,120,TAGS.LIST_8,1]));
|
|
34
|
+
parts.push(Buffer.from([TAGS.LIST_8,1,TAGS.BINARY_8,1,120]));
|
|
35
|
+
await assert.rejects(decodeBinaryNode(Buffer.concat(parts)),/BINARY_COMPLEXITY_LIMIT/);
|
|
36
|
+
});
|
|
37
|
+
test('pares JID recursivos se detienen antes de agotar la pila', async () => {
|
|
38
|
+
const parts = [Buffer.from([0,TAGS.LIST_8,1])];
|
|
39
|
+
for(let i=0;i<80;i++) parts.push(Buffer.from([TAGS.JID_PAIR]));
|
|
40
|
+
parts.push(Buffer.from([TAGS.BINARY_8,1,120]));
|
|
41
|
+
await assert.rejects(decodeBinaryNode(Buffer.concat(parts)),/BINARY_STRING_DEPTH_LIMIT/);
|
|
42
|
+
});
|
|
43
|
+
test('claves especiales de atributos no modifican el prototipo', async () => {
|
|
44
|
+
const attrs = JSON.parse('{"__proto__":"literal","id":"test"}');
|
|
45
|
+
const decoded = await decodeBinaryNode(encodeBinaryNode({ tag:'message',attrs }));
|
|
46
|
+
assert.equal(Object.hasOwn(decoded.attrs,'__proto__'),true);
|
|
47
|
+
assert.equal(decoded.attrs.__proto__,'literal'); assert.equal({}.polluted,undefined);
|
|
48
|
+
});
|
|
49
|
+
test('1000 entradas binarias aleatorias terminan sin bloquear', async () => {
|
|
50
|
+
let rejected = 0;
|
|
51
|
+
for(let i=0;i<1000;i++) {
|
|
52
|
+
const raw = Buffer.concat([Buffer.from([0]),randomBytes(1+(i%256))]);
|
|
53
|
+
try { await decodeBinaryNode(raw) } catch { rejected++ }
|
|
54
|
+
}
|
|
55
|
+
assert.ok(rejected>900);
|
|
56
|
+
});
|
|
57
|
+
test('inspector tolera null, menciones inválidas y valores numéricos corruptos', () => {
|
|
58
|
+
assert.equal(inspectMessage(null).type,'unknown');
|
|
59
|
+
const info = inspectMessage({ key:{remoteJid:'51900000001@s.whatsapp.net',id:'x'}, messageTimestamp:{toNumber(){throw new Error('bad')}},message:{extendedTextMessage:{text:'😺',contextInfo:{mentionedJid:33}}} });
|
|
60
|
+
assert.equal(info.timestamp,0); assert.deepEqual(info.mentions,[]); assert.equal(info.type,'extendedTextMessage');
|
|
61
|
+
});
|
|
62
|
+
test('envolturas circulares o nulas no producen recursión ilimitada', () => {
|
|
63
|
+
const raw = { message:{} }; raw.message.ephemeralMessage = {message:raw.message};
|
|
64
|
+
assert.equal(inspectMessage(raw).type,'unknown'); assert.equal(inspectMessage({message:{imageMessage:null}}).type,'unknown');
|
|
65
|
+
});
|
|
66
|
+
test('inspector limita menciones sin exponer bytes de medios', () => {
|
|
67
|
+
const info = inspectMessage({message:{imageMessage:{mediaKey:Buffer.from('private-key'),contextInfo:{mentionedJid:Array.from({length:300},(_,i)=>`${i}@lid`)}}}});
|
|
68
|
+
assert.equal(info.mentions.length,100); assert.equal(JSON.stringify(info).includes('private-key'),false);
|
|
69
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const test = require('node:test');
|
|
3
|
+
const assert = require('node:assert/strict');
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const vm = require('node:vm');
|
|
6
|
+
const crypto = require('node:crypto');
|
|
7
|
+
function noise() {
|
|
8
|
+
const sandbox = { exports:{}, Buffer, Uint8Array, ArrayBuffer, DataView, require(name) {
|
|
9
|
+
if(name==='@hapi/boom') return { Boom:Error };
|
|
10
|
+
if(name==='../../WAProto') return { proto:{} };
|
|
11
|
+
if(name==='../Defaults') return { NOISE_MODE:'test',WA_CERT_DETAILS:{SERIAL:0} };
|
|
12
|
+
if(name==='../WABinary') return {};
|
|
13
|
+
if(name==='./crypto') return { sha256:data=>crypto.createHash('sha256').update(data).digest() };
|
|
14
|
+
return require(require('node:path').resolve(__dirname,'../lib/Utils',name));
|
|
15
|
+
} };
|
|
16
|
+
vm.runInNewContext(fs.readFileSync(require.resolve('../lib/Utils/noise-handler'),'utf8'),sandbox);
|
|
17
|
+
const logger = { child(){return this},trace(){} };
|
|
18
|
+
return sandbox.exports.makeNoiseHandler({ keyPair:{ private:Buffer.alloc(32),public:Buffer.alloc(32) },NOISE_HEADER:Buffer.alloc(0),logger });
|
|
19
|
+
}
|
|
20
|
+
test('framing acepta fragmentación normal y múltiples frames', async()=>{
|
|
21
|
+
const handler = noise(), received=[];
|
|
22
|
+
await handler.decodeFrame(Buffer.from([0,0]),frame=>received.push(frame));
|
|
23
|
+
await handler.decodeFrame(Buffer.from([3,97,98,99,0,0,1,100]),frame=>received.push(frame));
|
|
24
|
+
assert.deepEqual(received.map(x=>x.toString()),['abc','d']);
|
|
25
|
+
});
|
|
26
|
+
test('framing rechaza tamaño cero y no deja bytes acumulándose', async()=>{
|
|
27
|
+
const handler = noise(), received=[];
|
|
28
|
+
await assert.rejects(handler.decodeFrame(Buffer.from([0,0,0]),()=>{}),/NOISE_EMPTY_FRAME/);
|
|
29
|
+
await handler.decodeFrame(Buffer.from([0,0,1,97]),frame=>received.push(frame));
|
|
30
|
+
assert.equal(received[0].toString(),'a');
|
|
31
|
+
});
|
|
32
|
+
test('framing rechaza acumulación superior al límite', async()=>{
|
|
33
|
+
await assert.rejects(noise().decodeFrame(Buffer.alloc(33*1024*1024),()=>{}),/NOISE_BUFFER_LIMIT/);
|
|
34
|
+
});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import makeWASocket, {proto, say, generateWAMessageContent, useMultiFileAuthState} from 'kage-baileys';
|
|
2
|
+
const message: proto.Message.$Properties = {conversation:'Prueba'};
|
|
3
|
+
const encoded: Uint8Array = proto.Message.encode(message).finish();
|
|
4
|
+
void encoded; void makeWASocket; void useMultiFileAuthState; void generateWAMessageContent;
|
|
5
|
+
say('kage baileys',{font:'simple',gradient:['red','magenta']});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const test = require('node:test');
|
|
3
|
+
const assert = require('node:assert/strict');
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const vm = require('node:vm');
|
|
7
|
+
const crypto = require('node:crypto');
|
|
8
|
+
const { createRequire } = require('node:module');
|
|
9
|
+
const { EventEmitter } = require('node:events');
|
|
10
|
+
const api = require('../lib');
|
|
11
|
+
const logger = require('pino')({level:'silent'});
|
|
12
|
+
const tick = () => new Promise(resolve => setImmediate(resolve));
|
|
13
|
+
|
|
14
|
+
test('API pública disponible en CJS y ESM en un único paquete', async()=>{
|
|
15
|
+
const esm=await import('../lib/index.mjs');for(const key of Object.keys(api))if(key!=='__esModule')assert.ok(key in esm,key);
|
|
16
|
+
assert.equal(esm.makeWASocket,api.makeWASocket);assert.equal(fs.existsSync(path.join(__dirname,'../vendor')),false);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('scoped event leases cannot flush another operation or a later generation', () => {
|
|
20
|
+
const ev = api.makeEventBuffer(logger), received=[];
|
|
21
|
+
ev.on('contacts.upsert', list=>received.push(...list));
|
|
22
|
+
const release1 = ev.buffer(), release2 = ev.buffer();
|
|
23
|
+
ev.emit('contacts.upsert', [{id:'1@s.whatsapp.net'}]);
|
|
24
|
+
release1(); assert.equal(ev.isBuffering(),true); release1(); assert.equal(ev.isBuffering(),true);
|
|
25
|
+
release2(); assert.equal(received.length,1);
|
|
26
|
+
const old = ev.buffer(); ev.flush(true); const current=ev.buffer();
|
|
27
|
+
old(); assert.equal(ev.isBuffering(),true); current(); assert.equal(ev.isBuffering(),false);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('reentrant events emitted while flushing are not discarded', () => {
|
|
31
|
+
const ev=api.makeEventBuffer(logger), ids=[];
|
|
32
|
+
ev.on('contacts.upsert', list=>{ids.push(...list.map(x=>x.id));if(ids.length===1)ev.emit('contacts.upsert',[{id:'second'}]);});
|
|
33
|
+
ev.buffer(); ev.emit('contacts.upsert',[{id:'first'}]);ev.flush();
|
|
34
|
+
assert.deepEqual(ids,['first','second']);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
function mockChats({history=true, processor=async()=>{}}={}) {
|
|
38
|
+
const timers=[];
|
|
39
|
+
const ev=api.makeEventBuffer(logger), ws=new EventEmitter();
|
|
40
|
+
const sock={ev,ws,authState:{creds:{me:{id:'51900000000@s.whatsapp.net'},accountSettings:{}},keys:{get:async()=>({}),set:async()=>{}}},query:async()=>({tag:'iq',attrs:{},content:[]}),sendNode:async()=>{},generateMessageTag:()=> 'tag',signalRepository:{},onUnexpectedError:()=>{}};
|
|
41
|
+
const filename=require.resolve('../lib/Socket/chats');const nativeRequire=createRequire(filename);
|
|
42
|
+
const sandbox={exports:{},Buffer,console,setTimeout(fn,ms){const t={fn,ms,unref(){}};timers.push(t);return t},clearTimeout(){},require(name){if(name==='./usync')return {makeUSyncSocket:()=>sock};if(name==='../Utils/process-message')return {__esModule:true,default:processor};return nativeRequire(name)}};
|
|
43
|
+
vm.runInNewContext(fs.readFileSync(filename,'utf8'),sandbox,{filename});
|
|
44
|
+
const config={...api.DEFAULT_CONNECTION_CONFIG,logger,fireInitQueries:false,markOnlineOnConnect:false,shouldSyncHistoryMessage:()=>history,initialSyncTimeoutMs:1000};
|
|
45
|
+
const chats=sandbox.exports.makeChatsSocket(config);
|
|
46
|
+
return {sock,chats,ev,timers};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
test('missing app-state key no longer creates an unmatched buffering level', () => {
|
|
50
|
+
const {ev,timers}=mockChats();let received=0;ev.on('chats.upsert',()=>received++);
|
|
51
|
+
ev.emit('connection.update',{receivedPendingNotifications:true});
|
|
52
|
+
ev.emit('chats.upsert',[{id:'123@g.us'}]);assert.equal(received,0);
|
|
53
|
+
timers.find(t=>t.ms===1000).fn();assert.equal(received,1);assert.equal(ev.isBuffering(),false);
|
|
54
|
+
ev.emit('connection.update',{connection:'close'});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('history disabled releases the initial lease even if keys are missing', () => {
|
|
58
|
+
const {ev}=mockChats({history:false});ev.emit('connection.update',{receivedPendingNotifications:true});
|
|
59
|
+
assert.equal(ev.isBuffering(),false);ev.emit('connection.update',{connection:'close'});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('a stalled history download never holds live upserts or their event leases', async () => {
|
|
63
|
+
let finish;const stalled=new Promise(resolve=>finish=resolve);
|
|
64
|
+
const {chats,ev,timers}=mockChats({processor:msg=>msg.key.id==='history'?stalled:Promise.resolve()});
|
|
65
|
+
ev.emit('connection.update',{receivedPendingNotifications:true});
|
|
66
|
+
const history={key:{remoteJid:'51900000000@s.whatsapp.net',id:'history',fromMe:false},message:{protocolMessage:{historySyncNotification:{syncType:api.proto.HistorySync.HistorySyncType.RECENT}}}};
|
|
67
|
+
const result=await Promise.race([chats.upsertMessage(history,'append').then(()=>true),new Promise(resolve=>setTimeout(()=>resolve(false),500))]);
|
|
68
|
+
assert.equal(result,true);
|
|
69
|
+
const messages=[];ev.on('messages.upsert',event=>messages.push(...event.messages));
|
|
70
|
+
timers.find(t=>t.ms===1000).fn();
|
|
71
|
+
await chats.upsertMessage({key:{remoteJid:'51900000001@s.whatsapp.net',id:'live'},message:{conversation:'ping'}},'notify');
|
|
72
|
+
assert.ok(messages.some(m=>m.key.id==='live'));assert.equal(ev.isBuffering(),false);
|
|
73
|
+
finish();await tick();ev.emit('connection.update',{connection:'close'});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('complete socket exposes supplemental methods and closes safely during handshake', async () => {
|
|
77
|
+
const socket=api.makeWASocket({auth:{creds:api.initAuthCreds(),keys:{get:async()=>({}),set:async()=>{}}},logger,waWebSocketUrl:'wss://127.0.0.1:1',connectTimeoutMs:1000,fireInitQueries:false,kageTools:{banner:false}});
|
|
78
|
+
for (const group of require('../maintenance/extension-inventory.json')) for (const key of group.methods) assert.ok(key in socket,key);
|
|
79
|
+
assert.equal(typeof socket.communityCreate,'function');
|
|
80
|
+
socket.end(new Error('local test complete'));
|
|
81
|
+
await new Promise(resolve=>setTimeout(resolve,30));
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('supplemental quick replies use the supplied converter and existing appPatch', async () => {
|
|
85
|
+
const calls=[];
|
|
86
|
+
const base={authState:{creds:{}},ev:api.makeEventBuffer(logger),ws:new EventEmitter(),appPatch:async patch=>calls.push(patch),query:async()=>{},chatModify:()=>{throw new Error('older converter must not be called')}};
|
|
87
|
+
const extra=require('../lib/Extensions/chats').attach(base,{...api.DEFAULT_CONNECTION_CONFIG,logger});
|
|
88
|
+
await extra.addOrEditQuickReply({shortcut:'hola',message:'Hola',timestamp:'123'});
|
|
89
|
+
await extra.removeQuickReply('123');
|
|
90
|
+
assert.equal(calls.length,2);assert.equal(calls[0].syncAction.quickReplyAction.shortcut,'hola');assert.equal(calls[1].syncAction.quickReplyAction.deleted,true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('supplemental business and community methods use the same query transport', async () => {
|
|
94
|
+
const calls=[];const base={authState:{creds:{}},query:async node=>{calls.push(node);return {tag:'iq',attrs:{},content:[]}},waUploadToServer:async()=>{},ev:new EventEmitter()};
|
|
95
|
+
const business=require('../lib/Extensions/business').attach(base,{});
|
|
96
|
+
await business.updateBussinesProfile({description:'Prueba'});await business.removeCoverPhoto();
|
|
97
|
+
const community=require('../lib/Extensions/community').attach(base,{});await community.communityQuery('123@g.us','get',[]);
|
|
98
|
+
assert.equal(calls.length,3);assert.equal(calls[0].attrs.xmlns,'w:biz');assert.equal(calls[2].attrs.xmlns,'w:g2');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test('banner includes requested creator and partner without ANSI in a non-TTY stream', () => {
|
|
102
|
+
const output=[];api.showKageBanner({stream:{columns:60,isTTY:false,write:text=>output.push(text)}});
|
|
103
|
+
const text=output.join('');assert.match(text,/Creado por xrljosedv/);assert.match(text,/Socio David Zuniga/);assert.ok(!text.includes('\x1b['));
|
|
104
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"noEmit": true,
|
|
8
|
+
"forceConsistentCasingInFileNames": true,
|
|
9
|
+
"skipLibCheck": false,
|
|
10
|
+
"types": [
|
|
11
|
+
"node"
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
"include": [
|
|
15
|
+
"test/types.test.ts"
|
|
16
|
+
],
|
|
17
|
+
"exclude": [
|
|
18
|
+
"node_modules"
|
|
19
|
+
]
|
|
20
|
+
}
|
package/WAProto/p.html
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
kage
|