@whanext/core 0.18.1 → 0.19.1

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 CHANGED
@@ -1,5 +1,49 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.19.1
4
+
5
+ - Corrige menus de lista no provider Zapo usando `interactiveMessage` Native Flow com `single_select` em vez do `listMessage` legado.
6
+ - Respostas de `single_select` agora são normalizadas explicitamente como `message.interactive.kind = "list"`.
7
+
8
+
9
+ ## 0.19.0
10
+
11
+ ### Comandos
12
+
13
+ - `prefix` passa a aceitar um prefixo único ou uma lista, como `['&', '!', '.']`; o primeiro continua sendo o prefixo principal usado por menus/help.
14
+ - Novo `prefixless` por comando para liberar somente gatilhos explicitamente escolhidos sem prefixo, como `prefixless: ['a']`.
15
+ - `ctx.prefix` agora representa o prefixo que realmente acionou a execução e fica vazio (`''`) em execuções prefixless.
16
+ - `ctx.commands.prefixes` expõe todos os prefixos configurados.
17
+ - Novo `app.commands.setPrefixes()` permite ativar, trocar ou desativar o modo multi-prefixo em runtime sem recriar a aplicação.
18
+ - IDs de respostas de botões/listas são encaminhados ao mesmo router de comandos, permitindo rows como `&open` ou aliases prefixless como `a`.
19
+
20
+ ### Interativos
21
+
22
+ - Novo envio de enquetes com `PollContent` e `app.message.poll()`.
23
+ - Novo menu de lista single-select com `ListContent`, seções/rows e `app.message.list()`.
24
+ - Botões Native Flow ganham o tipo `reply`, com ID de resposta, além de `copy` e `link`.
25
+ - Mensagens recebidas passam a expor `message.interactive` com `kind`, `id` e título visível quando disponível.
26
+ - Polls recebidas preservam a pergunta em `message.text` e continuam classificadas como `contentKind: 'poll'`.
27
+
28
+ ### Correções
29
+
30
+ - Corrigida a extração do `contextInfo.quotedMessage`: `extractQuotedZapoMessage()` agora usa o nó real retornado por `contentNode()` em vez do wrapper `{ type, node }`.
31
+ - Quoted messages são armazenadas como mensagens completas no mesmo cache recente do provider, preservando download de mídia e repost.
32
+ - `downloadMedia()` também mantém associação direta com as `MessageKey` emitidas no evento, com fallback por chave/ID para objetos reconstruídos.
33
+ - A correção cobre especialmente respostas a mídias `viewOnceMessageV2Extension`, como `&fig` sobre uma imagem/vídeo de visualização única.
34
+
35
+ ### Compatibilidade
36
+
37
+ - Configurações existentes com `prefix: '&'` continuam funcionando sem alteração.
38
+ - Nenhum comando se torna prefixless automaticamente; o recurso é opt-in por definição.
39
+ - A API anterior de botões `copy`/`link` permanece compatível.
40
+
41
+ ## 0.18.2
42
+
43
+ - Corrigida a normalização de mídias de visualização única recebidas no envelope `viewOnceMessageV2Extension` do protocolo do WhatsApp.
44
+ - Replies para imagens e vídeos de visualização única agora expõem corretamente `quoted.hasMedia`, `quoted.isViewOnce`, `quoted.contentKind` e `quoted.media`.
45
+ - O cache de quoted media preserva esse envelope para que `MediaService.download()` consiga recuperar a mídia ao responder comandos como `&fig`.
46
+
3
47
  ## 0.18.1
4
48
 
5
49
  ### Fixed
package/README.md CHANGED
@@ -34,7 +34,7 @@ await app.login({
34
34
  - API pública independente do provider, sem objetos ou tipos crus do Zapo.
35
35
  - Login por pairing code, sessão persistente e reconexão automática.
36
36
  - Uma ou várias contas independentes no mesmo processo com `createMulti()`.
37
- - Prefixo global e comandos declarativos com argumentos tipados.
37
+ - Prefixo único ou múltiplos prefixos, aliases prefixless opt-in e comandos declarativos com argumentos tipados.
38
38
  - Comandos exclusivos da conta conectada com `guards.owner()` ou `onlyOwner`.
39
39
  - Usuários normalizados entre JID, LID e PN.
40
40
  - Mensagens, replies, edição, exclusão, revogações, menções, mídias e download normalizado.
@@ -103,7 +103,41 @@ await app.login({
103
103
  });
104
104
  ```
105
105
 
106
- O prefixo é definido uma vez. O router identifica o comando, remove o prefixo e cria o `ArgsParser` automaticamente.
106
+ O prefixo pode ser único ou múltiplo. O router identifica qual prefixo foi usado, remove apenas esse prefixo e cria o `ArgsParser` automaticamente:
107
+
108
+ ```ts
109
+ const app = await create({
110
+ prefix: ['&', '!', '.'],
111
+ });
112
+ ```
113
+
114
+ O primeiro valor (`&`, no exemplo) é o prefixo principal usado por `app.commands.prefix` e pelo help automático. Todos ficam disponíveis em `app.commands.prefixes`.
115
+
116
+ O modo multi-prefixo também pode ser ativado ou trocado em runtime:
117
+
118
+ ```ts
119
+ app.commands.setPrefixes(['&', '!']);
120
+
121
+ // volta para um único prefixo
122
+ app.commands.setPrefixes('&');
123
+ ```
124
+
125
+ Comandos sem prefixo são **opt-in** e podem liberar somente aliases específicos:
126
+
127
+ ```ts
128
+ app.commands.command(defineCommand({
129
+ name: 'open',
130
+ aliases: ['abrir', 'a'],
131
+ prefixless: ['a'],
132
+ onlyGroup: true,
133
+ botMustBeAdmin: true,
134
+ async execute(ctx) {
135
+ await ctx.groups.open(ctx.chatId);
136
+ },
137
+ }));
138
+ ```
139
+
140
+ Nesse exemplo, `&open`, `!open`, `.abrir`, `&a` e `a` funcionam. `open` e `abrir` sem prefixo continuam sendo texto normal. Use `prefixless: true` somente quando quiser liberar sem prefixo o nome e todos os aliases daquele comando.
107
141
 
108
142
  ## Múltiplas contas
109
143
 
@@ -370,9 +404,11 @@ await app.message.react(sent, '👏🏻');
370
404
  await app.message.unreact(sent);
371
405
  ```
372
406
 
373
- ### Botões interativos
407
+ ### Interativos
408
+
409
+ #### Botões
374
410
 
375
- Por enquanto, a API pública suporta botões de **copiar código** e **abrir link**:
411
+ A API pública suporta botões de **copiar**, **abrir link** e **resposta rápida com ID**:
376
412
 
377
413
  ```ts
378
414
  await app.message.buttons(chatId, {
@@ -380,33 +416,74 @@ await app.message.buttons(chatId, {
380
416
  text: 'Escolha uma ação:',
381
417
  footer: 'WhaNext',
382
418
  buttons: [
383
- {
384
- type: 'copy',
385
- label: 'Copiar código',
386
- code: 'ABC-123',
387
- },
388
- {
389
- type: 'link',
390
- label: 'Abrir painel',
391
- url: 'https://example.com',
392
- },
419
+ { type: 'copy', label: 'Copiar código', code: 'ABC-123' },
420
+ { type: 'link', label: 'Abrir painel', url: 'https://example.com' },
421
+ { type: 'reply', label: 'Abrir grupo', id: '&open' },
393
422
  ],
394
423
  });
395
424
  ```
396
425
 
397
- Também funciona diretamente em replies de comandos, sem importar tipos do provider:
426
+ Também funciona diretamente em replies de comandos. Quando o usuário toca em um botão `reply`, o ID recebido fica em `message.interactive.id`; se esse ID representa um comando válido, o router o executa automaticamente.
398
427
 
399
428
  ```ts
400
429
  await ctx.reply({
401
430
  text: 'Use uma das opções abaixo.',
402
431
  buttons: [
403
- { type: 'copy', label: 'Copiar', code: 'ABC-123' },
404
- { type: 'link', label: 'Abrir site', url: 'https://example.com' },
432
+ { type: 'reply', label: 'Abrir', id: 'a' },
433
+ { type: 'link', label: 'Ajuda', url: 'https://example.com' },
434
+ ],
435
+ });
436
+ ```
437
+
438
+ `title`, `footer` e `mentions` são opcionais. Os tipos podem ser combinados na mesma mensagem.
439
+
440
+ #### Menu de lista
441
+
442
+ Para menus maiores, use uma lista single-select com seções e IDs estáveis:
443
+
444
+ ```ts
445
+ await app.message.list(chatId, {
446
+ title: 'Administração',
447
+ text: 'Escolha uma ação para o grupo.',
448
+ buttonText: 'Ver opções',
449
+ footer: 'WhaNext',
450
+ list: [
451
+ {
452
+ title: 'Grupo',
453
+ rows: [
454
+ { id: '&open', title: 'Abrir grupo', description: 'Libera mensagens' },
455
+ { id: '&close', title: 'Fechar grupo', description: 'Somente admins' },
456
+ ],
457
+ },
405
458
  ],
406
459
  });
407
460
  ```
408
461
 
409
- `title`, `footer` e `mentions` são opcionais. Os botões podem ser combinados na mesma mensagem.
462
+ A escolha chega normalizada em `message.interactive`:
463
+
464
+ ```ts
465
+ app.on('message', (message) => {
466
+ if (message.interactive) {
467
+ console.log(message.interactive.kind); // 'button' | 'list'
468
+ console.log(message.interactive.id);
469
+ }
470
+ });
471
+ ```
472
+
473
+ IDs como `&open` entram no router com prefixo. IDs como `a` entram sem prefixo somente quando aquele gatilho foi liberado com `prefixless`.
474
+
475
+ #### Enquetes
476
+
477
+ ```ts
478
+ await app.message.poll(chatId, {
479
+ poll: 'Verdade ou desafio?',
480
+ options: ['Verdade', 'Desafio'],
481
+ selectableCount: 1,
482
+ allowAddOption: false,
483
+ });
484
+ ```
485
+
486
+ `selectableCount` é opcional e assume `1`. A pergunta de uma poll recebida também fica disponível em `message.text`, com `message.contentKind === 'poll'`.
410
487
 
411
488
  `delete()` aceita `Message`, `SentMessage` ou `MessageKey`.
412
489
 
@@ -839,7 +916,7 @@ export default defineCommand({
839
916
  });
840
917
  ```
841
918
 
842
- `ctx.commands` fornece `catalog()`, `categories()`, `find()`, `has()`, `size` e `prefix`; ele não expõe detalhes internos do provider.
919
+ `ctx.commands` fornece `catalog()`, `categories()`, `find()`, `has()`, `size`, `prefix` e `prefixes`; ele não expõe detalhes internos do provider. `ctx.prefix` contém o prefixo que acionou aquela execução e é `''` quando o comando foi disparado por um gatilho prefixless.
843
920
 
844
921
  Se for necessário controlar extensões ou recursão:
845
922
 
package/dist/index.d.ts CHANGED
@@ -90,6 +90,12 @@ interface QuotedMessage {
90
90
  contentKind?: MessageContentKind;
91
91
  media?: MessageMedia;
92
92
  }
93
+ type InteractiveResponseKind = 'button' | 'list';
94
+ interface InteractiveResponse {
95
+ kind: InteractiveResponseKind;
96
+ id: string;
97
+ title?: string;
98
+ }
93
99
  interface MessageDeleted {
94
100
  key: MessageKey;
95
101
  message?: Message;
@@ -128,6 +134,7 @@ interface Message {
128
134
  contentKind?: MessageContentKind;
129
135
  media?: MessageMedia;
130
136
  quoted?: QuotedMessage;
137
+ interactive?: InteractiveResponse;
131
138
  }
132
139
  interface SentMessage {
133
140
  id: string;
@@ -164,7 +171,12 @@ interface CopyCodeButton {
164
171
  label: string;
165
172
  code: string;
166
173
  }
167
- type MessageButton = LinkButton | CopyCodeButton;
174
+ interface QuickReplyButton {
175
+ type: 'reply';
176
+ label: string;
177
+ id: string;
178
+ }
179
+ type MessageButton = LinkButton | CopyCodeButton | QuickReplyButton;
168
180
  interface ButtonsContent {
169
181
  text: string;
170
182
  buttons: MessageButton[];
@@ -172,6 +184,29 @@ interface ButtonsContent {
172
184
  footer?: string;
173
185
  mentions?: MentionTarget[];
174
186
  }
187
+ interface ListRow {
188
+ id: string;
189
+ title: string;
190
+ description?: string;
191
+ }
192
+ interface ListSection {
193
+ title?: string;
194
+ rows: readonly ListRow[];
195
+ }
196
+ interface ListContent {
197
+ list: readonly ListSection[];
198
+ text: string;
199
+ buttonText: string;
200
+ title?: string;
201
+ footer?: string;
202
+ mentions?: MentionTarget[];
203
+ }
204
+ interface PollContent {
205
+ poll: string;
206
+ options: readonly string[];
207
+ selectableCount?: number;
208
+ allowAddOption?: boolean;
209
+ }
175
210
  interface ImageContent {
176
211
  image: MediaSource;
177
212
  caption?: string;
@@ -193,7 +228,7 @@ interface AudioContent {
193
228
  interface StickerContent {
194
229
  sticker: MediaSource;
195
230
  }
196
- type MessageContent = TextContent | ButtonsContent | ImageContent | VideoContent | AudioContent | StickerContent;
231
+ type MessageContent = TextContent | ButtonsContent | ListContent | PollContent | ImageContent | VideoContent | AudioContent | StickerContent;
197
232
 
198
233
  type GroupAccess = 'open' | 'closed';
199
234
  type GroupRole = 'member' | 'admin' | 'owner';
@@ -494,10 +529,13 @@ declare class MessageService {
494
529
  unreact(message: Message | SentMessage | MessageKey): Promise<SentMessage>;
495
530
  text(chatId: string, text: string, mentions?: MentionTarget[]): Promise<SentMessage>;
496
531
  buttons(chatId: string, content: ButtonsContent): Promise<SentMessage>;
532
+ list(chatId: string, content: ListContent): Promise<SentMessage>;
533
+ poll(chatId: string, content: PollContent): Promise<SentMessage>;
497
534
  }
498
535
 
499
536
  interface CommandCatalogView {
500
537
  readonly prefix: string;
538
+ readonly prefixes: readonly string[];
501
539
  readonly size: number;
502
540
  catalog(options?: {
503
541
  category?: string;
@@ -628,6 +666,7 @@ interface CommandMetadata {
628
666
  name: string;
629
667
  description: string;
630
668
  aliases?: readonly string[];
669
+ prefixless?: boolean | readonly string[];
631
670
  category?: string;
632
671
  usage?: string;
633
672
  examples?: readonly string[];
@@ -684,7 +723,7 @@ interface LoadedCommand {
684
723
  declare function loadCommands(registrar: CommandRegistrar, dirPath: string | URL, options?: LoadCommandsOptions): Promise<LoadCommandsResult>;
685
724
 
686
725
  interface RouterOptions {
687
- prefix?: string;
726
+ prefix?: string | readonly string[];
688
727
  onError?: (error: WhaNextError, message: Message) => void | Promise<void>;
689
728
  onCommandError?: CommandErrorHandler;
690
729
  beforeExecute?: (context: CommandContext) => void | Promise<void>;
@@ -703,6 +742,8 @@ declare class CommandRouter {
703
742
  constructor(services: CommandRuntimeServices, options?: RouterOptions);
704
743
  constructor(group: GroupService, options?: RouterOptions);
705
744
  get prefix(): string;
745
+ get prefixes(): readonly string[];
746
+ setPrefixes(prefixes: string | readonly string[]): this;
706
747
  get size(): number;
707
748
  command(definition: CommandDefinition): this;
708
749
  load(dirPath: string | URL, options?: LoadCommandsOptions): Promise<LoadCommandsResult>;
@@ -787,7 +828,7 @@ interface AppHealth {
787
828
  }
788
829
  interface WhaNextAppOptions {
789
830
  phone?: string;
790
- prefix?: string;
831
+ prefix?: string | readonly string[];
791
832
  cache?: CacheOptions;
792
833
  logger?: LoggerConfig;
793
834
  mute?: MuteOptions;
@@ -832,7 +873,7 @@ interface CreateOptions {
832
873
  phone?: string;
833
874
  browser?: Browser;
834
875
  auth?: string;
835
- prefix?: string;
876
+ prefix?: string | readonly string[];
836
877
  cache?: CacheOptions;
837
878
  logger?: LoggerConfig;
838
879
  mute?: MuteOptions;
@@ -925,4 +966,4 @@ declare class SqliteMuteStore implements MuteStore {
925
966
  close(): void;
926
967
  }
927
968
 
928
- export { AccountService, type AddMuteOptions, type AddMuteResult, type AppEvents, type AppHealth, type AppHealthStatus, ArgsParser, type AudioContent, type BooleanOption, Browser, type ButtonsContent, type CacheOptions, type CacheStore, type CallEvent, type CallStatus, type ChangeResult, type CommandAccountContext, type CommandCatalogOptions, type CommandCatalogView, type CommandChatContext, type CommandConcurrency, type CommandContext, type CommandCooldown, type CommandDefinition, type CommandErrorHandler, type CommandGroupContext, type CommandGroupDefinition, type CommandGuard, type CommandHelpOptions, type CommandHooks, type CommandLocalization, type CommandMetadata, type CommandMiddleware, type CommandOptionDefinition, type CommandOptionSchema, type CommandOptionValue, type CommandOptionValues, type CommandRegistrar, CommandRouter, type CommandRuntimeServices, type CommandScope, type ConcurrencyStrategy, type ConnectionState, type ConnectionUpdate, type CopyCodeButton, type CreateMultiOptions, type CreateOptions, DeferredReply, type DownloadedMedia, type DurationOption, type EnumOption, type ExecutableCommandDefinition, type GroupAccess, type GroupAddressingMode, type GroupParticipant, type GroupParticipantAction, type GroupParticipantsChanged, type GroupRole, type GroupSnapshot, type GuardResult, type ImageContent, type InviteResult, type LinkButton, type LoadCommandsOptions, type LoadCommandsResult, type LoadedCommand, type LogContext, type LogEntry, type LogFormat, type LogLevel, type LogWriter, Logger, type LoggerConfig, type LoggerOptions, type LoginOptions, type MediaKind, type MediaSource, type MemberActionState, MemoryCache, type MemoryCacheStats, type MentionTarget, type Message, type MessageButton, type MessageContent, type MessageContentKind, type MessageDeleted, type MessageEdited, type MessageKey, type MessageMedia, type MultiAccountOptions, type MultiAppEvent, type MultiAppHealth, MultiCommandRouter, type MultiLoadCommandsResult, type MultiLoginOptions, type MuteChangeResult, type MuteEnforcement, type MuteOptions, type MuteRecord, MuteService, type MuteStore, type NumberOption, ParsedCommandOptions, type ParticipantUpdateResult, type PresenceState, type QuotedMessage, type ReconnectOptions, type RegisteredCommand, type RemoveMuteResult, type ReplyOptions, type RepostMessageOptions, type RouterOptions, type SentMessage, SqliteMuteStore, type StickerContent, type StoredMute, type StringOption, type TextContent, User, type UserData, type UserOption, type VideoContent, WhaNextApp, WhaNextError, type WhaNextErrorCode, WhaNextMultiApp, type WhatsAppProvider, create, createMulti, defineCommand, defineCommandGroup, defineCommands, defineSubcommand, guards, isCommandGroup, loadCommands, option, toWhaNextError };
969
+ export { AccountService, type AddMuteOptions, type AddMuteResult, type AppEvents, type AppHealth, type AppHealthStatus, ArgsParser, type AudioContent, type BooleanOption, Browser, type ButtonsContent, type CacheOptions, type CacheStore, type CallEvent, type CallStatus, type ChangeResult, type CommandAccountContext, type CommandCatalogOptions, type CommandCatalogView, type CommandChatContext, type CommandConcurrency, type CommandContext, type CommandCooldown, type CommandDefinition, type CommandErrorHandler, type CommandGroupContext, type CommandGroupDefinition, type CommandGuard, type CommandHelpOptions, type CommandHooks, type CommandLocalization, type CommandMetadata, type CommandMiddleware, type CommandOptionDefinition, type CommandOptionSchema, type CommandOptionValue, type CommandOptionValues, type CommandRegistrar, CommandRouter, type CommandRuntimeServices, type CommandScope, type ConcurrencyStrategy, type ConnectionState, type ConnectionUpdate, type CopyCodeButton, type CreateMultiOptions, type CreateOptions, DeferredReply, type DownloadedMedia, type DurationOption, type EnumOption, type ExecutableCommandDefinition, type GroupAccess, type GroupAddressingMode, type GroupParticipant, type GroupParticipantAction, type GroupParticipantsChanged, type GroupRole, type GroupSnapshot, type GuardResult, type ImageContent, type InteractiveResponse, type InteractiveResponseKind, type InviteResult, type LinkButton, type ListContent, type ListRow, type ListSection, type LoadCommandsOptions, type LoadCommandsResult, type LoadedCommand, type LogContext, type LogEntry, type LogFormat, type LogLevel, type LogWriter, Logger, type LoggerConfig, type LoggerOptions, type LoginOptions, type MediaKind, type MediaSource, type MemberActionState, MemoryCache, type MemoryCacheStats, type MentionTarget, type Message, type MessageButton, type MessageContent, type MessageContentKind, type MessageDeleted, type MessageEdited, type MessageKey, type MessageMedia, type MultiAccountOptions, type MultiAppEvent, type MultiAppHealth, MultiCommandRouter, type MultiLoadCommandsResult, type MultiLoginOptions, type MuteChangeResult, type MuteEnforcement, type MuteOptions, type MuteRecord, MuteService, type MuteStore, type NumberOption, ParsedCommandOptions, type ParticipantUpdateResult, type PollContent, type PresenceState, type QuickReplyButton, type QuotedMessage, type ReconnectOptions, type RegisteredCommand, type RemoveMuteResult, type ReplyOptions, type RepostMessageOptions, type RouterOptions, type SentMessage, SqliteMuteStore, type StickerContent, type StoredMute, type StringOption, type TextContent, User, type UserData, type UserOption, type VideoContent, WhaNextApp, WhaNextError, type WhaNextErrorCode, WhaNextMultiApp, type WhatsAppProvider, create, createMulti, defineCommand, defineCommandGroup, defineCommands, defineSubcommand, guards, isCommandGroup, loadCommands, option, toWhaNextError };