@whanext/core 0.11.0 → 0.13.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,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.13.0
4
+
5
+ - Adicionado `MessageService.repost()` para republicar qualquer mensagem do WhatsApp recebida recentemente, sem criar uma resposta.
6
+ - Adicionado suporte a `repostMessage()` em nível de provedor, com injeção de menções.
7
+ - As republicações preservam o payload estruturado original, permitindo que textos, mídias, figurinhas, enquetes, localizações, contatos e mensagens de catálogo sejam reutilizados através da API de alto nível.
8
+
9
+
10
+ ## 0.12.0 - Message content classification
11
+
12
+ ### Adicionado
13
+
14
+ - `Message.contentKind` para classificar o payload recebido sem expor tipos do Baileys.
15
+ - Novo tipo público `MessageContentKind`.
16
+ - Classificação nativa para texto, imagem, vídeo, áudio, documento, sticker, localização, contato, enquete e catálogo/produto.
17
+ - `unknown` como fallback para formatos ainda não normalizados pela biblioteca.
18
+
19
+ ### Compatibilidade
20
+
21
+ - `contentKind` é opcional no contrato público para manter compatibilidade com providers customizados e objetos `Message` criados por aplicações existentes.
22
+ - O `BaileysProvider` oficial sempre preenche `contentKind` nas mensagens recebidas.
23
+ - `message.media.kind` continua sendo a API indicada para mídia baixável; `contentKind` complementa essa API com tipos não-mídia.
3
24
 
4
25
  ## 0.11.0 - Command discovery
5
26
 
package/README.md CHANGED
@@ -220,6 +220,22 @@ if (app.isReady) {
220
220
 
221
221
  Toda mensagem possui `message.sender: User`. Menções ficam em `message.mentionedUsers`, e o remetente de um reply em `message.quoted?.sender`.
222
222
 
223
+ O provider Baileys também classifica o payload em `message.contentKind`, sem exigir acesso aos tipos internos do Baileys ou download da mídia:
224
+
225
+ ```ts
226
+ app.on('message', async (message) => {
227
+ if (message.contentKind === 'location') {
228
+ console.log('Localização recebida');
229
+ }
230
+
231
+ if (message.contentKind === 'poll') {
232
+ console.log('Enquete recebida');
233
+ }
234
+ });
235
+ ```
236
+
237
+ Os valores disponíveis são `text`, `image`, `video`, `audio`, `document`, `sticker`, `location`, `contact`, `poll`, `catalog` e `unknown`. Para mídia baixável, continue usando `message.media.kind`.
238
+
223
239
  ```ts
224
240
  app.on('message', async (message) => {
225
241
  console.log(message.sender.id);
@@ -787,3 +803,16 @@ MIT. Consulte [LICENSE](./LICENSE).
787
803
  ## Aviso
788
804
 
789
805
  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.
806
+
807
+
808
+ ### Reposting received messages
809
+
810
+ WhaNext can repost a recently received message while preserving its original WhatsApp payload:
811
+
812
+ ```ts
813
+ await app.message.repost(message.quoted!.key, message.chatId, {
814
+ mentions: users,
815
+ });
816
+ ```
817
+
818
+ The source message must still be present in the provider recent-message cache. This avoids downloading and reconstructing media or structured messages.
package/dist/index.d.ts CHANGED
@@ -65,6 +65,14 @@ interface MessageKey {
65
65
  participantId?: string;
66
66
  }
67
67
  type MediaKind = 'image' | 'video' | 'audio' | 'document' | 'sticker';
68
+ /**
69
+ * High-level classification of the received WhatsApp message payload.
70
+ *
71
+ * `media.kind` remains the source of truth for downloadable media.
72
+ * `contentKind` additionally exposes non-media payloads such as locations,
73
+ * contacts, polls and catalog/product messages without leaking Baileys types.
74
+ */
75
+ type MessageContentKind = 'text' | 'image' | 'video' | 'audio' | 'document' | 'sticker' | 'location' | 'contact' | 'poll' | 'catalog' | 'unknown';
68
76
  interface MessageMedia {
69
77
  kind: MediaKind;
70
78
  mimetype?: string;
@@ -99,6 +107,7 @@ interface Message {
99
107
  isReply: boolean;
100
108
  isViewOnce: boolean;
101
109
  hasMedia: boolean;
110
+ contentKind?: MessageContentKind;
102
111
  media?: MessageMedia;
103
112
  quoted?: QuotedMessage;
104
113
  }
@@ -120,6 +129,11 @@ type MediaSource = Uint8Array | {
120
129
  path: string;
121
130
  };
122
131
  type MentionTarget = string | User;
132
+ /** Options for reposting a previously received message without quoting it. */
133
+ interface RepostMessageOptions {
134
+ /** Hidden or visible mentions injected into the reposted payload. */
135
+ mentions?: readonly MentionTarget[];
136
+ }
123
137
  interface TextContent {
124
138
  text: string;
125
139
  mentions?: MentionTarget[];
@@ -229,6 +243,7 @@ interface WhatsAppProvider {
229
243
  requestPairingCode(phone: string): Promise<string>;
230
244
  on<Event extends keyof ProviderEvents>(event: Event, listener: (payload: ProviderEvents[Event]) => void | Promise<void>): Unsubscribe;
231
245
  sendMessage(chatId: string, content: MessageContent, replyTo?: MessageKey): Promise<SentMessage>;
246
+ repostMessage(source: MessageKey, chatId: string, options?: RepostMessageOptions): Promise<SentMessage>;
232
247
  reactToMessage(key: MessageKey, emoji?: string): Promise<SentMessage>;
233
248
  downloadMedia(key: MessageKey): Promise<DownloadedMedia>;
234
249
  editMessage(key: MessageKey, content: string): Promise<SentMessage>;
@@ -431,6 +446,12 @@ declare class MessageService {
431
446
  constructor(provider: WhatsAppProvider);
432
447
  send(chatId: string, content: MessageContent): Promise<SentMessage>;
433
448
  reply(message: Message, content: MessageContent): Promise<SentMessage>;
449
+ /**
450
+ * Reposts a cached received message into a chat without creating a reply.
451
+ * The original payload is preserved by the provider, so this works for
452
+ * media and structured WhatsApp messages as well as text.
453
+ */
454
+ repost(source: Message | MessageKey, chatId: string, options?: RepostMessageOptions): Promise<SentMessage>;
434
455
  edit(message: Message | SentMessage | MessageKey, text: string): Promise<SentMessage>;
435
456
  delete(message: Message | SentMessage | MessageKey): Promise<void>;
436
457
  react(message: Message | SentMessage | MessageKey, emoji: string): Promise<SentMessage>;
@@ -791,4 +812,4 @@ declare class SqliteMuteStore implements MuteStore {
791
812
  close(): void;
792
813
  }
793
814
 
794
- export { 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 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 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 MessageKey, type MessageMedia, 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 RouterOptions, type SentMessage, SqliteMuteStore, type StickerContent, type StoredMute, type StringOption, type TextContent, User, type UserData, type UserOption, type VideoContent, WhaNextApp, WhaNextError, type WhaNextErrorCode, type WhatsAppProvider, create, defineCommand, defineCommandGroup, defineCommands, defineSubcommand, guards, isCommandGroup, loadCommands, option, toWhaNextError };
815
+ export { 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 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 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 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, type WhatsAppProvider, create, defineCommand, defineCommandGroup, defineCommands, defineSubcommand, guards, isCommandGroup, loadCommands, option, toWhaNextError };
package/dist/index.js CHANGED
@@ -1946,6 +1946,15 @@ var MessageService = class {
1946
1946
  reply(message, content) {
1947
1947
  return this.#provider.sendMessage(message.chatId, content, message.keys);
1948
1948
  }
1949
+ /**
1950
+ * Reposts a cached received message into a chat without creating a reply.
1951
+ * The original payload is preserved by the provider, so this works for
1952
+ * media and structured WhatsApp messages as well as text.
1953
+ */
1954
+ repost(source, chatId, options = {}) {
1955
+ const key = "keys" in source ? source.keys : source;
1956
+ return this.#provider.repostMessage(key, chatId, options);
1957
+ }
1949
1958
  edit(message, text) {
1950
1959
  const key = "keys" in message ? message.keys : message;
1951
1960
  return this.#provider.editMessage(key, text);
@@ -2301,6 +2310,7 @@ function normalizeBaileysMessage(input) {
2301
2310
  });
2302
2311
  const mentionedUsers = (context?.mentionedJid ?? []).map((identity) => User.fromIdentities([identity]));
2303
2312
  const media = getMedia(type, node, Boolean(input.key.isViewOnce));
2313
+ const contentKind = getContentKind(type);
2304
2314
  const text = getText(content);
2305
2315
  const caption = getCaption(content);
2306
2316
  const quoted = getQuoted(context, chatId);
@@ -2318,7 +2328,8 @@ function normalizeBaileysMessage(input) {
2318
2328
  isGroup: chatId.endsWith("@g.us"),
2319
2329
  isReply: quoted !== void 0,
2320
2330
  isViewOnce: media?.viewOnce ?? false,
2321
- hasMedia: media !== void 0
2331
+ hasMedia: media !== void 0,
2332
+ contentKind
2322
2333
  };
2323
2334
  if (senderJid !== void 0) message.senderJid = senderJid;
2324
2335
  if (senderLid !== void 0) {
@@ -2331,6 +2342,42 @@ function normalizeBaileysMessage(input) {
2331
2342
  if (quoted !== void 0) message.quoted = quoted;
2332
2343
  return message;
2333
2344
  }
2345
+ function getContentKind(type) {
2346
+ switch (String(type ?? "")) {
2347
+ case "conversation":
2348
+ case "extendedTextMessage":
2349
+ case "buttonsResponseMessage":
2350
+ case "listResponseMessage":
2351
+ case "templateButtonReplyMessage":
2352
+ return "text";
2353
+ case "imageMessage":
2354
+ return "image";
2355
+ case "videoMessage":
2356
+ return "video";
2357
+ case "audioMessage":
2358
+ return "audio";
2359
+ case "documentMessage":
2360
+ case "documentWithCaptionMessage":
2361
+ return "document";
2362
+ case "stickerMessage":
2363
+ return "sticker";
2364
+ case "locationMessage":
2365
+ case "liveLocationMessage":
2366
+ return "location";
2367
+ case "contactMessage":
2368
+ case "contactsArrayMessage":
2369
+ return "contact";
2370
+ case "pollCreationMessage":
2371
+ case "pollCreationMessageV2":
2372
+ case "pollCreationMessageV3":
2373
+ return "poll";
2374
+ case "productMessage":
2375
+ case "orderMessage":
2376
+ return "catalog";
2377
+ default:
2378
+ return "unknown";
2379
+ }
2380
+ }
2334
2381
  function normalizeKey(key) {
2335
2382
  const normalized = {
2336
2383
  id: key.id ?? "",
@@ -2545,6 +2592,25 @@ var BaileysProvider = class {
2545
2592
  const result = await socket.sendMessage(chatId, this.#toContent(content), options);
2546
2593
  return this.#sent(result);
2547
2594
  }
2595
+ async repostMessage(source, chatId, options = {}) {
2596
+ const original = this.#messageStore.get(this.#messageStoreKey(source));
2597
+ if (!original?.message) {
2598
+ throw new WhaNextError(
2599
+ "MESSAGE_NOT_FOUND",
2600
+ "The source message is no longer available in the recent-message cache.",
2601
+ {
2602
+ context: { messageId: source.id, chatId: source.chatId },
2603
+ recoverable: true
2604
+ }
2605
+ );
2606
+ }
2607
+ const content = {
2608
+ forward: original,
2609
+ ...options.mentions && options.mentions.length > 0 ? { mentions: this.#mentions(options.mentions) } : {}
2610
+ };
2611
+ const result = await this.#requireSocket().sendMessage(chatId, content);
2612
+ return this.#sent(result);
2613
+ }
2548
2614
  async reactToMessage(key, emoji) {
2549
2615
  const result = await this.#requireSocket().sendMessage(key.chatId, {
2550
2616
  react: {