plogme 2.0.3 → 2.0.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.
- package/README.md +1530 -8
- package/lib/Socket/messages-send.js +184 -37
- package/lib/Utils/messages.js +10 -1
- package/lib/Utils/rich-message-utils.js +397 -6
- package/lib/index.d.ts +76 -2
- package/package.json +1 -1
|
@@ -376,6 +376,110 @@ const richMenuButton = (button, toast = '') => {
|
|
|
376
376
|
* This is an experimental WhatsApp rich-response format, not a standard
|
|
377
377
|
* buttonsMessage/nativeFlowMessage payload.
|
|
378
378
|
*/
|
|
379
|
+
/**
|
|
380
|
+
* Build a universal AIRich social/entity result card.
|
|
381
|
+
* The wire format is provider-neutral; Instagram is exposed as a dedicated
|
|
382
|
+
* convenience wrapper below because it is the primary supported profile view.
|
|
383
|
+
*/
|
|
384
|
+
export const prepareSocialEntityMessage = ({
|
|
385
|
+
username = '',
|
|
386
|
+
platform = 'GENERIC',
|
|
387
|
+
title = 'CODY AI',
|
|
388
|
+
subtitle = '⚉',
|
|
389
|
+
imageUrl = '',
|
|
390
|
+
entityId,
|
|
391
|
+
entityUrl = '',
|
|
392
|
+
entityType,
|
|
393
|
+
fullName = 'NOT found',
|
|
394
|
+
isVerified = false,
|
|
395
|
+
resultText = 'See results',
|
|
396
|
+
botJid = '867051314767696@bot',
|
|
397
|
+
responseId
|
|
398
|
+
} = {}) => {
|
|
399
|
+
const normalizedPlatform = String(platform || 'GENERIC').toUpperCase();
|
|
400
|
+
const profileUrl = entityUrl || (username ? `https://www.${normalizedPlatform.toLowerCase()}.com/${encodeURIComponent(username)}` : '');
|
|
401
|
+
const resolvedType = entityType || (normalizedPlatform === 'INSTAGRAM' ? 'IG_PROFILE' : 'SOCIAL_PROFILE');
|
|
402
|
+
const displayName = username || fullName || 'Profile';
|
|
403
|
+
const sections = [
|
|
404
|
+
{
|
|
405
|
+
view_model: {
|
|
406
|
+
primitives: [{
|
|
407
|
+
title,
|
|
408
|
+
subtitle,
|
|
409
|
+
...(imageUrl ? { image: { url: imageUrl, mime_type: 'image/png' } } : {}),
|
|
410
|
+
entity_id: String(entityId ?? username ?? ''),
|
|
411
|
+
entity_url: profileUrl,
|
|
412
|
+
entity_type: 'WEBSITE',
|
|
413
|
+
action_type: 'OPEN_URL',
|
|
414
|
+
is_verified: !!isVerified,
|
|
415
|
+
__typename: 'GenAICompactEntityPrimitive'
|
|
416
|
+
}],
|
|
417
|
+
__typename: 'GenAIActionRowLayoutViewModel'
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
view_model: {
|
|
422
|
+
primitives: [{ type: 'HORIZONTAL_LINE', __typename: 'GenAIDividerPrimitive' }],
|
|
423
|
+
__typename: 'GenAIVStackLayoutViewModel'
|
|
424
|
+
}
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
view_model: {
|
|
428
|
+
primitives: [
|
|
429
|
+
{ __typename: 'GenAISpacerPrimitive' },
|
|
430
|
+
{
|
|
431
|
+
text: `# {{social_entity_1}}${resultText}\0{{/social_entity_1}} `,
|
|
432
|
+
inline_entities: [{
|
|
433
|
+
key: 'social_entity_1',
|
|
434
|
+
metadata: {
|
|
435
|
+
__typename: 'GenAISocialEntityItem',
|
|
436
|
+
entity_id: String(entityId ?? username ?? ''),
|
|
437
|
+
entity_name: displayName,
|
|
438
|
+
entity_full_name: fullName,
|
|
439
|
+
entity_picture_url: imageUrl,
|
|
440
|
+
entity_url: profileUrl,
|
|
441
|
+
entity_type: resolvedType,
|
|
442
|
+
platform: normalizedPlatform,
|
|
443
|
+
is_verified: !!isVerified
|
|
444
|
+
}
|
|
445
|
+
}],
|
|
446
|
+
__typename: 'GenAIMarkdownTextUXPrimitive'
|
|
447
|
+
},
|
|
448
|
+
{ __typename: 'GenAISpacerPrimitive' }
|
|
449
|
+
],
|
|
450
|
+
__typename: 'GenAIActionRowLayoutViewModel'
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
];
|
|
454
|
+
return {
|
|
455
|
+
botForwardedMessage: {
|
|
456
|
+
message: {
|
|
457
|
+
richResponseMessage: {
|
|
458
|
+
messageType: 1,
|
|
459
|
+
submessages: [],
|
|
460
|
+
unifiedResponse: {
|
|
461
|
+
response_id: responseId || randomUUID(),
|
|
462
|
+
data: Buffer.from(JSON.stringify({ sections })).toString('base64')
|
|
463
|
+
},
|
|
464
|
+
contextInfo: {
|
|
465
|
+
isForwarded: true,
|
|
466
|
+
forwardOrigin: 4,
|
|
467
|
+
forwardedAiBotMessageInfo: { botJid }
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
/** Dedicated Instagram profile card using the universal social-entity wire format. */
|
|
476
|
+
export const prepareInstagramProfileMessage = (content = {}) => prepareSocialEntityMessage({
|
|
477
|
+
...content,
|
|
478
|
+
platform: 'INSTAGRAM',
|
|
479
|
+
entityType: 'IG_PROFILE',
|
|
480
|
+
entityUrl: content.entityUrl || (content.username ? `https://www.instagram.com/${encodeURIComponent(content.username)}` : undefined)
|
|
481
|
+
});
|
|
482
|
+
|
|
379
483
|
export const prepareRichMenuMessage = (content = {}) => {
|
|
380
484
|
const { header = {}, body = {}, footer = {}, verificationMetadata } = content || {};
|
|
381
485
|
const sections = [];
|
|
@@ -472,6 +576,101 @@ export const prepareRichMenuMessage = (content = {}) => {
|
|
|
472
576
|
* @param {'END'|'START'|'CENTER'} [options.alignment='END'] - Button alignment
|
|
473
577
|
* @returns {Object} Wrapped botForwardedMessage ready for relayMessage
|
|
474
578
|
*/
|
|
579
|
+
/** Build the manually serialized AIRich command menu shown in the screenshot. */
|
|
580
|
+
export const prepareAIRichCommandMenu = ({ image, title = 'CODY AI', subtitle = '', code = '', rows = [], buttons = [], footer = 'Developed by - CODY AI', latexImage, latexExpression = '' } = {}) => {
|
|
581
|
+
if (!image) throw new Error('AIRich command menu requires an image URL');
|
|
582
|
+
if (!Array.isArray(rows)) throw new Error('AIRich command-menu rows must be an array');
|
|
583
|
+
if (!Array.isArray(buttons)) throw new Error('AIRich command-menu buttons must be an array');
|
|
584
|
+
const safeRows = rows.map((row, index) => ({ command: String(row?.[0] ?? row?.command ?? `command_${index + 1}`), description: String(row?.[1] ?? row?.description ?? '') }));
|
|
585
|
+
const table = ['Command | Function', '--- | ---', ...safeRows.map((row) => `${row.command} | ${row.description}`)].join('\n');
|
|
586
|
+
const inlineEntities = latexImage ? [{ key: 'NGA', metadata: { latex_expression: String(latexExpression), latex_image: { url: String(latexImage), width: 400, height: 400 }, font_height: 83.333, padding: 15, __typename: 'GenAILatexItem' } }] : [];
|
|
587
|
+
const text = [String(title), subtitle && String(subtitle), code && `Code: ${String(code)}`, '## Command List', table, footer && String(footer)].filter(Boolean).join('\n');
|
|
588
|
+
const imagePrimitive = { __typename: 'GenAIImagePrimitive', preview_image: { __typename: 'GenAIMediaItem', mime_type: 'image/png', url: String(image) }, full_image: { __typename: 'GenAIMediaItem', mime_type: 'image/png', url: String(image) } };
|
|
589
|
+
const ctas = buttons.map((button, index) => ({ label: String(button?.text || button?.label || `Button ${index + 1}`), state: 'PENDING', kind: 'OTHER', tool_call_id: String(button?.id || `airich_menu_${index + 1}`), toast: { label: String(button?.toast || ''), __typename: 'GenAIExtWidgetToast' }, __typename: 'GenAI3PExtWidgetCTA' }));
|
|
590
|
+
const footerPrimitives = buttons.map((button, index) => ({ cta_text: String(button?.text || button?.label || `Button ${index + 1}`), cta_type: 'OPEN_URL', cta_url: String(button?.url || ''), __typename: 'GenAIFooterActionPrimitive' }));
|
|
591
|
+
footerPrimitives.push({ cta_text: '☷ OPEN MENU', cta_type: 'OPEN_URL', cta_url: String(buttons[0]?.url || ''), __typename: 'GenAIFooterActionPrimitive' });
|
|
592
|
+
const sections = [
|
|
593
|
+
{ __typename: 'GenAIUnifiedResponseSection', view_model: { __typename: 'GenAISingleLayoutViewModel', primitive: imagePrimitive } },
|
|
594
|
+
...(ctas.length ? [{ __typename: 'GenAIUnifiedResponseSection', view_model: { primitives: [{ __typename: 'GenAI3PExtWidgetPrimitive', header: { __typename: 'GenAI3PExtWidgetStandardHeader', title: 'Command List' }, body: { __typename: 'GenAIExtCalendarEventList', ctas, sections: [] } }], __typename: 'GenAIActionRowLayoutViewModel' } }] : []),
|
|
595
|
+
{ __typename: 'GenAIUnifiedResponseSection', view_model: { primitive: { text: `{{NGA}}${text}{{/NGA}}`, inline_entities: inlineEntities, __typename: 'GenAIMarkdownTextUXPrimitive' }, __typename: 'GenAISingleLayoutViewModel' } },
|
|
596
|
+
{ __typename: 'GenAIUnifiedResponseSection', view_model: { primitives: footerPrimitives, __typename: 'GenAIActionRowLayoutViewModel' } }
|
|
597
|
+
];
|
|
598
|
+
const richResponseMessage = proto.AIRichResponseMessage.create({
|
|
599
|
+
submessages: [{ messageType: RichSubMessageType.TEXT, messageText: text }],
|
|
600
|
+
messageType: proto.AIRichResponseMessageType.AI_RICH_RESPONSE_TYPE_STANDARD,
|
|
601
|
+
unifiedResponse: { data: Buffer.from(JSON.stringify({ response_id: randomUUID(), sections })) },
|
|
602
|
+
contextInfo: { isForwarded: true, forwardingScore: 1, forwardedAiBotMessageInfo: { botJid: '867051314767696@bot' }, forwardOrigin: 4 }
|
|
603
|
+
});
|
|
604
|
+
return wrapToBotForwardedMessage(richResponseMessage);
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Build an arbitrary A2UI interactive message. The command-menu helper below
|
|
609
|
+
* is only a convenience preset; callers may provide any supported component,
|
|
610
|
+
* widget, or native-flow payload through this universal builder.
|
|
611
|
+
*/
|
|
612
|
+
export const prepareA2UIMessage = ({ imageMessage, bloksWidget = {}, nativeFlowMessage, fallback = '', ...interactiveFields } = {}) => ({
|
|
613
|
+
interactiveMessage: {
|
|
614
|
+
...interactiveFields,
|
|
615
|
+
...(imageMessage ? { header: { imageMessage, hasMediaAttachment: true } } : {}),
|
|
616
|
+
bloksWidget: {
|
|
617
|
+
type: bloksWidget.type || 'im_a2ui',
|
|
618
|
+
data: typeof bloksWidget.data === 'string' ? bloksWidget.data : JSON.stringify(bloksWidget.data || {}),
|
|
619
|
+
...(bloksWidget.fallback || fallback ? { fallback: String(bloksWidget.fallback || fallback) } : {})
|
|
620
|
+
},
|
|
621
|
+
...(nativeFlowMessage ? {
|
|
622
|
+
nativeFlowMessage: {
|
|
623
|
+
...nativeFlowMessage,
|
|
624
|
+
...(nativeFlowMessage.buttons ? { buttons: nativeFlowMessage.buttons } : {})
|
|
625
|
+
}
|
|
626
|
+
} : {})
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
/** Build the A2UI interactive command menu used by the full-size card layout. */
|
|
631
|
+
export const prepareA2UICommandMenu = ({ imageMessage, title = 'CODY AI', rows = [], buttons = [], fallback = '' } = {}) => {
|
|
632
|
+
if (!Array.isArray(rows)) throw new Error('A2UI command-menu rows must be an array');
|
|
633
|
+
if (!Array.isArray(buttons)) throw new Error('A2UI command-menu buttons must be an array');
|
|
634
|
+
const safeRows = rows.map((row, index) => ({
|
|
635
|
+
title: String(row?.[0] ?? row?.title ?? `Command ${index + 1}`),
|
|
636
|
+
description: String(row?.[1] ?? row?.description ?? ''),
|
|
637
|
+
id: String(row?.[2] ?? row?.id ?? row?.[0] ?? `command_${index + 1}`)
|
|
638
|
+
}));
|
|
639
|
+
const safeButtons = buttons.map((button, index) => ({ id: `a2ui_button_${index + 1}`, label: String(button?.text || button?.label || `Button ${index + 1}`), url: String(button?.url || '') }));
|
|
640
|
+
const componentIds = ['title', 'divider', 'tableCard', 'footer', ...(safeButtons.length ? ['buttonRow'] : [])];
|
|
641
|
+
const components = [
|
|
642
|
+
{ id: 'root', component: 'Column', align: 'center', children: componentIds },
|
|
643
|
+
{ id: 'title', component: 'Text', text: String(title), variant: 'h2' },
|
|
644
|
+
{ id: 'divider', component: 'Divider' },
|
|
645
|
+
{ id: 'tableCard', component: 'Card', child: 'tableColumn' },
|
|
646
|
+
{ id: 'tableColumn', component: 'Column', children: ['headerRow', ...safeRows.flatMap((_, index) => [`divider_${index}`, `row_${index}`])] },
|
|
647
|
+
{ id: 'headerRow', component: 'Row', children: ['headerCommand', 'headerFunction'] },
|
|
648
|
+
{ id: 'headerCommand', component: 'Text', text: 'Command', variant: 'h5' },
|
|
649
|
+
{ id: 'headerFunction', component: 'Text', text: 'Function', variant: 'h5' },
|
|
650
|
+
...safeRows.flatMap((row, index) => [
|
|
651
|
+
{ id: `divider_${index}`, component: 'Divider' },
|
|
652
|
+
{ id: `row_${index}`, component: 'Row', children: [`command_${index}`, `description_${index}`] },
|
|
653
|
+
{ id: `command_${index}`, component: 'Text', text: row.title },
|
|
654
|
+
{ id: `description_${index}`, component: 'Text', text: row.description }
|
|
655
|
+
]),
|
|
656
|
+
{ id: 'footer', component: 'Text', text: 'Developed by - CODY AI', variant: 'caption' }
|
|
657
|
+
];
|
|
658
|
+
if (safeButtons.length) {
|
|
659
|
+
components.push({ id: 'buttonRow', component: 'Row', justify: 'spaceEvenly', children: safeButtons.flatMap((button, index) => [`buttonLabel_${index}`, `button_${index}`]) });
|
|
660
|
+
safeButtons.forEach((button, index) => {
|
|
661
|
+
components.push({ id: `buttonLabel_${index}`, component: 'Text', text: button.label });
|
|
662
|
+
components.push({ id: `button_${index}`, component: 'Button', child: `buttonLabel_${index}`, variant: 'primary', action: { call: 'openUrl', args: { url: button.url } } });
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
return {
|
|
666
|
+
interactiveMessage: {
|
|
667
|
+
...(imageMessage ? { header: { imageMessage, hasMediaAttachment: true } } : {}),
|
|
668
|
+
bloksWidget: { type: 'im_a2ui', data: JSON.stringify({ version: 'v0.9', createSurface: { surfaceId: 'plogme-a2ui-command-menu', catalogId: 'https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json', components } }), fallback: String(fallback || title) },
|
|
669
|
+
nativeFlowMessage: { messageParamsJson: JSON.stringify({ limited_time_offer: { text: String(title), url: safeButtons[0]?.url || '', copy_code: String(title), expiration_time: Date.now() * 1000 } }), buttons: [{ name: 'single_select', buttonParamsJson: JSON.stringify({ title: String(title), sections: [{ title: 'Available Commands', rows: safeRows }] }) }] }
|
|
670
|
+
}
|
|
671
|
+
};
|
|
672
|
+
};
|
|
673
|
+
|
|
475
674
|
export const prepareCopyToClipboardMessage = (options = {}) => {
|
|
476
675
|
const { text = '', label, alignment = 'END', verificationMetadata } = options;
|
|
477
676
|
const displayText = label || text;
|
|
@@ -697,9 +896,116 @@ export const prepareGridImageMessage = ({ imageUrls = [], verificationMetadata }
|
|
|
697
896
|
return wrapToBotForwardedMessage(richResponseMessage, verificationMetadata);
|
|
698
897
|
};
|
|
699
898
|
|
|
899
|
+
/**
|
|
900
|
+
* Build a video grid using the client-supported GenAIVideoPrimitive.
|
|
901
|
+
*
|
|
902
|
+
* WhatsApp exposes no AIRich GRID_VIDEO submessage type. Video grids therefore
|
|
903
|
+
* use the generic unified-response Grid layout with one GenAIVideoPrimitive per
|
|
904
|
+
* item. This is deliberately separate from prepareGridImageMessage so callers
|
|
905
|
+
* do not accidentally label video media as GenAIImagePrimitive.
|
|
906
|
+
*/
|
|
907
|
+
/** Build a full-width vertical album with one large media item per row. */
|
|
908
|
+
export const prepareVerticalAlbumMessage = ({ items = [], verificationMetadata } = {}) => {
|
|
909
|
+
if (!Array.isArray(items) || items.length < 2) throw new Error('Vertical album requires at least two media items');
|
|
910
|
+
const expiration = String(Date.now() + 30 * 24 * 60 * 60 * 1000);
|
|
911
|
+
const primitives = items.map((item) => {
|
|
912
|
+
const kind = item?.type || (item?.video ? 'video' : 'image');
|
|
913
|
+
if (kind === 'video') {
|
|
914
|
+
const video = item.video || item.data || item;
|
|
915
|
+
const url = typeof video === 'string' ? video : (video?.url || video?.videoUrl || '');
|
|
916
|
+
if (!url) throw new Error('Each video album item requires a video URL');
|
|
917
|
+
return buildImaginePrimitive({ mediaType: 'video', url, thumbnail: video?.thumbnail || video?.thumbnailUrl, mimeType: video?.mimeType || video?.mime_type || 'video/mp4', fileLength: video?.fileLength || video?.file_length || 0, duration: video?.duration || 0, status: 'READY' });
|
|
918
|
+
}
|
|
919
|
+
const image = item?.image || item?.data || item;
|
|
920
|
+
const sourceUrl = typeof image === 'string' ? image : (image?.sourceUrl || image?.url || '');
|
|
921
|
+
if (!sourceUrl) throw new Error('Each image album item requires an image URL');
|
|
922
|
+
const previewUrl = image?.imagePreviewUrl || image?.previewUrl || sourceUrl;
|
|
923
|
+
const fullUrl = image?.imageHighResUrl || image?.highResUrl || image?.url || sourceUrl;
|
|
924
|
+
const width = image?.width || item?.width || 600;
|
|
925
|
+
const height = image?.height || item?.height || 400;
|
|
926
|
+
const mimeType = image?.mimeType || image?.mime_type || 'image/png';
|
|
927
|
+
return {
|
|
928
|
+
preview_image: { url: previewUrl, url_fallback: previewUrl, mime_type: mimeType, expiration_timestamp_ms: expiration, width, height },
|
|
929
|
+
full_image: { url: fullUrl, url_fallback: fullUrl, mime_type: mimeType, expiration_timestamp_ms: expiration, width, height },
|
|
930
|
+
dark_mode_preview_image: { url: image?.darkModePreviewUrl || previewUrl, url_fallback: image?.darkModePreviewUrl || previewUrl, mime_type: mimeType, expiration_timestamp_ms: expiration, width, height },
|
|
931
|
+
dark_mode_full_image: { url: image?.darkModeHighResUrl || image?.darkModePreviewUrl || fullUrl, url_fallback: image?.darkModeHighResUrl || image?.darkModePreviewUrl || fullUrl, mime_type: mimeType, expiration_timestamp_ms: expiration, width, height },
|
|
932
|
+
display_mode: 'FULL_WIDTH',
|
|
933
|
+
full_width: true,
|
|
934
|
+
asset_query_status: 'FETCHED',
|
|
935
|
+
__typename: 'GenAIImagePrimitive'
|
|
936
|
+
};
|
|
937
|
+
});
|
|
938
|
+
const richResponseMessage = proto.AIRichResponseMessage.create({
|
|
939
|
+
submessages: [],
|
|
940
|
+
messageType: proto.AIRichResponseMessageType.AI_RICH_RESPONSE_TYPE_STANDARD,
|
|
941
|
+
unifiedResponse: { data: Buffer.from(JSON.stringify({ response_id: randomUUID(), sections: [{ view_model: { primitives, display_mode: 'FULL_WIDTH', full_width: true, __typename: 'GenAIVStackLayoutViewModel' } }] })) },
|
|
942
|
+
contextInfo: { isForwarded: true, forwardingScore: 1, forwardedAiBotMessageInfo: { botJid: '867051314767696@bot' }, forwardOrigin: 4 }
|
|
943
|
+
});
|
|
944
|
+
return wrapToBotForwardedMessage(richResponseMessage, verificationMetadata);
|
|
945
|
+
};
|
|
946
|
+
|
|
947
|
+
export const prepareVideoGridMessage = ({ videos = [], verificationMetadata } = {}) => {
|
|
948
|
+
if (!Array.isArray(videos) || videos.length < 2) {
|
|
949
|
+
throw new Error('Video grid requires at least two videos');
|
|
950
|
+
}
|
|
951
|
+
const primitives = videos.map((video) => {
|
|
952
|
+
const url = typeof video === 'string' ? video : (video?.url || video?.videoUrl || video?.reelsUrl || '');
|
|
953
|
+
if (!url) throw new Error('Each video-grid item requires a video URL');
|
|
954
|
+
const item = typeof video === 'string' ? {} : video;
|
|
955
|
+
return {
|
|
956
|
+
post_id: item.postId == null ? undefined : String(item.postId),
|
|
957
|
+
reels_url: url,
|
|
958
|
+
reels_title: item.title,
|
|
959
|
+
thumbnail_url: item.thumbnailUrl || item.thumbnail,
|
|
960
|
+
creator: item.creator,
|
|
961
|
+
avatar_url: item.avatarUrl,
|
|
962
|
+
content_hash: item.contentHash,
|
|
963
|
+
likes_count: item.likes,
|
|
964
|
+
comments_count: item.comments,
|
|
965
|
+
shares_count: item.shares,
|
|
966
|
+
is_verified: item.isVerified,
|
|
967
|
+
source_app: item.sourceApp,
|
|
968
|
+
video_delivery_response: {
|
|
969
|
+
progressive_urls: (item.progressiveUrls || []).map((value) => ({
|
|
970
|
+
progressive_url: value,
|
|
971
|
+
__typename: 'GenAIProgressiveUrlResponse'
|
|
972
|
+
})),
|
|
973
|
+
dash_manifests: (item.dashManifests || []).map((value) => ({
|
|
974
|
+
manifest_xml: value,
|
|
975
|
+
__typename: 'GenAIDashManifestResponse'
|
|
976
|
+
})),
|
|
977
|
+
__typename: 'GenAIVideoDeliveryResponse'
|
|
978
|
+
},
|
|
979
|
+
__typename: 'GenAIVideoPrimitive'
|
|
980
|
+
};
|
|
981
|
+
});
|
|
982
|
+
const unified = {
|
|
983
|
+
response_id: randomUUID(),
|
|
984
|
+
sections: [{
|
|
985
|
+
view_model: {
|
|
986
|
+
primitives,
|
|
987
|
+
__typename: 'GenAIGridLayoutViewModel'
|
|
988
|
+
}
|
|
989
|
+
}]
|
|
990
|
+
};
|
|
991
|
+
const richResponseMessage = proto.AIRichResponseMessage.create({
|
|
992
|
+
submessages: [],
|
|
993
|
+
messageType: proto.AIRichResponseMessageType.AI_RICH_RESPONSE_TYPE_STANDARD,
|
|
994
|
+
unifiedResponse: { data: Buffer.from(JSON.stringify(unified)) },
|
|
995
|
+
contextInfo: {
|
|
996
|
+
isForwarded: true,
|
|
997
|
+
forwardingScore: 1,
|
|
998
|
+
forwardedAiBotMessageInfo: { botJid: '867051314767696@bot' },
|
|
999
|
+
forwardOrigin: 4
|
|
1000
|
+
}
|
|
1001
|
+
});
|
|
1002
|
+
return wrapToBotForwardedMessage(richResponseMessage, verificationMetadata);
|
|
1003
|
+
};
|
|
1004
|
+
|
|
700
1005
|
export const prepareRichResponseMessage = (content) => {
|
|
701
1006
|
const { alignment, code, contentText, disclaimerText, footerText, headerText, imageText, inlineImage, inlineVideo, items, language, latex, links, noDonation, noHeading, posts, products, reels, sources: sourceInputs, suggested, richResponse, table, tapLinkUrl, title, verificationMetadata } = content;
|
|
702
1007
|
let submessages = [];
|
|
1008
|
+
const extraSections = [];
|
|
703
1009
|
if (Array.isArray(richResponse)) {
|
|
704
1010
|
submessages = richResponse.map((submessage) => {
|
|
705
1011
|
if (submessage.text) {
|
|
@@ -843,9 +1149,21 @@ export const prepareRichResponseMessage = (content) => {
|
|
|
843
1149
|
});
|
|
844
1150
|
}
|
|
845
1151
|
if (inlineVideo) {
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
1152
|
+
const video = typeof inlineVideo === 'string' ? { url: inlineVideo } : inlineVideo;
|
|
1153
|
+
submessages.push({ messageType: RichSubMessageType.TEXT, messageText: '[ CANNOT_LOAD_VIDEO - NIXEL ]' });
|
|
1154
|
+
extraSections.push({
|
|
1155
|
+
view_model: {
|
|
1156
|
+
primitive: buildImaginePrimitive({
|
|
1157
|
+
mediaType: 'video',
|
|
1158
|
+
url: video?.url || video?.videoUrl || '',
|
|
1159
|
+
mimeType: video?.mimeType || video?.mime_type || 'video/mp4',
|
|
1160
|
+
fileLength: video?.fileLength || video?.file_length || 0,
|
|
1161
|
+
duration: video?.duration || 0,
|
|
1162
|
+
thumbnail: video?.thumbnail,
|
|
1163
|
+
status: video?.status || 'READY'
|
|
1164
|
+
}),
|
|
1165
|
+
__typename: 'GenAISingleLayoutViewModel'
|
|
1166
|
+
}
|
|
849
1167
|
});
|
|
850
1168
|
}
|
|
851
1169
|
if (latex) {
|
|
@@ -863,7 +1181,40 @@ export const prepareRichResponseMessage = (content) => {
|
|
|
863
1181
|
title: 'PAIR CRYSN⚉VA AI ⩇⩇:⩇⩇',
|
|
864
1182
|
displayName: 'pair bot'
|
|
865
1183
|
};
|
|
866
|
-
const
|
|
1184
|
+
const rawSources = Array.isArray(sourceInputs) ? sourceInputs : [];
|
|
1185
|
+
// Existing `sources: [{ url, displayName, subtitle }]` callers use
|
|
1186
|
+
// citation entities. The serialized AIRich addSource format is the
|
|
1187
|
+
// distinct tuple form: `[[faviconUrl, sourceUrl, displayText], ...]`.
|
|
1188
|
+
const sourceTuples = rawSources.length > 0 && rawSources.every((source) => Array.isArray(source));
|
|
1189
|
+
if (sourceTuples) {
|
|
1190
|
+
const sourceCards = rawSources.map((source) => {
|
|
1191
|
+
const item = Array.isArray(source)
|
|
1192
|
+
? { icon: source[0], url: source[1], text: source[2] }
|
|
1193
|
+
: source;
|
|
1194
|
+
const sourceUrl = item.url || item.sourceUrl || item.source_url || '';
|
|
1195
|
+
return {
|
|
1196
|
+
source_type: 'THIRD_PARTY',
|
|
1197
|
+
source_display_name: item.text || item.displayName || item.sourceDisplayName || '',
|
|
1198
|
+
source_subtitle: item.subtitle || 'AI',
|
|
1199
|
+
source_url: sourceUrl,
|
|
1200
|
+
favicon: {
|
|
1201
|
+
url: item.icon || item.iconUrl || item.favicon || '',
|
|
1202
|
+
mime_type: item.mimeType || 'image/jpeg',
|
|
1203
|
+
width: item.width || 16,
|
|
1204
|
+
height: item.height || 16
|
|
1205
|
+
}
|
|
1206
|
+
};
|
|
1207
|
+
}).filter((source) => source.source_url || source.source_display_name);
|
|
1208
|
+
if (sourceCards.length) {
|
|
1209
|
+
extraSections.push({
|
|
1210
|
+
view_model: {
|
|
1211
|
+
primitive: { sources: sourceCards, __typename: 'GenAISearchResultPrimitive' },
|
|
1212
|
+
__typename: 'GenAISingleLayoutViewModel'
|
|
1213
|
+
}
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
const sourceLinks = (sourceTuples ? [] : (sourceInputs || links || [])).map((linkField) => {
|
|
867
1218
|
if (typeof linkField === 'string') return { text: '', url: linkField, displayName: linkField };
|
|
868
1219
|
return { text: linkField?.text || linkField?.displayName || linkField?.title || '', ...linkField };
|
|
869
1220
|
});
|
|
@@ -898,14 +1249,53 @@ export const prepareRichResponseMessage = (content) => {
|
|
|
898
1249
|
if (posts) {
|
|
899
1250
|
submessages.push({
|
|
900
1251
|
messageType: RichSubMessageType.TEXT,
|
|
901
|
-
messageText: '
|
|
1252
|
+
messageText: '[ CANNOT_LOAD_POST - NIXEL ]'
|
|
902
1253
|
});
|
|
1254
|
+
const postItems = (Array.isArray(posts) ? posts : [posts]).map((post) => ({
|
|
1255
|
+
title: post?.title || '',
|
|
1256
|
+
subtitle: post?.subtitle || '',
|
|
1257
|
+
username: post?.username || post?.user || '',
|
|
1258
|
+
profile_picture_url: post?.profile_picture_url || post?.profile || post?.profile_url || '',
|
|
1259
|
+
is_verified: !!(post?.is_verified ?? post?.verified),
|
|
1260
|
+
thumbnail_url: post?.thumbnail_url || post?.thumbnail || '',
|
|
1261
|
+
post_caption: post?.post_caption || post?.caption || '',
|
|
1262
|
+
likes_count: post?.likes_count ?? post?.like ?? 0,
|
|
1263
|
+
comments_count: post?.comments_count ?? post?.comment ?? 0,
|
|
1264
|
+
shares_count: post?.shares_count ?? post?.share ?? 0,
|
|
1265
|
+
post_url: post?.post_url || post?.url || '',
|
|
1266
|
+
post_deeplink: post?.post_deeplink || post?.deeplink || '',
|
|
1267
|
+
source_app: post?.source_app || post?.source || '',
|
|
1268
|
+
footer_label: post?.footer_label || post?.footer || '',
|
|
1269
|
+
footer_icon: post?.footer_icon || post?.icon || '',
|
|
1270
|
+
is_carousel: post?.is_carousel ?? true,
|
|
1271
|
+
orientation: post?.orientation || 'LANDSCAPE',
|
|
1272
|
+
post_type: post?.post_type || 'VIDEO',
|
|
1273
|
+
__typename: 'GenAIPostPrimitive'
|
|
1274
|
+
}));
|
|
1275
|
+
if (postItems.length) extraSections.push({ view_model: { primitives: postItems, __typename: 'GenAIHScrollLayoutViewModel' } });
|
|
903
1276
|
}
|
|
904
1277
|
if (products) {
|
|
905
1278
|
submessages.push({
|
|
906
1279
|
messageType: RichSubMessageType.TEXT,
|
|
907
|
-
messageText: '
|
|
1280
|
+
messageText: '[ CANNOT_LOAD_PRODUCT - NIXEL ]'
|
|
908
1281
|
});
|
|
1282
|
+
const productItems = (Array.isArray(products) ? products : [products]).map((product) => ({
|
|
1283
|
+
title: product?.title || '',
|
|
1284
|
+
brand: product?.brand || '',
|
|
1285
|
+
price: product?.price || '',
|
|
1286
|
+
sale_price: product?.sale_price || product?.salePrice || '',
|
|
1287
|
+
product_url: product?.product_url || product?.url || '',
|
|
1288
|
+
image: { url: product?.image?.url || product?.image || '' },
|
|
1289
|
+
additional_images: [{ url: product?.icon_url || product?.icon || '' }],
|
|
1290
|
+
__typename: 'GenAIProductItemCardPrimitive'
|
|
1291
|
+
}));
|
|
1292
|
+
if (productItems.length) {
|
|
1293
|
+
extraSections.push({
|
|
1294
|
+
view_model: Array.isArray(products)
|
|
1295
|
+
? { primitives: productItems, __typename: 'GenAIHScrollLayoutViewModel' }
|
|
1296
|
+
: { primitive: productItems[0], __typename: 'GenAISingleLayoutViewModel' }
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
909
1299
|
}
|
|
910
1300
|
if (suggested) {
|
|
911
1301
|
submessages.push({
|
|
@@ -934,6 +1324,7 @@ export const prepareRichResponseMessage = (content) => {
|
|
|
934
1324
|
}
|
|
935
1325
|
const uuid = randomUUID();
|
|
936
1326
|
const unified = toUnified(submessages, uuid);
|
|
1327
|
+
unified.sections = [...unified.sections.filter(Boolean), ...extraSections];
|
|
937
1328
|
const richResponseMessage = proto.AIRichResponseMessage.create({
|
|
938
1329
|
submessages,
|
|
939
1330
|
messageType: proto.AIRichResponseMessageType.AI_RICH_RESPONSE_TYPE_STANDARD,
|
package/lib/index.d.ts
CHANGED
|
@@ -224,7 +224,8 @@ export interface RelayMessageOptions {
|
|
|
224
224
|
|
|
225
225
|
export interface AlbumMediaInput {
|
|
226
226
|
image?: unknown;
|
|
227
|
-
|
|
227
|
+
video?: unknown;
|
|
228
|
+
type?: 'image' | 'video';
|
|
228
229
|
data?: unknown;
|
|
229
230
|
sourceUrl?: string;
|
|
230
231
|
imagePreviewUrl?: string;
|
|
@@ -246,10 +247,73 @@ export interface AnimatedWallpaperInput {
|
|
|
246
247
|
opacity?: number;
|
|
247
248
|
}
|
|
248
249
|
|
|
250
|
+
export interface A2UIMessageContent {
|
|
251
|
+
image?: unknown;
|
|
252
|
+
bloksWidget?: { type?: string; data?: unknown; fallback?: string };
|
|
253
|
+
nativeFlowMessage?: { messageParamsJson?: string; buttons?: unknown[]; [key: string]: unknown };
|
|
254
|
+
[key: string]: unknown;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export interface A2UICommandMenuContent {
|
|
258
|
+
image?: unknown;
|
|
259
|
+
title?: string;
|
|
260
|
+
rows?: Array<[string, string, string?] | { title?: string; description?: string; id?: string }>;
|
|
261
|
+
buttons?: Array<{ text?: string; label?: string; url?: string }>;
|
|
262
|
+
fallback?: string;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export interface SocialEntityContent {
|
|
266
|
+
username?: string;
|
|
267
|
+
platform?: string;
|
|
268
|
+
title?: string;
|
|
269
|
+
subtitle?: string;
|
|
270
|
+
imageUrl?: string;
|
|
271
|
+
entityId?: string | number;
|
|
272
|
+
entityUrl?: string;
|
|
273
|
+
entityType?: string;
|
|
274
|
+
fullName?: string;
|
|
275
|
+
isVerified?: boolean;
|
|
276
|
+
resultText?: string;
|
|
277
|
+
botJid?: string;
|
|
278
|
+
responseId?: string;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export interface AIRichCommandMenuContent {
|
|
282
|
+
image: string;
|
|
283
|
+
title?: string;
|
|
284
|
+
subtitle?: string;
|
|
285
|
+
code?: string;
|
|
286
|
+
rows?: Array<[string, string] | { command?: string; description?: string }>;
|
|
287
|
+
latexImage?: string;
|
|
288
|
+
latexExpression?: string;
|
|
289
|
+
buttons?: Array<{ id?: string; text?: string; label?: string; url?: string; toast?: string }>;
|
|
290
|
+
footer?: string;
|
|
291
|
+
}
|
|
292
|
+
|
|
249
293
|
export interface WASocket {
|
|
250
294
|
sendMessage(jid: string | string[], content: StatusContent | GroupStatusContent | Record<string, unknown>, options?: Partial<StatusOptions> & Record<string, unknown>): Promise<unknown>;
|
|
251
|
-
/** Send at least two image URL items as
|
|
295
|
+
/** Send at least two image/video URL items as a full-width AIRich vertical album. */
|
|
252
296
|
sendAlbumMessage(jid: string, medias: AlbumMediaInput[], options?: Record<string, unknown>): Promise<unknown>;
|
|
297
|
+
/** Send an AIRich unified-response grid of GenAIVideoPrimitive items. Requires at least two video URLs. */
|
|
298
|
+
sendVideoGridMessage(jid: string, videos: Array<string | {
|
|
299
|
+
url?: string;
|
|
300
|
+
videoUrl?: string;
|
|
301
|
+
reelsUrl?: string;
|
|
302
|
+
postId?: string | number;
|
|
303
|
+
title?: string;
|
|
304
|
+
thumbnailUrl?: string;
|
|
305
|
+
thumbnail?: string;
|
|
306
|
+
creator?: string;
|
|
307
|
+
avatarUrl?: string;
|
|
308
|
+
contentHash?: string;
|
|
309
|
+
likes?: number;
|
|
310
|
+
comments?: number;
|
|
311
|
+
shares?: number;
|
|
312
|
+
isVerified?: boolean;
|
|
313
|
+
sourceApp?: string;
|
|
314
|
+
progressiveUrls?: string[];
|
|
315
|
+
dashManifests?: string[];
|
|
316
|
+
}>, options?: Record<string, unknown>): Promise<unknown>;
|
|
253
317
|
/** Update the connected account's WhatsApp About/profile status. */
|
|
254
318
|
updateProfileStatus(status: string): Promise<void>;
|
|
255
319
|
/** Update the connected account's profile name. */
|
|
@@ -260,6 +324,16 @@ export interface WASocket {
|
|
|
260
324
|
relayMessage(jid: string, message: Record<string, unknown>, options?: RelayMessageOptions): Promise<unknown>;
|
|
261
325
|
/** Send an experimental Meta AI-style rich menu with image, card buttons, and an optional URL footer. */
|
|
262
326
|
richMenu(jid: string, content?: RichMenuContent, options?: Record<string, unknown>): Promise<unknown>;
|
|
327
|
+
/** Send a provider-neutral AIRich social/entity result card. */
|
|
328
|
+
sendSocialEntity(jid: string, content?: SocialEntityContent, options?: Record<string, unknown>): Promise<unknown>;
|
|
329
|
+
/** Send the primary Instagram profile result using the universal social/entity format. */
|
|
330
|
+
sendInstagramProfile(jid: string, content: SocialEntityContent, options?: Record<string, unknown>): Promise<unknown>;
|
|
331
|
+
/** Send arbitrary A2UI/interactive payloads with immediate relay/edit rendering. */
|
|
332
|
+
sendA2UIMessage(jid: string, content?: A2UIMessageContent, options?: Record<string, unknown>): Promise<unknown>;
|
|
333
|
+
/** Send the large A2UI card/menu with an image header, command table, URL buttons, and native-flow selector. */
|
|
334
|
+
sendA2UICommandMenu(jid: string, content?: A2UICommandMenuContent, options?: Record<string, unknown>): Promise<unknown>;
|
|
335
|
+
/** Send the manually serialized AIRich command card with image, Markdown, widget CTAs, and footer actions. */
|
|
336
|
+
sendAIRichCommandMenu(jid: string, content: AIRichCommandMenuContent, options?: Record<string, unknown>): Promise<unknown>;
|
|
263
337
|
/** Send raw HTML through the experimental WhatsApp HTML primitive. */
|
|
264
338
|
sendHtmlMessage(jid: string, content: { html: string; trustedSources?: string[] }, options?: Record<string, unknown>): Promise<unknown>;
|
|
265
339
|
/** Post a personal status using explicit viewers or a configured contact source/provider. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plogme",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "Production-ready Baileys fork ⚉ with AI groups, Meta AI prompting, safe compositing fallbacks, rich responses, interactive messages, albums, newsletters, and communities — without antiban wrappers.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"type": "module",
|