@kangwifi-pro/waliwa 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.
- package/LICENSE +21 -0
- package/README.md +600 -0
- package/dist/advanced/features.d.ts +211 -0
- package/dist/advanced/features.d.ts.map +1 -0
- package/dist/advanced/features.js +671 -0
- package/dist/advanced/features.js.map +1 -0
- package/dist/auth/auth-state.d.ts +89 -0
- package/dist/auth/auth-state.d.ts.map +1 -0
- package/dist/auth/auth-state.js +330 -0
- package/dist/auth/auth-state.js.map +1 -0
- package/dist/auth/pairing-code.d.ts +78 -0
- package/dist/auth/pairing-code.d.ts.map +1 -0
- package/dist/auth/pairing-code.js +191 -0
- package/dist/auth/pairing-code.js.map +1 -0
- package/dist/auth/qr-code.d.ts +60 -0
- package/dist/auth/qr-code.d.ts.map +1 -0
- package/dist/auth/qr-code.js +151 -0
- package/dist/auth/qr-code.js.map +1 -0
- package/dist/calls/handler.d.ts +61 -0
- package/dist/calls/handler.d.ts.map +1 -0
- package/dist/calls/handler.js +117 -0
- package/dist/calls/handler.js.map +1 -0
- package/dist/core/binary.d.ts +74 -0
- package/dist/core/binary.d.ts.map +1 -0
- package/dist/core/binary.js +448 -0
- package/dist/core/binary.js.map +1 -0
- package/dist/core/crypto.d.ts +60 -0
- package/dist/core/crypto.d.ts.map +1 -0
- package/dist/core/crypto.js +170 -0
- package/dist/core/crypto.js.map +1 -0
- package/dist/core/noise.d.ts +106 -0
- package/dist/core/noise.d.ts.map +1 -0
- package/dist/core/noise.js +307 -0
- package/dist/core/noise.js.map +1 -0
- package/dist/events/emitter.d.ts +44 -0
- package/dist/events/emitter.d.ts.map +1 -0
- package/dist/events/emitter.js +79 -0
- package/dist/events/emitter.js.map +1 -0
- package/dist/features/index.d.ts +342 -0
- package/dist/features/index.d.ts.map +1 -0
- package/dist/features/index.js +755 -0
- package/dist/features/index.js.map +1 -0
- package/dist/fixes/index.d.ts +333 -0
- package/dist/fixes/index.d.ts.map +1 -0
- package/dist/fixes/index.js +762 -0
- package/dist/fixes/index.js.map +1 -0
- package/dist/groups/management.d.ts +86 -0
- package/dist/groups/management.d.ts.map +1 -0
- package/dist/groups/management.js +443 -0
- package/dist/groups/management.js.map +1 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +236 -0
- package/dist/index.js.map +1 -0
- package/dist/messages/media.d.ts +93 -0
- package/dist/messages/media.d.ts.map +1 -0
- package/dist/messages/media.js +252 -0
- package/dist/messages/media.js.map +1 -0
- package/dist/messages/message-encoder.d.ts +70 -0
- package/dist/messages/message-encoder.d.ts.map +1 -0
- package/dist/messages/message-encoder.js +453 -0
- package/dist/messages/message-encoder.js.map +1 -0
- package/dist/messages/send.d.ts +101 -0
- package/dist/messages/send.d.ts.map +1 -0
- package/dist/messages/send.js +409 -0
- package/dist/messages/send.js.map +1 -0
- package/dist/recovery/index.d.ts +125 -0
- package/dist/recovery/index.d.ts.map +1 -0
- package/dist/recovery/index.js +584 -0
- package/dist/recovery/index.js.map +1 -0
- package/dist/skdm/index.d.ts +220 -0
- package/dist/skdm/index.d.ts.map +1 -0
- package/dist/skdm/index.js +600 -0
- package/dist/skdm/index.js.map +1 -0
- package/dist/socket/hybrid.d.ts +118 -0
- package/dist/socket/hybrid.d.ts.map +1 -0
- package/dist/socket/hybrid.js +352 -0
- package/dist/socket/hybrid.js.map +1 -0
- package/dist/socket/wa-socket.d.ts +300 -0
- package/dist/socket/wa-socket.d.ts.map +1 -0
- package/dist/socket/wa-socket.js +1094 -0
- package/dist/socket/wa-socket.js.map +1 -0
- package/dist/socket/ws-socket.d.ts +96 -0
- package/dist/socket/ws-socket.d.ts.map +1 -0
- package/dist/socket/ws-socket.js +302 -0
- package/dist/socket/ws-socket.js.map +1 -0
- package/dist/types/index.d.ts +444 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/jid.d.ts +46 -0
- package/dist/utils/jid.d.ts.map +1 -0
- package/dist/utils/jid.js +152 -0
- package/dist/utils/jid.js.map +1 -0
- package/dist/utils/logger.d.ts +45 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +79 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/retry.d.ts +43 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +175 -0
- package/dist/utils/retry.js.map +1 -0
- package/docs/API.md +745 -0
- package/docs/ARCHITECTURE.md +307 -0
- package/docs/BAILEYS_FIXES.md +360 -0
- package/docs/FEATURES.md +532 -0
- package/docs/PROTOCOL.md +489 -0
- package/docs/RECOVERY.md +409 -0
- package/docs/SKDM.md +233 -0
- package/examples/ai-bot/index.ts +479 -0
- package/examples/ai-bot/package.json +17 -0
- package/examples/echo-bot/index.ts +171 -0
- package/examples/echo-bot/package.json +18 -0
- package/examples/group-bot/index.ts +557 -0
- package/examples/group-bot/package.json +17 -0
- package/examples/rest-gateway/index.ts +499 -0
- package/examples/rest-gateway/package.json +19 -0
- package/package.json +75 -0
- package/src/advanced/features.ts +817 -0
- package/src/auth/auth-state.ts +342 -0
- package/src/auth/pairing-code.ts +246 -0
- package/src/auth/qr-code.ts +191 -0
- package/src/calls/handler.ts +153 -0
- package/src/core/binary.ts +464 -0
- package/src/core/crypto.ts +189 -0
- package/src/core/noise.ts +406 -0
- package/src/events/emitter.ts +88 -0
- package/src/features/index.ts +921 -0
- package/src/fixes/index.ts +882 -0
- package/src/groups/management.ts +497 -0
- package/src/index.ts +274 -0
- package/src/messages/media.ts +372 -0
- package/src/messages/message-encoder.ts +520 -0
- package/src/messages/send.ts +521 -0
- package/src/recovery/index.ts +704 -0
- package/src/skdm/index.ts +693 -0
- package/src/socket/hybrid.ts +414 -0
- package/src/socket/wa-socket.ts +1347 -0
- package/src/socket/ws-socket.ts +355 -0
- package/src/types/index.ts +457 -0
- package/src/utils/jid.ts +156 -0
- package/src/utils/logger.ts +84 -0
- package/src/utils/retry.ts +205 -0
- package/tsconfig.json +30 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Waliwa - Pairing code implementation (Mobile API style)
|
|
4
|
+
*
|
|
5
|
+
* Alternatif login tanpa scan QR - user input 8-character pairing code
|
|
6
|
+
* di WhatsApp mobile app. Mengikuti companion device registration flow.
|
|
7
|
+
*
|
|
8
|
+
* Format pairing code: "AB12-CD34" (8 alphanumeric, dash di tengah)
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.PairingCodeExpiry = void 0;
|
|
12
|
+
exports.generatePairingCode = generatePairingCode;
|
|
13
|
+
exports.isValidPairingCodeFormat = isValidPairingCodeFormat;
|
|
14
|
+
exports.normalizePairingCode = normalizePairingCode;
|
|
15
|
+
exports.buildPairingRequestNode = buildPairingRequestNode;
|
|
16
|
+
exports.parsePairingResult = parsePairingResult;
|
|
17
|
+
exports.normalizePhoneNumber = normalizePhoneNumber;
|
|
18
|
+
exports.isValidPhoneNumber = isValidPhoneNumber;
|
|
19
|
+
const crypto_js_1 = require("../core/crypto.js");
|
|
20
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
21
|
+
// ============== Pairing Code Generator ==============
|
|
22
|
+
/**
|
|
23
|
+
* Generate pairing code dari phone number + secret key
|
|
24
|
+
*
|
|
25
|
+
* Pairing code adalah 8 character alphanumeric yang derived dari:
|
|
26
|
+
* 1. Phone number user (target)
|
|
27
|
+
* 2. Server-side secret (sebagai HMAC key)
|
|
28
|
+
* 3. Random challenge untuk anti-replay
|
|
29
|
+
*
|
|
30
|
+
* User input code di WA mobile app, server verify dan pair device.
|
|
31
|
+
*/
|
|
32
|
+
function generatePairingCode(phoneNumber, creds, logger) {
|
|
33
|
+
const log = logger ?? new logger_js_1.SilentLogger();
|
|
34
|
+
// Sanitize phone number
|
|
35
|
+
const cleanPhone = phoneNumber.replace(/[^0-9]/g, '');
|
|
36
|
+
if (cleanPhone.length < 8) {
|
|
37
|
+
throw new Error(`Invalid phone number: ${phoneNumber}`);
|
|
38
|
+
}
|
|
39
|
+
// Get pairing secret dari creds (atau generate)
|
|
40
|
+
const secretKey = creds.pairingKey?.private ?? (0, crypto_js_1.getRandomBytes)(32);
|
|
41
|
+
const publicKey = creds.pairingKey?.public ?? new Uint8Array(32);
|
|
42
|
+
// Compute pairing code via HKDF derive
|
|
43
|
+
// Code derived from phone number + public key fingerprint
|
|
44
|
+
const phoneBytes = new TextEncoder().encode(cleanPhone);
|
|
45
|
+
const derived = (0, crypto_js_1.hkdfDerive)(secretKey, phoneBytes, new TextEncoder().encode('pairing-code-v1'), 4 // 4 bytes = 8 hex chars
|
|
46
|
+
);
|
|
47
|
+
// Convert 4 bytes ke 8-character alphanumeric
|
|
48
|
+
// Mapping: each nibble (4 bits) -> 1 character dari "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
|
|
49
|
+
// (avoid ambiguous chars: 0/O, 1/I)
|
|
50
|
+
const charSet = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
|
|
51
|
+
let code = '';
|
|
52
|
+
for (let i = 0; i < 4; i++) {
|
|
53
|
+
const byte = derived[i];
|
|
54
|
+
code += charSet[byte >> 4]; // high nibble
|
|
55
|
+
code += charSet[byte & 0xf]; // low nibble
|
|
56
|
+
}
|
|
57
|
+
// Format: "XXXX-XXXX"
|
|
58
|
+
const formattedCode = `${code.substring(0, 4)}-${code.substring(4, 8)}`;
|
|
59
|
+
log.info(`Generated pairing code untuk ${cleanPhone}: ${formattedCode}`);
|
|
60
|
+
log.debug(`Pairing public key: ${Buffer.from(publicKey).toString('hex').substring(0, 32)}...`);
|
|
61
|
+
return {
|
|
62
|
+
code: formattedCode,
|
|
63
|
+
phoneNumber: cleanPhone,
|
|
64
|
+
expiresAt: Date.now() + 65 * 1000 // 65 detik expiry
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// ============== Pairing Code Validation ==============
|
|
68
|
+
/**
|
|
69
|
+
* Validate pairing code format
|
|
70
|
+
*/
|
|
71
|
+
function isValidPairingCodeFormat(code) {
|
|
72
|
+
return /^[A-Z0-9]{4}-[A-Z0-9]{4}$/.test(code.toUpperCase());
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Normalize user input pairing code
|
|
76
|
+
* "ab12cd34" -> "AB12-CD34"
|
|
77
|
+
* "AB12 CD34" -> "AB12-CD34"
|
|
78
|
+
*/
|
|
79
|
+
function normalizePairingCode(input) {
|
|
80
|
+
const cleaned = input.replace(/[^A-Za-z0-9]/g, '').toUpperCase();
|
|
81
|
+
if (cleaned.length !== 8) {
|
|
82
|
+
throw new Error(`Pairing code must be 8 alphanumeric chars, got: ${input}`);
|
|
83
|
+
}
|
|
84
|
+
return `${cleaned.substring(0, 4)}-${cleaned.substring(4, 8)}`;
|
|
85
|
+
}
|
|
86
|
+
// ============== Pairing Request Builder ==============
|
|
87
|
+
/**
|
|
88
|
+
* Build pairing request binary node untuk kirim ke server
|
|
89
|
+
*
|
|
90
|
+
* <iq type="get" to="s.whatsapp.net">
|
|
91
|
+
* <pair code="AB12-CD34" phone="6281234567890" />
|
|
92
|
+
* </iq>
|
|
93
|
+
*/
|
|
94
|
+
function buildPairingRequestNode(code, phoneNumber, publicKey) {
|
|
95
|
+
const cleanPhone = phoneNumber.replace(/[^0-9]/g, '');
|
|
96
|
+
return {
|
|
97
|
+
tag: 'iq',
|
|
98
|
+
attrs: {
|
|
99
|
+
type: 'set',
|
|
100
|
+
to: 's.whatsapp.net',
|
|
101
|
+
id: `pair-${Date.now()}`
|
|
102
|
+
},
|
|
103
|
+
content: [
|
|
104
|
+
{
|
|
105
|
+
tag: 'pair',
|
|
106
|
+
attrs: {
|
|
107
|
+
code: code.replace('-', ''),
|
|
108
|
+
phone: cleanPhone,
|
|
109
|
+
pubkey: Buffer.from(publicKey).toString('base64')
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Parse pairing result dari binary node response
|
|
117
|
+
*/
|
|
118
|
+
function parsePairingResult(node) {
|
|
119
|
+
if (!node || node.tag !== 'iq') {
|
|
120
|
+
return { success: false, error: 'Invalid response node' };
|
|
121
|
+
}
|
|
122
|
+
const type = node.attrs?.type;
|
|
123
|
+
if (type === 'error') {
|
|
124
|
+
const errorNode = Array.isArray(node.content) && node.content.find((c) => c.tag === 'error');
|
|
125
|
+
return {
|
|
126
|
+
success: false,
|
|
127
|
+
error: errorNode?.attrs?.text || 'Pairing failed'
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const pairNode = Array.isArray(node.content) && node.content.find((c) => c.tag === 'pair');
|
|
131
|
+
if (!pairNode) {
|
|
132
|
+
return { success: false, error: 'Pair result not found' };
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
success: true,
|
|
136
|
+
jid: pairNode.attrs?.jid,
|
|
137
|
+
platform: pairNode.attrs?.platform,
|
|
138
|
+
businessName: pairNode.attrs?.business_name,
|
|
139
|
+
identityKey: pairNode.attrs?.identity
|
|
140
|
+
? new Uint8Array(Buffer.from(pairNode.attrs.identity, 'base64'))
|
|
141
|
+
: undefined
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
// ============== Phone Number Normalization ==============
|
|
145
|
+
/**
|
|
146
|
+
* Normalize phone number - hapus karakter non-digit
|
|
147
|
+
*/
|
|
148
|
+
function normalizePhoneNumber(phone) {
|
|
149
|
+
return phone.replace(/[^0-9]/g, '');
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Check apakah phone number valid (Indonesia: 10-15 digit)
|
|
153
|
+
*/
|
|
154
|
+
function isValidPhoneNumber(phone) {
|
|
155
|
+
const clean = normalizePhoneNumber(phone);
|
|
156
|
+
return clean.length >= 8 && clean.length <= 15;
|
|
157
|
+
}
|
|
158
|
+
// ============== Code Expiry Tracker ==============
|
|
159
|
+
class PairingCodeExpiry {
|
|
160
|
+
expiresAt;
|
|
161
|
+
onExpiry;
|
|
162
|
+
timer = null;
|
|
163
|
+
constructor(expiresAt, onExpiry) {
|
|
164
|
+
this.expiresAt = expiresAt;
|
|
165
|
+
this.onExpiry = onExpiry;
|
|
166
|
+
}
|
|
167
|
+
start() {
|
|
168
|
+
const msUntilExpiry = this.expiresAt - Date.now();
|
|
169
|
+
if (msUntilExpiry <= 0) {
|
|
170
|
+
this.onExpiry();
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
this.timer = setTimeout(() => {
|
|
174
|
+
this.onExpiry();
|
|
175
|
+
}, msUntilExpiry);
|
|
176
|
+
}
|
|
177
|
+
cancel() {
|
|
178
|
+
if (this.timer) {
|
|
179
|
+
clearTimeout(this.timer);
|
|
180
|
+
this.timer = null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
getRemainingMs() {
|
|
184
|
+
return Math.max(0, this.expiresAt - Date.now());
|
|
185
|
+
}
|
|
186
|
+
isExpired() {
|
|
187
|
+
return Date.now() >= this.expiresAt;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
exports.PairingCodeExpiry = PairingCodeExpiry;
|
|
191
|
+
//# sourceMappingURL=pairing-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairing-code.js","sourceRoot":"","sources":["../../src/auth/pairing-code.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Waliwa - QR code generation untuk WhatsApp Web Multi-Device pairing
|
|
3
|
+
*
|
|
4
|
+
* QR code berisi: ref + publicKey + pairingStaticKey (encoded sebagai comma-separated)
|
|
5
|
+
* User scan dengan WhatsApp mobile app untuk establish pairing
|
|
6
|
+
*/
|
|
7
|
+
import type { QRGenerationData, AuthenticationCreds, WALogger } from '../types/index.js';
|
|
8
|
+
/**
|
|
9
|
+
* Generate new QR reference (server-side correlation ID)
|
|
10
|
+
* Format: 8-character alphanumeric
|
|
11
|
+
*/
|
|
12
|
+
export declare function generateQRRef(): string;
|
|
13
|
+
/**
|
|
14
|
+
* Build QR code string untuk WA pairing
|
|
15
|
+
*
|
|
16
|
+
* Format:
|
|
17
|
+
* ref,publicKey(base64),pairingStaticKey(base64),clientDid,serverDid
|
|
18
|
+
*
|
|
19
|
+
* WhatsApp mobile app akan scan QR, kirim ke server, server pakai publicKey
|
|
20
|
+
* untuk encrypt pairing payload yang berisi identity keys.
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildQRString(ref: string, publicKey: Uint8Array, pairingStaticKey: Uint8Array,
|
|
23
|
+
/** Client device ID (noise static key fingerprint) */
|
|
24
|
+
clientDid?: Uint8Array): string;
|
|
25
|
+
export interface QRGenerationOptions {
|
|
26
|
+
creds: AuthenticationCreds;
|
|
27
|
+
logger?: WALogger;
|
|
28
|
+
/** Print QR ke terminal? */
|
|
29
|
+
printInTerminal?: boolean;
|
|
30
|
+
/** Small QR mode (lebih kompak di terminal) */
|
|
31
|
+
small?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Generate QR code data dari auth creds
|
|
35
|
+
*/
|
|
36
|
+
export declare function generateQRCodeData(opts: QRGenerationOptions): QRGenerationData;
|
|
37
|
+
/**
|
|
38
|
+
* QR refresh manager - auto-refresh QR setiap ~20 detik (sesuai WA server timeout)
|
|
39
|
+
* Untuk menghindari stale QR yang sudah expire
|
|
40
|
+
*/
|
|
41
|
+
export declare class QRRefreshManager {
|
|
42
|
+
private timer;
|
|
43
|
+
private onRefresh;
|
|
44
|
+
private onTimeout;
|
|
45
|
+
private refreshIntervalMs;
|
|
46
|
+
private maxRefreshCount;
|
|
47
|
+
private refreshCount;
|
|
48
|
+
private isRunning;
|
|
49
|
+
constructor(onRefresh: (qr: QRGenerationData) => void, onTimeout: () => void, refreshIntervalMs?: number, maxRefreshCount?: number);
|
|
50
|
+
start(initialQR: QRGenerationData): void;
|
|
51
|
+
private generateNewQRPlaceholder;
|
|
52
|
+
stop(): void;
|
|
53
|
+
}
|
|
54
|
+
export declare function parseQRString(qrString: string): {
|
|
55
|
+
ref: string;
|
|
56
|
+
publicKey: Uint8Array;
|
|
57
|
+
pairingStaticKey: Uint8Array;
|
|
58
|
+
clientDid?: Uint8Array;
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=qr-code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qr-code.d.ts","sourceRoot":"","sources":["../../src/auth/qr-code.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EACV,gBAAgB,EAChB,mBAAmB,EACnB,QAAQ,EACT,MAAM,mBAAmB,CAAC;AAQ3B;;;GAGG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAStC;AAID;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,UAAU,EACrB,gBAAgB,EAAE,UAAU;AAC5B,sDAAsD;AACtD,SAAS,CAAC,EAAE,UAAU,GACrB,MAAM,CAUR;AAID,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,mBAAmB,CAAC;IAC3B,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,4BAA4B;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,+CAA+C;IAC/C,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,mBAAmB,GAAG,gBAAgB,CA2B9E;AAID;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,SAAS,CAAiC;IAClD,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,SAAS,CAAkB;gBAGjC,SAAS,EAAE,CAAC,EAAE,EAAE,gBAAgB,KAAK,IAAI,EACzC,SAAS,EAAE,MAAM,IAAI,EACrB,iBAAiB,GAAE,MAAc,EACjC,eAAe,GAAE,MAAU;IAQ7B,KAAK,CAAC,SAAS,EAAE,gBAAgB,GAAG,IAAI;IAkBxC,OAAO,CAAC,wBAAwB;IAWhC,IAAI,IAAI,IAAI;CAOb;AAID,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,UAAU,CAAC;IACtB,gBAAgB,EAAE,UAAU,CAAC;IAC7B,SAAS,CAAC,EAAE,UAAU,CAAC;CACxB,CAWA"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Waliwa - QR code generation untuk WhatsApp Web Multi-Device pairing
|
|
4
|
+
*
|
|
5
|
+
* QR code berisi: ref + publicKey + pairingStaticKey (encoded sebagai comma-separated)
|
|
6
|
+
* User scan dengan WhatsApp mobile app untuk establish pairing
|
|
7
|
+
*/
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.QRRefreshManager = void 0;
|
|
13
|
+
exports.generateQRRef = generateQRRef;
|
|
14
|
+
exports.buildQRString = buildQRString;
|
|
15
|
+
exports.generateQRCodeData = generateQRCodeData;
|
|
16
|
+
exports.parseQRString = parseQRString;
|
|
17
|
+
const qrcode_terminal_1 = __importDefault(require("qrcode-terminal"));
|
|
18
|
+
const crypto_js_1 = require("../core/crypto.js");
|
|
19
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
20
|
+
// ============== QR Reference Generator ==============
|
|
21
|
+
let qrRefCounter = 0;
|
|
22
|
+
/**
|
|
23
|
+
* Generate new QR reference (server-side correlation ID)
|
|
24
|
+
* Format: 8-character alphanumeric
|
|
25
|
+
*/
|
|
26
|
+
function generateQRRef() {
|
|
27
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
28
|
+
let ref = '';
|
|
29
|
+
const rand = (0, crypto_js_1.getRandomBytes)(8);
|
|
30
|
+
for (let i = 0; i < 8; i++) {
|
|
31
|
+
ref += chars[rand[i] % chars.length];
|
|
32
|
+
}
|
|
33
|
+
qrRefCounter++;
|
|
34
|
+
return ref + qrRefCounter.toString(36);
|
|
35
|
+
}
|
|
36
|
+
// ============== QR Encoder ==============
|
|
37
|
+
/**
|
|
38
|
+
* Build QR code string untuk WA pairing
|
|
39
|
+
*
|
|
40
|
+
* Format:
|
|
41
|
+
* ref,publicKey(base64),pairingStaticKey(base64),clientDid,serverDid
|
|
42
|
+
*
|
|
43
|
+
* WhatsApp mobile app akan scan QR, kirim ke server, server pakai publicKey
|
|
44
|
+
* untuk encrypt pairing payload yang berisi identity keys.
|
|
45
|
+
*/
|
|
46
|
+
function buildQRString(ref, publicKey, pairingStaticKey,
|
|
47
|
+
/** Client device ID (noise static key fingerprint) */
|
|
48
|
+
clientDid) {
|
|
49
|
+
const pubKeyB64 = Buffer.from(publicKey).toString('base64');
|
|
50
|
+
const pairingB64 = Buffer.from(pairingStaticKey).toString('base64');
|
|
51
|
+
const didB64 = clientDid ? Buffer.from(clientDid).toString('base64') : '';
|
|
52
|
+
// Format: ref,publicKey,pairingStaticKey,clientDid
|
|
53
|
+
if (didB64) {
|
|
54
|
+
return `${ref},${pubKeyB64},${pairingB64},${didB64}`;
|
|
55
|
+
}
|
|
56
|
+
return `${ref},${pubKeyB64},${pairingB64}`;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Generate QR code data dari auth creds
|
|
60
|
+
*/
|
|
61
|
+
function generateQRCodeData(opts) {
|
|
62
|
+
const logger = opts.logger ?? new logger_js_1.SilentLogger();
|
|
63
|
+
if (!opts.creds.noiseKey) {
|
|
64
|
+
throw new Error('noiseKey tidak tersedia di creds - generate creds baru');
|
|
65
|
+
}
|
|
66
|
+
const ref = generateQRRef();
|
|
67
|
+
const publicKey = opts.creds.noiseKey.public;
|
|
68
|
+
const pairingStaticKey = opts.creds.pairingKey?.public ?? new Uint8Array(32);
|
|
69
|
+
const qrString = buildQRString(ref, publicKey, pairingStaticKey);
|
|
70
|
+
if (opts.printInTerminal) {
|
|
71
|
+
logger.info('Scan QR code ini dengan WhatsApp mobile app:\n');
|
|
72
|
+
qrcode_terminal_1.default.generate(qrString, { small: opts.small ?? true }, (qr) => {
|
|
73
|
+
console.log(qr);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
ref,
|
|
78
|
+
publicKey,
|
|
79
|
+
pairingStaticKey,
|
|
80
|
+
qrString,
|
|
81
|
+
timeout: 60 // 60 seconds default
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// ============== QR Refresh Manager ==============
|
|
85
|
+
/**
|
|
86
|
+
* QR refresh manager - auto-refresh QR setiap ~20 detik (sesuai WA server timeout)
|
|
87
|
+
* Untuk menghindari stale QR yang sudah expire
|
|
88
|
+
*/
|
|
89
|
+
class QRRefreshManager {
|
|
90
|
+
timer = null;
|
|
91
|
+
onRefresh;
|
|
92
|
+
onTimeout;
|
|
93
|
+
refreshIntervalMs;
|
|
94
|
+
maxRefreshCount;
|
|
95
|
+
refreshCount = 0;
|
|
96
|
+
isRunning = false;
|
|
97
|
+
constructor(onRefresh, onTimeout, refreshIntervalMs = 20000, maxRefreshCount = 5) {
|
|
98
|
+
this.onRefresh = onRefresh;
|
|
99
|
+
this.onTimeout = onTimeout;
|
|
100
|
+
this.refreshIntervalMs = refreshIntervalMs;
|
|
101
|
+
this.maxRefreshCount = maxRefreshCount;
|
|
102
|
+
}
|
|
103
|
+
start(initialQR) {
|
|
104
|
+
if (this.isRunning)
|
|
105
|
+
return;
|
|
106
|
+
this.isRunning = true;
|
|
107
|
+
this.refreshCount = 0;
|
|
108
|
+
this.onRefresh(initialQR);
|
|
109
|
+
this.timer = setInterval(() => {
|
|
110
|
+
this.refreshCount++;
|
|
111
|
+
if (this.refreshCount >= this.maxRefreshCount) {
|
|
112
|
+
this.stop();
|
|
113
|
+
this.onTimeout();
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
// Trigger refresh - caller akan generate new QR dan pass via update
|
|
117
|
+
this.onRefresh(this.generateNewQRPlaceholder());
|
|
118
|
+
}, this.refreshIntervalMs);
|
|
119
|
+
}
|
|
120
|
+
generateNewQRPlaceholder() {
|
|
121
|
+
// Placeholder - actual generation handled by caller
|
|
122
|
+
return {
|
|
123
|
+
ref: generateQRRef(),
|
|
124
|
+
publicKey: new Uint8Array(32),
|
|
125
|
+
pairingStaticKey: new Uint8Array(32),
|
|
126
|
+
qrString: '',
|
|
127
|
+
timeout: 20
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
stop() {
|
|
131
|
+
if (this.timer) {
|
|
132
|
+
clearInterval(this.timer);
|
|
133
|
+
this.timer = null;
|
|
134
|
+
}
|
|
135
|
+
this.isRunning = false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
exports.QRRefreshManager = QRRefreshManager;
|
|
139
|
+
// ============== QR Decoder (untuk reverse direction / debug) ==============
|
|
140
|
+
function parseQRString(qrString) {
|
|
141
|
+
const parts = qrString.split(',');
|
|
142
|
+
if (parts.length < 3) {
|
|
143
|
+
throw new Error('Invalid QR string format');
|
|
144
|
+
}
|
|
145
|
+
const [ref, pubKeyB64, pairingB64, didB64] = parts;
|
|
146
|
+
const publicKey = new Uint8Array(Buffer.from(pubKeyB64, 'base64'));
|
|
147
|
+
const pairingStaticKey = new Uint8Array(Buffer.from(pairingB64, 'base64'));
|
|
148
|
+
const clientDid = didB64 ? new Uint8Array(Buffer.from(didB64, 'base64')) : undefined;
|
|
149
|
+
return { ref, publicKey, pairingStaticKey, clientDid };
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=qr-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qr-code.js","sourceRoot":"","sources":["../../src/auth/qr-code.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Waliwa - Call handling
|
|
3
|
+
* Process incoming WhatsApp call events (offer, reject, terminate)
|
|
4
|
+
*/
|
|
5
|
+
import type { WACallEvent, JIDLike, WALogger } from '../types/index.js';
|
|
6
|
+
import { BinaryNode } from '../core/binary.js';
|
|
7
|
+
export interface CallHandlerOptions {
|
|
8
|
+
send: (data: Uint8Array) => void;
|
|
9
|
+
logger?: WALogger;
|
|
10
|
+
onCall?: (event: WACallEvent) => void;
|
|
11
|
+
/** Auto reject incoming calls */
|
|
12
|
+
autoReject?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Handle incoming WhatsApp call events
|
|
16
|
+
* Calls masuk sebagai binary node dengan tag "call"
|
|
17
|
+
*/
|
|
18
|
+
export declare class CallHandler {
|
|
19
|
+
private send;
|
|
20
|
+
private logger;
|
|
21
|
+
private onCall?;
|
|
22
|
+
private autoReject;
|
|
23
|
+
constructor(opts: CallHandlerOptions);
|
|
24
|
+
/**
|
|
25
|
+
* Handle call node
|
|
26
|
+
*/
|
|
27
|
+
handleCallNode(node: BinaryNode): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Reject incoming call
|
|
30
|
+
*/
|
|
31
|
+
rejectCall(callId: string, from: JIDLike): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Accept incoming call (voice only, no WebRTC support)
|
|
34
|
+
*/
|
|
35
|
+
acceptCall(callId: string, from: JIDLike): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Set call handler
|
|
38
|
+
*/
|
|
39
|
+
setHandler(handler: (event: WACallEvent) => void): void;
|
|
40
|
+
/**
|
|
41
|
+
* Enable/disable auto reject
|
|
42
|
+
*/
|
|
43
|
+
setAutoReject(enabled: boolean): void;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Voice message transcription hook
|
|
47
|
+
* Note: WhatsApp punya transcription API tapi memerlukan server-side processing
|
|
48
|
+
*/
|
|
49
|
+
export interface VoiceTranscriptionResult {
|
|
50
|
+
callId?: string;
|
|
51
|
+
messageId?: string;
|
|
52
|
+
text: string;
|
|
53
|
+
language?: string;
|
|
54
|
+
confidence?: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Transcribe voice message via external service (hook)
|
|
58
|
+
* User harus implement sendiri atau pakai service seperti Whisper
|
|
59
|
+
*/
|
|
60
|
+
export type VoiceTranscriptionFn = (audioBuffer: Buffer) => Promise<VoiceTranscriptionResult | null>;
|
|
61
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/calls/handler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,WAAW,EAEX,OAAO,EACP,QAAQ,EACT,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAoB,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAIjE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;IACjC,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACtC,iCAAiC;IACjC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,IAAI,CAA6B;IACzC,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,MAAM,CAAC,CAA+B;IAC9C,OAAO,CAAC,UAAU,CAAU;gBAEhB,IAAI,EAAE,kBAAkB;IAOpC;;OAEG;IACG,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BrD;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB9D;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB9D;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI;IAIvD;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;CAGtC;AAQD;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Waliwa - Call handling
|
|
4
|
+
* Process incoming WhatsApp call events (offer, reject, terminate)
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.CallHandler = void 0;
|
|
8
|
+
const binary_js_1 = require("../core/binary.js");
|
|
9
|
+
const jid_js_1 = require("../utils/jid.js");
|
|
10
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
11
|
+
/**
|
|
12
|
+
* Handle incoming WhatsApp call events
|
|
13
|
+
* Calls masuk sebagai binary node dengan tag "call"
|
|
14
|
+
*/
|
|
15
|
+
class CallHandler {
|
|
16
|
+
send;
|
|
17
|
+
logger;
|
|
18
|
+
onCall;
|
|
19
|
+
autoReject;
|
|
20
|
+
constructor(opts) {
|
|
21
|
+
this.send = opts.send;
|
|
22
|
+
this.logger = opts.logger ?? new logger_js_1.SilentLogger();
|
|
23
|
+
this.onCall = opts.onCall;
|
|
24
|
+
this.autoReject = opts.autoReject ?? false;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Handle call node
|
|
28
|
+
*/
|
|
29
|
+
async handleCallNode(node) {
|
|
30
|
+
const attrs = node.attrs || {};
|
|
31
|
+
const from = attrs.from;
|
|
32
|
+
const callId = attrs.id;
|
|
33
|
+
const isVideo = attrs.video === 'true';
|
|
34
|
+
const isGroup = attrs.group === 'true' || attrs.is_group === 'true';
|
|
35
|
+
const status = attrs.status || 'ringing';
|
|
36
|
+
const event = {
|
|
37
|
+
id: callId,
|
|
38
|
+
from,
|
|
39
|
+
to: attrs.to,
|
|
40
|
+
isVideo,
|
|
41
|
+
isGroup,
|
|
42
|
+
status,
|
|
43
|
+
timestamp: Date.now(),
|
|
44
|
+
offer: attrs.offer
|
|
45
|
+
};
|
|
46
|
+
this.logger.info(`Incoming ${isVideo ? 'video' : 'voice'} call from ${from} (id: ${callId})`);
|
|
47
|
+
if (this.onCall)
|
|
48
|
+
this.onCall(event);
|
|
49
|
+
// Auto reject jika enabled
|
|
50
|
+
if (this.autoReject && status === 'ringing') {
|
|
51
|
+
await this.rejectCall(callId, from);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Reject incoming call
|
|
56
|
+
*/
|
|
57
|
+
async rejectCall(callId, from) {
|
|
58
|
+
const node = {
|
|
59
|
+
tag: 'call',
|
|
60
|
+
attrs: {
|
|
61
|
+
id: callId,
|
|
62
|
+
from: attrs_to_placeholder(),
|
|
63
|
+
to: (0, jid_js_1.jidToString)(from),
|
|
64
|
+
action: 'reject',
|
|
65
|
+
type: 'reject'
|
|
66
|
+
},
|
|
67
|
+
content: undefined
|
|
68
|
+
};
|
|
69
|
+
try {
|
|
70
|
+
this.send((0, binary_js_1.encodeBinaryNode)(node));
|
|
71
|
+
this.logger.info(`Rejected call ${callId}`);
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
this.logger.error(`Failed to reject call: ${err.message}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Accept incoming call (voice only, no WebRTC support)
|
|
79
|
+
*/
|
|
80
|
+
async acceptCall(callId, from) {
|
|
81
|
+
const node = {
|
|
82
|
+
tag: 'call',
|
|
83
|
+
attrs: {
|
|
84
|
+
id: callId,
|
|
85
|
+
to: (0, jid_js_1.jidToString)(from),
|
|
86
|
+
action: 'accept',
|
|
87
|
+
type: 'accept'
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
try {
|
|
91
|
+
this.send((0, binary_js_1.encodeBinaryNode)(node));
|
|
92
|
+
this.logger.info(`Accepted call ${callId}`);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
this.logger.error(`Failed to accept call: ${err.message}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Set call handler
|
|
100
|
+
*/
|
|
101
|
+
setHandler(handler) {
|
|
102
|
+
this.onCall = handler;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Enable/disable auto reject
|
|
106
|
+
*/
|
|
107
|
+
setAutoReject(enabled) {
|
|
108
|
+
this.autoReject = enabled;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
exports.CallHandler = CallHandler;
|
|
112
|
+
// Helper placeholder for "to" field (own JID)
|
|
113
|
+
function attrs_to_placeholder() {
|
|
114
|
+
// Should be set to own JID
|
|
115
|
+
return '';
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/calls/handler.ts"],"names":[],"mappings":"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"}
|