@whanext/core 0.3.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/CHANGELOG.md +35 -0
- package/CONTRIBUTING.md +36 -0
- package/LICENSE +21 -0
- package/README.md +507 -0
- package/SECURITY.md +16 -0
- package/dist/index.d.ts +500 -0
- package/dist/index.js +1988 -0
- package/dist/index.js.map +1 -0
- package/package.json +71 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Todas as mudanças relevantes do projeto serão registradas neste arquivo.
|
|
4
|
+
|
|
5
|
+
## 0.3.0
|
|
6
|
+
|
|
7
|
+
### Adicionado
|
|
8
|
+
|
|
9
|
+
- Logger estruturado com níveis `debug`, `info`, `warn`, `error` e `silent`.
|
|
10
|
+
- Formatos de console `pretty` e `json`.
|
|
11
|
+
- Writer customizado, escopos filhos e redaction de dados sensíveis.
|
|
12
|
+
- Adaptação sanitizada dos logs internos do provider.
|
|
13
|
+
- `app.health()` e `app.isReady` para monitoramento.
|
|
14
|
+
- Workflows de CI e publicação npm com trusted publishing.
|
|
15
|
+
- Templates de issues, pull requests, segurança e contribuição.
|
|
16
|
+
|
|
17
|
+
### Alterado
|
|
18
|
+
|
|
19
|
+
- README reorganizado para GitHub e npm.
|
|
20
|
+
- Metadados do pacote preparados para publicação.
|
|
21
|
+
|
|
22
|
+
## 0.2.0
|
|
23
|
+
|
|
24
|
+
### Adicionado
|
|
25
|
+
|
|
26
|
+
- Modelo `User` com JID, LID, telefone, nome e menção.
|
|
27
|
+
- Resolução de usuários por menção, reply ou número.
|
|
28
|
+
- Mute permanente ou temporário com SQLite e store customizável.
|
|
29
|
+
- Exclusão automática de mensagens de usuários mutados.
|
|
30
|
+
|
|
31
|
+
## 0.1.0
|
|
32
|
+
|
|
33
|
+
### Adicionado
|
|
34
|
+
|
|
35
|
+
- Fundação da API, provider, conexão, comandos, grupos, membros, cache e mensagens.
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Contribuindo com o WhaNext
|
|
2
|
+
|
|
3
|
+
Obrigado pelo interesse em melhorar o projeto.
|
|
4
|
+
|
|
5
|
+
## Ambiente
|
|
6
|
+
|
|
7
|
+
- Node.js 22.5 ou superior
|
|
8
|
+
- npm compatível com o `package-lock.json`
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm ci
|
|
12
|
+
npm run check
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Princípios da API
|
|
16
|
+
|
|
17
|
+
- Baileys permanece restrito ao provider interno.
|
|
18
|
+
- A API pública usa modelos e erros do WhaNext.
|
|
19
|
+
- Operações dependentes de estado retornam resultados tipados e idempotentes.
|
|
20
|
+
- JID, LID e PN são resolvidos pela biblioteca.
|
|
21
|
+
- Novos comportamentos possuem testes sem conexão real com o WhatsApp.
|
|
22
|
+
- Imports com vários nomes mantêm um nome por linha.
|
|
23
|
+
- Código-fonte não contém comentários explicando implementação óbvia.
|
|
24
|
+
|
|
25
|
+
## Pull requests
|
|
26
|
+
|
|
27
|
+
Abra uma issue antes de mudanças grandes na API. Pull requests devem ser pequenos, ter uma motivação clara e incluir documentação quando alterarem o uso público.
|
|
28
|
+
|
|
29
|
+
Antes de enviar:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm run check
|
|
33
|
+
npm pack --dry-run
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Não envie pastas de sessão, bancos locais, números telefônicos, pairing codes ou logs sem sanitização.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 WhaNext contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
# WhaNext
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@whanext/core)
|
|
4
|
+
[](https://github.com/luanxdd/whanext-core/actions/workflows/ci.yml)
|
|
5
|
+
[](https://www.npmjs.com/package/@whanext/core)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](./LICENSE)
|
|
8
|
+
|
|
9
|
+
SDK de alto nível para construir aplicações de WhatsApp com TypeScript. O WhaNext organiza conexão, mensagens, mídias, usuários, grupos, comandos, cache, moderação e logs em uma API única; os detalhes do provider permanecem internos.
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import {
|
|
13
|
+
Browser,
|
|
14
|
+
create,
|
|
15
|
+
} from '@whanext/core';
|
|
16
|
+
|
|
17
|
+
const app = await create({
|
|
18
|
+
phone: process.env.PHONE,
|
|
19
|
+
browser: Browser.Windows,
|
|
20
|
+
auth: './session',
|
|
21
|
+
prefix: '!',
|
|
22
|
+
logger: 'info',
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
await app.login({
|
|
26
|
+
onCode(code) {
|
|
27
|
+
console.log('Código de pareamento:', code);
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Por que WhaNext?
|
|
33
|
+
|
|
34
|
+
- API pública sem objetos ou tipos crus do Baileys.
|
|
35
|
+
- Login por pairing code, sessão persistente e reconexão automática.
|
|
36
|
+
- Prefixo global e comandos declarativos com argumentos tipados.
|
|
37
|
+
- Usuários normalizados entre JID, LID e PN.
|
|
38
|
+
- Mensagens, replies, edição, exclusão, menções e mídias.
|
|
39
|
+
- Operações de grupos e membros com resultados idempotentes.
|
|
40
|
+
- Cache de metadados transparente e substituível.
|
|
41
|
+
- Mute permanente ou temporário com SQLite ou banco próprio.
|
|
42
|
+
- Logging estruturado, sanitizado e configurável.
|
|
43
|
+
- TypeScript estrito, testes unitários e declarações ESM.
|
|
44
|
+
|
|
45
|
+
## Requisitos
|
|
46
|
+
|
|
47
|
+
- Node.js 22.5 ou superior
|
|
48
|
+
- Projeto ESM
|
|
49
|
+
|
|
50
|
+
## Instalação
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install @whanext/core
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Início rápido
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import {
|
|
60
|
+
Browser,
|
|
61
|
+
create,
|
|
62
|
+
defineCommand,
|
|
63
|
+
} from '@whanext/core';
|
|
64
|
+
|
|
65
|
+
const app = await create({
|
|
66
|
+
phone: process.env.PHONE,
|
|
67
|
+
browser: Browser.Windows,
|
|
68
|
+
auth: './session',
|
|
69
|
+
prefix: '#',
|
|
70
|
+
logger: {
|
|
71
|
+
level: 'info',
|
|
72
|
+
format: 'pretty',
|
|
73
|
+
},
|
|
74
|
+
mute: {
|
|
75
|
+
enabled: true,
|
|
76
|
+
database: './data/whanext.sqlite',
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
app.router().command(
|
|
81
|
+
defineCommand({
|
|
82
|
+
name: 'ping',
|
|
83
|
+
description: 'Verifica se o bot está respondendo.',
|
|
84
|
+
|
|
85
|
+
async execute(message) {
|
|
86
|
+
const sent = await app.message.reply(message, {
|
|
87
|
+
text: 'Calculando...',
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
await app.message.edit(sent, 'Pong!');
|
|
91
|
+
},
|
|
92
|
+
}),
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
await app.login({
|
|
96
|
+
onCode(code) {
|
|
97
|
+
console.log('Código de pareamento:', code);
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
O prefixo é definido uma vez. O router identifica o comando, remove o prefixo e cria o `ArgsParser` automaticamente.
|
|
103
|
+
|
|
104
|
+
## Logging
|
|
105
|
+
|
|
106
|
+
O nível padrão é `info`. Estão disponíveis `debug`, `info`, `warn`, `error` e `silent`.
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
const app = await create({
|
|
110
|
+
logger: 'silent',
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Para desenvolvimento:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
const app = await create({
|
|
118
|
+
logger: {
|
|
119
|
+
level: 'debug',
|
|
120
|
+
format: 'pretty',
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Para produção e coleta centralizada:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
const app = await create({
|
|
129
|
+
logger: {
|
|
130
|
+
level: 'info',
|
|
131
|
+
format: 'json',
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Um writer próprio recebe entradas normalizadas:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
const app = await create({
|
|
140
|
+
logger: {
|
|
141
|
+
level: 'debug',
|
|
142
|
+
|
|
143
|
+
writer(entry) {
|
|
144
|
+
observability.write(entry);
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
interface LogEntry {
|
|
152
|
+
timestamp: string;
|
|
153
|
+
level: 'debug' | 'info' | 'warn' | 'error';
|
|
154
|
+
scope: string;
|
|
155
|
+
message: string;
|
|
156
|
+
context: Readonly<Record<string, unknown>>;
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
O logger:
|
|
161
|
+
|
|
162
|
+
- filtra antes de construir a saída;
|
|
163
|
+
- propaga alterações de nível para escopos internos;
|
|
164
|
+
- converte `Error`, `Date`, `bigint` e referências circulares;
|
|
165
|
+
- protege `auth`, `pairingCode`, `password`, `phone`, `secret`, `session` e `token`;
|
|
166
|
+
- nunca deixa uma falha do writer interromper a aplicação;
|
|
167
|
+
- transforma logs internos do provider em mensagens sanitizadas;
|
|
168
|
+
- mantém detalhes internos do provider em `debug`.
|
|
169
|
+
|
|
170
|
+
O nível pode ser alterado em execução:
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
app.logger.setLevel('debug');
|
|
174
|
+
app.logger.info('Configuração atualizada', {
|
|
175
|
+
feature: 'moderation',
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Chaves adicionais podem ser protegidas:
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
const app = await create({
|
|
183
|
+
logger: {
|
|
184
|
+
level: 'info',
|
|
185
|
+
redact: ['apiKey', 'customerId'],
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Health checks
|
|
191
|
+
|
|
192
|
+
`app.health()` entrega um snapshot sem consultar o WhatsApp novamente:
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
const health = app.health();
|
|
196
|
+
|
|
197
|
+
console.log(health.status);
|
|
198
|
+
console.log(health.ready);
|
|
199
|
+
console.log(health.state);
|
|
200
|
+
console.log(health.uptimeMs);
|
|
201
|
+
console.log(health.muteEnabled);
|
|
202
|
+
console.log(health.logLevel);
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Estados possíveis: `idle`, `starting`, `ready` e `stopped`.
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
server.get('/health', async () => app.health());
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Para uma verificação simples:
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
if (app.isReady) {
|
|
215
|
+
console.log('Aplicação pronta.');
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Usuários
|
|
220
|
+
|
|
221
|
+
Toda mensagem possui `message.sender: User`. Menções ficam em `message.mentionedUsers`, e o remetente de um reply em `message.quoted?.sender`.
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
app.on('message', async (message) => {
|
|
225
|
+
console.log(message.sender.id);
|
|
226
|
+
console.log(message.sender.jid);
|
|
227
|
+
console.log(message.sender.lid);
|
|
228
|
+
console.log(message.sender.phone);
|
|
229
|
+
console.log(message.sender.name);
|
|
230
|
+
console.log(message.sender.displayName);
|
|
231
|
+
console.log(message.sender.mention);
|
|
232
|
+
console.log(message.sender.identities);
|
|
233
|
+
});
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Dentro de comandos, o resolver aceita menção, reply ou número com DDI:
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
async execute(message, args) {
|
|
240
|
+
const user = await app.user.resolve(message, args);
|
|
241
|
+
|
|
242
|
+
await app.message.reply(message, {
|
|
243
|
+
text: `Olá, ${user.mention}!`,
|
|
244
|
+
mentions: [user],
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
O consumidor não precisa cortar servidores, remover sufixos de dispositivo ou comparar manualmente JID e LID.
|
|
250
|
+
|
|
251
|
+
## Mensagens
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
const sent = await app.message.send(chatId, {
|
|
255
|
+
text: 'Mensagem sem reply.',
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
await app.message.reply(message, {
|
|
259
|
+
text: 'Mensagem respondida.',
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
await app.message.edit(sent, 'Texto atualizado.');
|
|
263
|
+
await app.message.delete(sent);
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
`delete()` aceita `Message`, `SentMessage` ou `MessageKey`.
|
|
267
|
+
|
|
268
|
+
## Mídias
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
await app.media.image(chatId, {
|
|
272
|
+
image: { path: './photo.jpg' },
|
|
273
|
+
caption: `Olá, ${user.mention}!`,
|
|
274
|
+
mentions: [user],
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
await app.media.video(chatId, {
|
|
278
|
+
video: { url: 'https://example.com/video.mp4' },
|
|
279
|
+
caption: 'Novo vídeo',
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
await app.media.audio(chatId, {
|
|
283
|
+
audio: bytes,
|
|
284
|
+
mimetype: 'audio/ogg; codecs=opus',
|
|
285
|
+
voice: true,
|
|
286
|
+
});
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Texto, imagem e vídeo aceitam `User` diretamente em `mentions`.
|
|
290
|
+
|
|
291
|
+
## Grupos e membros
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
await app.group.open(groupId);
|
|
295
|
+
await app.group.close(groupId);
|
|
296
|
+
await app.group.invite(groupId);
|
|
297
|
+
await app.group.revokeInvite(groupId);
|
|
298
|
+
await app.group.pin(groupId, message.keys);
|
|
299
|
+
await app.group.unpin(groupId, message.keys);
|
|
300
|
+
|
|
301
|
+
await app.member.remove(groupId, user);
|
|
302
|
+
await app.member.promote(groupId, user);
|
|
303
|
+
await app.member.demote(groupId, user);
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Estados como `already_open`, `already_admin`, `not_admin`, `not_in_group` e `already_removed` evitam mutações redundantes. A biblioteca escolhe automaticamente a identidade correta para grupos LID ou PN e só retorna sucesso depois da confirmação do WhatsApp.
|
|
307
|
+
|
|
308
|
+
## Mute nativo
|
|
309
|
+
|
|
310
|
+
Quando habilitado, o mute é aplicado antes dos eventos públicos e do router. Mensagens de um usuário mutado são apagadas automaticamente.
|
|
311
|
+
|
|
312
|
+
```ts
|
|
313
|
+
const user = await app.user.resolve(message, args);
|
|
314
|
+
const durationMs = args.duration('tempo', {
|
|
315
|
+
optional: true,
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
const result = await app.mute.add(message.chatId, user, {
|
|
319
|
+
...(durationMs !== undefined ? { durationMs } : {}),
|
|
320
|
+
});
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Sem duração, o mute é permanente. O parser aceita `30s`, `5m`, `2h`, `7d`, `sempre`, `permanente` e `indefinido`.
|
|
324
|
+
|
|
325
|
+
```ts
|
|
326
|
+
await app.mute.isMuted(groupId, user);
|
|
327
|
+
await app.mute.get(groupId, user);
|
|
328
|
+
await app.mute.remove(groupId, user);
|
|
329
|
+
await app.mute.purgeExpired();
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Resultados distinguem `muted`, `updated`, `already_muted`, `unmuted` e `already_unmuted`.
|
|
333
|
+
|
|
334
|
+
O store padrão é SQLite com WAL, transações, espera controlada e índices de identidade e expiração:
|
|
335
|
+
|
|
336
|
+
```ts
|
|
337
|
+
const app = await create({
|
|
338
|
+
mute: {
|
|
339
|
+
enabled: true,
|
|
340
|
+
database: './data/whanext.sqlite',
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Um banco próprio pode implementar `MuteStore`:
|
|
346
|
+
|
|
347
|
+
```ts
|
|
348
|
+
import type {
|
|
349
|
+
MuteStore,
|
|
350
|
+
StoredMute,
|
|
351
|
+
} from '@whanext/core';
|
|
352
|
+
|
|
353
|
+
const store: MuteStore = {
|
|
354
|
+
async upsert(mute: StoredMute) {},
|
|
355
|
+
|
|
356
|
+
async find(groupId, identities) {
|
|
357
|
+
return undefined;
|
|
358
|
+
},
|
|
359
|
+
|
|
360
|
+
async delete(groupId, identities) {
|
|
361
|
+
return false;
|
|
362
|
+
},
|
|
363
|
+
|
|
364
|
+
async purgeExpired(now) {
|
|
365
|
+
return 0;
|
|
366
|
+
},
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
const app = await create({
|
|
370
|
+
mute: { store },
|
|
371
|
+
});
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
## Comandos
|
|
375
|
+
|
|
376
|
+
```ts
|
|
377
|
+
app.router().command(
|
|
378
|
+
defineCommand({
|
|
379
|
+
name: 'promover',
|
|
380
|
+
aliases: ['promote'],
|
|
381
|
+
description: 'Promove um membro.',
|
|
382
|
+
onlyGroup: true,
|
|
383
|
+
onlyAdmin: true,
|
|
384
|
+
botMustBeAdmin: true,
|
|
385
|
+
|
|
386
|
+
async execute(message, args) {
|
|
387
|
+
const user = await app.user.resolve(message, args);
|
|
388
|
+
const result = await app.member.promote(message.chatId, user);
|
|
389
|
+
|
|
390
|
+
await app.message.reply(message, {
|
|
391
|
+
text: result.changed
|
|
392
|
+
? `${user.mention} foi promovido.`
|
|
393
|
+
: `${user.mention} já é administrador.`,
|
|
394
|
+
mentions: [user],
|
|
395
|
+
});
|
|
396
|
+
},
|
|
397
|
+
}),
|
|
398
|
+
);
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Restrições disponíveis:
|
|
402
|
+
|
|
403
|
+
- `onlyGroup`
|
|
404
|
+
- `onlyPrivate`
|
|
405
|
+
- `onlyAdmin`
|
|
406
|
+
- `botMustBeAdmin`
|
|
407
|
+
|
|
408
|
+
`ArgsParser` possui `string`, `number`, `boolean`, `enum`, `user`, `duration`, `peek`, `skip` e `rest`. `args.user()` retorna `User`, nunca uma string crua.
|
|
409
|
+
|
|
410
|
+
## Cache externo
|
|
411
|
+
|
|
412
|
+
```ts
|
|
413
|
+
const app = await create({
|
|
414
|
+
cache: {
|
|
415
|
+
store: myRedisStore,
|
|
416
|
+
groupTtlMs: 300_000,
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
O cache padrão vive na instância do app. Eventos e mutações de grupo invalidam automaticamente entradas relacionadas.
|
|
422
|
+
|
|
423
|
+
## Presença
|
|
424
|
+
|
|
425
|
+
```ts
|
|
426
|
+
await app.chat.typing(chatId);
|
|
427
|
+
await app.chat.recording(chatId);
|
|
428
|
+
await app.chat.stopTyping(chatId);
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
## Erros
|
|
432
|
+
|
|
433
|
+
```ts
|
|
434
|
+
app.on('error', (error) => {
|
|
435
|
+
console.error(error.code);
|
|
436
|
+
console.error(error.context);
|
|
437
|
+
console.error(error.recoverable);
|
|
438
|
+
});
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
Todos os erros públicos usam `WhaNextError` e códigos estáveis. O logger registra erros normalizados sem exigir conhecimento do provider.
|
|
442
|
+
|
|
443
|
+
## API
|
|
444
|
+
|
|
445
|
+
| Domínio | Responsabilidade |
|
|
446
|
+
| --- | --- |
|
|
447
|
+
| `app.message` | Envio, reply, edição, exclusão e texto |
|
|
448
|
+
| `app.media` | Imagem, vídeo e áudio |
|
|
449
|
+
| `app.group` | Estado, convite, pin e metadados |
|
|
450
|
+
| `app.member` | Remoção, promoção e rebaixamento |
|
|
451
|
+
| `app.user` | Criação e resolução de usuários |
|
|
452
|
+
| `app.mute` | Mute, desmute, consulta e expiração |
|
|
453
|
+
| `app.chat` | Indicadores de presença |
|
|
454
|
+
| `app.logger` | Logging e nível em execução |
|
|
455
|
+
| `app.router()` | Registro e despacho de comandos |
|
|
456
|
+
| `app.health()` | Snapshot de saúde da aplicação |
|
|
457
|
+
|
|
458
|
+
## Exemplo executável
|
|
459
|
+
|
|
460
|
+
```bash
|
|
461
|
+
cp .env.example .env
|
|
462
|
+
npm ci
|
|
463
|
+
npm run build
|
|
464
|
+
npm run example
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
`examples/index.ts` cobre conexão, logging, health check, envio, reply, edição, exclusão, menções, grupos, membros, mute e desmute.
|
|
468
|
+
|
|
469
|
+
## Desenvolvimento
|
|
470
|
+
|
|
471
|
+
```bash
|
|
472
|
+
npm ci
|
|
473
|
+
npm run check
|
|
474
|
+
npm pack --dry-run
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
## Publicação
|
|
478
|
+
|
|
479
|
+
O repositório inclui:
|
|
480
|
+
|
|
481
|
+
- CI em Node.js 22 e 24;
|
|
482
|
+
- inspeção do pacote npm;
|
|
483
|
+
- publicação ao criar uma GitHub Release;
|
|
484
|
+
- trusted publishing por OIDC;
|
|
485
|
+
- provenance automática do npm;
|
|
486
|
+
- templates de bug, feature e pull request;
|
|
487
|
+
- política de segurança e guia de contribuição.
|
|
488
|
+
|
|
489
|
+
O `package.json` está vinculado ao repositório `luanxdd/whanext-core`, como exigido pelo npm para trusted publishing e provenance.
|
|
490
|
+
|
|
491
|
+
Depois da primeira publicação do pacote, configure no npm o trusted publisher apontando para `publish.yml` e para o environment `npm` do GitHub. O workflow não armazena um token npm de longa duração.
|
|
492
|
+
|
|
493
|
+
## Segurança
|
|
494
|
+
|
|
495
|
+
Consulte [SECURITY.md](./SECURITY.md) antes de reportar uma vulnerabilidade. Nunca publique sessão, pairing code, telefone, token, banco local ou logs não sanitizados.
|
|
496
|
+
|
|
497
|
+
## Contribuindo
|
|
498
|
+
|
|
499
|
+
Consulte [CONTRIBUTING.md](./CONTRIBUTING.md). Mudanças na API pública devem manter o provider isolado, incluir testes e atualizar a documentação correspondente.
|
|
500
|
+
|
|
501
|
+
## Licença
|
|
502
|
+
|
|
503
|
+
MIT. Consulte [LICENSE](./LICENSE).
|
|
504
|
+
|
|
505
|
+
## Aviso
|
|
506
|
+
|
|
507
|
+
WhaNext não é afiliado, autorizado ou mantido pelo WhatsApp ou pela Meta. O provider padrão usa uma integração não oficial; quem utiliza o projeto é responsável pelos termos aplicáveis e pelos riscos de bloqueio da conta.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Segurança
|
|
2
|
+
|
|
3
|
+
## Versões suportadas
|
|
4
|
+
|
|
5
|
+
| Versão | Suporte |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| 0.3.x | Sim |
|
|
8
|
+
| anteriores | Não |
|
|
9
|
+
|
|
10
|
+
## Reportando uma vulnerabilidade
|
|
11
|
+
|
|
12
|
+
Não abra uma issue pública para vulnerabilidades. Use o recurso privado de Security Advisories do repositório e inclua impacto, reprodução mínima e versão afetada.
|
|
13
|
+
|
|
14
|
+
Nunca envie sessão do WhatsApp, pairing code, telefone, token, banco de mute ou credenciais. Substitua identificadores reais por valores fictícios.
|
|
15
|
+
|
|
16
|
+
O projeto usa um provider não oficial e não pode eliminar riscos de bloqueio de conta ou mudanças no protocolo do WhatsApp.
|