@arcaelas/whatsapp 1.0.2

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/README.md ADDED
@@ -0,0 +1,367 @@
1
+ ![Arcaelas Insiders](https://raw.githubusercontent.com/arcaelas/dist/main/banner/svg/dark.svg#gh-dark-mode-only) ![Arcaelas Insiders](https://raw.githubusercontent.com/arcaelas/dist/main/banner/svg/light.svg#gh-light-mode-only)
2
+
3
+ # @arcaelas/whatsapp
4
+
5
+ > A **multi‑device**, storage‑agnostic WhatsApp client for Node.js.
6
+ >
7
+ > _Typed end‑to‑end · Sends any media · Zero‑boilerplate API · Written in TypeScript only_
8
+
9
+ <p align="center">
10
+ <a href="https://www.npmjs.com/package/@arcaelas/whatsapp"><img src="https://img.shields.io/npm/v/@arcaelas/whatsapp?color=cb3837" alt="npm version"></a>
11
+ <img src="https://img.shields.io/bundlephobia/minzip/@arcaelas/whatsapp?label=gzip" alt="bundle size">
12
+ <img src="https://img.shields.io/github/license/arcaelas/whatsapp" alt="MIT">
13
+ </p>
14
+
15
+ ---
16
+
17
+ ## Contents
18
+
19
+ - [Install](#install)
20
+ - [Quick Start](#quick-start)
21
+ - [Zero to Hero Guide](#zero-to-hero-guide)
22
+
23
+ - [1. Create a Store](#1-create-a-store)
24
+ - [2. Initialise the client](#2-initialise-the-client)
25
+ - [3. Read chats & messages](#3-read-chats--messages)
26
+ - [4. Send your first message](#4-send-your-first-message)
27
+
28
+ - [Interfaces & Types](#interfaces--types)
29
+
30
+ - [`IWhatsApp`](#iwhatsapp)
31
+ - [`Store`](#store)
32
+
33
+ - [API Reference](#api-reference)
34
+
35
+ - [Chats](#chats)
36
+ - [Messages](#messages)
37
+ - [Presence](#presence)
38
+ - [Media Helpers](#media-helpers)
39
+
40
+ - [Storage Back‑ends](#storage-back‑ends)
41
+
42
+ - [In‑memory](#in‑memory)
43
+ - [File‑system](#file‑system)
44
+ - [Redis](#redis)
45
+
46
+ - [Recipes](#recipes)
47
+ - [Troubleshooting](#troubleshooting)
48
+ - [Contributing](#contributing)
49
+ - [License](#license)
50
+
51
+ ---
52
+
53
+ ## Install
54
+
55
+ ```bash
56
+ # core package
57
+ yarn add @arcaelas/whatsapp
58
+
59
+ # peer dependency (Baileys – WhatsApp MD layer)
60
+ yarn add @whiskeysockets/baileys
61
+ # plus any media codecs you wish (ffmpeg, sharp, etc.)
62
+ ```
63
+
64
+ > Node 18+ required. Works in ESM & TypeScript projects out of the box.
65
+
66
+ ---
67
+
68
+ ## Quick Start
69
+
70
+ ```ts
71
+ import WhatsApp from '@arcaelas/whatsapp';
72
+ import qrcode from 'qrcode-terminal';
73
+
74
+ const socket = new WhatsApp({
75
+ phone: '+01000000000',
76
+ loginType: 'qr',
77
+ qr: (buffer) => qrcode.generate(buffer.toString('base64'), { small: true }),
78
+ });
79
+
80
+ await socket.ready(); // blocks until authenticated
81
+
82
+ const [chat] = await socket.chats();
83
+ await chat.send('Hello from Arcaelas 🤖');
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Zero‑to‑Hero Guide
89
+
90
+ ### 1. Create a Store
91
+
92
+ The library is **storage‑agnostic**. Implement the minimal `Store` contract once and reuse everywhere.
93
+
94
+ ```ts
95
+ /** Minimal in‑memory store for demos */
96
+ const MemoryStore: Store = {
97
+ map: new Map<string, string>(),
98
+
99
+ has(key) {
100
+ return this.map.has(key);
101
+ },
102
+ get(key) {
103
+ return JSON.parse(this.map.get(key) ?? 'null');
104
+ },
105
+ set(key, value) {
106
+ if (value == null) return this.delete(key);
107
+ this.map.set(key, JSON.stringify(value));
108
+ return true;
109
+ },
110
+ delete(key) {
111
+ return this.map.delete(key);
112
+ },
113
+ async *keys() {
114
+ for (const k of this.map.keys()) yield k;
115
+ },
116
+ async *values() {
117
+ for (const v of this.map.values()) yield JSON.parse(v);
118
+ },
119
+ async *entries() {
120
+ for (const [k, v] of this.map.entries()) yield [k, JSON.parse(v)];
121
+ },
122
+ clear() {
123
+ this.map.clear();
124
+ return true;
125
+ },
126
+ };
127
+ ```
128
+
129
+ ## Storage Layer
130
+
131
+ The client is completely storage-agnostic. You may use Redis, filesystem or in-memory persistence. The storage system must implement the `Store` interface.
132
+
133
+ ### Key structure (logical)
134
+
135
+ ```
136
+ account:{phone}:index → Account
137
+ account:{phone}:chat:{id}:index → Chat
138
+ account:{phone}:chat:{id}:message:{id}:index → Message
139
+ ```
140
+
141
+ ### Directory-style translation
142
+
143
+ ```
144
+ account/
145
+ └── {phone}/
146
+ ├── index
147
+ └── chat/
148
+ └── {id}/
149
+ ├── index
150
+ └── message/
151
+ └── {id}/
152
+ └── index
153
+ ```
154
+
155
+ > ✅ Esto permite separar metadatos de contenido, aplicar TTLs, y reducir lecturas innecesarias.
156
+
157
+ ### 2. Initialise the client
158
+
159
+ ```ts
160
+ const socket = new WhatsApp({
161
+ phone: '+584100000000',
162
+ loginType: 'code',
163
+ code: (pairCode) => console.log('Pair with:', pairCode),
164
+ store: MemoryStore,
165
+ });
166
+ ```
167
+
168
+ ### 3. Read chats & messages
169
+
170
+ ```ts
171
+ const chats = await socket.chats();
172
+ for (const chat of chats) {
173
+ console.log(`📨 ${chat.id} has ${await chat.messages().then((m) => m.length)} messages`);
174
+ }
175
+ ```
176
+
177
+ ### 4. Send your first message
178
+
179
+ ```ts
180
+ const [target] = chats;
181
+ await target.send('¡Hola Mundo!', { once: true });
182
+ ```
183
+
184
+ ---
185
+
186
+ ## Interfaces & Types
187
+
188
+ ### `IWhatsApp`
189
+
190
+ ```ts
191
+ interface IWhatsApp<T extends 'qr' | 'code'> {
192
+ phone: string;
193
+ store?: Store;
194
+ loginType: T;
195
+ code: T extends 'code' ? (code: string) => void : never;
196
+ qr: T extends 'qr' ? (buffer: Buffer) => void : never;
197
+ }
198
+ ```
199
+
200
+ | Field | Required | Description |
201
+ | ----------- | ------------- | ---------------------------------------------------------------------------------- |
202
+ | `phone` | ✔ | International format (`+5841…`). |
203
+ | `store` | ✖ | Backend persistence (defaults to in‑memory volatile store). |
204
+ | `loginType` | ✔ | `'qr'` or `'code'`. Determines which callback is required. |
205
+ | `code` | _Conditional_ | Fired once with the pairing **numeric code** when `loginType === 'code'`. |
206
+ | `qr` | _Conditional_ | Fired with a **Buffer JPG/PNG** containing the QR image when `loginType === 'qr'`. |
207
+
208
+ ### `Store`
209
+
210
+ Contract used everywhere the SDK needs persistence: creds, chats, media pointers… Full JSDoc in `src/types/Store.ts`.
211
+
212
+ ```ts
213
+ interface Store {
214
+ has(key: string): boolean | Promise<boolean>;
215
+ get(key: string): any | Promise<any>;
216
+ set(key: string, value: any): boolean | Promise<boolean>;
217
+ delete(key: string): boolean | Promise<boolean>;
218
+ keys(): AsyncGenerator<string>;
219
+ values(): AsyncGenerator<any>;
220
+ entries(): AsyncGenerator<[string, any]>;
221
+ clear(): boolean | Promise<boolean>;
222
+ scan?(pattern: string): string[] | Promise<string[]>;
223
+ }
224
+ ```
225
+
226
+ ---
227
+
228
+ ## API Reference
229
+
230
+ ### Chats
231
+
232
+ ```ts
233
+ socket.chats(): Promise<Chat[]>;
234
+ ```
235
+
236
+ | Method | Description |
237
+ | --------------------- | ---------------------------------------------------------------------- |
238
+ | `pin()` | Pin chat to top. |
239
+ | `mute()` / `unmute()` | Toggle notifications. |
240
+ | `seen()` | Mark as read. |
241
+ | `presence(state)` | Update own presence (`available`, `composing`, `recording`, `paused`). |
242
+ | `delete()` | Remove chat locally. |
243
+ | `messages()` | Fetch cached messages (lazy‑loaded). |
244
+
245
+ ### Messages
246
+
247
+ | Method | Description |
248
+ | ------------------- | ------------------------------------------ |
249
+ | `content()` | Returns payload: `string` or `Buffer`. |
250
+ | `reply(body, opts)` | Reply in thread. Supports all media types. |
251
+ | `seen()` | Mark as read. |
252
+ | `delete()` | Delete for everyone when possible. |
253
+ | `like(emoji)` | Simple reaction helper. |
254
+ | `forward(chatid)` | Forward to another chat. |
255
+
256
+ Return type fields:
257
+
258
+ ```ts
259
+ type MessageBase = {
260
+ id: string;
261
+ type: 'text' | 'image' | 'audio' | 'video' | 'location';
262
+ caption?: string;
263
+ once?: boolean;
264
+ ptt?: boolean; // push‑to‑talk
265
+ ptv?: boolean; // video‑note
266
+ };
267
+ ```
268
+
269
+ ### Presence
270
+
271
+ ```ts
272
+ await chat.presence('composing'); // typing…
273
+ ```
274
+
275
+ ### Media Helpers
276
+
277
+ All `send()`/`reply()` share the same overload signature:
278
+
279
+ ```ts
280
+ send(body: string | Buffer | { lat: number; lon: number }, opts?: SendOptions): Promise<Message>;
281
+
282
+ interface SendOptions {
283
+ type?: "audio" | "video" | "image" | "location";
284
+ caption?: string; // images
285
+ ptt?: boolean; // audio
286
+ ptv?: boolean; // video‑note
287
+ once?: boolean; // view‑once
288
+ }
289
+ ```
290
+
291
+ ---
292
+
293
+ ## Storage Back‑ends
294
+
295
+ ### In‑memory
296
+
297
+ Use the demo `MemoryStore` from the Zero‑to‑Hero section. Volatile.
298
+
299
+ ### File‑system
300
+
301
+ ```ts
302
+ import fs from 'node:fs/promises';
303
+
304
+ function FSStore(dir: string): Store {
305
+ /* … */
306
+ }
307
+ ```
308
+
309
+ Stores everything under `.cache/` exactly like the suggested tree.
310
+
311
+ ### Redis
312
+
313
+ ```ts
314
+ import { createClient } from 'redis';
315
+
316
+ function RedisStore(client = createClient()): Store {
317
+ /* … */
318
+ }
319
+ ```
320
+
321
+ Use `SCAN` for iteration and implement `scan(pattern)` via `KEYS`/`SCAN` glob.
322
+
323
+ ---
324
+
325
+ ## Recipes
326
+
327
+ ### Auto‑responder bot
328
+
329
+ ```ts
330
+ socket.on('message', async (msg) => {
331
+ if (msg.type === 'text' && msg.content().includes('ping')) {
332
+ await msg.reply('pong 🏓');
333
+ }
334
+ });
335
+ ```
336
+
337
+ ### Send location every hour
338
+
339
+ ```ts
340
+ setInterval(async () => {
341
+ await chat.send({ lat: 8.3014, lon: -62.7166 }, { type: 'location' });
342
+ }, 3.6e6);
343
+ ```
344
+
345
+ ---
346
+
347
+ ## Troubleshooting
348
+
349
+ | Error | Cause & Fix |
350
+ | ------------------------------- | -------------------------------------------------------------------------------------- |
351
+ | `401 – Session invalid` | Credentials expired → re‑authenticate (clear `store` keys for `auth:` prefix). |
352
+ | `ERR_PACKAGE_PATH_NOT_EXPORTED` | Make sure you import ESM build (`import …`). |
353
+ | `BaileysBoomError 428` | Connection closed by server – client will auto‑retry; ensure network clock is in sync. |
354
+
355
+ ---
356
+
357
+ ## Contributing
358
+
359
+ 1. Fork → branch → PR (conventional commits).
360
+ 2. `yarn lint && yarn test` must pass.
361
+ 3. Document new features in this README.
362
+
363
+ ---
364
+
365
+ ## License
366
+
367
+ MIT — © 2025 [Miguel Alejandro](https://github.com/arcaelas) / Arcaelas Insiders.
@@ -0,0 +1,217 @@
1
+ import { Noop } from '@arcaelas/utils';
2
+ import { type Boom } from '@hapi/boom';
3
+ import { Mutex } from 'async-mutex';
4
+ import * as Baileys from 'baileys';
5
+ import EventEmitter from 'node:events';
6
+ import Chat from './model/chat';
7
+ import Message from './model/message';
8
+ import Store, { Engine } from './static/Store';
9
+ interface IWhatsApp<T extends 'qr' | 'code'> {
10
+ phone: string;
11
+ store?: Engine;
12
+ loginType: T;
13
+ qr: T extends 'qr' ? Noop<[QR: string]> : never;
14
+ code: T extends 'code' ? Noop<[code: string]> : never;
15
+ }
16
+ interface EventMap {
17
+ error: [error: Error];
18
+ open: [];
19
+ close: [];
20
+ qr: [qr: string];
21
+ code: [code: string];
22
+ 'chat:created': [chat: Chat];
23
+ 'chat:updated': [chat: Chat];
24
+ 'chat:deleted': [id: string];
25
+ 'message:created': [message: Message];
26
+ 'message:updated': [message: Message];
27
+ 'message:deleted': [id: string];
28
+ }
29
+ /**
30
+ * @description
31
+ * Client for WhatsApp Web.
32
+ * @example
33
+ * const client = new WhatsApp({
34
+ * phone: '1234567890',
35
+ * loginType: 'qr',
36
+ * qr: (code) => console.log(code),
37
+ * });
38
+ */
39
+ export default class WhatsApp<T extends 'qr' | 'code'> extends EventEmitter<EventMap> {
40
+ protected readonly options: IWhatsApp<T>;
41
+ /**
42
+ * @description
43
+ * Socket for WhatsApp Web.
44
+ */
45
+ protected socket: Baileys.WASocket;
46
+ /**
47
+ * @description
48
+ * Store for WhatsApp Web.
49
+ */
50
+ protected store: Store;
51
+ /**
52
+ * @description
53
+ * Mutex, used to prevent concurrent access to the socket.
54
+ */
55
+ protected readonly mutex: Mutex;
56
+ constructor(options: IWhatsApp<T>);
57
+ /**
58
+ * @description
59
+ * Execute a function with the socket and store, means that the function will be executed only when the socket is ready.
60
+ * @param func Function to execute.
61
+ * @returns Result of the function.
62
+ */
63
+ tick<T extends Noop<[socket: Baileys.WASocket, store: Store], any>>(func: T): Promise<Awaited<ReturnType<T>>>;
64
+ documents(): Promise<Record<string, any>>;
65
+ chats(): Promise<Chat[]>;
66
+ messages(): Promise<Message[]>;
67
+ /**
68
+ * @description
69
+ * Connect to WhatsApp Web.
70
+ * @returns Promise that resolves to the socket.
71
+ */
72
+ protected connect(): Promise<{
73
+ logger: import("baileys/lib/Utils/logger").ILogger;
74
+ getOrderDetails: (orderId: string, tokenBase64: string) => Promise<import("baileys/lib/Types").OrderDetails>;
75
+ getCatalog: ({ jid, limit, cursor }: import("baileys/lib/Types").GetCatalogOptions) => Promise<{
76
+ products: import("baileys/lib/Types").Product[];
77
+ nextPageCursor: string | undefined;
78
+ }>;
79
+ getCollections: (jid?: string, limit?: number) => Promise<{
80
+ collections: import("baileys/lib/Types").CatalogCollection[];
81
+ }>;
82
+ productCreate: (create: import("baileys/lib/Types").ProductCreate) => Promise<import("baileys/lib/Types").Product>;
83
+ productDelete: (productIds: string[]) => Promise<{
84
+ deleted: number;
85
+ }>;
86
+ productUpdate: (productId: string, update: import("baileys/lib/Types").ProductUpdate) => Promise<import("baileys/lib/Types").Product>;
87
+ sendMessageAck: ({ tag, attrs, content }: import("baileys").BinaryNode, errorCode?: number) => Promise<void>;
88
+ sendRetryRequest: (node: import("baileys").BinaryNode, forceIncludeKeys?: boolean) => Promise<void>;
89
+ rejectCall: (callId: string, callFrom: string) => Promise<void>;
90
+ fetchMessageHistory: (count: number, oldestMsgKey: import("baileys/lib/Types").WAMessageKey, oldestMsgTimestamp: number | import("long").default) => Promise<string>;
91
+ requestPlaceholderResend: (messageKey: import("baileys/lib/Types").WAMessageKey) => Promise<string | undefined>;
92
+ getPrivacyTokens: (jids: string[]) => Promise<any>;
93
+ assertSessions: (jids: string[], force: boolean) => Promise<boolean>;
94
+ relayMessage: (jid: string, message: import("baileys/lib/Types").WAProto.IMessage, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, useCachedGroupMetadata, statusJidList }: import("baileys/lib/Types").MessageRelayOptions) => Promise<string>;
95
+ sendReceipt: (jid: string, participant: string | undefined, messageIds: string[], type: import("baileys/lib/Types").MessageReceiptType) => Promise<void>;
96
+ sendReceipts: (keys: import("baileys/lib/Types").WAMessageKey[], type: import("baileys/lib/Types").MessageReceiptType) => Promise<void>;
97
+ readMessages: (keys: import("baileys/lib/Types").WAMessageKey[]) => Promise<void>;
98
+ refreshMediaConn: (forceGet?: boolean) => Promise<import("baileys/lib/Types").MediaConnInfo>;
99
+ waUploadToServer: import("baileys/lib/Types").WAMediaUploadFunction;
100
+ fetchPrivacySettings: (force?: boolean) => Promise<{
101
+ [_: string]: string;
102
+ }>;
103
+ sendPeerDataOperationMessage: (pdoMessage: import("baileys/lib/Types").WAProto.Message.IPeerDataOperationRequestMessage) => Promise<string>;
104
+ createParticipantNodes: (jids: string[], message: import("baileys/lib/Types").WAProto.IMessage, extraAttrs?: import("baileys").BinaryNode["attrs"]) => Promise<{
105
+ nodes: import("baileys").BinaryNode[];
106
+ shouldIncludeDeviceIdentity: boolean;
107
+ }>;
108
+ getUSyncDevices: (jids: string[], useCache: boolean, ignoreZeroDevices: boolean) => Promise<import("baileys").JidWithDevice[]>;
109
+ updateMediaMessage: (message: import("baileys/lib/Types").WAProto.IWebMessageInfo) => Promise<import("baileys/lib/Types").WAProto.IWebMessageInfo>;
110
+ sendMessage: (jid: string, content: import("baileys/lib/Types").AnyMessageContent, options?: import("baileys/lib/Types").MiscMessageGenerationOptions) => Promise<import("baileys/lib/Types").WAProto.WebMessageInfo | undefined>;
111
+ groupMetadata: (jid: string) => Promise<import("baileys/lib/Types").GroupMetadata>;
112
+ groupCreate: (subject: string, participants: string[]) => Promise<import("baileys/lib/Types").GroupMetadata>;
113
+ groupLeave: (id: string) => Promise<void>;
114
+ groupUpdateSubject: (jid: string, subject: string) => Promise<void>;
115
+ groupRequestParticipantsList: (jid: string) => Promise<{
116
+ [key: string]: string;
117
+ }[]>;
118
+ groupRequestParticipantsUpdate: (jid: string, participants: string[], action: "approve" | "reject") => Promise<{
119
+ status: string;
120
+ jid: string;
121
+ }[]>;
122
+ groupParticipantsUpdate: (jid: string, participants: string[], action: import("baileys/lib/Types").ParticipantAction) => Promise<{
123
+ status: string;
124
+ jid: string;
125
+ content: import("baileys").BinaryNode;
126
+ }[]>;
127
+ groupUpdateDescription: (jid: string, description?: string) => Promise<void>;
128
+ groupInviteCode: (jid: string) => Promise<string | undefined>;
129
+ groupRevokeInvite: (jid: string) => Promise<string | undefined>;
130
+ groupAcceptInvite: (code: string) => Promise<string | undefined>;
131
+ groupRevokeInviteV4: (groupJid: string, invitedJid: string) => Promise<boolean>;
132
+ groupAcceptInviteV4: (key: string | import("baileys/lib/Types").WAProto.IMessageKey, inviteMessage: import("baileys/lib/Types").WAProto.Message.IGroupInviteMessage) => Promise<any>;
133
+ groupGetInviteInfo: (code: string) => Promise<import("baileys/lib/Types").GroupMetadata>;
134
+ groupToggleEphemeral: (jid: string, ephemeralExpiration: number) => Promise<void>;
135
+ groupSettingUpdate: (jid: string, setting: "announcement" | "not_announcement" | "locked" | "unlocked") => Promise<void>;
136
+ groupMemberAddMode: (jid: string, mode: "admin_add" | "all_member_add") => Promise<void>;
137
+ groupJoinApprovalMode: (jid: string, mode: "on" | "off") => Promise<void>;
138
+ groupFetchAllParticipating: () => Promise<{
139
+ [_: string]: import("baileys/lib/Types").GroupMetadata;
140
+ }>;
141
+ getBotListV2: () => Promise<import("baileys/lib/Types").BotListInfo[]>;
142
+ processingMutex: {
143
+ mutex<T_1>(code: () => Promise<T_1> | T_1): Promise<T_1>;
144
+ };
145
+ upsertMessage: (msg: import("baileys/lib/Types").WAProto.IWebMessageInfo, type: import("baileys/lib/Types").MessageUpsertType) => Promise<void>;
146
+ appPatch: (patchCreate: import("baileys/lib/Types").WAPatchCreate) => Promise<void>;
147
+ sendPresenceUpdate: (type: import("baileys/lib/Types").WAPresence, toJid?: string) => Promise<void>;
148
+ presenceSubscribe: (toJid: string, tcToken?: Buffer) => Promise<void>;
149
+ profilePictureUrl: (jid: string, type?: "preview" | "image", timeoutMs?: number) => Promise<string | undefined>;
150
+ onWhatsApp: (...jids: string[]) => Promise<{
151
+ jid: string;
152
+ exists: unknown;
153
+ lid: unknown;
154
+ }[] | undefined>;
155
+ fetchBlocklist: () => Promise<string[]>;
156
+ fetchStatus: (...jids: string[]) => Promise<import("baileys").USyncQueryResultList[] | undefined>;
157
+ fetchDisappearingDuration: (...jids: string[]) => Promise<import("baileys").USyncQueryResultList[] | undefined>;
158
+ updateProfilePicture: (jid: string, content: import("baileys/lib/Types").WAMediaUpload) => Promise<void>;
159
+ removeProfilePicture: (jid: string) => Promise<void>;
160
+ updateProfileStatus: (status: string) => Promise<void>;
161
+ updateProfileName: (name: string) => Promise<void>;
162
+ updateBlockStatus: (jid: string, action: "block" | "unblock") => Promise<void>;
163
+ updateCallPrivacy: (value: import("baileys/lib/Types").WAPrivacyCallValue) => Promise<void>;
164
+ updateMessagesPrivacy: (value: import("baileys/lib/Types").WAPrivacyMessagesValue) => Promise<void>;
165
+ updateLastSeenPrivacy: (value: import("baileys/lib/Types").WAPrivacyValue) => Promise<void>;
166
+ updateOnlinePrivacy: (value: import("baileys/lib/Types").WAPrivacyOnlineValue) => Promise<void>;
167
+ updateProfilePicturePrivacy: (value: import("baileys/lib/Types").WAPrivacyValue) => Promise<void>;
168
+ updateStatusPrivacy: (value: import("baileys/lib/Types").WAPrivacyValue) => Promise<void>;
169
+ updateReadReceiptsPrivacy: (value: import("baileys/lib/Types").WAReadReceiptsValue) => Promise<void>;
170
+ updateGroupsAddPrivacy: (value: import("baileys/lib/Types").WAPrivacyGroupAddValue) => Promise<void>;
171
+ updateDefaultDisappearingMode: (duration: number) => Promise<void>;
172
+ getBusinessProfile: (jid: string) => Promise<import("baileys/lib/Types").WABusinessProfile | void>;
173
+ resyncAppState: (collections: readonly ("critical_block" | "critical_unblock_low" | "regular_high" | "regular_low" | "regular")[], isInitialSync: boolean) => Promise<void>;
174
+ chatModify: (mod: import("baileys/lib/Types").ChatModification, jid: string) => Promise<void>;
175
+ cleanDirtyBits: (type: "account_sync" | "groups", fromTimestamp?: number | string) => Promise<void>;
176
+ addLabel: (jid: string, labels: import("baileys/lib/Types/Label").LabelActionBody) => Promise<void>;
177
+ addChatLabel: (jid: string, labelId: string) => Promise<void>;
178
+ removeChatLabel: (jid: string, labelId: string) => Promise<void>;
179
+ addMessageLabel: (jid: string, messageId: string, labelId: string) => Promise<void>;
180
+ removeMessageLabel: (jid: string, messageId: string, labelId: string) => Promise<void>;
181
+ star: (jid: string, messages: {
182
+ id: string;
183
+ fromMe?: boolean;
184
+ }[], star: boolean) => Promise<void>;
185
+ executeUSyncQuery: (usyncQuery: import("baileys").USyncQuery) => Promise<import("baileys").USyncQueryResult | undefined>;
186
+ type: "md";
187
+ ws: import("baileys/lib/Socket/Client").WebSocketClient;
188
+ ev: import("baileys/lib/Types").BaileysEventEmitter & {
189
+ process(handler: (events: Partial<import("baileys/lib/Types").BaileysEventMap>) => void | Promise<void>): (() => void);
190
+ buffer(): void;
191
+ createBufferedFunction<A extends any[], T_1>(work: (...args: A) => Promise<T_1>): ((...args: A) => Promise<T_1>);
192
+ flush(force?: boolean): boolean;
193
+ isBuffering(): boolean;
194
+ };
195
+ authState: {
196
+ creds: import("baileys/lib/Types").AuthenticationCreds;
197
+ keys: import("baileys/lib/Types").SignalKeyStoreWithTransaction;
198
+ };
199
+ signalRepository: import("baileys/lib/Types").SignalRepository;
200
+ user: import("baileys/lib/Types").Contact | undefined;
201
+ generateMessageTag: () => string;
202
+ query: (node: import("baileys").BinaryNode, timeoutMs?: number) => Promise<any>;
203
+ waitForMessage: <T_1>(msgId: string, timeoutMs?: number | undefined) => Promise<any>;
204
+ waitForSocketOpen: () => Promise<void>;
205
+ sendRawMessage: (data: Uint8Array | Buffer) => Promise<void>;
206
+ sendNode: (frame: import("baileys").BinaryNode) => Promise<void>;
207
+ logout: (msg?: string) => Promise<void>;
208
+ end: (error: Error | undefined) => void;
209
+ onUnexpectedError: (err: Error | import("@hapi/boom").Boom, msg: string) => void;
210
+ uploadPreKeys: (count?: number) => Promise<void>;
211
+ uploadPreKeysToServerIfRequired: () => Promise<void>;
212
+ requestPairingCode: (phoneNumber: string) => Promise<string>;
213
+ waitForConnectionUpdate: (check: (u: Partial<import("baileys/lib/Types").ConnectionState>) => Promise<boolean | undefined>, timeoutMs?: number) => Promise<void>;
214
+ sendWAMBuffer: (wamBuffer: Buffer) => Promise<any>;
215
+ }>;
216
+ }
217
+ export {};
package/build/index.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";var B=Object.create;var l=Object.defineProperty;var M=Object.getOwnPropertyDescriptor;var C=Object.getOwnPropertyNames;var A=Object.getPrototypeOf,W=Object.prototype.hasOwnProperty;var x=(o,r)=>{for(var t in r)l(o,t,{get:r[t],enumerable:!0})},y=(o,r,t,i)=>{if(r&&typeof r=="object"||typeof r=="function")for(let s of C(r))!W.call(o,s)&&s!==t&&l(o,s,{get:()=>r[s],enumerable:!(i=M(r,s))||i.enumerable});return o};var p=(o,r,t)=>(t=o!=null?B(A(o)):{},y(r||!o||!o.__esModule?l(t,"default",{value:o,enumerable:!0}):t,o)),P=o=>y(l({},"__esModule",{value:!0}),o);var q={};x(q,{default:()=>T});module.exports=P(q);var h=require("@arcaelas/utils"),w=require("async-mutex"),m=p(require("baileys")),f=p(require("node:events")),u=p(require("./model/chat")),g=p(require("./model/message")),k=p(require("./static/Store")),S=p(require("./static/useCache"));class T extends f.default{constructor(t){super({captureRejections:!0});this.options=t;this.store=new Map;this.mutex=new w.Mutex;this.on("error",i=>{this.listenerCount("error")===1&&console.error(i)}),this.store=new k.default(this.options.store??new Map),this.connect()}async tick(t){return this.mutex.acquire().then(()=>t(this.socket,this.store))}async documents(){return await this.tick(async(t,i)=>{const s={};for await(const n of i.document.values())s[n.key]=n.value;return s})}async chats(){return await this.tick(async(t,i)=>{const s=[];for await(const n of i.chat.values())s.push(new u.default(this,n));return s})}async messages(){return await this.tick(async(t,i)=>{const s=[];for await(const n of i.message.values())s.push(new g.default(this,n));return s})}async connect(){const t=(0,h.promify)(),{state:i,save:s}=await(0,S.default)(this.store),{version:n}=await m.fetchLatestBaileysVersion();if(this.socket=m.makeWASocket({version:n,syncFullHistory:!0,browser:m.Browsers.macOS("Descktop"),auth:{creds:i.creds,keys:m.makeCacheableSignalKeyStore(i.keys)},getMessage:async a=>{const e=await this.store.message.get(a.id);return e?e.message:void 0}}),this.socket.ev.process(async a=>{if(a["creds.update"]&&await s(),a["connection.update"]){const{connection:e,lastDisconnect:c}=a["connection.update"];if(e==="open")t.resolve(this.socket);else if(e==="close"&&c?.error?.output?.statusCode!==m.DisconnectReason.loggedOut){t.resolve(await this.connect());return}}await Promise.allSettled([].concat(a["messaging-history.set"]?.chats||[],a["chats.upsert"]||[]).map(async e=>{await this.store.chat.set(e),this.emit("chat:created",new u.default(this,e))})),await Promise.allSettled([].concat(a["chats.update"]||[]).map(async e=>{const c=await this.store.chat.get(e.id);if(c!==null){const d=(0,h.merge)(c,e);await this.store.chat.set(d),this.emit("chat:updated",new u.default(this,d))}}));for(const e of a["chats.delete"]??[])await this.store.chat.delete(e),this.emit("chat:deleted",e);await Promise.allSettled([].concat(a["messages.upsert"]?.messages||[]).map(async e=>{await this.store.message.set(e),this.emit("message:created",new g.default(this,e))})),await Promise.allSettled([].concat(a["messages.update"]||[]).map(async e=>{const c=await this.store.message.get(e.key.id);if(c!==null){const d=(0,h.merge)(c,e);await this.store.message.set(d),this.emit("message:updated",new g.default(this,d))}}))}),await this.socket.waitForSocketOpen(),await(0,h.sleep)(3e3),this.socket.authState.creds.registered)t.resolve(this.socket);else if(this.options.loginType==="code")await this.options.code(await this.socket.requestPairingCode(this.options.phone));else if(this.options.loginType==="qr"){const a=(0,h.promify)(),e=setTimeout(()=>a.reject("QR Timeout"),6e4);this.socket.ev.on("connection.update",async({qr:c})=>{if(c){clearTimeout(e);try{await this.options.qr(c),a.resolve(c)}catch(d){a.reject(d)}}}),await a}return t}}
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts"],
4
+ "sourcesContent": ["import { merge, Noop, promify, sleep } from '@arcaelas/utils';\nimport { type Boom } from '@hapi/boom';\nimport { Mutex } from 'async-mutex';\nimport * as Baileys from 'baileys';\nimport EventEmitter from 'node:events';\nimport Chat from './model/chat';\nimport Message from './model/message';\nimport Store, { Engine } from './static/Store';\nimport useCache from './static/useCache';\n\ninterface IWhatsApp<T extends 'qr' | 'code'> {\n phone: string;\n store?: Engine;\n loginType: T;\n qr: T extends 'qr' ? Noop<[QR: string]> : never;\n code: T extends 'code' ? Noop<[code: string]> : never;\n}\n\ninterface EventMap {\n error: [error: Error]\n open: []\n close: []\n qr: [qr: string]\n code: [code: string]\n 'chat:created': [chat: Chat]\n 'chat:updated': [chat: Chat]\n 'chat:deleted': [id: string]\n 'message:created': [message: Message]\n 'message:updated': [message: Message]\n 'message:deleted': [id: string]\n}\n\n/**\n * @description\n * Client for WhatsApp Web.\n * @example\n * const client = new WhatsApp({\n * phone: '1234567890',\n * loginType: 'qr',\n * qr: (code) => console.log(code),\n * });\n */\nexport default class WhatsApp<T extends 'qr' | 'code'> extends EventEmitter<EventMap> {\n /**\n * @description\n * Socket for WhatsApp Web.\n */\n protected socket: Baileys.WASocket;\n /**\n * @description\n * Store for WhatsApp Web.\n */\n protected store: Store = new Map() as any;\n /**\n * @description\n * Mutex, used to prevent concurrent access to the socket.\n */\n protected readonly mutex = new Mutex();\n\n constructor(protected readonly options: IWhatsApp<T>) {\n super({ captureRejections: true });\n this.on('error', (error) => {\n if (this.listenerCount('error') === 1) {\n console.error(error);\n }\n });\n this.store = new Store(this.options.store ?? new Map() as any);\n this.connect();\n }\n\n /**\n * @description\n * Execute a function with the socket and store, means that the function will be executed only when the socket is ready.\n * @param func Function to execute.\n * @returns Result of the function.\n */\n async tick<T extends Noop<[socket: Baileys.WASocket, store: Store], any>>(func: T): Promise<Awaited<ReturnType<T>>> {\n return this.mutex.acquire().then(() => func(this.socket, this.store));\n }\n\n async documents(): Promise<Record<string, any>> {\n return await this.tick(async (_, store) => {\n const documents: Record<string, any> = {}\n for await (const document of store.document.values()) {\n documents[document.key] = document.value;\n }\n return documents;\n });\n }\n\n async chats(): Promise<Chat[]> {\n return await this.tick(async (_, store) => {\n const chats: Chat[] = [];\n for await (const chat of store.chat.values()) {\n chats.push(new Chat(this, chat));\n }\n return chats;\n });\n }\n\n async messages(): Promise<Message[]> {\n return await this.tick(async (_, store) => {\n const messages: Message[] = [];\n for await (const message of store.message.values()) {\n messages.push(new Message(this, message));\n }\n return messages;\n });\n }\n\n /**\n * @description\n * Connect to WhatsApp Web.\n * @returns Promise that resolves to the socket.\n */\n protected async connect() {\n const promise = promify<Baileys.WASocket>();\n const { state, save } = await useCache(this.store);\n const { version } = await Baileys.fetchLatestBaileysVersion();\n this.socket = Baileys.makeWASocket({\n version,\n syncFullHistory: true,\n browser: Baileys.Browsers.macOS('Descktop'),\n auth: {\n creds: state.creds,\n keys: Baileys.makeCacheableSignalKeyStore(state.keys),\n },\n getMessage: async (key) => {\n const message = await this.store.message.get(key.id!);\n return message ? message.message! : undefined;\n },\n });\n this.socket.ev.process(async (event) => {\n if (event['creds.update']) {\n await save();\n }\n if (event['connection.update']) {\n const { connection, lastDisconnect } = event['connection.update'];\n if (connection === 'open') promise.resolve(this.socket);\n else if (connection === 'close' && (lastDisconnect?.error as Boom)?.output?.statusCode !== Baileys.DisconnectReason.loggedOut) {\n promise.resolve(await this.connect());\n return\n }\n }\n // prettier-ignore\n await Promise.allSettled(\n ([] as Baileys.Chat[])\n .concat(event['messaging-history.set']?.chats || [], event['chats.upsert'] || [])\n .map(async chat => {\n await this.store.chat.set(chat);\n this.emit('chat:created', new Chat(this, chat));\n })\n );\n // prettier-ignore\n await Promise.allSettled(\n ([] as Baileys.ChatUpdate[])\n .concat(event['chats.update'] || [])\n .map(async update => {\n const payload = await this.store.chat.get(update.id!);\n if (payload !== null) {\n const chat = merge(payload, update) as Baileys.Chat;\n await this.store.chat.set(chat);\n this.emit('chat:updated', new Chat(this, chat));\n }\n })\n );\n for (const key of (event['chats.delete'] ?? []) as string[]) {\n await this.store.chat.delete(key);\n this.emit('chat:deleted', key);\n }\n // prettier-ignore\n await Promise.allSettled(\n ([] as Baileys.WAMessage[])\n .concat(event['messages.upsert']?.messages || [])\n .map(async message => {\n await this.store.message.set(message);\n this.emit('message:created', new Message(this, message));\n })\n );\n // prettier-ignore\n await Promise.allSettled(\n ([] as Baileys.WAMessageUpdate[])\n .concat(event['messages.update'] || [])\n .map(async message => {\n const payload = await this.store.message.get(message.key.id!);\n if (payload !== null) {\n const chat = merge(payload, message) as Baileys.WAProto.WebMessageInfo;\n await this.store.message.set(chat);\n this.emit('message:updated', new Message(this, chat));\n }\n })\n );\n // TODO: Handle messages.delete\n })\n\n await this.socket.waitForSocketOpen();\n await sleep(3000);\n\n if (this.socket.authState.creds.registered) {\n promise.resolve(this.socket);\n } else if (this.options.loginType === 'code') {\n await this.options.code(\n await this.socket.requestPairingCode(this.options.phone)\n );\n } else if (this.options.loginType === 'qr') {\n const _qr = promify<string>();\n const timeout = setTimeout(() => _qr.reject('QR Timeout'), 60000);\n this.socket.ev.on('connection.update', async ({ qr }) => {\n if (qr) {\n clearTimeout(timeout);\n try {\n await this.options.qr(qr);\n _qr.resolve(qr);\n } catch (error) {\n _qr.reject(error);\n }\n }\n });\n await _qr;\n }\n return promise;\n }\n}"],
5
+ "mappings": "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",
6
+ "names": ["src_exports", "__export", "WhatsApp", "__toCommonJS", "import_utils", "import_async_mutex", "Baileys", "import_node_events", "import_chat", "import_message", "import_Store", "import_useCache", "EventEmitter", "options", "error", "Store", "func", "_", "store", "documents", "document", "chats", "chat", "Chat", "messages", "message", "Message", "promise", "state", "save", "useCache", "version", "key", "event", "connection", "lastDisconnect", "update", "payload", "_qr", "timeout", "qr"]
7
+ }
@@ -0,0 +1,13 @@
1
+ import { Noop } from '@arcaelas/utils';
2
+ import WhatsApp from 'src';
3
+ type Serialize<T> = {
4
+ [K in keyof T as T[K] extends Noop ? never : K]: T[K];
5
+ };
6
+ export default class Base<T> {
7
+ protected readonly $: WhatsApp<'code' | 'qr'>;
8
+ protected readonly _: Serialize<T>;
9
+ constructor($: WhatsApp<'code' | 'qr'>, _: Serialize<T>);
10
+ toJSON(): any;
11
+ toString(): string;
12
+ }
13
+ export {};