@whanext/core 0.14.3 → 0.15.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 CHANGED
@@ -1,6 +1,27 @@
1
1
  # Changelog
2
2
 
3
3
 
4
+ ## 0.15.0
5
+
6
+ ### Adicionado
7
+
8
+ - Suporte de alto nível a mensagens com botões interativos Native Flow.
9
+ - Botão `copy` para copiar códigos/textos usando `cta_copy`.
10
+ - Botão `link` para abrir URLs usando `cta_url`.
11
+ - Novo `ButtonsContent` no `MessageContent`, com `title`, `text`, `footer`, `mentions` e uma lista tipada de botões.
12
+ - Novo atalho `app.message.buttons(chatId, content)`; `ctx.reply({...})` e `app.message.reply(...)` também aceitam o novo conteúdo automaticamente.
13
+ - Exportação pública dos tipos `ButtonsContent`, `MessageButton`, `CopyCodeButton` e `LinkButton`.
14
+
15
+ ### Provider Baileys
16
+
17
+ - Mensagens interativas são geradas como `InteractiveMessage`/`NativeFlowMessage` e enviadas por `relayMessage`, sem expor protobufs ao consumidor.
18
+ - Replies e menções são preservados no envio de botões.
19
+ - O relay inclui os nós de compatibilidade necessários para Native Flow em chats privados e grupos.
20
+
21
+ ### Compatibilidade
22
+
23
+ - Nenhum contrato existente foi removido. Texto, mídia, reações, edição, delete e repost continuam usando as APIs anteriores.
24
+
4
25
  ## 0.14.3
5
26
 
6
27
  - Corrige uma rejeição órfã durante o login quando a solicitação de código de pareamento falha ao mesmo tempo em que o socket é fechado.
package/README.md CHANGED
@@ -340,6 +340,44 @@ await app.message.react(sent, '👏🏻');
340
340
  await app.message.unreact(sent);
341
341
  ```
342
342
 
343
+ ### Botões interativos
344
+
345
+ Por enquanto, a API pública suporta botões de **copiar código** e **abrir link**:
346
+
347
+ ```ts
348
+ await app.message.buttons(chatId, {
349
+ title: 'Acesso',
350
+ text: 'Escolha uma ação:',
351
+ footer: 'WhaNext',
352
+ buttons: [
353
+ {
354
+ type: 'copy',
355
+ label: 'Copiar código',
356
+ code: 'ABC-123',
357
+ },
358
+ {
359
+ type: 'link',
360
+ label: 'Abrir painel',
361
+ url: 'https://example.com',
362
+ },
363
+ ],
364
+ });
365
+ ```
366
+
367
+ Também funciona diretamente em replies de comandos, sem importar tipos do Baileys:
368
+
369
+ ```ts
370
+ await ctx.reply({
371
+ text: 'Use uma das opções abaixo.',
372
+ buttons: [
373
+ { type: 'copy', label: 'Copiar', code: 'ABC-123' },
374
+ { type: 'link', label: 'Abrir site', url: 'https://example.com' },
375
+ ],
376
+ });
377
+ ```
378
+
379
+ `title`, `footer` e `mentions` são opcionais. Os botões podem ser combinados na mesma mensagem.
380
+
343
381
  `delete()` aceita `Message`, `SentMessage` ou `MessageKey`.
344
382
 
345
383
  `react()` aceita os mesmos tipos e adiciona uma reação à mensagem. `unreact()` remove a reação da conta conectada.
package/dist/index.d.ts CHANGED
@@ -139,6 +139,24 @@ interface TextContent {
139
139
  text: string;
140
140
  mentions?: MentionTarget[];
141
141
  }
142
+ interface LinkButton {
143
+ type: 'link';
144
+ label: string;
145
+ url: string;
146
+ }
147
+ interface CopyCodeButton {
148
+ type: 'copy';
149
+ label: string;
150
+ code: string;
151
+ }
152
+ type MessageButton = LinkButton | CopyCodeButton;
153
+ interface ButtonsContent {
154
+ text: string;
155
+ buttons: MessageButton[];
156
+ title?: string;
157
+ footer?: string;
158
+ mentions?: MentionTarget[];
159
+ }
142
160
  interface ImageContent {
143
161
  image: MediaSource;
144
162
  caption?: string;
@@ -160,7 +178,7 @@ interface AudioContent {
160
178
  interface StickerContent {
161
179
  sticker: MediaSource;
162
180
  }
163
- type MessageContent = TextContent | ImageContent | VideoContent | AudioContent | StickerContent;
181
+ type MessageContent = TextContent | ButtonsContent | ImageContent | VideoContent | AudioContent | StickerContent;
164
182
 
165
183
  type GroupAccess = 'open' | 'closed';
166
184
  type GroupRole = 'member' | 'admin' | 'owner';
@@ -458,6 +476,7 @@ declare class MessageService {
458
476
  react(message: Message | SentMessage | MessageKey, emoji: string): Promise<SentMessage>;
459
477
  unreact(message: Message | SentMessage | MessageKey): Promise<SentMessage>;
460
478
  text(chatId: string, text: string, mentions?: MentionTarget[]): Promise<SentMessage>;
479
+ buttons(chatId: string, content: ButtonsContent): Promise<SentMessage>;
461
480
  }
462
481
 
463
482
  interface CommandCatalogView {
@@ -886,4 +905,4 @@ declare class SqliteMuteStore implements MuteStore {
886
905
  close(): void;
887
906
  }
888
907
 
889
- export { AccountService, type AddMuteOptions, type AddMuteResult, type AppEvents, type AppHealth, type AppHealthStatus, ArgsParser, type AudioContent, type BooleanOption, Browser, 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 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 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 MessageContent, type MessageContentKind, 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 };
908
+ 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 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 };
package/dist/index.js CHANGED
@@ -2036,6 +2036,9 @@ var MessageService = class {
2036
2036
  }
2037
2037
  return this.send(chatId, content);
2038
2038
  }
2039
+ buttons(chatId, content) {
2040
+ return this.send(chatId, content);
2041
+ }
2039
2042
  };
2040
2043
 
2041
2044
  // src/app/whanext-app.ts
@@ -2281,6 +2284,8 @@ import {
2281
2284
  Browsers,
2282
2285
  DisconnectReason,
2283
2286
  downloadMediaMessage,
2287
+ generateWAMessageFromContent,
2288
+ isJidGroup,
2284
2289
  makeWASocket,
2285
2290
  proto,
2286
2291
  useMultiFileAuthState
@@ -2685,6 +2690,9 @@ var BaileysProvider = class {
2685
2690
  }
2686
2691
  }
2687
2692
  async sendMessage(chatId, content, replyTo) {
2693
+ if ("buttons" in content) {
2694
+ return this.#sendButtons(chatId, content, replyTo);
2695
+ }
2688
2696
  const socket = this.#requireSocket();
2689
2697
  const options = replyTo ? {
2690
2698
  quoted: {
@@ -2928,6 +2936,111 @@ var BaileysProvider = class {
2928
2936
  }
2929
2937
  return this.#socket;
2930
2938
  }
2939
+ async #sendButtons(chatId, content, replyTo) {
2940
+ const socket = this.#requireSocket();
2941
+ const userJid = socket.user?.id;
2942
+ if (!userJid) {
2943
+ throw new WhaNextError(
2944
+ "PROVIDER_ERROR",
2945
+ "WhatsApp did not expose the current account identity for the interactive message."
2946
+ );
2947
+ }
2948
+ const interactiveMessage = proto.Message.InteractiveMessage.create({
2949
+ ...content.title !== void 0 ? {
2950
+ header: {
2951
+ title: content.title,
2952
+ hasMediaAttachment: false
2953
+ }
2954
+ } : {},
2955
+ body: { text: content.text },
2956
+ ...content.footer !== void 0 ? { footer: { text: content.footer } } : {},
2957
+ ...content.mentions && content.mentions.length > 0 ? {
2958
+ contextInfo: {
2959
+ mentionedJid: this.#mentions(content.mentions)
2960
+ }
2961
+ } : {},
2962
+ nativeFlowMessage: {
2963
+ buttons: content.buttons.map((button) => {
2964
+ if (button.type === "copy") {
2965
+ return {
2966
+ name: "cta_copy",
2967
+ buttonParamsJson: JSON.stringify({
2968
+ display_text: button.label,
2969
+ copy_code: button.code
2970
+ })
2971
+ };
2972
+ }
2973
+ return {
2974
+ name: "cta_url",
2975
+ buttonParamsJson: JSON.stringify({
2976
+ display_text: button.label,
2977
+ url: button.url,
2978
+ merchant_url: button.url
2979
+ })
2980
+ };
2981
+ }),
2982
+ messageParamsJson: "{}",
2983
+ messageVersion: 1
2984
+ }
2985
+ });
2986
+ const quoted = replyTo ? {
2987
+ key: this.#toWaKey(replyTo),
2988
+ message: { conversation: "" }
2989
+ } : void 0;
2990
+ const generated = generateWAMessageFromContent(
2991
+ chatId,
2992
+ { interactiveMessage },
2993
+ {
2994
+ userJid,
2995
+ ...quoted ? { quoted } : {}
2996
+ }
2997
+ );
2998
+ const messageId = generated.key.id;
2999
+ if (!generated.message || !messageId) {
3000
+ throw new WhaNextError(
3001
+ "PROVIDER_ERROR",
3002
+ "WhatsApp could not generate the interactive message."
3003
+ );
3004
+ }
3005
+ await socket.relayMessage(chatId, generated.message, {
3006
+ messageId,
3007
+ additionalNodes: this.#interactiveRelayNodes(chatId)
3008
+ });
3009
+ return this.#sent(generated);
3010
+ }
3011
+ #interactiveRelayNodes(chatId) {
3012
+ const bizNode = {
3013
+ tag: "biz",
3014
+ attrs: {
3015
+ actual_actors: "2",
3016
+ host_storage: "2",
3017
+ privacy_mode_ts: (Math.floor(Date.now() / 1e3) - 77980457).toString()
3018
+ },
3019
+ content: [
3020
+ {
3021
+ tag: "interactive",
3022
+ attrs: { type: "native_flow", v: "1" },
3023
+ content: [
3024
+ {
3025
+ tag: "native_flow",
3026
+ attrs: { v: "9", name: "mixed" }
3027
+ }
3028
+ ]
3029
+ },
3030
+ {
3031
+ tag: "quality_control",
3032
+ attrs: { source_type: "third_party" }
3033
+ }
3034
+ ]
3035
+ };
3036
+ if (isJidGroup(chatId)) {
3037
+ return [bizNode];
3038
+ }
3039
+ return [
3040
+ { tag: "bot", attrs: { biz_bot: "1" } },
3041
+ bizNode
3042
+ ];
3043
+ }
2931
3044
  #toContent(content) {
2932
3045
  if ("text" in content) {
2933
3046
  return {