@nuiisweety/baileys 0.1.1 → 0.1.3

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.
@@ -4,7 +4,25 @@ import { LANGUAGE_KEYWORDS } from '../WABinary/constants.js';
4
4
  import { CodeHighlightType, RichSubMessageType } from '../Types/RichType.js';
5
5
  import { proto } from '../../WAProto/index.js';
6
6
  import { unixTimestampSeconds } from './generics.js';
7
+
7
8
  const NOOP = new Set([]);
9
+
10
+ /* ─────────────────────────────────────────────────────────────
11
+ LATEX URL GENERATOR
12
+ WhatsApp client membutuhkan URL gambar render untuk setiap
13
+ LaTeX expression. Tanpa URL, pesan tampil kosong.
14
+ Gunakan latex.codecogs.com sebagai fallback gratis~
15
+ ───────────────────────────────────────────────────────────── */
16
+ const buildLatexUrl = (expression) => {
17
+ if (!expression) return null;
18
+ const encoded = encodeURIComponent(expression);
19
+ return `https://latex.codecogs.com/png.image?\dpi{150}\bg{white}${encoded}`;
20
+ };
21
+
22
+ /* ─────────────────────────────────────────────────────────────
23
+ TOKENIZER
24
+ ───────────────────────────────────────────────────────────── */
25
+
8
26
  export const tokenizeCode = (code, language = 'javascript') => {
9
27
  const keywords = LANGUAGE_KEYWORDS[language] || NOOP;
10
28
  const blocks = [];
@@ -13,181 +31,578 @@ export const tokenizeCode = (code, language = 'javascript') => {
13
31
  while ((match = LEXER_REGEX.exec(code)) !== null) {
14
32
  if (match[1]) {
15
33
  blocks.push({ highlightType: CodeHighlightType.COMMENT, codeContent: match[1] });
16
- }
17
- else if (match[2]) {
34
+ } else if (match[2]) {
18
35
  blocks.push({ highlightType: CodeHighlightType.STRING, codeContent: match[2] });
19
- }
20
- else if (match[3]) {
36
+ } else if (match[3]) {
21
37
  blocks.push({
22
38
  highlightType: keywords.has(match[3]) ? CodeHighlightType.KEYWORD : CodeHighlightType.METHOD,
23
39
  codeContent: match[3],
24
40
  });
25
- }
26
- else if (match[4]) {
41
+ } else if (match[4]) {
27
42
  blocks.push({
28
43
  highlightType: keywords.has(match[4]) ? CodeHighlightType.KEYWORD : CodeHighlightType.DEFAULT,
29
44
  codeContent: match[4],
30
45
  });
31
- }
32
- else if (match[5]) {
46
+ } else if (match[5]) {
33
47
  blocks.push({ highlightType: CodeHighlightType.NUMBER, codeContent: match[5] });
34
- }
35
- else {
48
+ } else {
36
49
  blocks.push({ highlightType: CodeHighlightType.DEFAULT, codeContent: match[6] });
37
50
  }
38
51
  }
39
52
  return blocks;
40
53
  };
54
+
55
+ /* ─────────────────────────────────────────────────────────────
56
+ INCOMING DECODER — parse rich message yang diterima
57
+ ───────────────────────────────────────────────────────────── */
58
+
59
+ /**
60
+ * Parse sebuah AIRichResponseSubMessage proto menjadi object JS yang mudah dipakai.
61
+ * Return null kalau tipe tidak dikenal.
62
+ */
63
+ export const parseRichSubMessage = (submessage) => {
64
+ if (!submessage) return null;
65
+ const type = submessage.messageType;
66
+
67
+ switch (type) {
68
+ case RichSubMessageType.TEXT:
69
+ return {
70
+ type: 'text',
71
+ text: submessage.messageText || '',
72
+ };
73
+
74
+ case RichSubMessageType.CODE:
75
+ return {
76
+ type: 'code',
77
+ language: submessage.codeMetadata?.codeLanguage || 'plain',
78
+ blocks: (submessage.codeMetadata?.codeBlocks || []).map(b => ({
79
+ highlight: CodeHighlightType[b.highlightType] ?? 'DEFAULT',
80
+ content: b.codeContent || '',
81
+ })),
82
+ /** Helper: ambil source code mentah tanpa highlight info */
83
+ get raw() {
84
+ return this.blocks.map(b => b.content).join('');
85
+ },
86
+ };
87
+
88
+ case RichSubMessageType.TABLE:
89
+ return {
90
+ type: 'table',
91
+ title: submessage.tableMetadata?.title || '',
92
+ rows: (submessage.tableMetadata?.rows || []).map(row => ({
93
+ isHeading: row.isHeading ?? false,
94
+ items: row.items || [],
95
+ })),
96
+ };
97
+
98
+ case RichSubMessageType.GRID_IMAGE:
99
+ return {
100
+ type: 'gridImage',
101
+ gridImageUrl: submessage.gridImageMetadata?.gridImageUrl || null,
102
+ imageUrls: submessage.gridImageMetadata?.imageUrls || [],
103
+ };
104
+
105
+ case RichSubMessageType.INLINE_IMAGE:
106
+ return {
107
+ type: 'inlineImage',
108
+ imageUrl: submessage.imageMetadata?.imageUrl || null,
109
+ imageText: submessage.imageMetadata?.imageText || '',
110
+ alignment: submessage.imageMetadata?.alignment ?? 0,
111
+ tapLinkUrl: submessage.imageMetadata?.tapLinkUrl || null,
112
+ };
113
+
114
+ case RichSubMessageType.DYNAMIC:
115
+ return {
116
+ type: 'dynamic',
117
+ dynamicType: submessage.dynamicMetadata?.type ?? 0,
118
+ version: submessage.dynamicMetadata?.version ?? 0,
119
+ url: submessage.dynamicMetadata?.url || null,
120
+ loopCount: submessage.dynamicMetadata?.loopCount ?? 0,
121
+ };
122
+
123
+ case RichSubMessageType.MAP:
124
+ return {
125
+ type: 'map',
126
+ centerLatitude: submessage.mapMetadata?.centerLatitude ?? 0,
127
+ centerLongitude: submessage.mapMetadata?.centerLongitude ?? 0,
128
+ latitudeDelta: submessage.mapMetadata?.latitudeDelta ?? 0,
129
+ longitudeDelta: submessage.mapMetadata?.longitudeDelta ?? 0,
130
+ showInfoList: submessage.mapMetadata?.showInfoList ?? false,
131
+ annotations: (submessage.mapMetadata?.annotations || []).map(a => ({
132
+ number: a.annotationNumber ?? 0,
133
+ latitude: a.latitude ?? 0,
134
+ longitude: a.longitude ?? 0,
135
+ title: a.title || '',
136
+ body: a.body || '',
137
+ })),
138
+ };
139
+
140
+ case RichSubMessageType.LATEX:
141
+ return {
142
+ type: 'latex',
143
+ text: submessage.latexMetadata?.text || '',
144
+ expressions: (submessage.latexMetadata?.expressions || []).map(e => ({
145
+ expression: e.latexExpression || '',
146
+ url: e.url || null,
147
+ width: e.width ?? 0,
148
+ height: e.height ?? 0,
149
+ })),
150
+ };
151
+
152
+ case RichSubMessageType.CONTENT_ITEMS:
153
+ return {
154
+ type: 'contentItems',
155
+ contentType: submessage.contentItemsMetadata?.contentType ?? 0,
156
+ items: (submessage.contentItemsMetadata?.itemsMetadata || []).map(item => {
157
+ if (item.reelItem) {
158
+ return {
159
+ kind: 'reel',
160
+ title: item.reelItem.title || '',
161
+ profileIconUrl: item.reelItem.profileIconUrl || null,
162
+ thumbnailUrl: item.reelItem.thumbnailUrl || null,
163
+ videoUrl: item.reelItem.videoUrl || null,
164
+ };
165
+ }
166
+ return { kind: 'unknown' };
167
+ }),
168
+ };
169
+
170
+ default:
171
+ return { type: 'unknown', raw: submessage };
172
+ }
173
+ };
174
+
175
+ /**
176
+ * Parse seluruh AIRichResponseMessage menjadi array parsed submessages.
177
+ * Bisa dipanggil langsung dari handler messages.upsert:
178
+ *
179
+ * const inner = normalizeMessageContent(msg.message)
180
+ * const parsed = parseRichMessage(inner?.richResponseMessage)
181
+ */
182
+ export const parseRichMessage = (richResponseMessage) => {
183
+ if (!richResponseMessage) return null;
184
+
185
+ const submessages = (richResponseMessage.submessages || [])
186
+ .map(parseRichSubMessage)
187
+ .filter(Boolean);
188
+
189
+ let unifiedData = null;
190
+ if (richResponseMessage.unifiedResponse?.data) {
191
+ try {
192
+ const buf = richResponseMessage.unifiedResponse.data;
193
+ unifiedData = JSON.parse(Buffer.isBuffer(buf) ? buf.toString('utf-8') : Buffer.from(buf).toString('utf-8'));
194
+ } catch (_) { /* ignore parse error */ }
195
+ }
196
+
197
+ return {
198
+ messageType: richResponseMessage.messageType ?? 0,
199
+ submessages,
200
+ unifiedData,
201
+ };
202
+ };
203
+
204
+ /* ─────────────────────────────────────────────────────────────
205
+ OUTGOING BUILDER — unified response JSON per subtype
206
+ ───────────────────────────────────────────────────────────── */
207
+
208
+ const buildUnifiedSection = (submessage) => {
209
+ switch (submessage.messageType) {
210
+ case RichSubMessageType.TEXT:
211
+ return {
212
+ view_model: {
213
+ primitive: {
214
+ text: submessage.messageText,
215
+ inline_entities: submessage.inlineEntities || [],
216
+ __typename: 'GenAIMarkdownTextUXPrimitive'
217
+ },
218
+ __typename: 'GenAISingleLayoutViewModel'
219
+ }
220
+ };
221
+
222
+ case RichSubMessageType.CODE: {
223
+ const cm = submessage.codeMetadata;
224
+ return {
225
+ view_model: {
226
+ primitive: {
227
+ language: cm.codeLanguage,
228
+ code_blocks: cm.codeBlocks.map(b => ({
229
+ content: b.codeContent,
230
+ type: CodeHighlightType[b.highlightType]
231
+ })),
232
+ __typename: 'GenAICodeUXPrimitive'
233
+ },
234
+ __typename: 'GenAISingleLayoutViewModel'
235
+ }
236
+ };
237
+ }
238
+
239
+ case RichSubMessageType.TABLE: {
240
+ const tm = submessage.tableMetadata;
241
+ return {
242
+ view_model: {
243
+ primitive: {
244
+ title: tm.title,
245
+ rows: tm.rows.map(row => ({
246
+ is_header: row.isHeading,
247
+ cells: row.items,
248
+ markdown_cells: row.items.map(item => ({ text: item }))
249
+ })),
250
+ __typename: 'GenATableUXPrimitive'
251
+ },
252
+ __typename: 'GenAISingleLayoutViewModel'
253
+ }
254
+ };
255
+ }
256
+
257
+ case RichSubMessageType.GRID_IMAGE: {
258
+ const gm = submessage.gridImageMetadata;
259
+ return {
260
+ view_model: {
261
+ primitive: {
262
+ grid_image_url: gm.gridImageUrl,
263
+ image_urls: gm.imageUrls,
264
+ __typename: 'GenAIGridImageUXPrimitive'
265
+ },
266
+ __typename: 'GenAISingleLayoutViewModel'
267
+ }
268
+ };
269
+ }
270
+
271
+ case RichSubMessageType.INLINE_IMAGE: {
272
+ const im = submessage.imageMetadata;
273
+ return {
274
+ view_model: {
275
+ primitive: {
276
+ image_url: im.imageUrl,
277
+ image_text: im.imageText,
278
+ alignment: im.alignment ?? 0,
279
+ tap_link_url: im.tapLinkUrl || null,
280
+ __typename: 'GenAIInlineImageUXPrimitive'
281
+ },
282
+ __typename: 'GenAISingleLayoutViewModel'
283
+ }
284
+ };
285
+ }
286
+
287
+ case RichSubMessageType.DYNAMIC: {
288
+ const dm = submessage.dynamicMetadata;
289
+ return {
290
+ view_model: {
291
+ primitive: {
292
+ type: dm.type ?? 0,
293
+ version: dm.version ?? 0,
294
+ url: dm.url,
295
+ loop_count: dm.loopCount ?? 0,
296
+ __typename: 'GenAIDynamicUXPrimitive'
297
+ },
298
+ __typename: 'GenAISingleLayoutViewModel'
299
+ }
300
+ };
301
+ }
302
+
303
+ case RichSubMessageType.MAP: {
304
+ const mm = submessage.mapMetadata;
305
+ return {
306
+ view_model: {
307
+ primitive: {
308
+ center_latitude: mm.centerLatitude,
309
+ center_longitude: mm.centerLongitude,
310
+ latitude_delta: mm.latitudeDelta,
311
+ longitude_delta: mm.longitudeDelta,
312
+ show_info_list: mm.showInfoList ?? false,
313
+ annotations: (mm.annotations || []).map(a => ({
314
+ annotation_number: a.annotationNumber,
315
+ latitude: a.latitude,
316
+ longitude: a.longitude,
317
+ title: a.title,
318
+ body: a.body
319
+ })),
320
+ __typename: 'GenAIMapUXPrimitive'
321
+ },
322
+ __typename: 'GenAISingleLayoutViewModel'
323
+ }
324
+ };
325
+ }
326
+
327
+ case RichSubMessageType.LATEX: {
328
+ const lm = submessage.latexMetadata;
329
+ return {
330
+ view_model: {
331
+ primitive: {
332
+ text: lm.text,
333
+ expressions: (lm.expressions || []).map(e => ({
334
+ latex_expression: e.latexExpression,
335
+ url: e.url,
336
+ width: e.width,
337
+ height: e.height,
338
+ font_height: e.fontHeight,
339
+ image_top_padding: e.imageTopPadding,
340
+ image_leading_padding: e.imageLeadingPadding,
341
+ image_bottom_padding: e.imageBottomPadding,
342
+ image_trailing_padding: e.imageTrailingPadding
343
+ })),
344
+ __typename: 'GenAILatexUXPrimitive'
345
+ },
346
+ __typename: 'GenAISingleLayoutViewModel'
347
+ }
348
+ };
349
+ }
350
+
351
+ case RichSubMessageType.CONTENT_ITEMS: {
352
+ const ci = submessage.contentItemsMetadata;
353
+ return {
354
+ view_model: {
355
+ primitive: {
356
+ content_type: ci.contentType ?? 0,
357
+ items_metadata: (ci.itemsMetadata || []).map(item => {
358
+ if (item.reelItem) {
359
+ return {
360
+ reel_item: {
361
+ title: item.reelItem.title,
362
+ profile_icon_url: item.reelItem.profileIconUrl,
363
+ thumbnail_url: item.reelItem.thumbnailUrl,
364
+ video_url: item.reelItem.videoUrl
365
+ }
366
+ };
367
+ }
368
+ return {};
369
+ }),
370
+ __typename: 'GenAIContentItemsUXPrimitive'
371
+ },
372
+ __typename: 'GenAISingleLayoutViewModel'
373
+ }
374
+ };
375
+ }
376
+
377
+ default:
378
+ return submessage;
379
+ }
380
+ };
381
+
41
382
  export const toUnified = (submessages) => ({
42
383
  response_id: randomUUID(),
43
- sections: submessages.map((submessage, index) => {
44
- switch (submessage.messageType) {
45
- case RichSubMessageType.CODE:
46
- const codeMetadata = submessage.codeMetadata;
47
- return {
48
- view_model: {
49
- primitive: {
50
- language: codeMetadata.codeLanguage,
51
- code_blocks: codeMetadata.codeBlocks.map((block) => ({ content: block.codeContent, type: CodeHighlightType[block.highlightType] })),
52
- __typename: 'GenAICodeUXPrimitive'
53
- },
54
- __typename: 'GenAISingleLayoutViewModel'
55
- }
56
- };
57
- case RichSubMessageType.TABLE:
58
- const tableMetadata = submessage.tableMetadata;
59
- return {
60
- view_model: {
61
- primitive: {
62
- title: tableMetadata.title,
63
- rows: tableMetadata.rows.map((row) => ({
64
- is_header: row.isHeading,
65
- cells: row.items,
66
- markdown_cells: row.items.map((item) => ({ text: item }))
67
- })),
68
- __typename: 'GenATableUXPrimitive'
69
- },
70
- __typename: 'GenAISingleLayoutViewModel'
71
- }
72
- };
73
- case RichSubMessageType.TEXT:
384
+ sections: submessages.map(buildUnifiedSection)
385
+ });
386
+
387
+ /* ─────────────────────────────────────────────────────────────
388
+ PREPARE HELPERS — builder per tipe submessage
389
+ ───────────────────────────────────────────────────────────── */
390
+
391
+ /** Buat submessage TEXT */
392
+ const makeTextSub = (text, inlineEntities) => ({
393
+ messageType: RichSubMessageType.TEXT,
394
+ messageText: text,
395
+ ...(inlineEntities ? { inlineEntities } : {})
396
+ });
397
+
398
+ /** Buat submessage CODE dengan auto-tokenize */
399
+ const makeCodeSub = (code, language = 'javascript') => ({
400
+ messageType: RichSubMessageType.CODE,
401
+ codeMetadata: {
402
+ codeLanguage: language,
403
+ codeBlocks: tokenizeCode(code, language)
404
+ }
405
+ });
406
+
407
+ /** Buat submessage TABLE */
408
+ const makeTableSub = (rows, title, noHeading) => ({
409
+ messageType: RichSubMessageType.TABLE,
410
+ tableMetadata: {
411
+ title: title || '',
412
+ rows: rows.map((items, i) => ({
413
+ isHeading: !noHeading && i === 0,
414
+ items
415
+ }))
416
+ }
417
+ });
418
+
419
+ /** Buat submessage GRID_IMAGE */
420
+ const makeGridImageSub = (gridImageUrl, imageUrls = []) => ({
421
+ messageType: RichSubMessageType.GRID_IMAGE,
422
+ gridImageMetadata: { gridImageUrl, imageUrls }
423
+ });
424
+
425
+ /** Buat submessage INLINE_IMAGE */
426
+ const makeInlineImageSub = (imageUrl, imageText = '', alignment = 0, tapLinkUrl = null) => ({
427
+ messageType: RichSubMessageType.INLINE_IMAGE,
428
+ imageMetadata: { imageUrl, imageText, alignment, tapLinkUrl }
429
+ });
430
+
431
+ /** Buat submessage DYNAMIC (animated image/GIF) */
432
+ const makeDynamicSub = (url, dynamicType = 1, version = 1, loopCount = 0) => ({
433
+ messageType: RichSubMessageType.DYNAMIC,
434
+ dynamicMetadata: { type: dynamicType, version, url, loopCount }
435
+ });
436
+
437
+ /** Buat submessage MAP */
438
+ const makeMapSub = (centerLatitude, centerLongitude, options = {}) => ({
439
+ messageType: RichSubMessageType.MAP,
440
+ mapMetadata: {
441
+ centerLatitude,
442
+ centerLongitude,
443
+ latitudeDelta: options.latitudeDelta ?? 0.05,
444
+ longitudeDelta: options.longitudeDelta ?? 0.05,
445
+ showInfoList: options.showInfoList ?? false,
446
+ annotations: (options.annotations || []).map((a, i) => ({
447
+ annotationNumber: a.number ?? i + 1,
448
+ latitude: a.latitude,
449
+ longitude: a.longitude,
450
+ title: a.title || '',
451
+ body: a.body || ''
452
+ }))
453
+ }
454
+ });
455
+
456
+ /** Buat submessage LATEX */
457
+ const makeLatexSub = (text, expressions = []) => ({
458
+ messageType: RichSubMessageType.LATEX,
459
+ latexMetadata: {
460
+ text,
461
+ expressions: expressions.map(e => {
462
+ const expr = e.expression || e.latexExpression || '';
463
+ // url WAJIB ada agar WA client bisa render — auto-generate jika tidak disupply
464
+ const url = e.url || buildLatexUrl(expr);
465
+ return {
466
+ latexExpression: expr,
467
+ url,
468
+ width: e.width ?? 120,
469
+ height: e.height ?? 40,
470
+ fontHeight: e.fontHeight ?? 0,
471
+ imageTopPadding: e.imageTopPadding ?? 0,
472
+ imageLeadingPadding: e.imageLeadingPadding ?? 0,
473
+ imageBottomPadding: e.imageBottomPadding ?? 0,
474
+ imageTrailingPadding: e.imageTrailingPadding ?? 0
475
+ };
476
+ })
477
+ }
478
+ });
479
+
480
+ /** Buat submessage CONTENT_ITEMS (carousel reel) */
481
+ const makeContentItemsSub = (items, contentType = 0) => ({
482
+ messageType: RichSubMessageType.CONTENT_ITEMS,
483
+ contentItemsMetadata: {
484
+ contentType,
485
+ itemsMetadata: items.map(item => {
486
+ if (item.reelItem || item.kind === 'reel') {
487
+ const r = item.reelItem || item;
74
488
  return {
75
- view_model: {
76
- primitive: {
77
- text: submessage.messageText,
78
- inline_entities: submessage.inlineEntities || [],
79
- __typename: 'GenAIMarkdownTextUXPrimitive'
80
- },
81
- __typename: 'GenAISingleLayoutViewModel'
489
+ reelItem: {
490
+ title: r.title || '',
491
+ profileIconUrl: r.profileIconUrl || null,
492
+ thumbnailUrl: r.thumbnailUrl || null,
493
+ videoUrl: r.videoUrl || null
82
494
  }
83
495
  };
84
- }
85
- return submessage;
86
- })
496
+ }
497
+ return {};
498
+ })
499
+ }
87
500
  });
501
+
502
+ /* ─────────────────────────────────────────────────────────────
503
+ MAIN BUILDER — prepareRichResponseMessage
504
+ ───────────────────────────────────────────────────────────── */
505
+
88
506
  export const prepareRichResponseMessage = (content) => {
89
- const { code, contentText, disclaimerText, footerText, headerText, language, links, noHeading, richResponse, table, title } = content;
507
+ const {
508
+ code, contentText, disclaimerText, footerText, headerText,
509
+ language, links, noHeading, richResponse, table, title,
510
+ // sub-types baru
511
+ gridImage, inlineImage, dynamic: dynamicContent,
512
+ map: mapContent, latex, contentItems
513
+ } = content;
514
+
90
515
  let submessages = [];
516
+
517
+ /* ── mode array (richResponse) — multi-section campuran ── */
91
518
  if (Array.isArray(richResponse)) {
92
- submessages = richResponse.map((submessage) => {
93
- if (submessage.text) {
94
- return {
95
- messageType: RichSubMessageType.TEXT,
96
- messageText: submessage.text,
97
- inlineEntities: submessage.inlineEntities
98
- };
99
- }
100
- else if (submessage.code) {
101
- return {
102
- messageType: RichSubMessageType.CODE,
103
- codeMetadata: {
104
- codeLanguage: submessage.language,
105
- codeBlocks: submessage.code
106
- }
107
- };
108
- }
109
- else if (submessage.table) {
110
- return {
111
- messageType: RichSubMessageType.TABLE,
112
- tableMetadata: {
113
- title: submessage.title,
114
- rows: submessage.table
115
- }
116
- };
117
- }
118
- return submessage;
119
- });
120
- }
121
- else {
122
- if (headerText) {
123
- submessages.push({
124
- messageType: RichSubMessageType.TEXT,
125
- messageText: headerText
126
- });
519
+ // headerText dan footerText tetap dihormati meskipun pakai mode array
520
+ if (headerText) submessages.push(makeTextSub(headerText));
521
+ submessages.push(...richResponse.map(sub => {
522
+ if (sub.text != null) return makeTextSub(sub.text, sub.inlineEntities);
523
+ if (sub.code != null) return makeCodeSub(sub.code, sub.language || 'javascript');
524
+ if (sub.table != null) return makeTableSub(sub.table, sub.title, sub.noHeading);
525
+ if (sub.gridImage != null) return makeGridImageSub(sub.gridImage.gridImageUrl, sub.gridImage.imageUrls);
526
+ if (sub.inlineImage != null) return makeInlineImageSub(sub.inlineImage.imageUrl, sub.inlineImage.imageText, sub.inlineImage.alignment, sub.inlineImage.tapLinkUrl);
527
+ if (sub.dynamic != null) return makeDynamicSub(sub.dynamic.url, sub.dynamic.type, sub.dynamic.version, sub.dynamic.loopCount);
528
+ if (sub.map != null) return makeMapSub(sub.map.centerLatitude, sub.map.centerLongitude, sub.map);
529
+ if (sub.latex != null) return makeLatexSub(sub.latex.text, sub.latex.expressions);
530
+ if (sub.contentItems != null) return makeContentItemsSub(sub.contentItems.items, sub.contentItems.contentType);
531
+ return sub; // passthrough kalau sudah bentuk proto
532
+ }).filter(Boolean));
533
+ if (footerText) submessages.push(makeTextSub(footerText));
534
+
535
+ /* ── mode flat (convenience fields) ── */
536
+ } else {
537
+ if (headerText) submessages.push(makeTextSub(headerText));
538
+ if (contentText) submessages.push(makeTextSub(contentText));
539
+
540
+ if (code) {
541
+ submessages.push(makeCodeSub(code, language || 'javascript'));
127
542
  }
128
- if (contentText) {
129
- submessages.push({
130
- messageType: RichSubMessageType.TEXT,
131
- messageText: contentText
132
- });
543
+
544
+ if (table) {
545
+ submessages.push(makeTableSub(table, title, noHeading));
133
546
  }
134
- if (code) {
135
- language ||= 'javascript';
136
- submessages.push({
137
- messageType: RichSubMessageType.CODE,
138
- codeMetadata: {
139
- codeLanguage: language,
140
- codeBlocks: tokenizeCode(code, language)
141
- }
142
- });
547
+
548
+ if (gridImage) {
549
+ submessages.push(makeGridImageSub(gridImage.gridImageUrl, gridImage.imageUrls));
550
+ }
551
+
552
+ if (inlineImage) {
553
+ submessages.push(makeInlineImageSub(inlineImage.imageUrl, inlineImage.imageText, inlineImage.alignment, inlineImage.tapLinkUrl));
143
554
  }
144
- else if (links) {
555
+
556
+ if (dynamicContent) {
557
+ submessages.push(makeDynamicSub(dynamicContent.url, dynamicContent.type, dynamicContent.version, dynamicContent.loopCount));
558
+ }
559
+
560
+ if (mapContent) {
561
+ submessages.push(makeMapSub(mapContent.centerLatitude, mapContent.centerLongitude, mapContent));
562
+ }
563
+
564
+ if (latex) {
565
+ submessages.push(makeLatexSub(latex.text, latex.expressions));
566
+ }
567
+
568
+ if (contentItems) {
569
+ submessages.push(makeContentItemsSub(contentItems.items, contentItems.contentType));
570
+ }
571
+
572
+ /* links — bisa dikombinasi dengan tipe lain di atas */
573
+ if (links && Array.isArray(links)) {
145
574
  links.forEach((linkField, index) => {
146
575
  const prefix = 'SS_' + index;
147
576
  const url = linkField.url || DONATE_URL;
148
- const sources = linkField.sources?.map((sourceField) => ({
577
+ const sources = (linkField.sources || []).map(s => ({
149
578
  source_type: 'THIRD_PARTY',
150
- source_display_name: sourceField.displayName || 'Donate',
151
- source_subtitle: sourceField.subtitle || 'Saweria',
152
- source_url: sourceField.url || url
579
+ source_display_name: s.displayName || 'Source',
580
+ source_subtitle: s.subtitle || '',
581
+ source_url: s.url || url
153
582
  }));
154
- submessages.push({
155
- messageType: RichSubMessageType.TEXT,
156
- messageText: linkField.text + ` {{${prefix}}}¹{{/${prefix}}} `,
157
- inlineEntities: [{
158
- key: prefix,
159
- metadata: {
160
- reference_id: index + 1,
161
- reference_url: url,
162
- reference_title: linkField.title || 'For Donation via Saweria',
163
- reference_display_name: linkField.displayName || 'Donation',
164
- sources: sources || [],
165
- __typename: 'GenAISearchCitationItem'
166
- }
167
- }]
168
- });
169
- });
170
- }
171
- else if (table) {
172
- submessages.push({
173
- messageType: RichSubMessageType.TABLE,
174
- tableMetadata: {
175
- title,
176
- rows: table.map((items, index) => ({
177
- isHeading: !noHeading && index == 0,
178
- items
179
- }))
180
- }
181
- });
182
- }
183
- if (footerText) {
184
- submessages.push({
185
- messageType: RichSubMessageType.TEXT,
186
- messageText: footerText
583
+ submessages.push(makeTextSub(
584
+ linkField.text + ` {{${prefix}}}¹{{/${prefix}}} `,
585
+ [{
586
+ key: prefix,
587
+ metadata: {
588
+ reference_id: index + 1,
589
+ reference_url: url,
590
+ reference_title: linkField.title || url,
591
+ reference_display_name: linkField.displayName || url,
592
+ sources,
593
+ __typename: 'GenAISearchCitationItem'
594
+ }
595
+ }]
596
+ ));
187
597
  });
188
598
  }
599
+
600
+ if (footerText) submessages.push(makeTextSub(footerText));
189
601
  }
602
+
603
+ /* build unifiedResponse JSON */
190
604
  const unified = toUnified(submessages);
605
+
191
606
  const richResponseMessage = proto.AIRichResponseMessage.create({
192
607
  submessages,
193
608
  messageType: proto.AIRichResponseMessageType.AI_RICH_RESPONSE_TYPE_STANDARD,
@@ -201,19 +616,28 @@ export const prepareRichResponseMessage = (content) => {
201
616
  forwardOrigin: 4
202
617
  }
203
618
  });
619
+
204
620
  const message = wrapToBotForwardedMessage(richResponseMessage);
205
621
  const botMetadata = message.messageContextInfo.botMetadata;
622
+
206
623
  if (disclaimerText) {
207
624
  botMetadata.messageDisclaimerText = disclaimerText;
208
625
  }
209
626
  botMetadata.botResponseId = unified.response_id;
627
+
210
628
  return message;
211
629
  };
630
+
631
+ /* ─────────────────────────────────────────────────────────────
632
+ BOT WRAPPER HELPERS
633
+ ───────────────────────────────────────────────────────────── */
634
+
212
635
  export const botMetadataSignature = () => {
213
636
  const signature = new Uint8Array(64);
214
637
  getRandomValues(signature);
215
638
  return signature;
216
639
  };
640
+
217
641
  export const botMetadataCertificate = (length = 685) => {
218
642
  const certificate = new Uint8Array(length);
219
643
  certificate[0] = 48;
@@ -221,6 +645,7 @@ export const botMetadataCertificate = (length = 685) => {
221
645
  getRandomValues(certificate.subarray(2));
222
646
  return certificate;
223
647
  };
648
+
224
649
  export const wrapToBotForwardedMessage = (richResponseMessage) => ({
225
650
  messageContextInfo: {
226
651
  botMetadata: {
@@ -243,4 +668,4 @@ export const wrapToBotForwardedMessage = (richResponseMessage) => ({
243
668
  message: { richResponseMessage }
244
669
  }
245
670
  });
246
- //# sourceMappingURL=rich-message-utils.js.map
671
+ //# sourceMappingURL=rich-message-utils.js.map