@arcaelas/whatsapp 4.5.0 → 6.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/README.md +282 -239
- package/build/cjs/decorators.js +0 -1
- package/build/cjs/index.d.ts +6 -4
- package/build/cjs/index.js +22 -3
- package/build/cjs/lib/bot/decorator.d.ts +2 -0
- package/build/cjs/lib/bot/decorator.js +6 -1
- package/build/cjs/lib/bot/decorators.d.ts +2 -2
- package/build/cjs/lib/bot/decorators.js +4 -4
- package/build/cjs/lib/bot/index.js +0 -1
- package/build/cjs/lib/chat/index.d.ts +381 -204
- package/build/cjs/lib/chat/index.js +243 -212
- package/build/cjs/lib/contact/index.d.ts +162 -179
- package/build/cjs/lib/contact/index.js +96 -191
- package/build/cjs/lib/internal.d.ts +40 -0
- package/build/cjs/lib/internal.js +38 -0
- package/build/cjs/lib/message/index.d.ts +468 -289
- package/build/cjs/lib/message/index.js +871 -743
- package/build/cjs/lib/status/index.d.ts +60 -70
- package/build/cjs/lib/status/index.js +106 -112
- package/build/cjs/lib/store/engine/index.d.ts +35 -7
- package/build/cjs/lib/store/engine/index.js +6 -4
- package/build/cjs/lib/store/engine/lib/file_system/index.d.ts +48 -8
- package/build/cjs/lib/store/engine/lib/file_system/index.js +117 -66
- package/build/cjs/lib/store/engine/lib/index.d.ts +112 -0
- package/build/cjs/lib/store/engine/lib/index.js +212 -0
- package/build/cjs/lib/store/engine/lib/redis/index.d.ts +44 -15
- package/build/cjs/lib/store/engine/lib/redis/index.js +75 -43
- package/build/cjs/lib/store/engine/lib/s3/index.d.ts +44 -1
- package/build/cjs/lib/store/engine/lib/s3/index.js +112 -65
- package/build/cjs/lib/store/engine/lib/sqlite/index.d.ts +110 -0
- package/build/cjs/lib/store/engine/lib/sqlite/index.js +132 -0
- package/build/cjs/lib/store/index.d.ts +7 -3
- package/build/cjs/lib/store/index.js +14 -5
- package/build/cjs/lib/whatsapp/index.d.ts +106 -68
- package/build/cjs/lib/whatsapp/index.js +393 -167
- package/build/esm/decorators.js +0 -1
- package/build/esm/index.d.ts +6 -4
- package/build/esm/index.js +5 -3
- package/build/esm/lib/bot/decorator.d.ts +2 -0
- package/build/esm/lib/bot/decorator.js +6 -1
- package/build/esm/lib/bot/decorators.d.ts +2 -2
- package/build/esm/lib/bot/decorators.js +4 -4
- package/build/esm/lib/bot/index.js +0 -1
- package/build/esm/lib/chat/index.d.ts +381 -204
- package/build/esm/lib/chat/index.js +243 -212
- package/build/esm/lib/contact/index.d.ts +162 -179
- package/build/esm/lib/contact/index.js +96 -190
- package/build/esm/lib/internal.d.ts +40 -0
- package/build/esm/lib/internal.js +34 -0
- package/build/esm/lib/message/index.d.ts +468 -289
- package/build/esm/lib/message/index.js +866 -738
- package/build/esm/lib/status/index.d.ts +60 -70
- package/build/esm/lib/status/index.js +105 -111
- package/build/esm/lib/store/engine/index.d.ts +35 -7
- package/build/esm/lib/store/engine/index.js +4 -3
- package/build/esm/lib/store/engine/lib/file_system/index.d.ts +48 -8
- package/build/esm/lib/store/engine/lib/file_system/index.js +117 -66
- package/build/esm/lib/store/engine/lib/index.d.ts +112 -0
- package/build/esm/lib/store/engine/lib/index.js +205 -0
- package/build/esm/lib/store/engine/lib/redis/index.d.ts +44 -15
- package/build/esm/lib/store/engine/lib/redis/index.js +73 -41
- package/build/esm/lib/store/engine/lib/s3/index.d.ts +44 -1
- package/build/esm/lib/store/engine/lib/s3/index.js +111 -64
- package/build/esm/lib/store/engine/lib/sqlite/index.d.ts +110 -0
- package/build/esm/lib/store/engine/lib/sqlite/index.js +128 -0
- package/build/esm/lib/store/index.d.ts +7 -3
- package/build/esm/lib/store/index.js +13 -5
- package/build/esm/lib/whatsapp/index.d.ts +106 -68
- package/build/esm/lib/whatsapp/index.js +395 -169
- package/package.json +25 -6
- package/build/cjs/decorators.js.map +0 -1
- package/build/cjs/index.js.map +0 -1
- package/build/cjs/lib/bot/decorator.js.map +0 -1
- package/build/cjs/lib/bot/decorators.js.map +0 -1
- package/build/cjs/lib/bot/index.js.map +0 -1
- package/build/cjs/lib/chat/index.js.map +0 -1
- package/build/cjs/lib/contact/index.js.map +0 -1
- package/build/cjs/lib/message/index.js.map +0 -1
- package/build/cjs/lib/status/index.js.map +0 -1
- package/build/cjs/lib/store/engine/index.js.map +0 -1
- package/build/cjs/lib/store/engine/lib/file_system/index.js.map +0 -1
- package/build/cjs/lib/store/engine/lib/redis/index.js.map +0 -1
- package/build/cjs/lib/store/engine/lib/s3/index.js.map +0 -1
- package/build/cjs/lib/store/index.js.map +0 -1
- package/build/cjs/lib/whatsapp/index.js.map +0 -1
- package/build/cjs/test.d.ts +0 -1
- package/build/cjs/test.js +0 -71
- package/build/cjs/test.js.map +0 -1
- package/build/esm/decorators.js.map +0 -1
- package/build/esm/index.js.map +0 -1
- package/build/esm/lib/bot/decorator.js.map +0 -1
- package/build/esm/lib/bot/decorators.js.map +0 -1
- package/build/esm/lib/bot/index.js.map +0 -1
- package/build/esm/lib/chat/index.js.map +0 -1
- package/build/esm/lib/contact/index.js.map +0 -1
- package/build/esm/lib/message/index.js.map +0 -1
- package/build/esm/lib/status/index.js.map +0 -1
- package/build/esm/lib/store/engine/index.js.map +0 -1
- package/build/esm/lib/store/engine/lib/file_system/index.js.map +0 -1
- package/build/esm/lib/store/engine/lib/redis/index.js.map +0 -1
- package/build/esm/lib/store/engine/lib/s3/index.js.map +0 -1
- package/build/esm/lib/store/index.js.map +0 -1
- package/build/esm/lib/whatsapp/index.js.map +0 -1
- package/build/esm/test.d.ts +0 -1
- package/build/esm/test.js +0 -66
- package/build/esm/test.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,362 +2,405 @@
|
|
|
2
2
|
|
|
3
3
|
# @arcaelas/whatsapp
|
|
4
4
|
|
|
5
|
-
>
|
|
5
|
+
> Cliente de WhatsApp para Node.js sobre **baileys**, con persistencia intercambiable.
|
|
6
6
|
>
|
|
7
|
-
>
|
|
7
|
+
> _TypeScript de punta a punta · Entidades con getters puros · Motores enchufables · DSL de decoradores_
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
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/
|
|
12
|
-
<img src="https://img.shields.io/github/license/arcaelas/whatsapp" alt="MIT">
|
|
11
|
+
<img src="https://img.shields.io/badge/node-%E2%89%A520-339933" alt="node >=20">
|
|
13
12
|
</p>
|
|
14
13
|
|
|
15
14
|
---
|
|
16
15
|
|
|
17
|
-
##
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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)
|
|
16
|
+
## Contenido
|
|
17
|
+
|
|
18
|
+
- [Instalación](#instalación)
|
|
19
|
+
- [Primeros pasos](#primeros-pasos)
|
|
20
|
+
- [Cliente](#cliente)
|
|
21
|
+
- [Entidades](#entidades)
|
|
22
|
+
- [Contact](#contact)
|
|
23
|
+
- [Chat](#chat)
|
|
24
|
+
- [Message](#message)
|
|
25
|
+
- [Feed](#feed)
|
|
26
|
+
- [Eventos](#eventos)
|
|
27
|
+
- [Motores de persistencia](#motores-de-persistencia)
|
|
28
|
+
- [Bots con decoradores](#bots-con-decoradores)
|
|
29
|
+
- [Recetas](#recetas)
|
|
30
|
+
- [Licencia](#licencia)
|
|
50
31
|
|
|
51
32
|
---
|
|
52
33
|
|
|
53
|
-
##
|
|
34
|
+
## Instalación
|
|
54
35
|
|
|
55
36
|
```bash
|
|
56
|
-
# core package
|
|
57
37
|
yarn add @arcaelas/whatsapp
|
|
58
38
|
```
|
|
59
39
|
|
|
60
|
-
|
|
40
|
+
Node 20 o superior. El paquete se distribuye en ESM y CJS.
|
|
41
|
+
|
|
42
|
+
**Peers opcionales**, solo si usás la función correspondiente:
|
|
43
|
+
|
|
44
|
+
| Paquete | Necesario para |
|
|
45
|
+
| --- | --- |
|
|
46
|
+
| `@aws-sdk/client-s3` | `S3Engine` |
|
|
47
|
+
| `sharp` o `jimp` | `wa.profile({ photo })` |
|
|
48
|
+
|
|
49
|
+
`RedisEngine` y `SQLiteEngine` no necesitan nada: reciben el cliente ya construido, así que servís vos el `ioredis`, `better-sqlite3` o `node:sqlite` que prefieras.
|
|
61
50
|
|
|
62
51
|
---
|
|
63
52
|
|
|
64
|
-
##
|
|
53
|
+
## Primeros pasos
|
|
65
54
|
|
|
66
55
|
```ts
|
|
67
|
-
import WhatsApp from '@arcaelas/whatsapp';
|
|
68
|
-
import qrcode from 'qrcode-terminal';
|
|
56
|
+
import WhatsApp, { FileSystemEngine } from '@arcaelas/whatsapp';
|
|
69
57
|
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
qr: (buffer) => qrcode.generate(buffer.toString('base64'), { small: true }),
|
|
58
|
+
const wa = new WhatsApp({
|
|
59
|
+
engine: new FileSystemEngine('.sessions/584144709840'),
|
|
60
|
+
phone: 584144709840,
|
|
74
61
|
});
|
|
75
62
|
|
|
76
|
-
|
|
63
|
+
wa.on('message:created', async (msg) => {
|
|
64
|
+
if (!msg.me && msg.caption === 'ping') {
|
|
65
|
+
await msg.text('pong 🏓');
|
|
66
|
+
}
|
|
67
|
+
});
|
|
77
68
|
|
|
78
|
-
|
|
79
|
-
await
|
|
69
|
+
// Con `phone` llega un PIN (string); sin `phone`, el QR como Buffer PNG.
|
|
70
|
+
await wa.connect((auth) => {
|
|
71
|
+
console.log(typeof auth === 'string' ? `PIN: ${auth}` : 'QR listo');
|
|
72
|
+
});
|
|
80
73
|
```
|
|
81
74
|
|
|
82
75
|
---
|
|
83
76
|
|
|
84
|
-
##
|
|
77
|
+
## Cliente
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
new WhatsApp({ engine, phone?, method?, autoclean?, reconnect?, sync? })
|
|
81
|
+
```
|
|
85
82
|
|
|
86
|
-
|
|
83
|
+
| Opción | Descripción |
|
|
84
|
+
| --- | --- |
|
|
85
|
+
| `engine` | Motor de persistencia. Único obligatorio. |
|
|
86
|
+
| `phone` | Teléfono de la cuenta. Habilita el emparejamiento por PIN; sin él la vinculación es siempre por QR. |
|
|
87
|
+
| `method` | `'otp'` (default) o `'qr'`. **Solo aplica cuando hay `phone`**. |
|
|
88
|
+
| `autoclean` | Al recibir `loggedOut`: `true` (default) vacía el motor; `false` borra solo las credenciales. |
|
|
89
|
+
| `reconnect` | `true` (default) reintenta indefinidamente cada 60 s. Acepta `false`, un número de intentos o `{ max, interval }`. |
|
|
90
|
+
| `sync` | Descarga el historial completo al vincular (default `true`). |
|
|
87
91
|
|
|
88
|
-
|
|
92
|
+
### Superficie
|
|
89
93
|
|
|
90
94
|
```ts
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
},
|
|
106
|
-
delete(key) {
|
|
107
|
-
return this.map.delete(key);
|
|
108
|
-
},
|
|
109
|
-
async *keys() {
|
|
110
|
-
for (const k of this.map.keys()) yield k;
|
|
111
|
-
},
|
|
112
|
-
async *values() {
|
|
113
|
-
for (const v of this.map.values()) yield JSON.parse(v);
|
|
114
|
-
},
|
|
115
|
-
async *entries() {
|
|
116
|
-
for (const [k, v] of this.map.entries()) yield [k, JSON.parse(v)];
|
|
117
|
-
},
|
|
118
|
-
clear() {
|
|
119
|
-
this.map.clear();
|
|
120
|
-
return true;
|
|
121
|
-
},
|
|
122
|
-
};
|
|
95
|
+
wa.engine // motor de persistencia
|
|
96
|
+
wa.contact // Contact de la cuenta autenticada, o null sin sesión
|
|
97
|
+
wa.Contact / wa.Chat / wa.Message // entidades ligadas a este cliente
|
|
98
|
+
|
|
99
|
+
await wa.connect(callback) // callback recibe el PIN (string) o el QR (Buffer PNG)
|
|
100
|
+
await wa.disconnect({ silent?, destroy? })
|
|
101
|
+
|
|
102
|
+
wa.on(event, handler) // devuelve la función para desuscribirse
|
|
103
|
+
wa.once(event, handler)
|
|
104
|
+
wa.off(event, handler)
|
|
105
|
+
wa.emit(event, ...args)
|
|
106
|
+
|
|
107
|
+
await wa.profile({ name?, content?, photo? }) // nombre público, bio y foto
|
|
108
|
+
await wa.feed({ content?, caption?, contacts }) // publica un estado
|
|
123
109
|
```
|
|
124
110
|
|
|
125
|
-
|
|
111
|
+
El estado interno (socket de baileys, emisor, credenciales) es **privado de verdad**: no está accesible desde la instancia. Todo pasa por los métodos de arriba.
|
|
126
112
|
|
|
127
|
-
|
|
113
|
+
### Perfil y estados
|
|
128
114
|
|
|
129
|
-
|
|
115
|
+
```ts
|
|
116
|
+
await wa.profile({ name: 'Ventas', content: 'Atendemos 9-18h' });
|
|
117
|
+
await wa.profile({ photo: buffer }); // o una URL
|
|
118
|
+
await wa.profile({ photo: null }); // elimina la foto
|
|
130
119
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
120
|
+
const post = await wa.feed({
|
|
121
|
+
caption: '¡Estamos en vivo!',
|
|
122
|
+
contacts: ['584144709840', '56963091328'], // audiencia obligatoria
|
|
123
|
+
});
|
|
135
124
|
```
|
|
136
125
|
|
|
137
|
-
|
|
126
|
+
`contacts` no es opcional: WhatsApp no entrega el estado a nadie fuera de esa lista. Con `content` (Buffer) se publica imagen o video —el tipo se deduce de la firma del binario— y `caption` queda como pie.
|
|
138
127
|
|
|
139
|
-
|
|
140
|
-
account/
|
|
141
|
-
└── {phone}/
|
|
142
|
-
├── index
|
|
143
|
-
└── chat/
|
|
144
|
-
└── {id}/
|
|
145
|
-
├── index
|
|
146
|
-
└── message/
|
|
147
|
-
└── {id}/
|
|
148
|
-
└── index
|
|
149
|
-
```
|
|
128
|
+
---
|
|
150
129
|
|
|
151
|
-
|
|
130
|
+
## Entidades
|
|
152
131
|
|
|
153
|
-
|
|
132
|
+
Cada entidad expone getters puros sobre su documento y métodos que actúan contra WhatsApp. `wa.Contact`, `wa.Chat` y `wa.Message` ya vienen ligados al cliente; las clases sueltas se importan del paquete cuando querés `instanceof` o los estáticos con el cliente explícito.
|
|
133
|
+
|
|
134
|
+
### Contact
|
|
154
135
|
|
|
155
136
|
```ts
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
137
|
+
const person = await wa.Contact.get('584144709840'); // teléfono, JID o LID
|
|
138
|
+
const page = await wa.Contact.list(0, 50);
|
|
139
|
+
|
|
140
|
+
if (person) {
|
|
141
|
+
person.name // agenda → nombre público → nombre verificado → teléfono
|
|
142
|
+
person.phone // solo del JID PN, nunca del LID; null si no es determinable
|
|
143
|
+
person.jid // '584144709840@s.whatsapp.net' | null
|
|
144
|
+
person.lid // '123456789@lid' | null
|
|
145
|
+
person.photo // URL de la foto | null
|
|
146
|
+
await person.chat();
|
|
147
|
+
}
|
|
162
148
|
```
|
|
163
149
|
|
|
164
|
-
|
|
150
|
+
`get` lee del motor y, si el contacto no está persistido, lo descubre por red y lo materializa.
|
|
151
|
+
|
|
152
|
+
### Chat
|
|
165
153
|
|
|
166
154
|
```ts
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
155
|
+
const chat = await wa.Chat.get('584144709840');
|
|
156
|
+
const chats = await wa.Chat.list(0, 50);
|
|
157
|
+
|
|
158
|
+
if (chat) {
|
|
159
|
+
chat.id // teléfono en contactos; id crudo en grupos y LIDs
|
|
160
|
+
chat.name
|
|
161
|
+
chat.type // 'contact' | 'group'
|
|
162
|
+
chat.archived // boolean
|
|
163
|
+
chat.pinned // boolean
|
|
164
|
+
chat.muted // fecha ISO UTC hasta la que está silenciado, o null
|
|
165
|
+
chat.count // mensajes sin leer
|
|
166
|
+
|
|
167
|
+
await chat.content(); // descripción del grupo, o bio del contacto en un 1:1
|
|
168
|
+
await chat.messages(0, 50);
|
|
169
|
+
await chat.members(0, 50);
|
|
170
|
+
await chat.typing(true);
|
|
171
|
+
await chat.recording(true);
|
|
172
|
+
await chat.archive(true);
|
|
173
|
+
await chat.pin(true); // false si ya hay 3 fijados: WhatsApp descarta el cuarto
|
|
174
|
+
await chat.mute('2026-08-01T10:00:00Z'); // o false para desactivar
|
|
175
|
+
await chat.seen(); // marca el chat completo como leído
|
|
176
|
+
await chat.clear(); // vacía los mensajes, conserva el chat
|
|
177
|
+
await chat.delete(); // elimina el chat (sale del grupo si aplica)
|
|
170
178
|
}
|
|
171
179
|
```
|
|
172
180
|
|
|
173
|
-
###
|
|
181
|
+
### Message
|
|
174
182
|
|
|
175
183
|
```ts
|
|
176
|
-
|
|
177
|
-
|
|
184
|
+
import { Poll, Image } from '@arcaelas/whatsapp';
|
|
185
|
+
|
|
186
|
+
const page = await wa.Message.list(cid, 0, 50); // del más reciente al más antiguo
|
|
187
|
+
const msg = await wa.Message.get(cid, mid);
|
|
178
188
|
```
|
|
179
189
|
|
|
180
|
-
|
|
190
|
+
**Propiedades**
|
|
181
191
|
|
|
182
|
-
|
|
192
|
+
| | |
|
|
193
|
+
| --- | --- |
|
|
194
|
+
| `id` `cid` `mid` | identificadores del mensaje, su chat y el mensaje citado |
|
|
195
|
+
| `from` `me` | JID del autor y si soy yo |
|
|
196
|
+
| `type` | `text` `image` `video` `audio` `sticker` `document` `location` `poll` `vcard` `event` |
|
|
197
|
+
| `mime` | `text/plain`, `text/json` en poll/location/vcard/event, el real en media |
|
|
198
|
+
| `caption` | texto del mensaje o pie del media |
|
|
199
|
+
| `status` | `'error'` `'pending'` `'sent'` `'delivered'` `'read'` `'played'` |
|
|
200
|
+
| `read` `starred` `forwarded` `edited` `once` | banderas del mensaje |
|
|
201
|
+
| `created_at` `expires_at` | fechas en ISO UTC (`expires_at` solo en mensajes temporales) |
|
|
183
202
|
|
|
184
|
-
|
|
203
|
+
**Métodos**
|
|
185
204
|
|
|
186
205
|
```ts
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
206
|
+
await msg.author(); // Contact
|
|
207
|
+
await msg.chat(); // Chat
|
|
208
|
+
await msg.message(); // el mensaje citado, si hay `mid`
|
|
209
|
+
await msg.content(); // Buffer
|
|
210
|
+
await msg.stream(); // Readable
|
|
211
|
+
await msg.reactions(); // [{ emoji, count }]
|
|
212
|
+
|
|
213
|
+
await msg.react('❤️'); // emoji vacío la retira
|
|
214
|
+
await msg.star(true);
|
|
215
|
+
await msg.seen();
|
|
216
|
+
await msg.edit('texto corregido'); // texto, imagen o video propios
|
|
217
|
+
await msg.forward('584121234567'); // CID, Chat o Contact destino
|
|
218
|
+
await msg.delete(); // solo en mi dispositivo
|
|
219
|
+
await msg.delete(true); // para todos
|
|
220
|
+
|
|
221
|
+
await msg.text('respuesta'); // responder citando este mensaje
|
|
222
|
+
await msg.image(buffer, { caption: '…' });
|
|
194
223
|
```
|
|
195
224
|
|
|
196
|
-
|
|
197
|
-
| ----------- | ------------- | ---------------------------------------------------------------------------------- |
|
|
198
|
-
| `phone` | ✔ | International format (`+5841…`). |
|
|
199
|
-
| `store` | ✖ | Backend persistence (defaults to in‑memory volatile store). |
|
|
200
|
-
| `loginType` | ✔ | `'qr'` or `'code'`. Determines which callback is required. |
|
|
201
|
-
| `code` | _Conditional_ | Fired once with the pairing **numeric code** when `loginType === 'code'`. |
|
|
202
|
-
| `qr` | _Conditional_ | Fired with a **Buffer JPG/PNG** containing the QR image when `loginType === 'qr'`. |
|
|
225
|
+
**Envío** (los mismos nueve por tipo, en instancia para responder y en el cliente para iniciar):
|
|
203
226
|
|
|
204
|
-
|
|
227
|
+
```ts
|
|
228
|
+
await wa.Message.text(cid, 'hola', { once: true });
|
|
229
|
+
await wa.Message.image(cid, buffer, { caption: 'mirá' });
|
|
230
|
+
await wa.Message.video(cid, buffer);
|
|
231
|
+
await wa.Message.audio(cid, buffer, { ptt: true });
|
|
232
|
+
await wa.Message.location(cid, { lat: 8.3, lng: -62.7 });
|
|
233
|
+
await wa.Message.poll(cid, { content: '¿Qué pedimos?', options: [{ content: 'Pizza' }, { content: 'Sushi' }] });
|
|
234
|
+
await wa.Message.document(cid, buffer, { file_name: 'contrato.pdf' });
|
|
235
|
+
await wa.Message.vcard(cid, [{ name: 'Ana', phone: '+584121234567' }]);
|
|
236
|
+
await wa.Message.event(cid, { name: 'Demo', start: new Date() });
|
|
237
|
+
```
|
|
205
238
|
|
|
206
|
-
|
|
239
|
+
**Subclases** — el tipo decide la instancia, así que `instanceof` alcanza:
|
|
207
240
|
|
|
208
241
|
```ts
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
get(key: string): any | Promise<any>;
|
|
212
|
-
set(key: string, value: any): boolean | Promise<boolean>;
|
|
213
|
-
delete(key: string): boolean | Promise<boolean>;
|
|
214
|
-
keys(): AsyncGenerator<string>;
|
|
215
|
-
values(): AsyncGenerator<any>;
|
|
216
|
-
entries(): AsyncGenerator<[string, any]>;
|
|
217
|
-
clear(): boolean | Promise<boolean>;
|
|
218
|
-
scan?(pattern: string): string[] | Promise<string[]>;
|
|
219
|
-
}
|
|
242
|
+
if (msg instanceof Image) console.log(msg.width, msg.height, msg.size, await msg.thumb());
|
|
243
|
+
if (msg instanceof Poll) console.log(msg.options, msg.multiple, await msg.votes());
|
|
220
244
|
```
|
|
221
245
|
|
|
222
|
-
|
|
246
|
+
| Clase | Agrega |
|
|
247
|
+
| --- | --- |
|
|
248
|
+
| `Text` | `preview()` → `{ link, name, content, thumb }` del enlace citado |
|
|
249
|
+
| `Image` | `width` `height` `size` `thumb()` |
|
|
250
|
+
| `Video` | `width` `height` `size` `duration` `thumb()` |
|
|
251
|
+
| `Audio` | `ptt` `duration` `size` `waveform` (0-100, lista para pintar) |
|
|
252
|
+
| `Sticker` | `width` `height` `size` `animated` |
|
|
253
|
+
| `Document` | `name` `pages` `size` |
|
|
254
|
+
| `Location` | `lat` `lng` `live` `link` (Google Maps) |
|
|
255
|
+
| `Poll` | `options` `multiple` `votes()` `select(i)` |
|
|
256
|
+
| `VCard` | `contacts` |
|
|
257
|
+
| `Event` | `name` `start` `end` `canceled` `place` `link` |
|
|
223
258
|
|
|
224
|
-
|
|
259
|
+
### Feed
|
|
225
260
|
|
|
226
|
-
|
|
261
|
+
Publicaciones de estado (`status@broadcast`). Extiende `Message`, así que hereda `author()`, `content()`, `stream()` y `caption`.
|
|
227
262
|
|
|
228
263
|
```ts
|
|
229
|
-
|
|
264
|
+
wa.on('feed:created', async (post) => {
|
|
265
|
+
console.log((await post.author()).name, post.caption, post.expires_at);
|
|
266
|
+
await post.view(); // envía el read receipt
|
|
267
|
+
});
|
|
230
268
|
```
|
|
231
269
|
|
|
232
|
-
|
|
233
|
-
| --------------------- | ---------------------------------------------------------------------- |
|
|
234
|
-
| `pin()` | Pin chat to top. |
|
|
235
|
-
| `mute()` / `unmute()` | Toggle notifications. |
|
|
236
|
-
| `seen()` | Mark as read. |
|
|
237
|
-
| `presence(state)` | Update own presence (`available`, `composing`, `recording`, `paused`). |
|
|
238
|
-
| `delete()` | Remove chat locally. |
|
|
239
|
-
| `messages()` | Fetch cached messages (lazy‑loaded). |
|
|
240
|
-
|
|
241
|
-
### Messages
|
|
270
|
+
Lo que un estado no admite (`react`, `star`, `edit`, `forward`, `delete`, responder) lanza `ERR_FEED_UNSUPPORTED`.
|
|
242
271
|
|
|
243
|
-
|
|
244
|
-
| ------------------- | ------------------------------------------ |
|
|
245
|
-
| `content()` | Returns payload: `string` or `Buffer`. |
|
|
246
|
-
| `reply(body, opts)` | Reply in thread. Supports all media types. |
|
|
247
|
-
| `seen()` | Mark as read. |
|
|
248
|
-
| `delete()` | Delete for everyone when possible. |
|
|
249
|
-
| `like(emoji)` | Simple reaction helper. |
|
|
250
|
-
| `forward(chatid)` | Forward to another chat. |
|
|
272
|
+
---
|
|
251
273
|
|
|
252
|
-
|
|
274
|
+
## Eventos
|
|
253
275
|
|
|
254
276
|
```ts
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
type: 'text' | 'image' | 'audio' | 'video' | 'location';
|
|
258
|
-
caption?: string;
|
|
259
|
-
once?: boolean;
|
|
260
|
-
ptt?: boolean; // push‑to‑talk
|
|
261
|
-
ptv?: boolean; // video‑note
|
|
262
|
-
};
|
|
277
|
+
const off = wa.on('message:created', (msg, chat) => { … });
|
|
278
|
+
off(); // desuscribe
|
|
263
279
|
```
|
|
264
280
|
|
|
265
|
-
|
|
281
|
+
| Evento | Argumentos |
|
|
282
|
+
| --- | --- |
|
|
283
|
+
| `connected` `disconnected` | `(wa)` |
|
|
284
|
+
| `contact:created` `contact:updated` | `(contact, chat, wa)` |
|
|
285
|
+
| `chat:created` `chat:deleted` | `(chat, wa)` |
|
|
286
|
+
| `chat:pinned` `chat:unpinned` | `(chat, wa)` |
|
|
287
|
+
| `chat:archived` `chat:unarchived` | `(chat, wa)` |
|
|
288
|
+
| `chat:muted` `chat:unmuted` | `(chat, wa)` |
|
|
289
|
+
| `message:created` `message:updated` `message:deleted` | `(message, chat, wa)` |
|
|
290
|
+
| `message:starred` `message:unstarred` `message:forwarded` `message:seen` | `(message, chat, wa)` |
|
|
291
|
+
| `message:reacted` | `(message, chat, emoji, wa)` |
|
|
292
|
+
| `feed:created` `feed:updated` `feed:deleted` | `(feed, wa)` |
|
|
266
293
|
|
|
267
|
-
|
|
268
|
-
await chat.presence('composing'); // typing…
|
|
269
|
-
```
|
|
294
|
+
---
|
|
270
295
|
|
|
271
|
-
|
|
296
|
+
## Motores de persistencia
|
|
272
297
|
|
|
273
|
-
|
|
298
|
+
Un motor es un almacén key/value de strings bajo rutas jerárquicas. El contrato completo:
|
|
274
299
|
|
|
275
300
|
```ts
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
301
|
+
interface Engine {
|
|
302
|
+
get(path: string): Promise<string | null>;
|
|
303
|
+
set(path: string, value: string, score?: number): Promise<void>;
|
|
304
|
+
unset(path: string): Promise<boolean>; // borra el sub-árbol
|
|
305
|
+
list(path: string, offset?: number, limit?: number): Promise<string[]>; // hijos directos, score DESC
|
|
306
|
+
count(path: string): Promise<number>;
|
|
307
|
+
clear(): Promise<void>;
|
|
308
|
+
|
|
309
|
+
get_buffer?(path: string): Promise<Buffer | null>; // opcional: binarios sin base64
|
|
310
|
+
set_buffer?(path: string, data: Buffer, score?: number): Promise<void>;
|
|
284
311
|
}
|
|
285
312
|
```
|
|
286
313
|
|
|
287
|
-
|
|
314
|
+
`score` fija el orden de `list` (los mensajes pasan su `created_at`), de modo que reescribir historia antigua no altera la cronología. Los binarios son opcionales: un motor que no los implemente sigue siendo válido y la librería cae al documento serializado.
|
|
315
|
+
|
|
316
|
+
```ts
|
|
317
|
+
import Database from 'better-sqlite3';
|
|
318
|
+
import IORedis from 'ioredis';
|
|
319
|
+
import { S3Client } from '@aws-sdk/client-s3';
|
|
320
|
+
import { FileSystemEngine, SQLiteEngine, RedisEngine, S3Engine } from '@arcaelas/whatsapp';
|
|
321
|
+
|
|
322
|
+
new FileSystemEngine('.sessions/584144709840');
|
|
323
|
+
new SQLiteEngine(new Database('.sessions/584144709840.db'));
|
|
324
|
+
new RedisEngine(new IORedis(), 'wa:584144709840');
|
|
325
|
+
new S3Engine({ s3: new S3Client({}), bucket: 'sesiones', basedir: 'wa/584144709840' });
|
|
326
|
+
```
|
|
288
327
|
|
|
289
|
-
|
|
328
|
+
`SQLiteEngine` es el más eficiente de los integrados. Sobre un chat real de 55.146 mensajes, frente al filesystem: 220 MB → 64 MB en disco, primer `list` 115 ms → 0,6 ms, y dos archivos en total en lugar de ~110.000 inodes.
|
|
290
329
|
|
|
291
|
-
|
|
330
|
+
Cada cliente necesita **su propio** motor: nunca compartas una instancia entre dos cuentas.
|
|
292
331
|
|
|
293
|
-
|
|
332
|
+
---
|
|
294
333
|
|
|
295
|
-
|
|
334
|
+
## Bots con decoradores
|
|
296
335
|
|
|
297
336
|
```ts
|
|
298
|
-
import
|
|
299
|
-
|
|
300
|
-
function FSStore(dir: string): Store {
|
|
301
|
-
/* … */
|
|
302
|
-
}
|
|
303
|
-
```
|
|
337
|
+
import { FileSystemEngine, type Chat, type Message } from '@arcaelas/whatsapp';
|
|
338
|
+
import { WhatsAppBot, connect, command, from, every } from '@arcaelas/whatsapp/decorators';
|
|
304
339
|
|
|
305
|
-
|
|
340
|
+
class Bot extends WhatsAppBot {
|
|
341
|
+
@connect()
|
|
342
|
+
async on_ready() {
|
|
343
|
+
console.log('conectado');
|
|
344
|
+
}
|
|
306
345
|
|
|
307
|
-
|
|
346
|
+
@command('/precio')
|
|
347
|
+
async price(msg: Message, chat: Chat, args: string[]) {
|
|
348
|
+
await msg.text(`Consultando ${args[0] ?? 'el catálogo'}…`);
|
|
349
|
+
}
|
|
308
350
|
|
|
309
|
-
|
|
310
|
-
|
|
351
|
+
@from('584144709840')
|
|
352
|
+
async only_admin(msg: Message) {
|
|
353
|
+
await msg.react('👑');
|
|
354
|
+
}
|
|
311
355
|
|
|
312
|
-
|
|
313
|
-
|
|
356
|
+
@every(3_600_000)
|
|
357
|
+
async hourly() {
|
|
358
|
+
console.log('cada hora, mientras esté conectado');
|
|
359
|
+
}
|
|
314
360
|
}
|
|
361
|
+
|
|
362
|
+
const bot = new Bot({ engine: new FileSystemEngine('.sessions/bot'), phone: 584144709840 });
|
|
363
|
+
await bot.connect((auth) => console.log(auth));
|
|
315
364
|
```
|
|
316
365
|
|
|
317
|
-
|
|
366
|
+
Decoradores disponibles: `@on`, `@once`, `@connect`, `@disconnect`, `@command`, `@guard`, `@from`, `@pipe`, `@every`, `@delay`, `@pair`, y `@Bot` como decorador de clase.
|
|
318
367
|
|
|
319
368
|
---
|
|
320
369
|
|
|
321
|
-
##
|
|
370
|
+
## Recetas
|
|
322
371
|
|
|
323
|
-
|
|
372
|
+
**Descargar el media de cada imagen recibida**
|
|
324
373
|
|
|
325
374
|
```ts
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
375
|
+
import { writeFile } from 'node:fs/promises';
|
|
376
|
+
import { Image } from '@arcaelas/whatsapp';
|
|
377
|
+
|
|
378
|
+
wa.on('message:created', async (msg) => {
|
|
379
|
+
if (msg instanceof Image) {
|
|
380
|
+
await writeFile(`${msg.id}.jpg`, await msg.content());
|
|
329
381
|
}
|
|
330
382
|
});
|
|
331
383
|
```
|
|
332
384
|
|
|
333
|
-
|
|
385
|
+
**Responder solo en grupos**
|
|
334
386
|
|
|
335
387
|
```ts
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
388
|
+
wa.on('message:created', async (msg, chat) => {
|
|
389
|
+
if (chat.type === 'group' && !msg.me) {
|
|
390
|
+
await msg.react('👀');
|
|
391
|
+
}
|
|
392
|
+
});
|
|
339
393
|
```
|
|
340
394
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
## Troubleshooting
|
|
344
|
-
|
|
345
|
-
| Error | Cause & Fix |
|
|
346
|
-
| ------------------------------- | -------------------------------------------------------------------------------------- |
|
|
347
|
-
| `401 – Session invalid` | Credentials expired → re‑authenticate (clear `store` keys for `auth:` prefix). |
|
|
348
|
-
| `ERR_PACKAGE_PATH_NOT_EXPORTED` | Make sure you import ESM build (`import …`). |
|
|
349
|
-
| `BaileysBoomError 428` | Connection closed by server – client will auto‑retry; ensure network clock is in sync. |
|
|
350
|
-
|
|
351
|
-
---
|
|
352
|
-
|
|
353
|
-
## Contributing
|
|
395
|
+
**Reconectar con límite y cerrar en silencio**
|
|
354
396
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
397
|
+
```ts
|
|
398
|
+
const wa = new WhatsApp({ engine, phone: 584144709840, reconnect: { max: 5, interval: 30 } });
|
|
399
|
+
await wa.disconnect({ silent: true }); // no emite `disconnected`
|
|
400
|
+
```
|
|
358
401
|
|
|
359
402
|
---
|
|
360
403
|
|
|
361
|
-
##
|
|
404
|
+
## Licencia
|
|
362
405
|
|
|
363
|
-
|
|
406
|
+
ISC — © 2026 [Miguel Alejandro](https://github.com/arcaelas) / Arcaelas Insiders.
|
package/build/cjs/decorators.js
CHANGED
|
@@ -24,4 +24,3 @@ Object.defineProperty(exports, "pipe", { enumerable: true, get: function () { re
|
|
|
24
24
|
Object.defineProperty(exports, "pair", { enumerable: true, get: function () { return bot_1.pair; } });
|
|
25
25
|
Object.defineProperty(exports, "HANDLERS", { enumerable: true, get: function () { return bot_1.HANDLERS; } });
|
|
26
26
|
Object.defineProperty(exports, "decorator", { enumerable: true, get: function () { return bot_1.decorator; } });
|
|
27
|
-
//# sourceMappingURL=decorators.js.map
|