@pontalabs/baileys 1.2.3 → 1.2.5

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.
@@ -14,6 +14,7 @@ import { USyncQuery, USyncUser } from '../WAUSync/index.js';
14
14
  import { makeNewsletterSocket } from './newsletter.js';
15
15
  import kikyy from './dugong.js';
16
16
  import { generateTableContent, generateTableContentV2, generateListContent, generateCodeBlockContent, generateCodeBlockContentV2, generateLinkContent, generateLinkContentV2, generateRichMessageContent, generateUnifiedResponseContent, captureUnifiedResponse, generateLatexContent, generateLatexImageContent, generateLatexInlineImageContent } from '../Utils/rich-message-utils.js';
17
+ import { AIRich, Button, ButtonV2, Carousel } from '../Utils/mbuilder.js';
17
18
  export const makeMessagesSocket = (config) => {
18
19
  const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: httpRequestOptions, patchMessageBeforeSending, cachedGroupMetadata, enableRecentMessageCache, maxMsgRetryCount } = config;
19
20
  const sock = makeNewsletterSocket(config);
@@ -1132,20 +1133,156 @@ export const makeMessagesSocket = (config) => {
1132
1133
  sendMessage: sendMessageImpl = async (jid, content, options = {}) => {
1133
1134
  const userJid = authState.creds.me.id;
1134
1135
 
1135
- // `buildDelay` sends each item in `content.items` as its own Rich AI
1136
- // message, waiting between items. Without buildDelay, the existing
1137
- // mixed-items behavior is unchanged (all items are one Rich AI message).
1138
- if (content && !Array.isArray(jid) && Array.isArray(content.items) && content.items.length > 1) {
1136
+ // `items` may contain Rich AI build descriptors as well as the
1137
+ // interactive builders exported by mbuilder.js. When buildDelay is
1138
+ // enabled, keep ONE WhatsApp message id and progressively edit that
1139
+ // message instead of sending a new bubble for every item.
1140
+ if (content && !Array.isArray(jid) && Array.isArray(content.items) && content.items.length > 0) {
1141
+ const items = content.items;
1139
1142
  const buildDelay = Number(options.buildDelay ?? content.buildDelay ?? 0);
1140
- if (Number.isFinite(buildDelay) && buildDelay > 0) {
1143
+ const hasDelay = Number.isFinite(buildDelay) && buildDelay > 0;
1144
+ const isInteractiveBuilder = (item) => item instanceof Button || item instanceof ButtonV2 || item instanceof Carousel;
1145
+ const isAIRichBuilder = (item) => item instanceof AIRich;
1146
+ const hasInteractiveBuilder = items.some(isInteractiveBuilder);
1147
+
1148
+ if (hasDelay || hasInteractiveBuilder) {
1141
1149
  const sendOptions = { ...options };
1142
1150
  delete sendOptions.buildDelay;
1143
- const results = [];
1144
- for (let index = 0; index < content.items.length; index++) {
1145
- if (index > 0) await delay(buildDelay);
1146
- results.push(await sendMessageImpl(jid, content.items[index], sendOptions));
1151
+ delete sendOptions.additionalNodes;
1152
+
1153
+ // One target id for the entire progressive sequence. If the
1154
+ // caller supplied messageId, it is used; otherwise the first
1155
+ // generated message id becomes the target for all edits.
1156
+ let targetMessageId = options.messageId;
1157
+ let lastMessage;
1158
+
1159
+ const buildItemMessage = async (item) => {
1160
+ if (isInteractiveBuilder(item)) {
1161
+ item.setClient({ waUploadToServer });
1162
+ return await item.build(jid, { ...sendOptions, messageId: targetMessageId });
1163
+ }
1164
+
1165
+ if (isAIRichBuilder(item)) {
1166
+ return await item.build(jid, { ...sendOptions, messageId: targetMessageId });
1167
+ }
1168
+
1169
+ // Send Rich AI descriptors through the normal Rich AI
1170
+ // generator, but force the same message id.
1171
+ return await generateWAMessage(jid, { items: [item] }, {
1172
+ logger,
1173
+ userJid,
1174
+ upload: waUploadToServer,
1175
+ mediaCache: config.mediaCache,
1176
+ options: config.options,
1177
+ ...sendOptions,
1178
+ messageId: targetMessageId || generateMessageIDV2(userJid)
1179
+ });
1180
+ };
1181
+
1182
+ const isRichAiMessage = (msg) => !!(
1183
+ msg?.message?.botForwardedMessage?.message?.richResponseMessage ||
1184
+ msg?.message?.richResponseMessage
1185
+ );
1186
+
1187
+ const relayEdit = async (msg, targetId) => {
1188
+ const editedMessage = msg.message;
1189
+ const editMessage = await generateWAMessageFromContent(jid, {
1190
+ botForwardedMessage: { message: { protocolMessage: {
1191
+ key: { remoteJid: jid, fromMe: true, id: targetId },
1192
+ type: 14,
1193
+ editedMessage
1194
+ } } }
1195
+ }, { messageId: generateMessageIDV2(userJid) });
1196
+
1197
+ await relayMessage(jid, editMessage.message, {
1198
+ messageId: editMessage.key.id,
1199
+ additionalNodes: options.additionalNodes || []
1200
+ });
1201
+ return editMessage;
1202
+ };
1203
+
1204
+ // Build a cumulative snapshot on every step. This is the
1205
+ // important part of buildDelay: each edit contains everything
1206
+ // built so far, rather than replacing the previous message
1207
+ // with only the newest item.
1208
+ const cumulativeItems = [];
1209
+
1210
+ for (let index = 0; index < items.length; index++) {
1211
+ if (index > 0 && hasDelay) {
1212
+ await delay(buildDelay);
1213
+ }
1214
+
1215
+ cumulativeItems.push(items[index]);
1216
+
1217
+ // AIRich instances represent an already assembled rich
1218
+ // response. For these, use their own build() directly.
1219
+ // For ordinary descriptors we rebuild the cumulative set.
1220
+ let msg;
1221
+ if (cumulativeItems.length === 1 && isAIRichBuilder(cumulativeItems[0])) {
1222
+ msg = await buildItemMessage(cumulativeItems[0]);
1223
+ } else if (cumulativeItems.every(isInteractiveBuilder) && cumulativeItems.length === 1) {
1224
+ msg = await buildItemMessage(cumulativeItems[0]);
1225
+ } else {
1226
+ const hasAirich = cumulativeItems.some(isAIRichBuilder);
1227
+ if (hasAirich) {
1228
+ // Mixed AIRich + descriptor items are flattened by
1229
+ // taking the AIRich message as the current snapshot
1230
+ // and are intentionally not sent as media.
1231
+ const ai = cumulativeItems.find(isAIRichBuilder);
1232
+ msg = await ai.build(jid, { ...sendOptions, messageId: targetMessageId });
1233
+ } else {
1234
+ msg = await generateWAMessage(jid, { items: cumulativeItems }, {
1235
+ logger,
1236
+ userJid,
1237
+ upload: waUploadToServer,
1238
+ mediaCache: config.mediaCache,
1239
+ options: config.options,
1240
+ ...sendOptions,
1241
+ messageId: targetMessageId || generateMessageIDV2(userJid)
1242
+ });
1243
+ }
1244
+ }
1245
+
1246
+ if (!targetMessageId) {
1247
+ targetMessageId = msg.key.id;
1248
+ }
1249
+
1250
+ if (index === 0) {
1251
+ const additionalNodes = isInteractiveBuilder(items[index])
1252
+ ? [
1253
+ {
1254
+ tag: 'biz',
1255
+ attrs: {},
1256
+ content: [{
1257
+ tag: 'interactive',
1258
+ attrs: { type: 'native_flow', v: '1' },
1259
+ content: [{ tag: 'native_flow', attrs: { v: '9', name: 'mixed' } }],
1260
+ }],
1261
+ },
1262
+ ...(options.additionalNodes || []),
1263
+ ]
1264
+ : (options.additionalNodes || []);
1265
+
1266
+ await relayMessage(jid, msg.message, {
1267
+ messageId: targetMessageId,
1268
+ additionalNodes,
1269
+ ...sendOptions,
1270
+ });
1271
+
1272
+ if (isRichAiMessage(msg) && options.bypassDownload !== false) {
1273
+ await relayEdit(msg, targetMessageId);
1274
+ }
1275
+ } else {
1276
+ await relayEdit(msg, targetMessageId);
1277
+ }
1278
+
1279
+ lastMessage = msg;
1280
+ }
1281
+
1282
+ if (lastMessage) {
1283
+ lastMessage.key.id = targetMessageId;
1147
1284
  }
1148
- return results;
1285
+ return lastMessage;
1149
1286
  }
1150
1287
  }
1151
1288
  // Update rahmi's userJid in case it changed after login
@@ -1,7 +1,7 @@
1
1
  export declare const VERSION: string;
2
- export declare class Button { constructor(client: any, options?: any); loadFrom(data: any): this; build(jid: string, options?: any): any; send(jid: string, options?: any): Promise<any>; [key: string]: any; }
3
- export declare class ButtonV2 { constructor(client: any, options?: any); loadFrom(data: any): this; build(jid: string, options?: any): any; send(jid: string, options?: any): Promise<any>; [key: string]: any; }
4
- export declare class Carousel { constructor(client: any, options?: any); loadFrom(data: any): this; build(jid: string, options?: any): any; send(jid: string, options?: any): Promise<any>; [key: string]: any; }
2
+ export declare class Button { constructor(client?: any, options?: any); loadFrom(data: any): this; build(jid: string, options?: any): any; send(jid: string, options?: any): Promise<any>; [key: string]: any; }
3
+ export declare class ButtonV2 { constructor(client?: any, options?: any); loadFrom(data: any): this; build(jid: string, options?: any): any; send(jid: string, options?: any): Promise<any>; [key: string]: any; }
4
+ export declare class Carousel { constructor(client?: any, options?: any); loadFrom(data: any): this; build(jid: string, options?: any): any; send(jid: string, options?: any): Promise<any>; [key: string]: any; }
5
5
  export declare class AIRich {
6
6
  constructor(client: any, options?: { dynamic?: boolean; unsupportedTypeAlert?: boolean });
7
7
  loadFrom(msg: any): this;
@@ -38,6 +38,12 @@ export declare class AIRich {
38
38
  export declare class Toolkit { [key: string]: any; }
39
39
  export declare const build: {
40
40
  AIRich: (client: any, options?: any) => AIRich;
41
+ button: (client?: any) => Button;
42
+ buttonV2: (client?: any) => ButtonV2;
43
+ carousel: (client?: any) => Carousel;
44
+ Button: (client?: any) => Button;
45
+ ButtonV2: (client?: any) => ButtonV2;
46
+ Carousel: (client?: any) => Carousel;
41
47
  richText: (text: string) => any;
42
48
  richFOAText: (text: string) => any;
43
49
  richHtml: (html: string) => any;
@@ -557,11 +557,8 @@ class BaseBuilder {
557
557
  class Button extends BaseBuilder {
558
558
  #client;
559
559
 
560
- constructor(client) {
560
+ constructor(client = null) {
561
561
  super();
562
- if (!client) {
563
- throw new Error('Socket is required');
564
- }
565
562
  this.#client = client;
566
563
 
567
564
  this._buttons = [];
@@ -571,6 +568,11 @@ class Button extends BaseBuilder {
571
568
  this._params = {};
572
569
  }
573
570
 
571
+ setClient(client) {
572
+ this.#client = client;
573
+ return this;
574
+ }
575
+
574
576
  loadFrom(msg) {
575
577
  if (!msg) throw new Error('interactiveMessage needed');
576
578
  if (!msg.interactiveMessage) throw new Error('interactiveMessage not found');
@@ -891,18 +893,19 @@ class Button extends BaseBuilder {
891
893
  class ButtonV2 extends BaseBuilder {
892
894
  #client;
893
895
 
894
- constructor(client) {
896
+ constructor(client = null) {
895
897
  super();
896
- if (!client) {
897
- throw new Error('Socket is required');
898
- }
899
-
900
898
  this.#client = client;
901
899
  this._image;
902
900
  this._data;
903
901
  this._buttons = [];
904
902
  }
905
903
 
904
+ setClient(client) {
905
+ this.#client = client;
906
+ return this;
907
+ }
908
+
906
909
  loadFrom(msg) {
907
910
  if (!msg) throw new Error('buttonsMessage needed');
908
911
  if (!msg.buttonsMessage) throw new Error('buttonsMessage not found');
@@ -1059,16 +1062,17 @@ class ButtonV2 extends BaseBuilder {
1059
1062
  class Carousel extends BaseBuilder {
1060
1063
  #client;
1061
1064
 
1062
- constructor(client) {
1065
+ constructor(client = null) {
1063
1066
  super();
1064
- if (!client) {
1065
- throw new Error('Socket is required');
1066
- }
1067
-
1068
1067
  this.#client = client;
1069
1068
  this._cards = [];
1070
1069
  }
1071
1070
 
1071
+ setClient(client) {
1072
+ this.#client = client;
1073
+ return this;
1074
+ }
1075
+
1072
1076
  loadFrom(msg) {
1073
1077
  if (!msg) throw new Error('interactiveMessage needed');
1074
1078
  if (!msg.interactiveMessage) throw new Error('interactiveMessage not found');
@@ -3201,6 +3205,13 @@ class AIRich extends BaseBuilder {
3201
3205
  */
3202
3206
  const build = {
3203
3207
  AIRich: (client, options) => new AIRich(client, options),
3208
+ button: (client = null) => new Button(client),
3209
+ buttonV2: (client = null) => new ButtonV2(client),
3210
+ carousel: (client = null) => new Carousel(client),
3211
+ // Class aliases for developers who prefer constructor-style naming.
3212
+ Button: (client = null) => new Button(client),
3213
+ ButtonV2: (client = null) => new ButtonV2(client),
3214
+ Carousel: (client = null) => new Carousel(client),
3204
3215
  richText: (text) => ({ richText: text }),
3205
3216
  richFOAText: (text) => ({ richFOAText: text }),
3206
3217
  richCode: (code, language = 'javascript') => ({ richCode: code, language }),
@@ -826,6 +826,34 @@ const normalizeTableInput = (t, fallbackTitle, fallbackNoHeading) => {
826
826
  return { rows: [], title: fallbackTitle, noHeading: fallbackNoHeading };
827
827
  };
828
828
 
829
+ /** Buat unified section untuk RICH CARD / ENTITY */
830
+ const makeRichCardSection = (data) => {
831
+ const item = data || {};
832
+ const thumbnailUrl = item.thumbnail_url ?? item.thumbnailUrl ?? item.thumbnail ?? '';
833
+ const profileUrl = item.profile_url ?? item.profileUrl ?? thumbnailUrl;
834
+
835
+ return {
836
+ view_model: {
837
+ primitive: {
838
+ __typename: 'GenAICompactEntityPrimitive',
839
+ title: item.title ?? '',
840
+ subtitle: item.subtitle ?? '',
841
+ secondary_subtitle: item.description ?? '',
842
+ entity_id: item.entity_id ?? item.entityId ?? '',
843
+ entity_url: item.entity_url ?? item.entityUrl ?? '',
844
+ entity_type: item.entity_type ?? 'PAGE',
845
+ action_type: item.action_type ?? 'FOLLOW',
846
+ is_verified: !!item.verification,
847
+ image: {
848
+ url: thumbnailUrl,
849
+ url_fallback: profileUrl
850
+ }
851
+ },
852
+ __typename: 'GenAISingleLayoutViewModel'
853
+ }
854
+ };
855
+ };
856
+
829
857
  /* ─────────────────────────────────────────────────────────────
830
858
  MAIN BUILDER — prepareRichResponseMessage
831
859
  ───────────────────────────────────────────────────────────── */
@@ -839,7 +867,7 @@ export const prepareRichResponseMessage = (content) => {
839
867
  map: mapContent, latex, contentItems,
840
868
  // sub-types baru (prefixed 'rich' agar tidak konflik dengan media biasa)
841
869
  richImage, richVideo, reels, source, richProduct, richPost, tip, suggest,
842
- richWidget, widget, richFooterAction, footerAction, richMetadata, metadata, richFOAText, foaText, richHtml,
870
+ richWidget, widget, richFooterAction, footerAction, richMetadata, metadata, richFOAText, foaText, richHtml, richCard,
843
871
  // opsi tambahan (belum ada di versi ponta.zip semula)
844
872
  aiForwarded
845
873
  } = content;
@@ -928,6 +956,7 @@ export const prepareRichResponseMessage = (content) => {
928
956
  if (sub.footerAction != null) return push(null, makeFooterActionSection(sub.footerAction));
929
957
  if (sub.metadata != null) { const built = makeMetadataSub(sub.metadata); return push(built.sub, built.section); }
930
958
  if (sub.foaText != null) { const built = makeMetadataSub(sub.foaText, 'FOATextPrimitive'); return push(built.sub, built.section); }
959
+ if (sub.richCard != null) return push(null, makeRichCardSection(sub.richCard));
931
960
  if (sub.richHtml != null) return push(null, makeHtmlSection(sub.richHtml));
932
961
  // passthrough — kalau sudah bentuk proto submessage manual
933
962
  return push(sub, buildUnifiedSection(sub));
@@ -1538,6 +1567,7 @@ const translateReadmeItem = (item) => {
1538
1567
  if (item.richText != null) { out.text = item.richText; }
1539
1568
  if (item.richFOAText != null) { out.foaText = item.richFOAText; }
1540
1569
  if (item.richHtml != null) { out.richHtml = item.richHtml; }
1570
+ if (item.richCard != null) { out.richCard = item.richCard; }
1541
1571
  if (item.richCode != null) { out.code = item.richCode; out.language = item.language; }
1542
1572
  if (item.richTable != null) { out.table = item.richTable; }
1543
1573
  if (item.richSource != null) { out.source = item.richSource; }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pontalabs/baileys",
3
3
  "type": "module",
4
- "version": "1.2.3",
4
+ "version": "1.2.5",
5
5
  "description": "PontaLabs Baileys is a lightweight, modern, and customizable WhatsApp Web API library built on Baileys.",
6
6
  "keywords": [
7
7
  "whatsapp",