@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,457 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Waliwa - Core Type Definitions
|
|
3
|
+
* Type definitions untuk semua entity WhatsApp (Message, Chat, Group, Call, dll)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Readable } from 'stream';
|
|
7
|
+
|
|
8
|
+
// ============== JID Types ==============
|
|
9
|
+
|
|
10
|
+
export type JIDServer = 'c.us' | 'g.us' | 's.whatsapp.net' | 'broadcast' | 'call';
|
|
11
|
+
|
|
12
|
+
export interface JID {
|
|
13
|
+
user: string;
|
|
14
|
+
server: JIDServer;
|
|
15
|
+
device?: number;
|
|
16
|
+
/** Fully-qualified JID string, e.g. "6281234567890@s.whatsapp.net" */
|
|
17
|
+
toString(): string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type JIDLike = string | JID;
|
|
21
|
+
|
|
22
|
+
// ============== Message Types ==============
|
|
23
|
+
|
|
24
|
+
export type MessageType =
|
|
25
|
+
| 'text'
|
|
26
|
+
| 'image'
|
|
27
|
+
| 'video'
|
|
28
|
+
| 'audio'
|
|
29
|
+
| 'document'
|
|
30
|
+
| 'sticker'
|
|
31
|
+
| 'location'
|
|
32
|
+
| 'contact'
|
|
33
|
+
| 'contacts'
|
|
34
|
+
| 'buttons'
|
|
35
|
+
| 'list'
|
|
36
|
+
| 'template'
|
|
37
|
+
| 'order'
|
|
38
|
+
| 'product'
|
|
39
|
+
| 'poll'
|
|
40
|
+
| 'reaction'
|
|
41
|
+
| 'revoked'
|
|
42
|
+
| 'system'
|
|
43
|
+
| 'unknown';
|
|
44
|
+
|
|
45
|
+
export interface MessageContent {
|
|
46
|
+
text?: string;
|
|
47
|
+
caption?: string;
|
|
48
|
+
image?: { stream: Readable; mimetype: string; fileLength?: number };
|
|
49
|
+
video?: { stream: Readable; mimetype: string; seconds?: number; gifPlayback?: boolean };
|
|
50
|
+
audio?: { stream: Readable; mimetype: string; seconds?: number; ptt?: boolean };
|
|
51
|
+
document?: { stream: Readable; mimetype: string; fileName?: string; pageCount?: number };
|
|
52
|
+
sticker?: { stream: Readable; mimetype: string; isAnimated?: boolean };
|
|
53
|
+
location?: { degreesLatitude: number; degreesLongitude: number; name?: string; address?: string };
|
|
54
|
+
contact?: { displayName: string; vcard: string };
|
|
55
|
+
contacts?: { displayName: string; contacts: Array<{ displayName: string; vcard: string }> };
|
|
56
|
+
poll?: { name: string; values: string[]; selectableCounts?: number[]; toMessage?: MessageContent };
|
|
57
|
+
buttonReply?: { selectedDisplayText: string; selectedButtonId: string };
|
|
58
|
+
listReply?: { title: string; description: string; singleSelectReply: { selectedRowId: string } };
|
|
59
|
+
// Context
|
|
60
|
+
quoted?: WAMessage;
|
|
61
|
+
mentions?: JIDLike[];
|
|
62
|
+
// Misc
|
|
63
|
+
edit?: string;
|
|
64
|
+
ephemeralExpiration?: number;
|
|
65
|
+
viewOnce?: boolean;
|
|
66
|
+
contextInfo?: ContextInfo;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface ContextInfo {
|
|
70
|
+
stanzaId?: string;
|
|
71
|
+
participant?: JIDLike;
|
|
72
|
+
quotedMessage?: Partial<MessageContent>;
|
|
73
|
+
mentionedJid?: string[];
|
|
74
|
+
externalAdReply?: ExternalAdReply;
|
|
75
|
+
forwardingScore?: number;
|
|
76
|
+
isForwarded?: boolean;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface ExternalAdReply {
|
|
80
|
+
title: string;
|
|
81
|
+
body?: string;
|
|
82
|
+
mediaType?: number;
|
|
83
|
+
renderLargerThumbnail?: boolean;
|
|
84
|
+
thumbnail?: Buffer;
|
|
85
|
+
thumbnailUrl?: string;
|
|
86
|
+
sourceUrl?: string;
|
|
87
|
+
sourceId?: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface WAMessage {
|
|
91
|
+
key: WAMessageKey;
|
|
92
|
+
message?: MessageContent & { [k: string]: any };
|
|
93
|
+
messageTimestamp: number;
|
|
94
|
+
pushName?: string;
|
|
95
|
+
participant?: JIDLike;
|
|
96
|
+
status: MessageStatus;
|
|
97
|
+
ephemeralOutOfSync?: boolean;
|
|
98
|
+
/** Reply info */
|
|
99
|
+
quoted?: WAMessage;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface WAMessageKey {
|
|
103
|
+
remoteJid: JIDLike;
|
|
104
|
+
fromMe: boolean;
|
|
105
|
+
id: string;
|
|
106
|
+
participant?: JIDLike;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export type MessageStatus = 'pending' | 'sent' | 'delivered' | 'read' | 'played' | 'error';
|
|
110
|
+
|
|
111
|
+
// ============== Chat Types ==============
|
|
112
|
+
|
|
113
|
+
export interface WAChat {
|
|
114
|
+
id: JIDLike;
|
|
115
|
+
name?: string;
|
|
116
|
+
unreadCount: number;
|
|
117
|
+
lastMessage?: WAMessage;
|
|
118
|
+
pinned: boolean;
|
|
119
|
+
archived: boolean;
|
|
120
|
+
isGroup: boolean;
|
|
121
|
+
isMuted: boolean;
|
|
122
|
+
muteEndTime?: number;
|
|
123
|
+
conversationTimestamp: number;
|
|
124
|
+
ephemeralSetting?: number;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ============== Group Types ==============
|
|
128
|
+
|
|
129
|
+
export interface WAGroupMetadata {
|
|
130
|
+
id: JIDLike;
|
|
131
|
+
subject: string;
|
|
132
|
+
subjectOwner?: JIDLike;
|
|
133
|
+
subjectTime?: number;
|
|
134
|
+
desc?: string;
|
|
135
|
+
descOwner?: JIDLike;
|
|
136
|
+
descTime?: number;
|
|
137
|
+
creation?: number;
|
|
138
|
+
owner?: JIDLike;
|
|
139
|
+
participants: WAGroupParticipant[];
|
|
140
|
+
restrict?: boolean;
|
|
141
|
+
announce?: boolean;
|
|
142
|
+
isCommunity?: boolean;
|
|
143
|
+
isParentGroup?: boolean;
|
|
144
|
+
linkedParent?: JIDLike;
|
|
145
|
+
ephemeralSetting?: number;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface WAGroupParticipant {
|
|
149
|
+
id: JIDLike;
|
|
150
|
+
isAdmin: boolean;
|
|
151
|
+
isSuperAdmin: boolean;
|
|
152
|
+
/** "admin" | "member" | "superadmin" */
|
|
153
|
+
role?: string;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface GroupCreateOptions {
|
|
157
|
+
subject: string;
|
|
158
|
+
participants: JIDLike[];
|
|
159
|
+
desc?: string;
|
|
160
|
+
/** Idle time (seconds) before group is auto-disbanded */
|
|
161
|
+
ephemeralSetting?: number;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface GroupUpdateSubjectOptions {
|
|
165
|
+
subject: string;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface GroupUpdateDescriptionOptions {
|
|
169
|
+
desc: string;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ============== Call Types ==============
|
|
173
|
+
|
|
174
|
+
export interface WACallEvent {
|
|
175
|
+
id: string;
|
|
176
|
+
from: JIDLike;
|
|
177
|
+
to: JIDLike;
|
|
178
|
+
isVideo: boolean;
|
|
179
|
+
isGroup: boolean;
|
|
180
|
+
status: CallStatus;
|
|
181
|
+
timestamp: number;
|
|
182
|
+
/** Optional WebRTC SDP offer if available */
|
|
183
|
+
offer?: string;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export type CallStatus = 'ringing' | 'accepted' | 'rejected' | 'missed' | 'timeout';
|
|
187
|
+
|
|
188
|
+
// ============== Presence ==============
|
|
189
|
+
|
|
190
|
+
export type PresenceType = 'available' | 'unavailable' | 'composing' | 'recording' | 'paused';
|
|
191
|
+
|
|
192
|
+
export interface PresenceUpdate {
|
|
193
|
+
jid: JIDLike;
|
|
194
|
+
presence: PresenceType;
|
|
195
|
+
lastKnownPresence?: PresenceType;
|
|
196
|
+
t?: number;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ============== Auth Types ==============
|
|
200
|
+
|
|
201
|
+
export interface AuthState {
|
|
202
|
+
creds: AuthenticationCreds;
|
|
203
|
+
keys: SignalKeyStore;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export interface AuthenticationCreds {
|
|
207
|
+
/** Our registered phone number with country code, e.g. "6281234567890" */
|
|
208
|
+
me?: JIDLike;
|
|
209
|
+
/** Whether pairing was completed */
|
|
210
|
+
registered: boolean;
|
|
211
|
+
/** Noise protocol static key pair */
|
|
212
|
+
noiseKey?: KeyPair;
|
|
213
|
+
/** Pairing static key (server side identity) */
|
|
214
|
+
pairingKey?: KeyPair;
|
|
215
|
+
/** Identity key pair */
|
|
216
|
+
identityId?: KeyPair;
|
|
217
|
+
/** Signed pre-key */
|
|
218
|
+
signedIdentityKey?: KeyPair;
|
|
219
|
+
signedPreKey?: SignedKeyPair;
|
|
220
|
+
/** Registration ID */
|
|
221
|
+
registrationId?: number;
|
|
222
|
+
/** Advance details for multi-device */
|
|
223
|
+
advSecretKey?: string;
|
|
224
|
+
/** Next pre-key ID counter */
|
|
225
|
+
nextPreKeyId: number;
|
|
226
|
+
firstUnuploadedPreKeyId: number;
|
|
227
|
+
/** Has the account been "uploaded" */
|
|
228
|
+
accountSettings?: {
|
|
229
|
+
unarchiveChats: boolean;
|
|
230
|
+
};
|
|
231
|
+
/** Server tokens */
|
|
232
|
+
serverToken?: string;
|
|
233
|
+
clientToken?: string;
|
|
234
|
+
/** Last seen phone */
|
|
235
|
+
lastAccountSyncTimestamp?: number;
|
|
236
|
+
/** Mark-as-read setting */
|
|
237
|
+
markOnlineLastSeen?: boolean;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export interface KeyPair {
|
|
241
|
+
private: Uint8Array;
|
|
242
|
+
public: Uint8Array;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export interface SignedKeyPair {
|
|
246
|
+
keyId: number;
|
|
247
|
+
keyPair: KeyPair;
|
|
248
|
+
signature: Uint8Array;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export interface SignalKeyStore {
|
|
252
|
+
preKeys: Record<number, KeyPair>;
|
|
253
|
+
sessions: Record<string, any>;
|
|
254
|
+
identityKeys: Record<string, KeyPair>;
|
|
255
|
+
senderKeys: Record<string, any>;
|
|
256
|
+
appStateKeys: Record<string, Uint8Array>;
|
|
257
|
+
appStateSyncKeys: Record<string, Uint8Array>;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export interface QRGenerationData {
|
|
261
|
+
/** QR data ref */
|
|
262
|
+
ref: string;
|
|
263
|
+
/** Public key for pairing */
|
|
264
|
+
publicKey: Uint8Array;
|
|
265
|
+
/** Pairing static key */
|
|
266
|
+
pairingStaticKey: Uint8Array;
|
|
267
|
+
/** Encoded QR string */
|
|
268
|
+
qrString: string;
|
|
269
|
+
/** QR timeout in seconds */
|
|
270
|
+
timeout: number;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export interface PairingCodeResult {
|
|
274
|
+
/** 8-character pairing code e.g. "AB12-CD34" */
|
|
275
|
+
code: string;
|
|
276
|
+
/** Phone number with country code */
|
|
277
|
+
phoneNumber: string;
|
|
278
|
+
/** Expiry timestamp */
|
|
279
|
+
expiresAt: number;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// ============== Media Types ==============
|
|
283
|
+
|
|
284
|
+
export interface MediaUploadResult {
|
|
285
|
+
url: string;
|
|
286
|
+
directPath: string;
|
|
287
|
+
mediaKey: Uint8Array;
|
|
288
|
+
encFileHash: Uint8Array;
|
|
289
|
+
fileEncSHA256: Uint8Array;
|
|
290
|
+
fileSHA256: Uint8Array;
|
|
291
|
+
fileLength: number;
|
|
292
|
+
mimetype: string;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export interface MediaDownloadResult {
|
|
296
|
+
buffer: Buffer;
|
|
297
|
+
mimetype: string;
|
|
298
|
+
fileLength: number;
|
|
299
|
+
/** Original filename if document */
|
|
300
|
+
fileName?: string;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// ============== Events ==============
|
|
304
|
+
|
|
305
|
+
export interface WAEventMap {
|
|
306
|
+
'connection.update': ConnectionUpdate;
|
|
307
|
+
'creds.update': AuthenticationCreds;
|
|
308
|
+
'qr.update': QRGenerationData;
|
|
309
|
+
'pairing-code.update': PairingCodeResult;
|
|
310
|
+
'message.upsert': { messages: WAMessage[]; type: MessageUpsertType };
|
|
311
|
+
'message.update': { messages: WAMessage[]; keys: WAMessageKey[] };
|
|
312
|
+
'message.delete': { keys: WAMessageKey[]; messageRevokes?: WAMessage[] };
|
|
313
|
+
'message.reaction': { key: WAMessageKey; reaction: string; sender?: JIDLike };
|
|
314
|
+
'presence.update': PresenceUpdate;
|
|
315
|
+
'chats.upsert': WAChat[];
|
|
316
|
+
'chats.update': Partial<WAChat>[];
|
|
317
|
+
'chats.delete': string[];
|
|
318
|
+
'groups.upsert': WAGroupMetadata[];
|
|
319
|
+
'groups.update': Partial<WAGroupMetadata>[];
|
|
320
|
+
'group-participants.update': GroupParticipantUpdate;
|
|
321
|
+
'calls': WACallEvent;
|
|
322
|
+
'contacts.upsert': ContactRecord[];
|
|
323
|
+
'contacts.update': Partial<ContactRecord>[];
|
|
324
|
+
'blocklist.update': string[];
|
|
325
|
+
'messaging-history.set': { chats: WAChat[]; contacts: ContactRecord[]; messages: WAMessage[]; isLatest: boolean };
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export type MessageUpsertType = 'append' | 'notify';
|
|
329
|
+
|
|
330
|
+
export interface ContactRecord {
|
|
331
|
+
id: JIDLike;
|
|
332
|
+
name?: string;
|
|
333
|
+
notify?: string;
|
|
334
|
+
verifiedName?: string;
|
|
335
|
+
imgUrl?: string;
|
|
336
|
+
status?: string;
|
|
337
|
+
lid?: string;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export interface GroupParticipantUpdate {
|
|
341
|
+
id: JIDLike;
|
|
342
|
+
participants: JIDLike[];
|
|
343
|
+
action: 'add' | 'remove' | 'promote' | 'demote';
|
|
344
|
+
author?: JIDLike;
|
|
345
|
+
timestamp: number;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// ============== Connection ==============
|
|
349
|
+
|
|
350
|
+
export interface ConnectionUpdate {
|
|
351
|
+
connection: 'close' | 'open' | 'connecting';
|
|
352
|
+
lastDisconnect?: { error: Error | undefined; output: any };
|
|
353
|
+
qr?: string;
|
|
354
|
+
isNewLogin?: boolean;
|
|
355
|
+
receivedPendingNotifications?: boolean;
|
|
356
|
+
isOnline?: boolean;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// ============== Config ==============
|
|
360
|
+
|
|
361
|
+
export interface WaliwaConfig {
|
|
362
|
+
/** Authentication state provider (file-based by default) */
|
|
363
|
+
authState?: AuthState;
|
|
364
|
+
/** Custom logger */
|
|
365
|
+
logger?: WALogger;
|
|
366
|
+
/** Print QR to terminal */
|
|
367
|
+
printQRInTerminal?: boolean;
|
|
368
|
+
/** Mobile API mode: use pairing code instead of QR */
|
|
369
|
+
mobile?: boolean;
|
|
370
|
+
/** Phone number for pairing code mode */
|
|
371
|
+
phoneNumber?: string;
|
|
372
|
+
/** Browser identification [name, short name, version] */
|
|
373
|
+
browser?: [string, string, string];
|
|
374
|
+
/** WebSocket reconnect config */
|
|
375
|
+
reconnectInterval?: number;
|
|
376
|
+
maxReconnectAttempts?: number;
|
|
377
|
+
/** Request full sync on connect */
|
|
378
|
+
syncFullHistory?: boolean;
|
|
379
|
+
/** Maximum message retry count */
|
|
380
|
+
maxMsgRetryCount?: number;
|
|
381
|
+
/** Custom WebSocket URL */
|
|
382
|
+
wsURL?: string;
|
|
383
|
+
/** Timeout untuk connect (ms) */
|
|
384
|
+
connectTimeoutMs?: number;
|
|
385
|
+
/** Keep alive interval (ms) */
|
|
386
|
+
keepAliveIntervalMs?: number;
|
|
387
|
+
/** RAM optimization level: 1 = balanced, 2 = aggressive, 3 = ultra */
|
|
388
|
+
ramOptimizationLevel?: 1 | 2 | 3;
|
|
389
|
+
/** Auto mark read received messages */
|
|
390
|
+
markOnlineOnConnect?: boolean;
|
|
391
|
+
/** Custom HTTP agent for media upload/download */
|
|
392
|
+
fetchAgent?: any;
|
|
393
|
+
/** Multi-Branch SKDM Recovery configuration */
|
|
394
|
+
recovery?: RecoveryConfig;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Multi-Branch SKDM Recovery configuration
|
|
399
|
+
*
|
|
400
|
+
* Saat disconnect terjadi, sistem mencoba multiple recovery strategies in order
|
|
401
|
+
* berdasarkan disconnect reason. Each branch punya retry limit sendiri.
|
|
402
|
+
*/
|
|
403
|
+
export interface RecoveryConfig {
|
|
404
|
+
/** Enable recovery system (default: true) */
|
|
405
|
+
enabled?: boolean;
|
|
406
|
+
/** Max attempts per branch (default: 3) */
|
|
407
|
+
maxAttemptsPerBranch?: number;
|
|
408
|
+
/** Total max attempts across all branches (default: 20) */
|
|
409
|
+
maxTotalAttempts?: number;
|
|
410
|
+
/** Initial delay untuk backoff (default: 1000ms) */
|
|
411
|
+
initialDelayMs?: number;
|
|
412
|
+
/** Max delay untuk backoff (default: 60000ms) */
|
|
413
|
+
maxDelayMs?: number;
|
|
414
|
+
/** Delay khusus untuk rate limited (default: 300000ms = 5min) */
|
|
415
|
+
rateLimitedDelayMs?: number;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export interface WALogger {
|
|
419
|
+
level: 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace';
|
|
420
|
+
child(meta?: Record<string, any>): WALogger;
|
|
421
|
+
fatal(msg: string, ...args: any[]): void;
|
|
422
|
+
error(msg: string, ...args: any[]): void;
|
|
423
|
+
warn(msg: string, ...args: any[]): void;
|
|
424
|
+
info(msg: string, ...args: any[]): void;
|
|
425
|
+
debug(msg: string, ...args: any[]): void;
|
|
426
|
+
trace(msg: string, ...args: any[]): void;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// ============== Send Message Result ==============
|
|
430
|
+
|
|
431
|
+
export interface SendMessageResult {
|
|
432
|
+
/** Message ID generated */
|
|
433
|
+
key: WAMessageKey;
|
|
434
|
+
/** Message status */
|
|
435
|
+
status: MessageStatus;
|
|
436
|
+
/** Optional message timestamp */
|
|
437
|
+
messageTimestamp?: number;
|
|
438
|
+
/** Optional participants (for groups) */
|
|
439
|
+
participants?: JIDLike[];
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// ============== Poll Types ==============
|
|
443
|
+
|
|
444
|
+
export interface PollMessageContent {
|
|
445
|
+
name: string;
|
|
446
|
+
values: string[];
|
|
447
|
+
selectableCounts?: number[];
|
|
448
|
+
toMessage?: MessageContent;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
export interface PollUpdate {
|
|
452
|
+
pollCreationMessageKey: WAMessageKey;
|
|
453
|
+
voteCasterJid: JIDLike;
|
|
454
|
+
selectedOptions: string[];
|
|
455
|
+
/** Timestamp of vote */
|
|
456
|
+
timestamp: number;
|
|
457
|
+
}
|
package/src/utils/jid.ts
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Waliwa - JID (Jabber ID) utilities
|
|
3
|
+
* WhatsApp pakai JID mirip XMPP: "user@s.whatsapp.net", "group-id@g.us", etc.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { JID, JIDLike, JIDServer } from '../types/index.js';
|
|
7
|
+
|
|
8
|
+
const DEFAULT_SERVER: JIDServer = 's.whatsapp.net';
|
|
9
|
+
const GROUP_SERVER: JIDServer = 'g.us';
|
|
10
|
+
const USER_SERVER: JIDServer = 'c.us';
|
|
11
|
+
const BROADCAST_SERVER: JIDServer = 'broadcast';
|
|
12
|
+
const CALL_SERVER: JIDServer = 'call';
|
|
13
|
+
|
|
14
|
+
// ============== Parsing ==============
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Parse string jadi JID object
|
|
18
|
+
* Format: "user@server" atau "user:device@server"
|
|
19
|
+
*/
|
|
20
|
+
export function parseJid(input: JIDLike): JID {
|
|
21
|
+
if (typeof input === 'object' && input !== null && 'user' in input) {
|
|
22
|
+
return input;
|
|
23
|
+
}
|
|
24
|
+
const str = String(input);
|
|
25
|
+
const [userPart, serverPart] = str.split('@');
|
|
26
|
+
if (!serverPart) {
|
|
27
|
+
// Default ke s.whatsapp.net jika tidak ada server
|
|
28
|
+
return {
|
|
29
|
+
user: str,
|
|
30
|
+
server: DEFAULT_SERVER,
|
|
31
|
+
toString: () => `${str}@${DEFAULT_SERVER}`
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// Check for device suffix: "user:device@server"
|
|
35
|
+
const [user, deviceStr] = userPart.split(':');
|
|
36
|
+
const device = deviceStr ? parseInt(deviceStr, 10) : undefined;
|
|
37
|
+
const server = serverPart as JIDServer;
|
|
38
|
+
|
|
39
|
+
const fullJid = device
|
|
40
|
+
? `${user}:${device}@${server}`
|
|
41
|
+
: `${user}@${server}`;
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
user,
|
|
45
|
+
server,
|
|
46
|
+
device,
|
|
47
|
+
toString: () => fullJid
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Normalize JID ke string
|
|
53
|
+
*/
|
|
54
|
+
export function jidToString(jid: JIDLike): string {
|
|
55
|
+
if (typeof jid === 'string') return jid;
|
|
56
|
+
if (typeof jid === 'object' && jid !== null && 'toString' in jid) {
|
|
57
|
+
return jid.toString();
|
|
58
|
+
}
|
|
59
|
+
return String(jid);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get user ID saja (tanpa @server)
|
|
64
|
+
*/
|
|
65
|
+
export function jidUser(jid: JIDLike): string {
|
|
66
|
+
return parseJid(jid).user;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get server dari JID
|
|
71
|
+
*/
|
|
72
|
+
export function jidServer(jid: JIDLike): JIDServer {
|
|
73
|
+
return parseJid(jid).server;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ============== Type Checks ==============
|
|
77
|
+
|
|
78
|
+
export function isGroupJid(jid: JIDLike): boolean {
|
|
79
|
+
return jidServer(jid) === GROUP_SERVER;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function isUserJid(jid: JIDLike): boolean {
|
|
83
|
+
const server = jidServer(jid);
|
|
84
|
+
return server === USER_SERVER || server === DEFAULT_SERVER;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function isBroadcastJid(jid: JIDLike): boolean {
|
|
88
|
+
return jidServer(jid) === BROADCAST_SERVER;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function isCallJid(jid: JIDLike): boolean {
|
|
92
|
+
return jidServer(jid) === CALL_SERVER;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function isStatusJid(jid: JIDLike): boolean {
|
|
96
|
+
return jidUser(jid) === 'status';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function isNewsletter(jid: JIDLike): boolean {
|
|
100
|
+
const server = jidServer(jid);
|
|
101
|
+
return server === 's.whatsapp.net' && jidUser(jid).startsWith('120363');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function isLidJid(jid: JIDLike): boolean {
|
|
105
|
+
return jidUser(jid).startsWith('xyz:') || jidUser(jid).match(/^\d+$/) === null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ============== Builders ==============
|
|
109
|
+
|
|
110
|
+
export function userJid(phoneNumber: string | number): string {
|
|
111
|
+
const str = String(phoneNumber).replace(/[^0-9]/g, '');
|
|
112
|
+
return `${str}@${DEFAULT_SERVER}`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function groupJid(groupId: string): string {
|
|
116
|
+
const str = String(groupId).replace(/[^0-9-]/g, '');
|
|
117
|
+
return `${str}@${GROUP_SERVER}`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function broadcastJid(broadcastId: string): string {
|
|
121
|
+
return `${broadcastId}@${BROADCAST_SERVER}`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ============== Validation ==============
|
|
125
|
+
|
|
126
|
+
export function isValidJid(jid: JIDLike): boolean {
|
|
127
|
+
try {
|
|
128
|
+
const parsed = parseJid(jid);
|
|
129
|
+
return parsed.user.length > 0 && parsed.server.length > 0;
|
|
130
|
+
} catch {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Compare 2 JID (case-insensitive, ignore device suffix)
|
|
137
|
+
*/
|
|
138
|
+
export function jidEqual(a: JIDLike, b: JIDLike): boolean {
|
|
139
|
+
const ja = parseJid(a);
|
|
140
|
+
const jb = parseJid(b);
|
|
141
|
+
return ja.user === jb.user && ja.server === jb.server;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Get phone number dari user JID
|
|
146
|
+
*/
|
|
147
|
+
export function jidToPhoneNumber(jid: JIDLike): string {
|
|
148
|
+
return jidUser(jid);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Convert phone number ke JID
|
|
153
|
+
*/
|
|
154
|
+
export function phoneNumberToJid(phone: string): string {
|
|
155
|
+
return userJid(phone);
|
|
156
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Waliwa - Logger utility (pino-based, RAM-efficient)
|
|
3
|
+
* Default level 'warn' untuk production hemat RAM.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { WALogger } from '../types/index.js';
|
|
7
|
+
|
|
8
|
+
type LogLevel = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace';
|
|
9
|
+
const LEVEL_PRIORITY: Record<LogLevel, number> = {
|
|
10
|
+
fatal: 60, error: 50, warn: 40, info: 30, debug: 20, trace: 10
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Lightweight logger - lebih ringan dari pino untuk RAM efficiency
|
|
15
|
+
* Tetap API-compatible dengan pino agar mudah di-swap
|
|
16
|
+
*/
|
|
17
|
+
export class DefaultLogger implements WALogger {
|
|
18
|
+
level: LogLevel = 'warn';
|
|
19
|
+
private meta: Record<string, any> = {};
|
|
20
|
+
|
|
21
|
+
constructor(level: LogLevel = 'warn') {
|
|
22
|
+
this.level = level;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
child(meta?: Record<string, any>): WALogger {
|
|
26
|
+
const child = new DefaultLogger(this.level);
|
|
27
|
+
child.meta = { ...this.meta, ...(meta || {}) };
|
|
28
|
+
return child;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private shouldLog(level: LogLevel): boolean {
|
|
32
|
+
return LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[this.level];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private format(level: LogLevel, msg: string, args: any[]): string {
|
|
36
|
+
const ts = new Date().toISOString();
|
|
37
|
+
const metaStr = Object.keys(this.meta).length
|
|
38
|
+
? ' ' + JSON.stringify(this.meta)
|
|
39
|
+
: '';
|
|
40
|
+
const argsStr = args.length ? ' ' + args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ')
|
|
41
|
+
: '';
|
|
42
|
+
return `[${ts}] ${level.toUpperCase()}${metaStr} ${msg}${argsStr}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private log(level: LogLevel, msg: string, args: any[]): void {
|
|
46
|
+
if (!this.shouldLog(level)) return;
|
|
47
|
+
const formatted = this.format(level, msg, args);
|
|
48
|
+
if (level === 'fatal' || level === 'error') {
|
|
49
|
+
console.error(formatted);
|
|
50
|
+
} else if (level === 'warn') {
|
|
51
|
+
console.warn(formatted);
|
|
52
|
+
} else {
|
|
53
|
+
console.log(formatted);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fatal(msg: string, ...args: any[]): void { this.log('fatal', msg, args); }
|
|
58
|
+
error(msg: string, ...args: any[]): void { this.log('error', msg, args); }
|
|
59
|
+
warn(msg: string, ...args: any[]): void { this.log('warn', msg, args); }
|
|
60
|
+
info(msg: string, ...args: any[]): void { this.log('info', msg, args); }
|
|
61
|
+
debug(msg: string, ...args: any[]): void { this.log('debug', msg, args); }
|
|
62
|
+
trace(msg: string, ...args: any[]): void { this.log('trace', msg, args); }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Default logger instance (singleton, RAM-friendly)
|
|
67
|
+
*/
|
|
68
|
+
export const defaultLogger = new DefaultLogger(process.env.WALIWA_LOG_LEVEL as LogLevel || 'warn');
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Silent logger - untuk testing atau production mode tanpa logs
|
|
72
|
+
*/
|
|
73
|
+
export class SilentLogger implements WALogger {
|
|
74
|
+
level: LogLevel = 'silent' as any;
|
|
75
|
+
child(): WALogger { return this; }
|
|
76
|
+
fatal(): void {}
|
|
77
|
+
error(): void {}
|
|
78
|
+
warn(): void {}
|
|
79
|
+
info(): void {}
|
|
80
|
+
debug(): void {}
|
|
81
|
+
trace(): void {}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const silentLogger = new SilentLogger();
|