plogme 2.0.2 → 2.0.4
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 +206 -10
- package/lib/Socket/messages-send.js +262 -133
- package/lib/Utils/messages.js +1 -23
- package/lib/Utils/rich-message-utils.js +320 -7
- package/lib/index.d.ts +58 -1
- package/package.json +1 -1
|
@@ -62,6 +62,30 @@ export const toUnified = (submessages, uuid) => ({
|
|
|
62
62
|
}
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
|
+
case RichSubMessageType.GRID_IMAGE: {
|
|
66
|
+
const imageUrls = submessage.gridImageMetadata?.imageUrls || [];
|
|
67
|
+
if (!imageUrls.length) return null;
|
|
68
|
+
const expiration = String(Date.now() + 30 * 24 * 60 * 60 * 1000);
|
|
69
|
+
const primitives = imageUrls.map(img => {
|
|
70
|
+
const previewUrl = img.imagePreviewUrl || img.sourceUrl || '';
|
|
71
|
+
const fullUrl = img.imageHighResUrl || img.imagePreviewUrl || img.sourceUrl || '';
|
|
72
|
+
const width = img.width || 600;
|
|
73
|
+
const height = img.height || 400;
|
|
74
|
+
const darkPreviewUrl = img.darkModePreviewUrl || previewUrl;
|
|
75
|
+
const darkFullUrl = img.darkModeHighResUrl || img.darkModePreviewUrl || fullUrl;
|
|
76
|
+
const darkWidth = img.darkWidth || width;
|
|
77
|
+
const darkHeight = img.darkHeight || height;
|
|
78
|
+
return {
|
|
79
|
+
preview_image: { url: previewUrl, mime_type: 'image/png', expiration_timestamp_ms: expiration, width, height },
|
|
80
|
+
full_image: { url: fullUrl, mime_type: 'image/png', expiration_timestamp_ms: expiration, width, height },
|
|
81
|
+
dark_mode_preview_image: { url: darkPreviewUrl, mime_type: 'image/png', expiration_timestamp_ms: expiration, width: darkWidth, height: darkHeight },
|
|
82
|
+
dark_mode_full_image: { url: darkFullUrl, mime_type: 'image/png', expiration_timestamp_ms: expiration, width: darkWidth, height: darkHeight },
|
|
83
|
+
asset_query_status: 'FETCHED',
|
|
84
|
+
__typename: 'GenAIImagePrimitive'
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
return { view_model: { primitives, __typename: 'GenAIGridLayoutViewModel' } };
|
|
88
|
+
}
|
|
65
89
|
case RichSubMessageType.CONTENT_ITEMS: {
|
|
66
90
|
const items = submessage.contentItemsMetadata?.itemsMetadata || [];
|
|
67
91
|
const primitives = items.map((item) => {
|
|
@@ -448,6 +472,78 @@ export const prepareRichMenuMessage = (content = {}) => {
|
|
|
448
472
|
* @param {'END'|'START'|'CENTER'} [options.alignment='END'] - Button alignment
|
|
449
473
|
* @returns {Object} Wrapped botForwardedMessage ready for relayMessage
|
|
450
474
|
*/
|
|
475
|
+
/** Build the manually serialized AIRich command menu shown in the screenshot. */
|
|
476
|
+
export const prepareAIRichCommandMenu = ({ image, title = 'CODY AI', subtitle = '', code = '', rows = [], buttons = [], footer = 'Developed by - CODY AI', latexImage, latexExpression = '' } = {}) => {
|
|
477
|
+
if (!image) throw new Error('AIRich command menu requires an image URL');
|
|
478
|
+
if (!Array.isArray(rows)) throw new Error('AIRich command-menu rows must be an array');
|
|
479
|
+
if (!Array.isArray(buttons)) throw new Error('AIRich command-menu buttons must be an array');
|
|
480
|
+
const safeRows = rows.map((row, index) => ({ command: String(row?.[0] ?? row?.command ?? `command_${index + 1}`), description: String(row?.[1] ?? row?.description ?? '') }));
|
|
481
|
+
const table = ['Command | Function', '--- | ---', ...safeRows.map((row) => `${row.command} | ${row.description}`)].join('\n');
|
|
482
|
+
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' } }] : [];
|
|
483
|
+
const text = [String(title), subtitle && String(subtitle), code && `Code: ${String(code)}`, '## Command List', table, footer && String(footer)].filter(Boolean).join('\n');
|
|
484
|
+
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) } };
|
|
485
|
+
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' }));
|
|
486
|
+
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' }));
|
|
487
|
+
footerPrimitives.push({ cta_text: '☷ OPEN MENU', cta_type: 'OPEN_URL', cta_url: String(buttons[0]?.url || ''), __typename: 'GenAIFooterActionPrimitive' });
|
|
488
|
+
const sections = [
|
|
489
|
+
{ __typename: 'GenAIUnifiedResponseSection', view_model: { __typename: 'GenAISingleLayoutViewModel', primitive: imagePrimitive } },
|
|
490
|
+
...(ctas.length ? [{ __typename: 'GenAIUnifiedResponseSection', view_model: { primitives: [{ __typename: 'GenAI3PExtWidgetPrimitive', header: { __typename: 'GenAI3PExtWidgetStandardHeader', title: 'Command List' }, body: { __typename: 'GenAIExtCalendarEventList', ctas, sections: [] } }], __typename: 'GenAIActionRowLayoutViewModel' } }] : []),
|
|
491
|
+
{ __typename: 'GenAIUnifiedResponseSection', view_model: { primitive: { text: `{{NGA}}${text}{{/NGA}}`, inline_entities: inlineEntities, __typename: 'GenAIMarkdownTextUXPrimitive' }, __typename: 'GenAISingleLayoutViewModel' } },
|
|
492
|
+
{ __typename: 'GenAIUnifiedResponseSection', view_model: { primitives: footerPrimitives, __typename: 'GenAIActionRowLayoutViewModel' } }
|
|
493
|
+
];
|
|
494
|
+
const richResponseMessage = proto.AIRichResponseMessage.create({
|
|
495
|
+
submessages: [{ messageType: RichSubMessageType.TEXT, messageText: text }],
|
|
496
|
+
messageType: proto.AIRichResponseMessageType.AI_RICH_RESPONSE_TYPE_STANDARD,
|
|
497
|
+
unifiedResponse: { data: Buffer.from(JSON.stringify({ response_id: randomUUID(), sections })) },
|
|
498
|
+
contextInfo: { isForwarded: true, forwardingScore: 1, forwardedAiBotMessageInfo: { botJid: '867051314767696@bot' }, forwardOrigin: 4 }
|
|
499
|
+
});
|
|
500
|
+
return wrapToBotForwardedMessage(richResponseMessage);
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
/** Build the A2UI interactive command menu used by the full-size card layout. */
|
|
504
|
+
export const prepareA2UICommandMenu = ({ imageMessage, title = 'CODY AI', rows = [], buttons = [], fallback = '' } = {}) => {
|
|
505
|
+
if (!Array.isArray(rows)) throw new Error('A2UI command-menu rows must be an array');
|
|
506
|
+
if (!Array.isArray(buttons)) throw new Error('A2UI command-menu buttons must be an array');
|
|
507
|
+
const safeRows = rows.map((row, index) => ({
|
|
508
|
+
title: String(row?.[0] ?? row?.title ?? `Command ${index + 1}`),
|
|
509
|
+
description: String(row?.[1] ?? row?.description ?? ''),
|
|
510
|
+
id: String(row?.[2] ?? row?.id ?? row?.[0] ?? `command_${index + 1}`)
|
|
511
|
+
}));
|
|
512
|
+
const safeButtons = buttons.map((button, index) => ({ id: `a2ui_button_${index + 1}`, label: String(button?.text || button?.label || `Button ${index + 1}`), url: String(button?.url || '') }));
|
|
513
|
+
const componentIds = ['title', 'divider', 'tableCard', 'footer', ...(safeButtons.length ? ['buttonRow'] : [])];
|
|
514
|
+
const components = [
|
|
515
|
+
{ id: 'root', component: 'Column', align: 'center', children: componentIds },
|
|
516
|
+
{ id: 'title', component: 'Text', text: String(title), variant: 'h2' },
|
|
517
|
+
{ id: 'divider', component: 'Divider' },
|
|
518
|
+
{ id: 'tableCard', component: 'Card', child: 'tableColumn' },
|
|
519
|
+
{ id: 'tableColumn', component: 'Column', children: ['headerRow', ...safeRows.flatMap((_, index) => [`divider_${index}`, `row_${index}`])] },
|
|
520
|
+
{ id: 'headerRow', component: 'Row', children: ['headerCommand', 'headerFunction'] },
|
|
521
|
+
{ id: 'headerCommand', component: 'Text', text: 'Command', variant: 'h5' },
|
|
522
|
+
{ id: 'headerFunction', component: 'Text', text: 'Function', variant: 'h5' },
|
|
523
|
+
...safeRows.flatMap((row, index) => [
|
|
524
|
+
{ id: `divider_${index}`, component: 'Divider' },
|
|
525
|
+
{ id: `row_${index}`, component: 'Row', children: [`command_${index}`, `description_${index}`] },
|
|
526
|
+
{ id: `command_${index}`, component: 'Text', text: row.title },
|
|
527
|
+
{ id: `description_${index}`, component: 'Text', text: row.description }
|
|
528
|
+
]),
|
|
529
|
+
{ id: 'footer', component: 'Text', text: 'Developed by - CODY AI', variant: 'caption' }
|
|
530
|
+
];
|
|
531
|
+
if (safeButtons.length) {
|
|
532
|
+
components.push({ id: 'buttonRow', component: 'Row', justify: 'spaceEvenly', children: safeButtons.flatMap((button, index) => [`buttonLabel_${index}`, `button_${index}`]) });
|
|
533
|
+
safeButtons.forEach((button, index) => {
|
|
534
|
+
components.push({ id: `buttonLabel_${index}`, component: 'Text', text: button.label });
|
|
535
|
+
components.push({ id: `button_${index}`, component: 'Button', child: `buttonLabel_${index}`, variant: 'primary', action: { call: 'openUrl', args: { url: button.url } } });
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
return {
|
|
539
|
+
interactiveMessage: {
|
|
540
|
+
...(imageMessage ? { header: { imageMessage, hasMediaAttachment: true } } : {}),
|
|
541
|
+
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) },
|
|
542
|
+
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 }] }) }] }
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
};
|
|
546
|
+
|
|
451
547
|
export const prepareCopyToClipboardMessage = (options = {}) => {
|
|
452
548
|
const { text = '', label, alignment = 'END', verificationMetadata } = options;
|
|
453
549
|
const displayText = label || text;
|
|
@@ -649,9 +745,138 @@ export const prepareRichLinkMessage = (options = {}) => {
|
|
|
649
745
|
return wrapToBotForwardedMessage(richResponseMessage, verificationMetadata);
|
|
650
746
|
};
|
|
651
747
|
|
|
748
|
+
/** Build an AIRich GRID_IMAGE album instead of the legacy albumMessage collection. */
|
|
749
|
+
export const prepareGridImageMessage = ({ imageUrls = [], verificationMetadata } = {}) => {
|
|
750
|
+
if (!Array.isArray(imageUrls) || imageUrls.length < 2) {
|
|
751
|
+
throw new Error('GRID_IMAGE album requires at least two images');
|
|
752
|
+
}
|
|
753
|
+
const submessages = [{
|
|
754
|
+
messageType: RichSubMessageType.GRID_IMAGE,
|
|
755
|
+
gridImageMetadata: { imageUrls }
|
|
756
|
+
}];
|
|
757
|
+
const unified = toUnified(submessages, randomUUID());
|
|
758
|
+
const richResponseMessage = proto.AIRichResponseMessage.create({
|
|
759
|
+
submessages,
|
|
760
|
+
messageType: proto.AIRichResponseMessageType.AI_RICH_RESPONSE_TYPE_STANDARD,
|
|
761
|
+
unifiedResponse: { data: Buffer.from(JSON.stringify(unified)) },
|
|
762
|
+
contextInfo: {
|
|
763
|
+
isForwarded: true,
|
|
764
|
+
forwardingScore: 1,
|
|
765
|
+
forwardedAiBotMessageInfo: { botJid: '867051314767696@bot' },
|
|
766
|
+
forwardOrigin: 4
|
|
767
|
+
}
|
|
768
|
+
});
|
|
769
|
+
return wrapToBotForwardedMessage(richResponseMessage, verificationMetadata);
|
|
770
|
+
};
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* Build a video grid using the client-supported GenAIVideoPrimitive.
|
|
774
|
+
*
|
|
775
|
+
* WhatsApp exposes no AIRich GRID_VIDEO submessage type. Video grids therefore
|
|
776
|
+
* use the generic unified-response Grid layout with one GenAIVideoPrimitive per
|
|
777
|
+
* item. This is deliberately separate from prepareGridImageMessage so callers
|
|
778
|
+
* do not accidentally label video media as GenAIImagePrimitive.
|
|
779
|
+
*/
|
|
780
|
+
/** Build a full-width vertical album with one large media item per row. */
|
|
781
|
+
export const prepareVerticalAlbumMessage = ({ items = [], verificationMetadata } = {}) => {
|
|
782
|
+
if (!Array.isArray(items) || items.length < 2) throw new Error('Vertical album requires at least two media items');
|
|
783
|
+
const expiration = String(Date.now() + 30 * 24 * 60 * 60 * 1000);
|
|
784
|
+
const primitives = items.map((item) => {
|
|
785
|
+
const kind = item?.type || (item?.video ? 'video' : 'image');
|
|
786
|
+
if (kind === 'video') {
|
|
787
|
+
const video = item.video || item.data || item;
|
|
788
|
+
const url = typeof video === 'string' ? video : (video?.url || video?.videoUrl || '');
|
|
789
|
+
if (!url) throw new Error('Each video album item requires a video URL');
|
|
790
|
+
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' });
|
|
791
|
+
}
|
|
792
|
+
const image = item?.image || item?.data || item;
|
|
793
|
+
const sourceUrl = typeof image === 'string' ? image : (image?.sourceUrl || image?.url || '');
|
|
794
|
+
if (!sourceUrl) throw new Error('Each image album item requires an image URL');
|
|
795
|
+
const previewUrl = image?.imagePreviewUrl || image?.previewUrl || sourceUrl;
|
|
796
|
+
const fullUrl = image?.imageHighResUrl || image?.highResUrl || image?.url || sourceUrl;
|
|
797
|
+
const width = image?.width || item?.width || 600;
|
|
798
|
+
const height = image?.height || item?.height || 400;
|
|
799
|
+
const mimeType = image?.mimeType || image?.mime_type || 'image/png';
|
|
800
|
+
return {
|
|
801
|
+
preview_image: { url: previewUrl, mime_type: mimeType, expiration_timestamp_ms: expiration, width, height },
|
|
802
|
+
full_image: { url: fullUrl, mime_type: mimeType, expiration_timestamp_ms: expiration, width, height },
|
|
803
|
+
dark_mode_preview_image: { url: image?.darkModePreviewUrl || previewUrl, mime_type: mimeType, expiration_timestamp_ms: expiration, width, height },
|
|
804
|
+
dark_mode_full_image: { url: image?.darkModeHighResUrl || image?.darkModePreviewUrl || fullUrl, mime_type: mimeType, expiration_timestamp_ms: expiration, width, height },
|
|
805
|
+
asset_query_status: 'FETCHED',
|
|
806
|
+
__typename: 'GenAIImagePrimitive'
|
|
807
|
+
};
|
|
808
|
+
});
|
|
809
|
+
const richResponseMessage = proto.AIRichResponseMessage.create({
|
|
810
|
+
submessages: [],
|
|
811
|
+
messageType: proto.AIRichResponseMessageType.AI_RICH_RESPONSE_TYPE_STANDARD,
|
|
812
|
+
unifiedResponse: { data: Buffer.from(JSON.stringify({ response_id: randomUUID(), sections: [{ view_model: { primitives, __typename: 'GenAIVStackLayoutViewModel' } }] })) },
|
|
813
|
+
contextInfo: { isForwarded: true, forwardingScore: 1, forwardedAiBotMessageInfo: { botJid: '867051314767696@bot' }, forwardOrigin: 4 }
|
|
814
|
+
});
|
|
815
|
+
return wrapToBotForwardedMessage(richResponseMessage, verificationMetadata);
|
|
816
|
+
};
|
|
817
|
+
|
|
818
|
+
export const prepareVideoGridMessage = ({ videos = [], verificationMetadata } = {}) => {
|
|
819
|
+
if (!Array.isArray(videos) || videos.length < 2) {
|
|
820
|
+
throw new Error('Video grid requires at least two videos');
|
|
821
|
+
}
|
|
822
|
+
const primitives = videos.map((video) => {
|
|
823
|
+
const url = typeof video === 'string' ? video : (video?.url || video?.videoUrl || video?.reelsUrl || '');
|
|
824
|
+
if (!url) throw new Error('Each video-grid item requires a video URL');
|
|
825
|
+
const item = typeof video === 'string' ? {} : video;
|
|
826
|
+
return {
|
|
827
|
+
post_id: item.postId == null ? undefined : String(item.postId),
|
|
828
|
+
reels_url: url,
|
|
829
|
+
reels_title: item.title,
|
|
830
|
+
thumbnail_url: item.thumbnailUrl || item.thumbnail,
|
|
831
|
+
creator: item.creator,
|
|
832
|
+
avatar_url: item.avatarUrl,
|
|
833
|
+
content_hash: item.contentHash,
|
|
834
|
+
likes_count: item.likes,
|
|
835
|
+
comments_count: item.comments,
|
|
836
|
+
shares_count: item.shares,
|
|
837
|
+
is_verified: item.isVerified,
|
|
838
|
+
source_app: item.sourceApp,
|
|
839
|
+
video_delivery_response: {
|
|
840
|
+
progressive_urls: (item.progressiveUrls || []).map((value) => ({
|
|
841
|
+
progressive_url: value,
|
|
842
|
+
__typename: 'GenAIProgressiveUrlResponse'
|
|
843
|
+
})),
|
|
844
|
+
dash_manifests: (item.dashManifests || []).map((value) => ({
|
|
845
|
+
manifest_xml: value,
|
|
846
|
+
__typename: 'GenAIDashManifestResponse'
|
|
847
|
+
})),
|
|
848
|
+
__typename: 'GenAIVideoDeliveryResponse'
|
|
849
|
+
},
|
|
850
|
+
__typename: 'GenAIVideoPrimitive'
|
|
851
|
+
};
|
|
852
|
+
});
|
|
853
|
+
const unified = {
|
|
854
|
+
response_id: randomUUID(),
|
|
855
|
+
sections: [{
|
|
856
|
+
view_model: {
|
|
857
|
+
primitives,
|
|
858
|
+
__typename: 'GenAIGridLayoutViewModel'
|
|
859
|
+
}
|
|
860
|
+
}]
|
|
861
|
+
};
|
|
862
|
+
const richResponseMessage = proto.AIRichResponseMessage.create({
|
|
863
|
+
submessages: [],
|
|
864
|
+
messageType: proto.AIRichResponseMessageType.AI_RICH_RESPONSE_TYPE_STANDARD,
|
|
865
|
+
unifiedResponse: { data: Buffer.from(JSON.stringify(unified)) },
|
|
866
|
+
contextInfo: {
|
|
867
|
+
isForwarded: true,
|
|
868
|
+
forwardingScore: 1,
|
|
869
|
+
forwardedAiBotMessageInfo: { botJid: '867051314767696@bot' },
|
|
870
|
+
forwardOrigin: 4
|
|
871
|
+
}
|
|
872
|
+
});
|
|
873
|
+
return wrapToBotForwardedMessage(richResponseMessage, verificationMetadata);
|
|
874
|
+
};
|
|
875
|
+
|
|
652
876
|
export const prepareRichResponseMessage = (content) => {
|
|
653
877
|
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;
|
|
654
878
|
let submessages = [];
|
|
879
|
+
const extraSections = [];
|
|
655
880
|
if (Array.isArray(richResponse)) {
|
|
656
881
|
submessages = richResponse.map((submessage) => {
|
|
657
882
|
if (submessage.text) {
|
|
@@ -770,7 +995,10 @@ export const prepareRichResponseMessage = (content) => {
|
|
|
770
995
|
reelSource: reel?.reelSource || reel?.reel_source || 'IG',
|
|
771
996
|
isVerified: !!(reel?.isVerified ?? reel?.is_verified)
|
|
772
997
|
} })) : [];
|
|
773
|
-
const resolvedItems =
|
|
998
|
+
const resolvedItems = [
|
|
999
|
+
...(Array.isArray(items) ? items : []),
|
|
1000
|
+
...reelItems
|
|
1001
|
+
];
|
|
774
1002
|
if (resolvedItems?.length) {
|
|
775
1003
|
submessages.push({
|
|
776
1004
|
messageType: RichSubMessageType.CONTENT_ITEMS,
|
|
@@ -792,9 +1020,21 @@ export const prepareRichResponseMessage = (content) => {
|
|
|
792
1020
|
});
|
|
793
1021
|
}
|
|
794
1022
|
if (inlineVideo) {
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
1023
|
+
const video = typeof inlineVideo === 'string' ? { url: inlineVideo } : inlineVideo;
|
|
1024
|
+
submessages.push({ messageType: RichSubMessageType.TEXT, messageText: '[ CANNOT_LOAD_VIDEO - NIXEL ]' });
|
|
1025
|
+
extraSections.push({
|
|
1026
|
+
view_model: {
|
|
1027
|
+
primitive: buildImaginePrimitive({
|
|
1028
|
+
mediaType: 'video',
|
|
1029
|
+
url: video?.url || video?.videoUrl || '',
|
|
1030
|
+
mimeType: video?.mimeType || video?.mime_type || 'video/mp4',
|
|
1031
|
+
fileLength: video?.fileLength || video?.file_length || 0,
|
|
1032
|
+
duration: video?.duration || 0,
|
|
1033
|
+
thumbnail: video?.thumbnail,
|
|
1034
|
+
status: video?.status || 'READY'
|
|
1035
|
+
}),
|
|
1036
|
+
__typename: 'GenAISingleLayoutViewModel'
|
|
1037
|
+
}
|
|
798
1038
|
});
|
|
799
1039
|
}
|
|
800
1040
|
if (latex) {
|
|
@@ -812,7 +1052,40 @@ export const prepareRichResponseMessage = (content) => {
|
|
|
812
1052
|
title: 'PAIR CRYSN⚉VA AI ⩇⩇:⩇⩇',
|
|
813
1053
|
displayName: 'pair bot'
|
|
814
1054
|
};
|
|
815
|
-
const
|
|
1055
|
+
const rawSources = Array.isArray(sourceInputs) ? sourceInputs : [];
|
|
1056
|
+
// Existing `sources: [{ url, displayName, subtitle }]` callers use
|
|
1057
|
+
// citation entities. The serialized AIRich addSource format is the
|
|
1058
|
+
// distinct tuple form: `[[faviconUrl, sourceUrl, displayText], ...]`.
|
|
1059
|
+
const sourceTuples = rawSources.length > 0 && rawSources.every((source) => Array.isArray(source));
|
|
1060
|
+
if (sourceTuples) {
|
|
1061
|
+
const sourceCards = rawSources.map((source) => {
|
|
1062
|
+
const item = Array.isArray(source)
|
|
1063
|
+
? { icon: source[0], url: source[1], text: source[2] }
|
|
1064
|
+
: source;
|
|
1065
|
+
const sourceUrl = item.url || item.sourceUrl || item.source_url || '';
|
|
1066
|
+
return {
|
|
1067
|
+
source_type: 'THIRD_PARTY',
|
|
1068
|
+
source_display_name: item.text || item.displayName || item.sourceDisplayName || '',
|
|
1069
|
+
source_subtitle: item.subtitle || 'AI',
|
|
1070
|
+
source_url: sourceUrl,
|
|
1071
|
+
favicon: {
|
|
1072
|
+
url: item.icon || item.iconUrl || item.favicon || '',
|
|
1073
|
+
mime_type: item.mimeType || 'image/jpeg',
|
|
1074
|
+
width: item.width || 16,
|
|
1075
|
+
height: item.height || 16
|
|
1076
|
+
}
|
|
1077
|
+
};
|
|
1078
|
+
}).filter((source) => source.source_url || source.source_display_name);
|
|
1079
|
+
if (sourceCards.length) {
|
|
1080
|
+
extraSections.push({
|
|
1081
|
+
view_model: {
|
|
1082
|
+
primitive: { sources: sourceCards, __typename: 'GenAISearchResultPrimitive' },
|
|
1083
|
+
__typename: 'GenAISingleLayoutViewModel'
|
|
1084
|
+
}
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
const sourceLinks = (sourceTuples ? [] : (sourceInputs || links || [])).map((linkField) => {
|
|
816
1089
|
if (typeof linkField === 'string') return { text: '', url: linkField, displayName: linkField };
|
|
817
1090
|
return { text: linkField?.text || linkField?.displayName || linkField?.title || '', ...linkField };
|
|
818
1091
|
});
|
|
@@ -847,14 +1120,53 @@ export const prepareRichResponseMessage = (content) => {
|
|
|
847
1120
|
if (posts) {
|
|
848
1121
|
submessages.push({
|
|
849
1122
|
messageType: RichSubMessageType.TEXT,
|
|
850
|
-
messageText: '
|
|
1123
|
+
messageText: '[ CANNOT_LOAD_POST - NIXEL ]'
|
|
851
1124
|
});
|
|
1125
|
+
const postItems = (Array.isArray(posts) ? posts : [posts]).map((post) => ({
|
|
1126
|
+
title: post?.title || '',
|
|
1127
|
+
subtitle: post?.subtitle || '',
|
|
1128
|
+
username: post?.username || post?.user || '',
|
|
1129
|
+
profile_picture_url: post?.profile_picture_url || post?.profile || post?.profile_url || '',
|
|
1130
|
+
is_verified: !!(post?.is_verified ?? post?.verified),
|
|
1131
|
+
thumbnail_url: post?.thumbnail_url || post?.thumbnail || '',
|
|
1132
|
+
post_caption: post?.post_caption || post?.caption || '',
|
|
1133
|
+
likes_count: post?.likes_count ?? post?.like ?? 0,
|
|
1134
|
+
comments_count: post?.comments_count ?? post?.comment ?? 0,
|
|
1135
|
+
shares_count: post?.shares_count ?? post?.share ?? 0,
|
|
1136
|
+
post_url: post?.post_url || post?.url || '',
|
|
1137
|
+
post_deeplink: post?.post_deeplink || post?.deeplink || '',
|
|
1138
|
+
source_app: post?.source_app || post?.source || '',
|
|
1139
|
+
footer_label: post?.footer_label || post?.footer || '',
|
|
1140
|
+
footer_icon: post?.footer_icon || post?.icon || '',
|
|
1141
|
+
is_carousel: post?.is_carousel ?? true,
|
|
1142
|
+
orientation: post?.orientation || 'LANDSCAPE',
|
|
1143
|
+
post_type: post?.post_type || 'VIDEO',
|
|
1144
|
+
__typename: 'GenAIPostPrimitive'
|
|
1145
|
+
}));
|
|
1146
|
+
if (postItems.length) extraSections.push({ view_model: { primitives: postItems, __typename: 'GenAIHScrollLayoutViewModel' } });
|
|
852
1147
|
}
|
|
853
1148
|
if (products) {
|
|
854
1149
|
submessages.push({
|
|
855
1150
|
messageType: RichSubMessageType.TEXT,
|
|
856
|
-
messageText: '
|
|
1151
|
+
messageText: '[ CANNOT_LOAD_PRODUCT - NIXEL ]'
|
|
857
1152
|
});
|
|
1153
|
+
const productItems = (Array.isArray(products) ? products : [products]).map((product) => ({
|
|
1154
|
+
title: product?.title || '',
|
|
1155
|
+
brand: product?.brand || '',
|
|
1156
|
+
price: product?.price || '',
|
|
1157
|
+
sale_price: product?.sale_price || product?.salePrice || '',
|
|
1158
|
+
product_url: product?.product_url || product?.url || '',
|
|
1159
|
+
image: { url: product?.image?.url || product?.image || '' },
|
|
1160
|
+
additional_images: [{ url: product?.icon_url || product?.icon || '' }],
|
|
1161
|
+
__typename: 'GenAIProductItemCardPrimitive'
|
|
1162
|
+
}));
|
|
1163
|
+
if (productItems.length) {
|
|
1164
|
+
extraSections.push({
|
|
1165
|
+
view_model: Array.isArray(products)
|
|
1166
|
+
? { primitives: productItems, __typename: 'GenAIHScrollLayoutViewModel' }
|
|
1167
|
+
: { primitive: productItems[0], __typename: 'GenAISingleLayoutViewModel' }
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
858
1170
|
}
|
|
859
1171
|
if (suggested) {
|
|
860
1172
|
submessages.push({
|
|
@@ -883,6 +1195,7 @@ export const prepareRichResponseMessage = (content) => {
|
|
|
883
1195
|
}
|
|
884
1196
|
const uuid = randomUUID();
|
|
885
1197
|
const unified = toUnified(submessages, uuid);
|
|
1198
|
+
unified.sections = [...unified.sections.filter(Boolean), ...extraSections];
|
|
886
1199
|
const richResponseMessage = proto.AIRichResponseMessage.create({
|
|
887
1200
|
submessages,
|
|
888
1201
|
messageType: proto.AIRichResponseMessageType.AI_RICH_RESPONSE_TYPE_STANDARD,
|
package/lib/index.d.ts
CHANGED
|
@@ -225,6 +225,17 @@ export interface RelayMessageOptions {
|
|
|
225
225
|
export interface AlbumMediaInput {
|
|
226
226
|
image?: unknown;
|
|
227
227
|
video?: unknown;
|
|
228
|
+
type?: 'image' | 'video';
|
|
229
|
+
data?: unknown;
|
|
230
|
+
sourceUrl?: string;
|
|
231
|
+
imagePreviewUrl?: string;
|
|
232
|
+
imageHighResUrl?: string;
|
|
233
|
+
darkModePreviewUrl?: string;
|
|
234
|
+
darkModeHighResUrl?: string;
|
|
235
|
+
width?: number;
|
|
236
|
+
height?: number;
|
|
237
|
+
darkWidth?: number;
|
|
238
|
+
darkHeight?: number;
|
|
228
239
|
caption?: string;
|
|
229
240
|
[key: string]: unknown;
|
|
230
241
|
}
|
|
@@ -236,10 +247,50 @@ export interface AnimatedWallpaperInput {
|
|
|
236
247
|
opacity?: number;
|
|
237
248
|
}
|
|
238
249
|
|
|
250
|
+
export interface A2UICommandMenuContent {
|
|
251
|
+
image?: unknown;
|
|
252
|
+
title?: string;
|
|
253
|
+
rows?: Array<[string, string, string?] | { title?: string; description?: string; id?: string }>;
|
|
254
|
+
buttons?: Array<{ text?: string; label?: string; url?: string }>;
|
|
255
|
+
fallback?: string;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export interface AIRichCommandMenuContent {
|
|
259
|
+
image: string;
|
|
260
|
+
title?: string;
|
|
261
|
+
subtitle?: string;
|
|
262
|
+
code?: string;
|
|
263
|
+
rows?: Array<[string, string] | { command?: string; description?: string }>;
|
|
264
|
+
latexImage?: string;
|
|
265
|
+
latexExpression?: string;
|
|
266
|
+
buttons?: Array<{ id?: string; text?: string; label?: string; url?: string; toast?: string }>;
|
|
267
|
+
footer?: string;
|
|
268
|
+
}
|
|
269
|
+
|
|
239
270
|
export interface WASocket {
|
|
240
271
|
sendMessage(jid: string | string[], content: StatusContent | GroupStatusContent | Record<string, unknown>, options?: Partial<StatusOptions> & Record<string, unknown>): Promise<unknown>;
|
|
241
|
-
/** Send at least two image/video items as
|
|
272
|
+
/** Send at least two image/video URL items as a full-width AIRich vertical album. */
|
|
242
273
|
sendAlbumMessage(jid: string, medias: AlbumMediaInput[], options?: Record<string, unknown>): Promise<unknown>;
|
|
274
|
+
/** Send an AIRich unified-response grid of GenAIVideoPrimitive items. Requires at least two video URLs. */
|
|
275
|
+
sendVideoGridMessage(jid: string, videos: Array<string | {
|
|
276
|
+
url?: string;
|
|
277
|
+
videoUrl?: string;
|
|
278
|
+
reelsUrl?: string;
|
|
279
|
+
postId?: string | number;
|
|
280
|
+
title?: string;
|
|
281
|
+
thumbnailUrl?: string;
|
|
282
|
+
thumbnail?: string;
|
|
283
|
+
creator?: string;
|
|
284
|
+
avatarUrl?: string;
|
|
285
|
+
contentHash?: string;
|
|
286
|
+
likes?: number;
|
|
287
|
+
comments?: number;
|
|
288
|
+
shares?: number;
|
|
289
|
+
isVerified?: boolean;
|
|
290
|
+
sourceApp?: string;
|
|
291
|
+
progressiveUrls?: string[];
|
|
292
|
+
dashManifests?: string[];
|
|
293
|
+
}>, options?: Record<string, unknown>): Promise<unknown>;
|
|
243
294
|
/** Update the connected account's WhatsApp About/profile status. */
|
|
244
295
|
updateProfileStatus(status: string): Promise<void>;
|
|
245
296
|
/** Update the connected account's profile name. */
|
|
@@ -250,6 +301,10 @@ export interface WASocket {
|
|
|
250
301
|
relayMessage(jid: string, message: Record<string, unknown>, options?: RelayMessageOptions): Promise<unknown>;
|
|
251
302
|
/** Send an experimental Meta AI-style rich menu with image, card buttons, and an optional URL footer. */
|
|
252
303
|
richMenu(jid: string, content?: RichMenuContent, options?: Record<string, unknown>): Promise<unknown>;
|
|
304
|
+
/** Send the large A2UI card/menu with an image header, command table, URL buttons, and native-flow selector. */
|
|
305
|
+
sendA2UICommandMenu(jid: string, content?: A2UICommandMenuContent, options?: Record<string, unknown>): Promise<unknown>;
|
|
306
|
+
/** Send the manually serialized AIRich command card with image, Markdown, widget CTAs, and footer actions. */
|
|
307
|
+
sendAIRichCommandMenu(jid: string, content: AIRichCommandMenuContent, options?: Record<string, unknown>): Promise<unknown>;
|
|
253
308
|
/** Send raw HTML through the experimental WhatsApp HTML primitive. */
|
|
254
309
|
sendHtmlMessage(jid: string, content: { html: string; trustedSources?: string[] }, options?: Record<string, unknown>): Promise<unknown>;
|
|
255
310
|
/** Post a personal status using explicit viewers or a configured contact source/provider. */
|
|
@@ -270,6 +325,8 @@ export interface WASocket {
|
|
|
270
325
|
sendBotPlanning(jid: string, content?: { text?: string; steps?: Array<string | { title?: string; name?: string; body?: string }>; queryPlan?: boolean; botJid?: string }, options?: Record<string, unknown>): Promise<unknown>;
|
|
271
326
|
/** Send a rich response with experimental resolved tool-call metadata. */
|
|
272
327
|
sendBotToolResult(jid: string, content?: { text?: string; toolCallId?: string; resolutionData?: unknown; resolutionDataSerialized?: string; botJid?: string }, options?: Record<string, unknown>): Promise<unknown>;
|
|
328
|
+
/** Send one tolerant AIRich response containing every supported section supplied in content. */
|
|
329
|
+
sendPlogmeMessage(jid: string, content?: Record<string, unknown>, quoted?: unknown, options?: Record<string, unknown>): Promise<unknown>;
|
|
273
330
|
/** Resolve a WhatsApp Channel invite code or invite URL to channel metadata. */
|
|
274
331
|
newsletterGetInviteInfo(inviteCodeOrUrl: string): Promise<Record<string, unknown> | null>;
|
|
275
332
|
/** React to a Channel post; pass an empty string, null, or undefined to remove the reaction. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plogme",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
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",
|