@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 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.
@@ -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
+ [![npm](https://img.shields.io/npm/v/%40whanext%2Fcore.svg)](https://www.npmjs.com/package/@whanext/core)
4
+ [![CI](https://github.com/luanxdd/whanext-core/actions/workflows/ci.yml/badge.svg)](https://github.com/luanxdd/whanext-core/actions/workflows/ci.yml)
5
+ [![Node.js](https://img.shields.io/node/v/%40whanext%2Fcore.svg)](https://www.npmjs.com/package/@whanext/core)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-6-3178c6.svg)](https://www.typescriptlang.org/)
7
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](./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.