@nerxa/socketon 0.0.0-stage → 1.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EXPERIMENTAL.md +350 -0
- package/LICENSE +24 -0
- package/README.md +6317 -2
- package/WAProto/index.d.ts +17301 -0
- package/WAProto/index.js +125088 -0
- package/engine-requirements.js +14 -0
- package/package.json +100 -4
- package/src/Defaults/index.d.ts +152 -0
- package/src/Defaults/index.js +164 -0
- package/src/MessageBuilder/bot-signature.d.ts +22 -0
- package/src/MessageBuilder/bot-signature.js +122 -0
- package/src/MessageBuilder/extras.d.ts +449 -0
- package/src/MessageBuilder/extras.js +1356 -0
- package/src/MessageBuilder/index.d.ts +210 -0
- package/src/MessageBuilder/index.js +3594 -0
- package/src/MessageBuilder/metaai.d.ts +438 -0
- package/src/MessageBuilder/metaai.js +890 -0
- package/src/Signal/Group/ciphertext-message.d.ts +9 -0
- package/src/Signal/Group/ciphertext-message.js +11 -0
- package/src/Signal/Group/group-session-builder.d.ts +7 -0
- package/src/Signal/Group/group-session-builder.js +27 -0
- package/src/Signal/Group/group_cipher.d.ts +11 -0
- package/src/Signal/Group/group_cipher.js +78 -0
- package/src/Signal/Group/index.d.ts +11 -0
- package/src/Signal/Group/index.js +11 -0
- package/src/Signal/Group/keyhelper.d.ts +6 -0
- package/src/Signal/Group/keyhelper.js +17 -0
- package/src/Signal/Group/sender-chain-key.d.ts +13 -0
- package/src/Signal/Group/sender-chain-key.js +25 -0
- package/src/Signal/Group/sender-key-distribution-message.d.ts +16 -0
- package/src/Signal/Group/sender-key-distribution-message.js +62 -0
- package/src/Signal/Group/sender-key-message.d.ts +18 -0
- package/src/Signal/Group/sender-key-message.js +65 -0
- package/src/Signal/Group/sender-key-name.d.ts +11 -0
- package/src/Signal/Group/sender-key-name.js +47 -0
- package/src/Signal/Group/sender-key-record.d.ts +12 -0
- package/src/Signal/Group/sender-key-record.js +40 -0
- package/src/Signal/Group/sender-key-state.d.ts +16 -0
- package/src/Signal/Group/sender-key-state.js +83 -0
- package/src/Signal/Group/sender-message-key.d.ts +11 -0
- package/src/Signal/Group/sender-message-key.js +25 -0
- package/src/Signal/libsignal.d.ts +54 -0
- package/src/Signal/libsignal.js +427 -0
- package/src/Signal/lid-mapping.d.ts +20 -0
- package/src/Signal/lid-mapping.js +274 -0
- package/src/Socket/Client/index.d.ts +2 -0
- package/src/Socket/Client/index.js +3 -0
- package/src/Socket/Client/tcp.js +177 -0
- package/src/Socket/Client/types.d.ts +5 -0
- package/src/Socket/Client/types.js +9 -0
- package/src/Socket/Client/websocket.d.ts +12 -0
- package/src/Socket/Client/websocket.js +53 -0
- package/src/Socket/business.d.ts +431 -0
- package/src/Socket/business.js +379 -0
- package/src/Socket/chats.d.ts +247 -0
- package/src/Socket/chats.js +1484 -0
- package/src/Socket/communities.d.ts +598 -0
- package/src/Socket/communities.js +421 -0
- package/src/Socket/email.js +145 -0
- package/src/Socket/groups.d.ts +298 -0
- package/src/Socket/groups.js +339 -0
- package/src/Socket/index.d.ts +581 -0
- package/src/Socket/index.js +37 -0
- package/src/Socket/messages-recv.d.ts +370 -0
- package/src/Socket/messages-recv.js +1864 -0
- package/src/Socket/messages-send.d.ts +390 -0
- package/src/Socket/messages-send.js +1574 -0
- package/src/Socket/mex.d.ts +1 -0
- package/src/Socket/mex.js +44 -0
- package/src/Socket/mobile-builders.js +72 -0
- package/src/Socket/mobile.js +424 -0
- package/src/Socket/newsletter.d.ts +468 -0
- package/src/Socket/newsletter.js +671 -0
- package/src/Socket/socket.d.ts +77 -0
- package/src/Socket/socket.js +1106 -0
- package/src/Store/index.d.ts +3 -0
- package/src/Store/index.js +3 -0
- package/src/Store/make-in-memory-store.d.ts +62 -0
- package/src/Store/make-in-memory-store.js +396 -0
- package/src/Store/make-ordered-dictionary.d.ts +12 -0
- package/src/Store/make-ordered-dictionary.js +78 -0
- package/src/Store/object-repository.d.ts +10 -0
- package/src/Store/object-repository.js +23 -0
- package/src/Types/Auth.d.ts +1 -0
- package/src/Types/Auth.js +1 -0
- package/src/Types/Bussines.d.ts +1 -0
- package/src/Types/Bussines.js +1 -0
- package/src/Types/Call.d.ts +1 -0
- package/src/Types/Call.js +1 -0
- package/src/Types/Chat.d.ts +2 -0
- package/src/Types/Chat.js +24 -0
- package/src/Types/Contact.d.ts +1 -0
- package/src/Types/Contact.js +1 -0
- package/src/Types/Events.d.ts +1 -0
- package/src/Types/Events.js +0 -0
- package/src/Types/GroupMetadata.d.ts +5 -0
- package/src/Types/GroupMetadata.js +6 -0
- package/src/Types/Label.d.ts +2 -0
- package/src/Types/Label.js +24 -0
- package/src/Types/LabelAssociation.d.ts +2 -0
- package/src/Types/LabelAssociation.js +6 -0
- package/src/Types/Message.d.ts +13 -0
- package/src/Types/Message.js +23 -0
- package/src/Types/Mex.d.ts +2 -0
- package/src/Types/Mex.js +95 -0
- package/src/Types/Product.d.ts +1 -0
- package/src/Types/Product.js +1 -0
- package/src/Types/Signal.d.ts +1 -0
- package/src/Types/Signal.js +0 -0
- package/src/Types/Socket.d.ts +1 -0
- package/src/Types/Socket.js +0 -0
- package/src/Types/State.d.ts +5 -0
- package/src/Types/State.js +54 -0
- package/src/Types/USync.d.ts +1 -0
- package/src/Types/USync.js +0 -0
- package/src/Types/index.d.ts +13 -0
- package/src/Types/index.js +25 -0
- package/src/Utils/auth-store.d.ts +29 -0
- package/src/Utils/auth-store.js +76 -0
- package/src/Utils/auth-utils.d.ts +58 -0
- package/src/Utils/auth-utils.js +273 -0
- package/src/Utils/browser-utils.d.ts +9 -0
- package/src/Utils/browser-utils.js +28 -0
- package/src/Utils/business.d.ts +49 -0
- package/src/Utils/business.js +226 -0
- package/src/Utils/chat-utils.d.ts +408 -0
- package/src/Utils/chat-utils.js +846 -0
- package/src/Utils/companion-reg-client-utils.d.ts +4 -0
- package/src/Utils/companion-reg-client-utils.js +39 -0
- package/src/Utils/crypto.d.ts +39 -0
- package/src/Utils/crypto.js +110 -0
- package/src/Utils/decode-wa-message.d.ts +99 -0
- package/src/Utils/decode-wa-message.js +373 -0
- package/src/Utils/event-buffer.d.ts +22 -0
- package/src/Utils/event-buffer.js +624 -0
- package/src/Utils/generics.d.ts +63 -0
- package/src/Utils/generics.js +375 -0
- package/src/Utils/group-metadata-cache.d.ts +4 -0
- package/src/Utils/group-metadata-cache.js +25 -0
- package/src/Utils/history.d.ts +75 -0
- package/src/Utils/history.js +131 -0
- package/src/Utils/html-app.d.ts +19 -0
- package/src/Utils/html-app.js +96 -0
- package/src/Utils/html-multiplayer.d.ts +10 -0
- package/src/Utils/html-multiplayer.js +94 -0
- package/src/Utils/identity-change-handler.d.ts +13 -0
- package/src/Utils/identity-change-handler.js +48 -0
- package/src/Utils/index.d.ts +44 -0
- package/src/Utils/index.js +44 -0
- package/src/Utils/link-preview-metadata.d.ts +47 -0
- package/src/Utils/link-preview-metadata.js +88 -0
- package/src/Utils/link-preview.d.ts +12 -0
- package/src/Utils/link-preview.js +79 -0
- package/src/Utils/logger.d.ts +2 -0
- package/src/Utils/logger.js +2 -0
- package/src/Utils/lt-hash.d.ts +6 -0
- package/src/Utils/lt-hash.js +3 -0
- package/src/Utils/make-mutex.d.ts +6 -0
- package/src/Utils/make-mutex.js +32 -0
- package/src/Utils/message-retry-manager.d.ts +79 -0
- package/src/Utils/message-retry-manager.js +255 -0
- package/src/Utils/messages-media.d.ts +136 -0
- package/src/Utils/messages-media.js +814 -0
- package/src/Utils/messages.d.ts +62 -0
- package/src/Utils/messages.js +2223 -0
- package/src/Utils/mobile-companion-crypto.js +191 -0
- package/src/Utils/modern-messages.d.ts +150 -0
- package/src/Utils/modern-messages.js +496 -0
- package/src/Utils/native-flow.d.ts +11 -0
- package/src/Utils/native-flow.js +115 -0
- package/src/Utils/newsletter-status.d.ts +158 -0
- package/src/Utils/newsletter-status.js +651 -0
- package/src/Utils/noise-handler.d.ts +20 -0
- package/src/Utils/noise-handler.js +200 -0
- package/src/Utils/offline-node-processor.d.ts +9 -0
- package/src/Utils/offline-node-processor.js +33 -0
- package/src/Utils/optional-media.d.ts +3 -0
- package/src/Utils/optional-media.js +45 -0
- package/src/Utils/payment-messages.d.ts +66 -0
- package/src/Utils/payment-messages.js +160 -0
- package/src/Utils/pre-key-manager.d.ts +25 -0
- package/src/Utils/pre-key-manager.js +95 -0
- package/src/Utils/process-message.d.ts +60 -0
- package/src/Utils/process-message.js +688 -0
- package/src/Utils/reporting-utils.d.ts +12 -0
- package/src/Utils/reporting-utils.js +256 -0
- package/src/Utils/scheduled-message.d.ts +51 -0
- package/src/Utils/scheduled-message.js +112 -0
- package/src/Utils/sender-key-memory.d.ts +19 -0
- package/src/Utils/sender-key-memory.js +51 -0
- package/src/Utils/signal.d.ts +82 -0
- package/src/Utils/signal.js +193 -0
- package/src/Utils/stanza-ack.d.ts +16 -0
- package/src/Utils/stanza-ack.js +30 -0
- package/src/Utils/status-stickers.d.ts +127 -0
- package/src/Utils/status-stickers.js +260 -0
- package/src/Utils/sync-action-utils.d.ts +2 -0
- package/src/Utils/sync-action-utils.js +44 -0
- package/src/Utils/tc-token-utils.d.ts +29 -0
- package/src/Utils/tc-token-utils.js +155 -0
- package/src/Utils/ttl-cache.d.ts +28 -0
- package/src/Utils/ttl-cache.js +120 -0
- package/src/Utils/use-mongo-auth-state.d.ts +21 -0
- package/src/Utils/use-mongo-auth-state.js +67 -0
- package/src/Utils/use-multi-file-auth-state.d.ts +11 -0
- package/src/Utils/use-multi-file-auth-state.js +106 -0
- package/src/Utils/use-mysql-auth-state.d.ts +19 -0
- package/src/Utils/use-mysql-auth-state.js +79 -0
- package/src/Utils/use-postgres-auth-state.d.ts +19 -0
- package/src/Utils/use-postgres-auth-state.js +79 -0
- package/src/Utils/use-redis-auth-state.d.ts +19 -0
- package/src/Utils/use-redis-auth-state.js +89 -0
- package/src/Utils/use-single-file-auth-state.d.ts +10 -0
- package/src/Utils/use-single-file-auth-state.js +108 -0
- package/src/Utils/use-sqlite-auth-state.d.ts +12 -0
- package/src/Utils/use-sqlite-auth-state.js +120 -0
- package/src/Utils/useNekoDBAuth.d.ts +19 -0
- package/src/Utils/useNekoDBAuth.js +111 -0
- package/src/Utils/username.d.ts +12 -0
- package/src/Utils/username.js +58 -0
- package/src/Utils/validate-connection.d.ts +43 -0
- package/src/Utils/validate-connection.js +277 -0
- package/src/Utils/voice-recognition.d.ts +53 -0
- package/src/Utils/voice-recognition.js +219 -0
- package/src/Voip/audio-feeder.d.ts +24 -0
- package/src/Voip/audio-feeder.js +268 -0
- package/src/Voip/index.d.ts +88 -0
- package/src/Voip/index.js +936 -0
- package/src/Voip/relay-transport.d.ts +5 -0
- package/src/Voip/relay-transport.js +824 -0
- package/src/Voip/signaling.d.ts +11 -0
- package/src/Voip/signaling.js +1099 -0
- package/src/Voip/types.d.ts +2 -0
- package/src/Voip/types.js +12 -0
- package/src/Voip/video-feeder.d.ts +24 -0
- package/src/Voip/video-feeder.js +268 -0
- package/src/Voip/wasm-engine.d.ts +26 -0
- package/src/Voip/wasm-engine.js +1307 -0
- package/src/Voip/worker-bootstrap.js +1023 -0
- package/src/WABinary/constants.d.ts +22 -0
- package/src/WABinary/constants.js +1300 -0
- package/src/WABinary/decode.d.ts +9 -0
- package/src/WABinary/decode.js +261 -0
- package/src/WABinary/encode.d.ts +2 -0
- package/src/WABinary/encode.js +219 -0
- package/src/WABinary/generic-utils.d.ts +76 -0
- package/src/WABinary/generic-utils.js +214 -0
- package/src/WABinary/index.d.ts +5 -0
- package/src/WABinary/index.js +5 -0
- package/src/WABinary/jid-utils.d.ts +30 -0
- package/src/WABinary/jid-utils.js +97 -0
- package/src/WABinary/types.d.ts +1 -0
- package/src/WABinary/types.js +0 -0
- package/src/WAM/BinaryInfo.d.ts +7 -0
- package/src/WAM/BinaryInfo.js +9 -0
- package/src/WAM/constants.d.ts +34926 -0
- package/src/WAM/constants.js +22852 -0
- package/src/WAM/encode.d.ts +1 -0
- package/src/WAM/encode.js +148 -0
- package/src/WAM/index.d.ts +3 -0
- package/src/WAM/index.js +3 -0
- package/src/WAUSync/Protocols/USyncContactProtocol.d.ts +36 -0
- package/src/WAUSync/Protocols/USyncContactProtocol.js +50 -0
- package/src/WAUSync/Protocols/USyncDeviceProtocol.d.ts +22 -0
- package/src/WAUSync/Protocols/USyncDeviceProtocol.js +51 -0
- package/src/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +12 -0
- package/src/WAUSync/Protocols/USyncDisappearingModeProtocol.js +26 -0
- package/src/WAUSync/Protocols/USyncStatusProtocol.d.ts +12 -0
- package/src/WAUSync/Protocols/USyncStatusProtocol.js +37 -0
- package/src/WAUSync/Protocols/USyncUsernameProtocol.d.ts +9 -0
- package/src/WAUSync/Protocols/USyncUsernameProtocol.js +23 -0
- package/src/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +38 -0
- package/src/WAUSync/Protocols/UsyncBotProfileProtocol.js +49 -0
- package/src/WAUSync/Protocols/UsyncLIDProtocol.d.ts +14 -0
- package/src/WAUSync/Protocols/UsyncLIDProtocol.js +28 -0
- package/src/WAUSync/Protocols/index.d.ts +5 -0
- package/src/WAUSync/Protocols/index.js +5 -0
- package/src/WAUSync/USyncQuery.d.ts +20 -0
- package/src/WAUSync/USyncQuery.js +93 -0
- package/src/WAUSync/USyncUser.d.ts +16 -0
- package/src/WAUSync/USyncUser.js +30 -0
- package/src/WAUSync/index.d.ts +3 -0
- package/src/WAUSync/index.js +3 -0
- package/src/assets/wasm/README.md +76 -0
- package/src/assets/wasm/loader.js +5 -0
- package/src/assets/wasm/whatsapp.wasm +0 -0
- package/src/assets/wasm/worker-modules.js +273 -0
- package/src/index.d.ts +43 -0
- package/src/index.js +48 -0
|
@@ -0,0 +1,1356 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto'
|
|
2
|
+
import { AIRich } from './index.js'
|
|
3
|
+
import { extractMessageContent, generateWAMessageFromContent, prepareWAMessageMedia } from '../Utils/messages.js'
|
|
4
|
+
import { checkHtmlApp } from '../Utils/html-app.js'
|
|
5
|
+
|
|
6
|
+
export const AI_RICH_LAYOUTS = Object.freeze([
|
|
7
|
+
'Single',
|
|
8
|
+
'HScroll',
|
|
9
|
+
'ActionRow',
|
|
10
|
+
'VStack',
|
|
11
|
+
'Grid',
|
|
12
|
+
'FlexibleCountGrid',
|
|
13
|
+
'RichListItem',
|
|
14
|
+
'AddonAction',
|
|
15
|
+
'MultipleResponse',
|
|
16
|
+
'IGSuggestedBloomCard'
|
|
17
|
+
])
|
|
18
|
+
|
|
19
|
+
export const AI_RICH_PRIMITIVES = Object.freeze([
|
|
20
|
+
'GenAIMarkdownTextUXPrimitive',
|
|
21
|
+
'GenAICodeUXPrimitive',
|
|
22
|
+
'GenATableUXPrimitive',
|
|
23
|
+
'GenAIMetadataTextPrimitive',
|
|
24
|
+
'GenAISearchResultPrimitive',
|
|
25
|
+
'GenAIReelPrimitive',
|
|
26
|
+
'GenAIPostPrimitive',
|
|
27
|
+
'GenAIProductItemCardPrimitive',
|
|
28
|
+
'GenAIImaginePrimitive',
|
|
29
|
+
'GenAIFollowUpSuggestionPillPrimitive',
|
|
30
|
+
'FOATextPrimitive',
|
|
31
|
+
'FOABloksPrimitive',
|
|
32
|
+
'GenAIImagePrimitive',
|
|
33
|
+
'GenAIDividerPrimitive',
|
|
34
|
+
'GenAISpacerPrimitive',
|
|
35
|
+
'GenAITaskPrimitive',
|
|
36
|
+
'GenAILatexUXPrimitive',
|
|
37
|
+
'GenAIBotThinkingStatusPrimitive',
|
|
38
|
+
'GenAIBotProgressStatusPrimitive',
|
|
39
|
+
'GenAIMetaSubsQuotaUpsellPrimitive',
|
|
40
|
+
'GenAIMapPrimitive',
|
|
41
|
+
'GenAIVideoPrimitive',
|
|
42
|
+
'GenAIReminderPrimitive',
|
|
43
|
+
'GenAICommentPrimitive',
|
|
44
|
+
'GenAICompactEntityPrimitive',
|
|
45
|
+
'GenAIActionListPrimitive',
|
|
46
|
+
'GenAISportsWidgetPrimitive',
|
|
47
|
+
'GenAISearchAdPrimitive',
|
|
48
|
+
'GenAISearchResultV2Primitive',
|
|
49
|
+
'GenAISearchPlannerStepsPrimitive',
|
|
50
|
+
'GenAISearchPlannerStepSnippetPrimitive',
|
|
51
|
+
'GenAIChainOfThoughtStepPrimitive',
|
|
52
|
+
'GenAIChainingSuggestionPrimitive',
|
|
53
|
+
'GenAILocationPermissionPrimitive',
|
|
54
|
+
'GenAITimestampPlaceholderPrimitive',
|
|
55
|
+
'GenAIP13NTransparencyPrimitive',
|
|
56
|
+
'GenAIProfessionalConsentPrimitive',
|
|
57
|
+
'GenAI3PAccountLinkingUpsellPrimitive',
|
|
58
|
+
'GenAI3PExtWidgetPrimitive',
|
|
59
|
+
'GenAIFilePrimitive',
|
|
60
|
+
'GenAIFileLinkPrimitive',
|
|
61
|
+
'GenAIFooterActionPrimitive',
|
|
62
|
+
'GenAIClippyWidgetPrimitive',
|
|
63
|
+
'GenAIClippyFollowUpPrimitive',
|
|
64
|
+
'GenAIaeacdsnwHtmlPrimitive',
|
|
65
|
+
'GenAIQuizPrimitive',
|
|
66
|
+
'GenAIActionGroupPrimitive',
|
|
67
|
+
'GenAIFusedComparisonTablePrimitive'
|
|
68
|
+
])
|
|
69
|
+
|
|
70
|
+
export const AI_RICH_PRIMITIVE_INTERFACE = 'GenAIUXPrimitive'
|
|
71
|
+
|
|
72
|
+
export const AI_RICH_PRIMITIVES_WITHOUT_SCHEMA = Object.freeze([
|
|
73
|
+
'GenAIClippyWidgetPrimitive',
|
|
74
|
+
'GenAIClippyFollowUpPrimitive'
|
|
75
|
+
])
|
|
76
|
+
|
|
77
|
+
export const AI_RICH_ITEMS = Object.freeze([
|
|
78
|
+
'GenAIMediaItem',
|
|
79
|
+
'GenAIPlaceDetailsItem',
|
|
80
|
+
'GenAIPlaceEntityItem',
|
|
81
|
+
'GenAISocialEntityItem',
|
|
82
|
+
'GenAIProductEntityItem',
|
|
83
|
+
'GenAISourcedItem',
|
|
84
|
+
'GenAITopicLinkItem',
|
|
85
|
+
'GenAIThreadSurfingItem',
|
|
86
|
+
'GenAISideBySideSurveyItem',
|
|
87
|
+
'GenAIIGCoachSuggestedPromptItem',
|
|
88
|
+
'GenAI3PExtCalendarEventItem',
|
|
89
|
+
'GenAI3PAccountLinkingBottomsheetAppItem',
|
|
90
|
+
'GenAIActionListRow',
|
|
91
|
+
'GenAISportsTeam',
|
|
92
|
+
'GenAISportsVenue',
|
|
93
|
+
'GenAISportsGroup',
|
|
94
|
+
'GenAIContextualSourcesViewModel'
|
|
95
|
+
])
|
|
96
|
+
|
|
97
|
+
export const AI_RICH_HTML_PRIMITIVE = 'GenAIaeacdsnwHtmlPrimitive'
|
|
98
|
+
export const AI_RICH_HTML_PRIMITIVE_CLASS = 'FOAHtmlPrimitive'
|
|
99
|
+
|
|
100
|
+
export const AI_RICH_INLINE_ENTITIES = Object.freeze([
|
|
101
|
+
'GenAIInlineLinkItem',
|
|
102
|
+
'GenAISearchCitationItem',
|
|
103
|
+
'GenAILatexItem',
|
|
104
|
+
'GenAIDeeplinkItem'
|
|
105
|
+
])
|
|
106
|
+
|
|
107
|
+
export const AI_RICH_PRIMITIVES_WEB_RENDERED = Object.freeze([
|
|
108
|
+
'GenAIMarkdownTextUXPrimitive',
|
|
109
|
+
'GenAICodeUXPrimitive',
|
|
110
|
+
'GenAILatexUXPrimitive',
|
|
111
|
+
'GenATableUXPrimitive',
|
|
112
|
+
'GenAIMetadataTextPrimitive',
|
|
113
|
+
'GenAIDividerPrimitive',
|
|
114
|
+
'GenAISpacerPrimitive',
|
|
115
|
+
'GenAIImagePrimitive',
|
|
116
|
+
'GenAIImaginePrimitive',
|
|
117
|
+
'GenAIReelPrimitive',
|
|
118
|
+
'GenAIPostPrimitive',
|
|
119
|
+
'GenAIProductItemCardPrimitive',
|
|
120
|
+
'GenAISearchResultPrimitive',
|
|
121
|
+
'GenAIBotThinkingStatusPrimitive',
|
|
122
|
+
'GenAIBotProgressStatusPrimitive',
|
|
123
|
+
'GenAIMetaSubsQuotaUpsellPrimitive',
|
|
124
|
+
'FOATextPrimitive',
|
|
125
|
+
'FOABloksPrimitive'
|
|
126
|
+
])
|
|
127
|
+
|
|
128
|
+
export const DividerType = Object.freeze({ DOT: 'DOT', HORIZONTAL_LINE: 'HORIZONTAL_LINE' })
|
|
129
|
+
export const ImagineType = Object.freeze({ IMAGINE: 'IMAGINE', ANIMATE: 'ANIMATE', MEMU: 'MEMU' })
|
|
130
|
+
export const ImagineStatus = Object.freeze({ GENERATING: 'GENERATING', READY: 'READY', FAILED: 'FAILED' })
|
|
131
|
+
export const ThinkingIcon = Object.freeze({ THINKING: 'THINKING', WEB_SEARCH: 'WEB_SEARCH', META_SEARCH: 'META_SEARCH' })
|
|
132
|
+
export const TaskStatus = Object.freeze({ PENDING: 'PENDING', RUNNING: 'RUNNING', DONE: 'DONE' })
|
|
133
|
+
export const FooterActionType = Object.freeze({
|
|
134
|
+
OPEN_FULL_VIEW: 'OPEN_FULL_VIEW',
|
|
135
|
+
DOWNLOAD_MEDIA: 'DOWNLOAD_MEDIA',
|
|
136
|
+
GENERATE_IMAGE: 'GENERATE_IMAGE',
|
|
137
|
+
CANCEL_REASONING: 'CANCEL_REASONING',
|
|
138
|
+
UPGRADE_TO_SUBS: 'UPGRADE_TO_SUBS',
|
|
139
|
+
COPY_LINK: 'COPY_LINK',
|
|
140
|
+
REMIX_MEDIA: 'REMIX_MEDIA',
|
|
141
|
+
USE_TEMPLATE: 'USE_TEMPLATE'
|
|
142
|
+
})
|
|
143
|
+
export const AddonActionType = Object.freeze({
|
|
144
|
+
COPY_TO_CLIPBOARD: 'COPY_TO_CLIPBOARD',
|
|
145
|
+
SEND_TO_CHAT: 'SEND_TO_CHAT',
|
|
146
|
+
FOLLOW_UP_PROMPT: 'FOLLOW_UP_PROMPT'
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
const trimEmpty = (object) => {
|
|
150
|
+
for (const key of Object.keys(object)) {
|
|
151
|
+
if (object[key] === undefined) {
|
|
152
|
+
delete object[key]
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return object
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export const dividerSection = ({ dividerType = DividerType.HORIZONTAL_LINE } = {}) =>
|
|
159
|
+
AIRich.newLayout('Single', {
|
|
160
|
+
divider_type: dividerType,
|
|
161
|
+
__typename: 'GenAIDividerPrimitive'
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
export const spacerSection = ({ spacing = 1 } = {}) =>
|
|
165
|
+
AIRich.newLayout('Single', {
|
|
166
|
+
spacing,
|
|
167
|
+
__typename: 'GenAISpacerPrimitive'
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
export const imageSection = (url, { fallbackUrl, previewUrl, previewFallbackUrl } = {}) =>
|
|
171
|
+
AIRich.newLayout('Single', {
|
|
172
|
+
full_image: { url, url_fallback: fallbackUrl ?? '' },
|
|
173
|
+
preview_image: { url: previewUrl ?? url, url_fallback: previewFallbackUrl ?? fallbackUrl ?? '' },
|
|
174
|
+
__typename: 'GenAIImagePrimitive'
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
export const taskSection = ({ taskId, title = '', subtitle = '', status = TaskStatus.PENDING }) => {
|
|
178
|
+
if (!taskId) {
|
|
179
|
+
throw new TypeError('taskSection requires taskId, an empty id makes WhatsApp drop the item')
|
|
180
|
+
}
|
|
181
|
+
return AIRich.newLayout('Single', {
|
|
182
|
+
task_id: String(taskId),
|
|
183
|
+
title,
|
|
184
|
+
subtitle,
|
|
185
|
+
status,
|
|
186
|
+
__typename: 'GenAITaskPrimitive'
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export const latexSection = (expression, { image, width = 100, height = 100, fontHeight = 83.333333333333, padding = 15 } = {}) =>
|
|
191
|
+
AIRich.newLayout('Single', {
|
|
192
|
+
latex_expression: expression,
|
|
193
|
+
...(image
|
|
194
|
+
? {
|
|
195
|
+
latex_image: { url: image, width, height },
|
|
196
|
+
font_height: fontHeight,
|
|
197
|
+
padding
|
|
198
|
+
}
|
|
199
|
+
: {}),
|
|
200
|
+
__typename: 'GenAILatexUXPrimitive'
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
const statusSection = (typename) => (title, { icon = ThinkingIcon.THINKING, inProgress = true, metaSearchApps = [], thoughtDurationSec } = {}) =>
|
|
204
|
+
AIRich.newLayout('Single', trimEmpty({
|
|
205
|
+
title,
|
|
206
|
+
icon,
|
|
207
|
+
is_in_progress: inProgress,
|
|
208
|
+
meta_search_apps: metaSearchApps,
|
|
209
|
+
thought_duration_sec: thoughtDurationSec === undefined ? undefined : Number(thoughtDurationSec),
|
|
210
|
+
__typename: typename
|
|
211
|
+
}))
|
|
212
|
+
|
|
213
|
+
export const thinkingSection = statusSection('GenAIBotThinkingStatusPrimitive')
|
|
214
|
+
export const progressSection = statusSection('GenAIBotProgressStatusPrimitive')
|
|
215
|
+
|
|
216
|
+
export const lockHeight = (height) => {
|
|
217
|
+
const px = Number(height)
|
|
218
|
+
if (!Number.isFinite(px) || px <= 0) {
|
|
219
|
+
throw new TypeError('height must be a positive number of pixels')
|
|
220
|
+
}
|
|
221
|
+
return '<style>html,body{margin:0;padding:0;height:' + px + 'px;max-height:' + px + 'px;overflow:hidden}'
|
|
222
|
+
+ '#__wrap{height:' + px + 'px;overflow-y:auto;-webkit-overflow-scrolling:touch;touch-action:pan-y}</style>'
|
|
223
|
+
+ '<script>document.addEventListener("DOMContentLoaded",function(){'
|
|
224
|
+
+ 'var w=document.createElement("div");w.id="__wrap";'
|
|
225
|
+
+ 'while(document.body.firstChild)w.appendChild(document.body.firstChild);'
|
|
226
|
+
+ 'document.body.appendChild(w)});<' + '/script>'
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export const HTML_APP_BRIDGE = 'AndroidBridge'
|
|
230
|
+
|
|
231
|
+
export const dataUri = (bytes, mimetype) => {
|
|
232
|
+
if (typeof mimetype !== 'string' || !mimetype.includes('/')) {
|
|
233
|
+
throw new TypeError('dataUri needs a mimetype like video/mp4')
|
|
234
|
+
}
|
|
235
|
+
const buffer = Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes)
|
|
236
|
+
if (!buffer.length) {
|
|
237
|
+
throw new TypeError('dataUri needs a non-empty buffer')
|
|
238
|
+
}
|
|
239
|
+
return 'data:' + mimetype + ';base64,' + buffer.toString('base64')
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const MEDIA_TAGS = new Set(['video', 'audio', 'img'])
|
|
243
|
+
|
|
244
|
+
export const htmlMedia = (bytes, { mimetype, tag = 'video', label = 'Tap untuk memuat media', poster, id = 'm' + randomUUID().slice(0, 8), attributes = '' } = {}) => {
|
|
245
|
+
if (!MEDIA_TAGS.has(tag)) {
|
|
246
|
+
throw new TypeError('htmlMedia tag must be one of ' + [...MEDIA_TAGS].join(', '))
|
|
247
|
+
}
|
|
248
|
+
if (typeof id !== 'string' || !/^[A-Za-z][A-Za-z0-9_-]*$/.test(id)) {
|
|
249
|
+
throw new TypeError('htmlMedia id must start with a letter and stay alphanumeric')
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const uri = dataUri(bytes, mimetype)
|
|
253
|
+
const controls = tag === 'img' ? '' : ' controls preload="none" playsinline'
|
|
254
|
+
const cover = poster && tag === 'video' ? ' poster="' + String(poster).replace(/"/g, '"') + '"' : ''
|
|
255
|
+
const json = JSON.stringify(uri).replace(/</g, '\\u003c').replace(/>/g, '\\u003e')
|
|
256
|
+
|
|
257
|
+
return '<' + tag + ' id="' + id + '"' + controls + cover + (attributes ? ' ' + attributes : '') + '></' + tag + '>'
|
|
258
|
+
+ '<button id="' + id + '_go" type="button">' + String(label).replace(/[<>&]/g, c => ({ '<': '<', '>': '>', '&': '&' })[c]) + '</button>'
|
|
259
|
+
+ '<script>(function(){'
|
|
260
|
+
+ 'var src=' + json + ',el=document.getElementById(' + JSON.stringify(id) + '),go=document.getElementById(' + JSON.stringify(id + '_go') + ');'
|
|
261
|
+
+ 'go.addEventListener("click",function(){'
|
|
262
|
+
+ 'if(el.src)return;el.src=src;go.disabled=true;go.textContent="\\u2713";'
|
|
263
|
+
+ 'if(el.play)el.play().catch(function(){})});'
|
|
264
|
+
+ '})();<' + '/script>'
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export const scrollControls = ({ target = '#__wrap', step = 140, holdMs = 70, size = 36, side = 'right', inset = 6 } = {}) => {
|
|
268
|
+
const px = Number(step)
|
|
269
|
+
const hold = Number(holdMs)
|
|
270
|
+
const box = Number(size)
|
|
271
|
+
const edge = Number(inset)
|
|
272
|
+
if (typeof target !== 'string' || target.trim() === '') {
|
|
273
|
+
throw new TypeError('scrollControls target must be a CSS selector')
|
|
274
|
+
}
|
|
275
|
+
if (!Number.isFinite(px) || px <= 0) {
|
|
276
|
+
throw new TypeError('scrollControls step must be a positive number of pixels')
|
|
277
|
+
}
|
|
278
|
+
if (!Number.isFinite(hold) || hold < 16) {
|
|
279
|
+
throw new TypeError('scrollControls holdMs must be at least 16')
|
|
280
|
+
}
|
|
281
|
+
if (!Number.isFinite(box) || box < 24) {
|
|
282
|
+
throw new TypeError('scrollControls size must be at least 24 pixels')
|
|
283
|
+
}
|
|
284
|
+
if (side !== 'right' && side !== 'left') {
|
|
285
|
+
throw new TypeError('scrollControls side must be "right" or "left"')
|
|
286
|
+
}
|
|
287
|
+
if (!Number.isFinite(edge) || edge < 0) {
|
|
288
|
+
throw new TypeError('scrollControls inset must not be negative')
|
|
289
|
+
}
|
|
290
|
+
return '<style>'
|
|
291
|
+
+ '#__sc{position:fixed;' + side + ':' + edge + 'px;bottom:' + edge + 'px;z-index:2147483647;'
|
|
292
|
+
+ 'display:none;flex-direction:column;gap:' + Math.round(box / 6) + 'px}'
|
|
293
|
+
+ '#__sc button{width:' + box + 'px;height:' + box + 'px;padding:0;border:0;'
|
|
294
|
+
+ 'border-radius:' + Math.round(box / 4) + 'px;background:rgba(0,0,0,.6);color:#fff;'
|
|
295
|
+
+ 'font:' + Math.round(box / 2) + 'px/1 system-ui;touch-action:none;cursor:pointer;'
|
|
296
|
+
+ '-webkit-tap-highlight-color:transparent}'
|
|
297
|
+
+ '#__sc button:disabled{opacity:.3}'
|
|
298
|
+
+ '</style>'
|
|
299
|
+
+ '<script>(function(){'
|
|
300
|
+
+ 'var STEP=' + px + ',HOLD=' + hold + ',SEL=' + JSON.stringify(target) + ';'
|
|
301
|
+
+ 'var box,up,down,timer=null;'
|
|
302
|
+
+ 'function pane(){return document.querySelector(SEL)||document.scrollingElement||document.body}'
|
|
303
|
+
+ 'function room(){var p=pane();return p?p.scrollHeight-p.clientHeight:0}'
|
|
304
|
+
+ 'function sync(){if(!box)return;var p=pane(),r=room();'
|
|
305
|
+
+ 'box.style.display=r>4?"flex":"none";if(r<=4)return;'
|
|
306
|
+
+ 'up.disabled=p.scrollTop<=0;down.disabled=p.scrollTop>=r-1}'
|
|
307
|
+
+ 'function move(by){var p=pane();if(!p)return;p.scrollTop+=by;sync()}'
|
|
308
|
+
+ 'function stop(){if(timer!==null){clearInterval(timer);timer=null}}'
|
|
309
|
+
+ 'function press(e,by){e.preventDefault();e.stopPropagation();stop();move(by);'
|
|
310
|
+
+ 'timer=setInterval(function(){move(by)},HOLD)}'
|
|
311
|
+
+ 'function make(label,by){var b=document.createElement("button");b.type="button";'
|
|
312
|
+
+ 'b.textContent=label;b.setAttribute("aria-label",by<0?"scroll up":"scroll down");'
|
|
313
|
+
+ 'b.addEventListener("touchstart",function(e){press(e,by)},{passive:false});'
|
|
314
|
+
+ 'b.addEventListener("touchend",function(e){e.preventDefault();stop()},{passive:false});'
|
|
315
|
+
+ 'b.addEventListener("touchcancel",stop);'
|
|
316
|
+
+ 'b.addEventListener("mousedown",function(e){press(e,by)});'
|
|
317
|
+
+ 'b.addEventListener("mouseup",stop);b.addEventListener("mouseleave",stop);'
|
|
318
|
+
+ 'b.addEventListener("click",function(e){e.preventDefault();e.stopPropagation()});'
|
|
319
|
+
+ 'return b}'
|
|
320
|
+
+ 'function start(){if(box)return;'
|
|
321
|
+
+ 'box=document.createElement("div");box.id="__sc";'
|
|
322
|
+
+ 'up=make("\\u25b2",-STEP);down=make("\\u25bc",STEP);'
|
|
323
|
+
+ 'box.appendChild(up);box.appendChild(down);document.body.appendChild(box);'
|
|
324
|
+
+ 'var p=pane();if(p&&p.addEventListener)p.addEventListener("scroll",sync,{passive:true});'
|
|
325
|
+
+ 'if(typeof ResizeObserver==="function"){try{new ResizeObserver(sync).observe(document.body)}catch(e){}}'
|
|
326
|
+
+ 'addEventListener("load",sync);sync()}'
|
|
327
|
+
+ 'if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",start);else start();'
|
|
328
|
+
+ 'window.__scrollControls={by:move,sync:sync};'
|
|
329
|
+
+ '})();<' + '/script>'
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export const autoHeight = ({ min = 60, max = 900, settleMs = 120, maxReports = 24 } = {}) => {
|
|
333
|
+
const low = Number(min)
|
|
334
|
+
const high = Number(max)
|
|
335
|
+
const wait = Number(settleMs)
|
|
336
|
+
const cap = Number(maxReports)
|
|
337
|
+
if (!Number.isFinite(low) || low <= 0) {
|
|
338
|
+
throw new TypeError('autoHeight min must be a positive number of pixels')
|
|
339
|
+
}
|
|
340
|
+
if (!Number.isFinite(high) || high <= low) {
|
|
341
|
+
throw new TypeError('autoHeight max must be greater than min')
|
|
342
|
+
}
|
|
343
|
+
if (!Number.isFinite(wait) || wait < 0) {
|
|
344
|
+
throw new TypeError('autoHeight settleMs must not be negative')
|
|
345
|
+
}
|
|
346
|
+
if (!Number.isFinite(cap) || cap < 1) {
|
|
347
|
+
throw new TypeError('autoHeight maxReports must be at least 1')
|
|
348
|
+
}
|
|
349
|
+
return '<style>html,body{margin:0;padding:0}</style>'
|
|
350
|
+
+ '<script>(function(){'
|
|
351
|
+
+ 'var LOW=' + low + ',HIGH=' + high + ',WAIT=' + wait + ',CAP=' + cap + ';'
|
|
352
|
+
+ 'var sent=[],timer=null,stopped=false;'
|
|
353
|
+
+ 'function measure(){var d=document.documentElement,b=document.body;'
|
|
354
|
+
+ 'var h=Math.max(d?d.scrollHeight:0,b?b.scrollHeight:0,b?b.offsetHeight:0);'
|
|
355
|
+
+ 'return Math.min(HIGH,Math.max(LOW,Math.ceil(h)))}'
|
|
356
|
+
+ 'function report(){if(stopped)return;var h=measure();'
|
|
357
|
+
+ 'if(sent.length&&sent[sent.length-1]===h)return;'
|
|
358
|
+
+ 'if(sent.length>=2&&sent[sent.length-2]===h){stopped=true;return}'
|
|
359
|
+
+ 'sent.push(h);if(sent.length>=CAP)stopped=true;'
|
|
360
|
+
+ 'try{window.AndroidBridge.updateSize(h)}catch(e){stopped=true}}'
|
|
361
|
+
+ 'function schedule(){if(stopped)return;'
|
|
362
|
+
+ 'if(timer!==null)clearTimeout(timer);timer=setTimeout(function(){timer=null;report()},WAIT)}'
|
|
363
|
+
+ 'function start(){report();'
|
|
364
|
+
+ 'if(typeof ResizeObserver==="function"&&document.body){'
|
|
365
|
+
+ 'try{new ResizeObserver(schedule).observe(document.body)}catch(e){}}'
|
|
366
|
+
+ 'addEventListener("load",schedule)}'
|
|
367
|
+
+ 'if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",start);else start();'
|
|
368
|
+
+ '})();<' + '/script>'
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export const htmlSection = (html, { trustedSources = [], height, typename = AI_RICH_HTML_PRIMITIVE } = {}) => {
|
|
372
|
+
if (typeof html !== 'string' || html.trim() === '') {
|
|
373
|
+
throw new TypeError('htmlSection requires a non-empty HTML string')
|
|
374
|
+
}
|
|
375
|
+
if (!Array.isArray(trustedSources)) {
|
|
376
|
+
throw new TypeError('htmlSection trustedSources must be an array of strings')
|
|
377
|
+
}
|
|
378
|
+
if (typeof typename !== 'string' || typename.trim() === '') {
|
|
379
|
+
throw new TypeError('htmlSection typename must be a non-empty string')
|
|
380
|
+
}
|
|
381
|
+
return AIRich.newLayout('Single', {
|
|
382
|
+
payload: height === undefined ? html : lockHeight(height) + html,
|
|
383
|
+
trusted_sources: trustedSources.map(String),
|
|
384
|
+
__typename: typename
|
|
385
|
+
})
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export const sendHtmlApp = async (sock, jid, html, { title = '', label, trustedSources, height, autoHeight: auto, scrollButtons, typename, id, bypassDownload = false, guard = 'warn', ...options } = {}) => {
|
|
389
|
+
if (!sock) {
|
|
390
|
+
throw new TypeError('sendHtmlApp requires a socket as the first argument')
|
|
391
|
+
}
|
|
392
|
+
if (!jid) {
|
|
393
|
+
throw new TypeError('sendHtmlApp requires a target jid')
|
|
394
|
+
}
|
|
395
|
+
if (auto && height !== undefined) {
|
|
396
|
+
throw new TypeError('sendHtmlApp takes either height or autoHeight, not both')
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (auto) {
|
|
400
|
+
html = autoHeight(auto === true ? undefined : auto) + html
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (scrollButtons) {
|
|
404
|
+
html += scrollControls(scrollButtons === true ? undefined : scrollButtons)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (guard !== false) {
|
|
408
|
+
const report = checkHtmlApp(html, { height })
|
|
409
|
+
if (!report.ok && guard === true) {
|
|
410
|
+
throw new TypeError('this html app will not behave in the WebView:\n ' + report.problems.join('\n '))
|
|
411
|
+
}
|
|
412
|
+
if (guard === 'warn') {
|
|
413
|
+
for (const problem of report.problems) {
|
|
414
|
+
sock.logger?.warn({ jid }, 'html app: ' + problem)
|
|
415
|
+
}
|
|
416
|
+
for (const warning of report.warnings) {
|
|
417
|
+
sock.logger?.warn({ jid }, 'html app: ' + warning)
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const rich = new AIRich(sock)
|
|
423
|
+
|
|
424
|
+
if (title) {
|
|
425
|
+
rich.setTitle(title)
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
rich._addContent(
|
|
429
|
+
htmlSection(html, { trustedSources, height, ...(typename ? { typename } : {}) }),
|
|
430
|
+
label ? { messageType: 2, messageText: String(label) } : undefined,
|
|
431
|
+
id ? { id } : {}
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
return rich.send(jid, { bypassDownload, ...options })
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export const HTML_MIME_TYPE = 'text/html'
|
|
438
|
+
|
|
439
|
+
export const sendHtmlDocument = async (sock, jid, html, { fileName = 'app.html', caption, ...options } = {}) => {
|
|
440
|
+
if (!sock) {
|
|
441
|
+
throw new TypeError('sendHtmlDocument requires a socket as the first argument')
|
|
442
|
+
}
|
|
443
|
+
if (!jid) {
|
|
444
|
+
throw new TypeError('sendHtmlDocument requires a target jid')
|
|
445
|
+
}
|
|
446
|
+
if (typeof html !== 'string' || html.trim() === '') {
|
|
447
|
+
throw new TypeError('sendHtmlDocument requires a non-empty HTML string')
|
|
448
|
+
}
|
|
449
|
+
const name = String(fileName)
|
|
450
|
+
if (!/\.html?$/i.test(name)) {
|
|
451
|
+
throw new TypeError('sendHtmlDocument fileName must end with .html or .htm')
|
|
452
|
+
}
|
|
453
|
+
return sock.sendMessage(jid, {
|
|
454
|
+
document: Buffer.from(html, 'utf-8'),
|
|
455
|
+
mimetype: HTML_MIME_TYPE,
|
|
456
|
+
fileName: name,
|
|
457
|
+
...(caption ? { caption: String(caption) } : {}),
|
|
458
|
+
...options
|
|
459
|
+
})
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const fileArtifact = (typename) => (url, { title = '', fileExtension = 'html', fileLength = 0, pageCount, previewImage } = {}) => {
|
|
463
|
+
if (typeof url !== 'string' || url.trim() === '') {
|
|
464
|
+
throw new TypeError('a file section requires a non-empty url')
|
|
465
|
+
}
|
|
466
|
+
return AIRich.newLayout('Single', trimEmpty({
|
|
467
|
+
title: String(title),
|
|
468
|
+
url,
|
|
469
|
+
file_extension: String(fileExtension).replace(/^\./, ''),
|
|
470
|
+
file_length: Number(fileLength) || 0,
|
|
471
|
+
page_count: pageCount === undefined ? undefined : Number(pageCount) || 0,
|
|
472
|
+
preview_image: previewImage,
|
|
473
|
+
__typename: typename
|
|
474
|
+
}))
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
export const fileSection = fileArtifact('GenAIFilePrimitive')
|
|
478
|
+
export const fileLinkSection = fileArtifact('GenAIFileLinkPrimitive')
|
|
479
|
+
|
|
480
|
+
const b64 = (value) => (value === undefined || value === null ? undefined : Buffer.isBuffer(value) || value instanceof Uint8Array ? Buffer.from(value).toString('base64') : String(value))
|
|
481
|
+
|
|
482
|
+
export const botMediaMetadata = (documentMessage) => {
|
|
483
|
+
if (!documentMessage) {
|
|
484
|
+
throw new TypeError('botMediaMetadata requires a prepared document message')
|
|
485
|
+
}
|
|
486
|
+
return trimEmpty({
|
|
487
|
+
fileSha256: b64(documentMessage.fileSha256),
|
|
488
|
+
mediaKey: b64(documentMessage.mediaKey),
|
|
489
|
+
fileEncSha256: b64(documentMessage.fileEncSha256),
|
|
490
|
+
directPath: documentMessage.directPath,
|
|
491
|
+
mediaKeyTimestamp: documentMessage.mediaKeyTimestamp ? Number(documentMessage.mediaKeyTimestamp) : undefined,
|
|
492
|
+
mimetype: documentMessage.mimetype
|
|
493
|
+
})
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
export const prepareFileArtifact = async (sock, content, { mimetype = HTML_MIME_TYPE, fileName = 'app.html', title, id } = {}) => {
|
|
497
|
+
if (!sock) {
|
|
498
|
+
throw new TypeError('prepareFileArtifact requires a socket as the first argument')
|
|
499
|
+
}
|
|
500
|
+
const body = typeof content === 'string' ? Buffer.from(content, 'utf-8') : content
|
|
501
|
+
if (!Buffer.isBuffer(body) && !(body instanceof Uint8Array)) {
|
|
502
|
+
throw new TypeError('prepareFileArtifact content must be a string or a buffer')
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const prepared = await prepareWAMessageMedia(
|
|
506
|
+
{ document: Buffer.from(body), mimetype, fileName },
|
|
507
|
+
{ upload: sock.waUploadToServer }
|
|
508
|
+
)
|
|
509
|
+
const documentMessage = prepared.documentMessage
|
|
510
|
+
const media = botMediaMetadata(documentMessage)
|
|
511
|
+
const mediaId = id ?? randomUUID()
|
|
512
|
+
|
|
513
|
+
return {
|
|
514
|
+
mediaId,
|
|
515
|
+
media,
|
|
516
|
+
documentMessage,
|
|
517
|
+
section: AIRich.newLayout('Single', {
|
|
518
|
+
title: String(title || fileName),
|
|
519
|
+
url: documentMessage.url ?? '',
|
|
520
|
+
file_extension: fileName.includes('.') ? fileName.split('.').pop() : '',
|
|
521
|
+
file_length: Number(documentMessage.fileLength) || 0,
|
|
522
|
+
preview_image: {
|
|
523
|
+
media_id: mediaId,
|
|
524
|
+
mime_type: mimetype,
|
|
525
|
+
url: documentMessage.url ?? '',
|
|
526
|
+
url_fallback: ''
|
|
527
|
+
},
|
|
528
|
+
__typename: 'GenAIFilePrimitive'
|
|
529
|
+
}),
|
|
530
|
+
mediaDetails: { id: mediaId, previewMedia: media, highResMedia: media }
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
export const sendHtmlArtifact = async (sock, jid, html, { fileName = 'app.html', title, label, id, bypassDownload = false, ...options } = {}) => {
|
|
535
|
+
if (!jid) {
|
|
536
|
+
throw new TypeError('sendHtmlArtifact requires a target jid')
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const artifact = await prepareFileArtifact(sock, html, { mimetype: HTML_MIME_TYPE, fileName, title, id })
|
|
540
|
+
const rich = new AIRich(sock)
|
|
541
|
+
|
|
542
|
+
if (title) {
|
|
543
|
+
rich.setTitle(title)
|
|
544
|
+
}
|
|
545
|
+
rich.setBotMetadata({ unifiedResponseMutation: { mediaDetailsMetadataList: [artifact.mediaDetails] } })
|
|
546
|
+
rich._addContent(
|
|
547
|
+
artifact.section,
|
|
548
|
+
label ? { messageType: 2, messageText: String(label) } : undefined,
|
|
549
|
+
{}
|
|
550
|
+
)
|
|
551
|
+
|
|
552
|
+
const message = await rich.send(jid, { bypassDownload, ...options })
|
|
553
|
+
return { message, mediaId: artifact.mediaId }
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
export const SourceProvider = Object.freeze({ UNKNOWN: 0, BING: 1, GOOGLE: 2, SUPPORT: 3, OTHER: 4 })
|
|
557
|
+
|
|
558
|
+
export const botSourcesMetadata = (sources) => {
|
|
559
|
+
if (!Array.isArray(sources) || sources.length === 0) {
|
|
560
|
+
throw new TypeError('botSourcesMetadata requires a non-empty array of sources')
|
|
561
|
+
}
|
|
562
|
+
return {
|
|
563
|
+
sources: sources.map((source, index) => {
|
|
564
|
+
if (!source || typeof source !== 'object' || Array.isArray(source)) {
|
|
565
|
+
throw new TypeError('each source must be a plain object')
|
|
566
|
+
}
|
|
567
|
+
if (!source.url) {
|
|
568
|
+
throw new TypeError('each source requires a url')
|
|
569
|
+
}
|
|
570
|
+
return trimEmpty({
|
|
571
|
+
provider: source.provider ?? SourceProvider.OTHER,
|
|
572
|
+
sourceProviderUrl: String(source.url),
|
|
573
|
+
sourceTitle: source.title === undefined ? undefined : String(source.title),
|
|
574
|
+
sourceQuery: source.query === undefined ? undefined : String(source.query),
|
|
575
|
+
faviconCdnUrl: source.favicon === undefined ? undefined : String(source.favicon),
|
|
576
|
+
thumbnailCdnUrl: source.thumbnail === undefined ? undefined : String(source.thumbnail),
|
|
577
|
+
citationNumber: source.citation === undefined ? index + 1 : Number(source.citation)
|
|
578
|
+
})
|
|
579
|
+
})
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
export const EMBEDDED_SCREEN_PRESENTATION = Object.freeze({
|
|
584
|
+
HALF_HEIGHT: 'HALF_HEIGHT',
|
|
585
|
+
FULL_HEIGHT: 'FULL_HEIGHT'
|
|
586
|
+
})
|
|
587
|
+
|
|
588
|
+
export const EMBEDDED_SCREEN_TYPENAME = 'FOAUnifiedResponseEmbeddedScreen'
|
|
589
|
+
export const EMBEDDED_SCREEN_TABBED_TYPENAME = 'FOAEmbeddedScreenContentTabbed'
|
|
590
|
+
export const EMBEDDED_SCREEN_SINGLE_TYPENAME = 'FOAEmbeddedSingleScreen'
|
|
591
|
+
export const EMBEDDED_SCREEN_CONTEXTUAL_TYPENAME = 'GenAIEmbeddedContextualScreenContent'
|
|
592
|
+
export const EMBEDDED_SCREEN_TAB_TYPENAME = 'FOAUnifiedResponseTab'
|
|
593
|
+
export const AI_RICH_SECTION_TYPENAME = AIRich.SECTION_TYPENAME
|
|
594
|
+
export const AI_RICH_UNIFIED_RESPONSE_TYPENAME = AIRich.UNIFIED_RESPONSE_TYPENAME
|
|
595
|
+
export const AI_RICH_UNIFIED_RESPONSE_TYPENAME_APP = 'GenAIUnifiedResponse'
|
|
596
|
+
export const AI_RICH_NESTED_UNIFIED_RESPONSE_TYPENAME = 'GenAINestedUnifiedResponse'
|
|
597
|
+
|
|
598
|
+
export const AI_RICH_NODES = Object.freeze([
|
|
599
|
+
'GenAIQuizQuestion',
|
|
600
|
+
'GenAIActionButton',
|
|
601
|
+
'GenAIFusedComparisonColumn',
|
|
602
|
+
'GenAIFusedComparisonRow',
|
|
603
|
+
'GenAIFusedComparisonCTA',
|
|
604
|
+
'FOAEmbeddedSingleScreen',
|
|
605
|
+
'GenAI3PAccountLinkingBottomsheet',
|
|
606
|
+
'GenAI3PExtArtifactConfirmation',
|
|
607
|
+
'GenAI3PExtCalendarAttendee',
|
|
608
|
+
'GenAI3PExtCalendarDateSection',
|
|
609
|
+
'GenAI3PExtCalendarEventList',
|
|
610
|
+
'GenAI3PExtConnectorPendingToolCall',
|
|
611
|
+
'GenAI3PExtWidgetCTA',
|
|
612
|
+
'GenAI3PExtWidgetStandardHeader',
|
|
613
|
+
'GenAI3PExtWidgetToast',
|
|
614
|
+
'GenAIAmericanFootballGameContent',
|
|
615
|
+
'GenAIChainOfThoughtStepMarkdownText',
|
|
616
|
+
'GenAICodeBlockData',
|
|
617
|
+
'GenAIEmbeddedContextualScreenContent',
|
|
618
|
+
'GenAIImaginePrimitiveStatusUpdate',
|
|
619
|
+
'GenAIImagineThumbnail',
|
|
620
|
+
'GenAIMapItemLocation',
|
|
621
|
+
'GenAIMapItemStaticMap',
|
|
622
|
+
'GenAIMarketplaceMetadata',
|
|
623
|
+
'GenAIMetaSubsQuotaUpsellButton',
|
|
624
|
+
'GenAINestedUnifiedResponse',
|
|
625
|
+
'GenAIP13nUiSignalValue',
|
|
626
|
+
'GenAIP13nUiSignals',
|
|
627
|
+
'GenAIPlaceDetailsItemCategory',
|
|
628
|
+
'GenAIPlaceDetailsItemOpeningHours',
|
|
629
|
+
'GenAIPlaceDetailsItemOpeningHoursTime',
|
|
630
|
+
'GenAIPlaceDetailsItemRating',
|
|
631
|
+
'GenAISearchPlannerInstruction',
|
|
632
|
+
'GenAISearchPlannerStep',
|
|
633
|
+
'GenAISoccerGameContent',
|
|
634
|
+
'GenAISportsTeamIcon',
|
|
635
|
+
'GenAISportsTeamRecord',
|
|
636
|
+
'GenAIStepDeepResearchEntry',
|
|
637
|
+
'GenAIStepSkillEntry',
|
|
638
|
+
'GenAIStepSubagentEntry',
|
|
639
|
+
'GenAIStepThoughtEntry',
|
|
640
|
+
'GenAIStepWebLinkSource',
|
|
641
|
+
'GenAITableCell',
|
|
642
|
+
'GenAITableRow',
|
|
643
|
+
'GenAITextInlineEntity',
|
|
644
|
+
'GenAIThreadSurfingPrompt',
|
|
645
|
+
'GenAIUnifiedResponse',
|
|
646
|
+
'GenAIVideoMusicClipInfo',
|
|
647
|
+
'GenAIVideoTextToSpeechInfo',
|
|
648
|
+
'GenAIWidgetPlacesItemAddress'
|
|
649
|
+
])
|
|
650
|
+
|
|
651
|
+
const readTypename = (label, value) => {
|
|
652
|
+
if (value === undefined) {
|
|
653
|
+
return undefined
|
|
654
|
+
}
|
|
655
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
656
|
+
throw new TypeError(label + ' typename must be a non-empty string')
|
|
657
|
+
}
|
|
658
|
+
return value
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
export const embeddedTab = ({ id, header, tabHeader, sections = [], typename } = {}) => {
|
|
662
|
+
if (!Array.isArray(sections)) {
|
|
663
|
+
throw new TypeError('embeddedTab sections must be an array of sections')
|
|
664
|
+
}
|
|
665
|
+
return trimEmpty({
|
|
666
|
+
__typename: readTypename('embeddedTab', typename),
|
|
667
|
+
id: id ?? randomUUID(),
|
|
668
|
+
header,
|
|
669
|
+
tab_header: tabHeader,
|
|
670
|
+
sections
|
|
671
|
+
})
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
export const embeddedTabbedContent = (tabs, { typename = EMBEDDED_SCREEN_TABBED_TYPENAME } = {}) => {
|
|
675
|
+
if (!Array.isArray(tabs)) {
|
|
676
|
+
throw new TypeError('embeddedTabbedContent tabs must be an array of tabs')
|
|
677
|
+
}
|
|
678
|
+
return { __typename: readTypename('embeddedTabbedContent', typename), tabs }
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
export const AI_RICH_RESPONSE_KEYS = Object.freeze([
|
|
682
|
+
'chunk_id', 'surface', 'product', 'thread_type', 'is_forwarded', 'is_optimistic', 'nested_responses'
|
|
683
|
+
])
|
|
684
|
+
|
|
685
|
+
export const embeddedScreen = ({ id, title, content, tabs, tabsTypename, typename, header, body, artifacts, steps, stepEntries, sources, pollId } = {}) => {
|
|
686
|
+
if (content !== undefined && !Array.isArray(content)) {
|
|
687
|
+
throw new TypeError('embeddedScreen content must be an array of sections')
|
|
688
|
+
}
|
|
689
|
+
if (tabs !== undefined && !Array.isArray(tabs)) {
|
|
690
|
+
throw new TypeError('embeddedScreen tabs must be an array of tabs')
|
|
691
|
+
}
|
|
692
|
+
for (const [name, value] of [['artifacts', artifacts], ['steps', steps], ['stepEntries', stepEntries], ['sources', sources]]) {
|
|
693
|
+
if (value !== undefined && !Array.isArray(value)) {
|
|
694
|
+
throw new TypeError('embeddedScreen ' + name + ' must be an array')
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
const entries = content === undefined ? [] : [...content]
|
|
698
|
+
if (tabs !== undefined) {
|
|
699
|
+
entries.push(embeddedTabbedContent(tabs, { typename: tabsTypename ?? EMBEDDED_SCREEN_TABBED_TYPENAME }))
|
|
700
|
+
}
|
|
701
|
+
return trimEmpty({
|
|
702
|
+
__typename: readTypename('embeddedScreen', typename),
|
|
703
|
+
id: id ?? randomUUID(),
|
|
704
|
+
title: title === undefined ? undefined : String(title),
|
|
705
|
+
content: entries.length ? entries : undefined,
|
|
706
|
+
header,
|
|
707
|
+
body,
|
|
708
|
+
artifacts,
|
|
709
|
+
steps,
|
|
710
|
+
step_entries: stepEntries,
|
|
711
|
+
sources,
|
|
712
|
+
poll_id: pollId
|
|
713
|
+
})
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
export const readEmbeddedTabs = (screen) => {
|
|
717
|
+
const tabs = []
|
|
718
|
+
if (Array.isArray(screen?.tabs)) {
|
|
719
|
+
tabs.push(...screen.tabs)
|
|
720
|
+
}
|
|
721
|
+
for (const entry of Array.isArray(screen?.content) ? screen.content : []) {
|
|
722
|
+
if (Array.isArray(entry?.tabs)) {
|
|
723
|
+
tabs.push(...entry.tabs)
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
return tabs
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
export const readEmbeddedSections = (screen) => {
|
|
730
|
+
const sections = []
|
|
731
|
+
for (const entry of Array.isArray(screen?.content) ? screen.content : []) {
|
|
732
|
+
if (entry?.view_model) {
|
|
733
|
+
sections.push(entry)
|
|
734
|
+
continue
|
|
735
|
+
}
|
|
736
|
+
for (const section of Array.isArray(entry?.sections) ? entry.sections : []) {
|
|
737
|
+
sections.push(section)
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
for (const tab of readEmbeddedTabs(screen)) {
|
|
741
|
+
for (const section of Array.isArray(tab?.sections) ? tab.sections : []) {
|
|
742
|
+
sections.push(section)
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return sections
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
export const footerActionSection = (actionType, { buttonText = '', url } = {}) => {
|
|
749
|
+
if (!Object.values(FooterActionType).includes(actionType)) {
|
|
750
|
+
throw new TypeError('footerActionSection actionType must be one of ' + Object.values(FooterActionType).join(', '))
|
|
751
|
+
}
|
|
752
|
+
return AIRich.newLayout('Single', {
|
|
753
|
+
cta_text: String(buttonText),
|
|
754
|
+
cta_type: actionType,
|
|
755
|
+
...(url ? { cta_url: String(url) } : {}),
|
|
756
|
+
__typename: 'GenAIFooterActionPrimitive'
|
|
757
|
+
})
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
export const BLOKS_A2UI_TYPE = 'im_a2ui'
|
|
761
|
+
export const BLOKS_A2UI_REPLY_ACTION = 'a2ui_reply_action'
|
|
762
|
+
export const BLOKS_A2UI_SUPPORTED_ELEMENTS = Object.freeze(['info_card', 'list_card'])
|
|
763
|
+
|
|
764
|
+
const bloksPayloadData = (data) => {
|
|
765
|
+
if (data === undefined || data === null) {
|
|
766
|
+
return ''
|
|
767
|
+
}
|
|
768
|
+
if (typeof data === 'string') {
|
|
769
|
+
return data
|
|
770
|
+
}
|
|
771
|
+
if (typeof data !== 'object' || Array.isArray(data)) {
|
|
772
|
+
throw new TypeError('bloks data must be a JSON string or a plain object')
|
|
773
|
+
}
|
|
774
|
+
return JSON.stringify(data)
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
const bloksType = (type, caller) => {
|
|
778
|
+
if (typeof type !== 'string' || type.trim() === '') {
|
|
779
|
+
throw new TypeError(caller + ' requires a non-empty bloks type')
|
|
780
|
+
}
|
|
781
|
+
return type
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
export const bloksSection = (type, data, { uuid, initialResponse = '', versioningId = '' } = {}) =>
|
|
785
|
+
AIRich.newLayout('Single', {
|
|
786
|
+
type: bloksType(type, 'bloksSection'),
|
|
787
|
+
data: bloksPayloadData(data),
|
|
788
|
+
uuid: uuid ?? randomUUID(),
|
|
789
|
+
initial_response: String(initialResponse),
|
|
790
|
+
versioning_id: String(versioningId),
|
|
791
|
+
__typename: 'FOABloksPrimitive'
|
|
792
|
+
})
|
|
793
|
+
|
|
794
|
+
export const bloksWidget = ({ type, data, uuid, fallback = '' } = {}) => ({
|
|
795
|
+
type: bloksType(type, 'bloksWidget'),
|
|
796
|
+
data: bloksPayloadData(data),
|
|
797
|
+
uuid: uuid ?? randomUUID(),
|
|
798
|
+
fallback: String(fallback)
|
|
799
|
+
})
|
|
800
|
+
|
|
801
|
+
export const A2UI_VERSION = 'v0.9'
|
|
802
|
+
export const A2UI_BASIC_CATALOG = 'https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json'
|
|
803
|
+
export const A2UI_ROOT_ID = 'root'
|
|
804
|
+
|
|
805
|
+
const a2uiNode = (component) => (id, extra = {}) => {
|
|
806
|
+
if (typeof id !== 'string' || id.trim() === '') {
|
|
807
|
+
throw new TypeError('every a2ui component needs a non-empty id')
|
|
808
|
+
}
|
|
809
|
+
return trimEmpty({ id, component, ...extra })
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
export const a2uiText = (id, text, { variant = 'body' } = {}) =>
|
|
813
|
+
a2uiNode('Text')(id, { text: String(text), variant })
|
|
814
|
+
|
|
815
|
+
export const a2uiImage = (id, url, { variant = 'header', fit = 'cover' } = {}) => {
|
|
816
|
+
if (typeof url !== 'string' || url.trim() === '') {
|
|
817
|
+
throw new TypeError('a2uiImage requires a url')
|
|
818
|
+
}
|
|
819
|
+
return a2uiNode('Image')(id, { url, variant, fit })
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const a2uiContainer = (component) => (id, children = []) => {
|
|
823
|
+
if (!Array.isArray(children)) {
|
|
824
|
+
throw new TypeError('a2ui container children must be an array of component ids')
|
|
825
|
+
}
|
|
826
|
+
return a2uiNode(component)(id, { children: children.map(String) })
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
export const a2uiColumn = a2uiContainer('Column')
|
|
830
|
+
export const a2uiRow = a2uiContainer('Row')
|
|
831
|
+
|
|
832
|
+
export const a2uiCard = (id, child) => {
|
|
833
|
+
if (typeof child !== 'string' || child.trim() === '') {
|
|
834
|
+
throw new TypeError('a2uiCard takes the id of one child component, not an array')
|
|
835
|
+
}
|
|
836
|
+
return a2uiNode('Card')(id, { child })
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
const a2uiElement = (type) => (element = {}) => {
|
|
840
|
+
if (element === null || typeof element !== 'object' || Array.isArray(element)) {
|
|
841
|
+
throw new TypeError('a2ui ' + type + ' element must be a plain object')
|
|
842
|
+
}
|
|
843
|
+
return { type, ...element }
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
export const a2uiInfoCard = a2uiElement('info_card')
|
|
847
|
+
export const a2uiListCard = a2uiElement('list_card')
|
|
848
|
+
|
|
849
|
+
const A2UI_FALLBACK_KEYS = Object.freeze(['text', 'title', 'label', 'body'])
|
|
850
|
+
|
|
851
|
+
export const a2uiFallback = (components) => {
|
|
852
|
+
const list = Array.isArray(components)
|
|
853
|
+
? components
|
|
854
|
+
: (components && typeof components === 'object' ? [components] : [])
|
|
855
|
+
return list
|
|
856
|
+
.map(component => A2UI_FALLBACK_KEYS
|
|
857
|
+
.map(key => component?.[key])
|
|
858
|
+
.find(value => typeof value === 'string' && value.trim() !== '') ?? '')
|
|
859
|
+
.filter(Boolean)
|
|
860
|
+
.join('\n')
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
export const a2uiSurface = (components, { surfaceId, root = A2UI_ROOT_ID, catalogId, sendDataModel, version = A2UI_VERSION, type, title } = {}) => {
|
|
864
|
+
if (!Array.isArray(components) || components.length === 0) {
|
|
865
|
+
throw new TypeError('a2uiSurface requires at least one component')
|
|
866
|
+
}
|
|
867
|
+
if (!components.some(component => component?.id === root)) {
|
|
868
|
+
throw new TypeError('a2ui components must include one with id "' + root + '" to serve as the root')
|
|
869
|
+
}
|
|
870
|
+
return trimEmpty({
|
|
871
|
+
version,
|
|
872
|
+
type,
|
|
873
|
+
title,
|
|
874
|
+
createSurface: trimEmpty({
|
|
875
|
+
surfaceId: surfaceId ?? 'card-' + randomUUID(),
|
|
876
|
+
root,
|
|
877
|
+
catalogId,
|
|
878
|
+
sendDataModel: sendDataModel === undefined ? undefined : !!sendDataModel,
|
|
879
|
+
components
|
|
880
|
+
})
|
|
881
|
+
})
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
export const a2uiWidget = (components, { uuid, surfaceId, root, catalogId, sendDataModel, version, type, title, fallback, data } = {}) => {
|
|
885
|
+
const id = uuid ?? randomUUID()
|
|
886
|
+
const payload = data ?? a2uiSurface(components, { surfaceId: surfaceId ?? 'card-' + id, root, catalogId, sendDataModel, version, type, title })
|
|
887
|
+
return bloksWidget({
|
|
888
|
+
type: BLOKS_A2UI_TYPE,
|
|
889
|
+
uuid: id,
|
|
890
|
+
fallback: fallback === undefined ? a2uiFallback(Array.isArray(components) && components.length ? components : payload) : fallback,
|
|
891
|
+
data: payload
|
|
892
|
+
})
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
export const sendA2UI = async (sock, jid, components, { buttons = [], contextInfo, messageId, additionalNodes = [], ...options } = {}) => {
|
|
896
|
+
if (!sock) {
|
|
897
|
+
throw new TypeError('sendA2UI requires a socket as the first argument')
|
|
898
|
+
}
|
|
899
|
+
if (!jid) {
|
|
900
|
+
throw new TypeError('sendA2UI requires a target jid')
|
|
901
|
+
}
|
|
902
|
+
if (!Array.isArray(buttons)) {
|
|
903
|
+
throw new TypeError('sendA2UI buttons must be an array of native flow buttons')
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
const widget = a2uiWidget(components, options)
|
|
907
|
+
const text = widget.fallback
|
|
908
|
+
|
|
909
|
+
const msg = generateWAMessageFromContent(
|
|
910
|
+
jid,
|
|
911
|
+
{
|
|
912
|
+
interactiveMessage: trimEmpty({
|
|
913
|
+
nativeFlowMessage: { buttons, messageParamsJson: JSON.stringify({}), messageVersion: 1 },
|
|
914
|
+
bloksWidget: widget,
|
|
915
|
+
...(text ? { body: { text } } : {}),
|
|
916
|
+
contextInfo
|
|
917
|
+
})
|
|
918
|
+
},
|
|
919
|
+
{ messageId, ...options }
|
|
920
|
+
)
|
|
921
|
+
|
|
922
|
+
await sock.relayMessage(msg.key.remoteJid, msg.message, {
|
|
923
|
+
messageId: msg.key.id,
|
|
924
|
+
additionalNodes: [
|
|
925
|
+
{
|
|
926
|
+
tag: 'biz',
|
|
927
|
+
attrs: {},
|
|
928
|
+
content: [{ tag: 'interactive', attrs: { type: 'native_flow', v: '1' }, content: [{ tag: 'native_flow', attrs: { v: '9', name: 'mixed' } }] }]
|
|
929
|
+
},
|
|
930
|
+
...additionalNodes
|
|
931
|
+
],
|
|
932
|
+
...options
|
|
933
|
+
})
|
|
934
|
+
|
|
935
|
+
return msg
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
export const buildA2UISection = (components, { uuid, surfaceId, root, catalogId, sendDataModel, version, type, title, data, initialResponse, versioningId } = {}) =>
|
|
939
|
+
bloksSection(
|
|
940
|
+
BLOKS_A2UI_TYPE,
|
|
941
|
+
data ?? a2uiSurface(components, { surfaceId, root, catalogId, sendDataModel, version, type, title }),
|
|
942
|
+
{ uuid, initialResponse, versioningId }
|
|
943
|
+
)
|
|
944
|
+
|
|
945
|
+
export const sendA2UIBloks = async (sock, jid, components, { uuid, surfaceId, root, catalogId, sendDataModel, version, type, title, data, initialResponse, versioningId, ...options } = {}) => {
|
|
946
|
+
if (!sock) {
|
|
947
|
+
throw new TypeError('sendA2UIBloks requires a socket as the first argument')
|
|
948
|
+
}
|
|
949
|
+
if (!jid) {
|
|
950
|
+
throw new TypeError('sendA2UIBloks requires a target jid')
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
const rich = new AIRich(sock)
|
|
954
|
+
rich.addSection(buildA2UISection(components, { uuid, surfaceId, root, catalogId, sendDataModel, version, type, title, data, initialResponse, versioningId }))
|
|
955
|
+
|
|
956
|
+
return rich.send(jid, options)
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
export const AI_RICH_SUBMESSAGE_TEXT = 2
|
|
960
|
+
export const AI_RICH_SUBMESSAGE_INLINE_IMAGE = 3
|
|
961
|
+
export const AI_RICH_SUBMESSAGE_TABLE = 4
|
|
962
|
+
export const AI_RICH_SUBMESSAGE_CONTENT_ITEMS = 9
|
|
963
|
+
|
|
964
|
+
const AIRICH_TYPENAME = 'FOABloksPrimitive'
|
|
965
|
+
|
|
966
|
+
const AI_RICH_SUBMESSAGE_FACTORIES = Object.freeze({
|
|
967
|
+
text: (text) => ({
|
|
968
|
+
messageType: AI_RICH_SUBMESSAGE_TEXT,
|
|
969
|
+
messageText: String(text)
|
|
970
|
+
}),
|
|
971
|
+
inline_image: ({ previewUrl, highResUrl, sourceUrl, text = '', alignment, tapLinkUrl } = {}) => {
|
|
972
|
+
if (typeof previewUrl !== 'string' || previewUrl.trim() === '') {
|
|
973
|
+
throw new TypeError('inline_image requires a previewUrl')
|
|
974
|
+
}
|
|
975
|
+
return {
|
|
976
|
+
messageType: AI_RICH_SUBMESSAGE_INLINE_IMAGE,
|
|
977
|
+
imageMetadata: trimEmpty({
|
|
978
|
+
imageUrl: trimEmpty({
|
|
979
|
+
imagePreviewUrl: previewUrl,
|
|
980
|
+
imageHighResUrl: highResUrl,
|
|
981
|
+
sourceUrl
|
|
982
|
+
}),
|
|
983
|
+
imageText: text,
|
|
984
|
+
alignment,
|
|
985
|
+
tapLinkUrl
|
|
986
|
+
})
|
|
987
|
+
}
|
|
988
|
+
},
|
|
989
|
+
table: ({ rows, title } = {}) => {
|
|
990
|
+
if (!Array.isArray(rows) || rows.length === 0) {
|
|
991
|
+
throw new TypeError('table requires a non-empty rows array')
|
|
992
|
+
}
|
|
993
|
+
return {
|
|
994
|
+
messageType: AI_RICH_SUBMESSAGE_TABLE,
|
|
995
|
+
tableMetadata: trimEmpty({
|
|
996
|
+
rows: rows.map(row => ({ items: row.items.map(String), isHeading: !!row.isHeading })),
|
|
997
|
+
title
|
|
998
|
+
})
|
|
999
|
+
}
|
|
1000
|
+
},
|
|
1001
|
+
content_items: ({ items, ...rest } = {}) => {
|
|
1002
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
1003
|
+
throw new TypeError('content_items requires a non-empty items array')
|
|
1004
|
+
}
|
|
1005
|
+
return {
|
|
1006
|
+
messageType: AI_RICH_SUBMESSAGE_CONTENT_ITEMS,
|
|
1007
|
+
contentItemsMetadata: { ...rest, items }
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
})
|
|
1011
|
+
|
|
1012
|
+
export const AI_RICH_SUBMESSAGE_KINDS = Object.freeze(Object.keys(AI_RICH_SUBMESSAGE_FACTORIES))
|
|
1013
|
+
|
|
1014
|
+
export const aiRichSubmessage = (kind, payload = {}) => {
|
|
1015
|
+
const factory = AI_RICH_SUBMESSAGE_FACTORIES[kind]
|
|
1016
|
+
|
|
1017
|
+
if (!factory) {
|
|
1018
|
+
throw new TypeError('unknown AIRich submessage kind "' + kind + '"; expected one of ' + AI_RICH_SUBMESSAGE_KINDS.join(', '))
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
return factory(payload)
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
export const aiRichText = (text) => aiRichSubmessage('text', text)
|
|
1025
|
+
|
|
1026
|
+
export const aiRichInlineImage = (image) => aiRichSubmessage('inline_image', image)
|
|
1027
|
+
|
|
1028
|
+
export const aiRichTable = (table) => aiRichSubmessage('table', table)
|
|
1029
|
+
|
|
1030
|
+
export const aiRichContentItems = (content) => aiRichSubmessage('content_items', content)
|
|
1031
|
+
|
|
1032
|
+
export const AIRichMessage = {
|
|
1033
|
+
SUBMESSAGE_KINDS: AI_RICH_SUBMESSAGE_KINDS,
|
|
1034
|
+
text: aiRichText,
|
|
1035
|
+
inlineImage: aiRichInlineImage,
|
|
1036
|
+
table: aiRichTable,
|
|
1037
|
+
contentItems: aiRichContentItems,
|
|
1038
|
+
submessage: aiRichSubmessage,
|
|
1039
|
+
textSection: (text) => AIRich.newLayout('Single', {
|
|
1040
|
+
__typename: AIRICH_TYPENAME,
|
|
1041
|
+
messageText: String(text)
|
|
1042
|
+
}),
|
|
1043
|
+
nativeFlowSection: (name, params, { uuid, initialResponse = '', versioningId = '' } = {}) =>
|
|
1044
|
+
bloksSection(name, params, { uuid, initialResponse, versioningId })
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
export const sendAIRichMessage = async (
|
|
1048
|
+
sock,
|
|
1049
|
+
jid,
|
|
1050
|
+
{ sections = [], text, nativeFlow, submessages = [], forwardWrapper, ...options } = {}
|
|
1051
|
+
) => {
|
|
1052
|
+
if (!sock) {
|
|
1053
|
+
throw new TypeError('sendAIRichMessage requires a socket as the first argument')
|
|
1054
|
+
}
|
|
1055
|
+
if (!jid) {
|
|
1056
|
+
throw new TypeError('sendAIRichMessage requires a target jid')
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
const rich = new AIRich(sock)
|
|
1060
|
+
let sectionCount = 0
|
|
1061
|
+
|
|
1062
|
+
if (text !== undefined && text !== null && String(text) !== '') {
|
|
1063
|
+
rich.addSection(AIRichMessage.textSection(text))
|
|
1064
|
+
sectionCount += 1
|
|
1065
|
+
}
|
|
1066
|
+
for (const section of sections) {
|
|
1067
|
+
rich.addSection(section)
|
|
1068
|
+
sectionCount += 1
|
|
1069
|
+
}
|
|
1070
|
+
if (nativeFlow !== undefined) {
|
|
1071
|
+
if (!nativeFlow || typeof nativeFlow !== 'object') {
|
|
1072
|
+
throw new TypeError('sendAIRichMessage nativeFlow must be { name, params }')
|
|
1073
|
+
}
|
|
1074
|
+
rich.addSection(AIRichMessage.nativeFlowSection(nativeFlow.name, nativeFlow.params ?? {}))
|
|
1075
|
+
sectionCount += 1
|
|
1076
|
+
}
|
|
1077
|
+
for (const submessage of submessages) {
|
|
1078
|
+
rich.addSubmessage(submessage)
|
|
1079
|
+
}
|
|
1080
|
+
if (sectionCount === 0 && !submessages.length) {
|
|
1081
|
+
throw new TypeError('sendAIRichMessage needs at least one section, text, nativeFlow, or submessage')
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
return rich.send(jid, { ...(forwardWrapper === undefined ? {} : { forwardWrapper }), ...options })
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
export const sendBloksWidget = async (sock, jid, { type, data, uuid, fallback = '', body, contextInfo, messageId, additionalNodes = [], ...options } = {}) => {
|
|
1088
|
+
if (!sock) {
|
|
1089
|
+
throw new TypeError('sendBloksWidget requires a socket as the first argument')
|
|
1090
|
+
}
|
|
1091
|
+
if (!jid) {
|
|
1092
|
+
throw new TypeError('sendBloksWidget requires a target jid')
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
const widget = bloksWidget({ type, data, uuid, fallback })
|
|
1096
|
+
const text = body === undefined ? widget.fallback : String(body)
|
|
1097
|
+
|
|
1098
|
+
const msg = generateWAMessageFromContent(
|
|
1099
|
+
jid,
|
|
1100
|
+
{
|
|
1101
|
+
interactiveMessage: {
|
|
1102
|
+
bloksWidget: widget,
|
|
1103
|
+
...(text ? { body: { text } } : {}),
|
|
1104
|
+
...(contextInfo ? { contextInfo } : {})
|
|
1105
|
+
}
|
|
1106
|
+
},
|
|
1107
|
+
{ messageId, ...options }
|
|
1108
|
+
)
|
|
1109
|
+
|
|
1110
|
+
await sock.relayMessage(msg.key.remoteJid, msg.message, {
|
|
1111
|
+
messageId: msg.key.id,
|
|
1112
|
+
additionalNodes: [
|
|
1113
|
+
{
|
|
1114
|
+
tag: 'biz',
|
|
1115
|
+
attrs: {},
|
|
1116
|
+
content: [{ tag: 'interactive', attrs: { type: 'native_flow', v: '1' }, content: [{ tag: 'native_flow', attrs: { v: '9', name: 'mixed' } }] }]
|
|
1117
|
+
},
|
|
1118
|
+
...additionalNodes
|
|
1119
|
+
],
|
|
1120
|
+
...options
|
|
1121
|
+
})
|
|
1122
|
+
|
|
1123
|
+
return msg
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
export const decodeBloksWidget = (msg) => {
|
|
1127
|
+
const message = msg?.message ?? msg
|
|
1128
|
+
const interactive =
|
|
1129
|
+
message?.interactiveMessage ??
|
|
1130
|
+
message?.viewOnceMessage?.message?.interactiveMessage ??
|
|
1131
|
+
message?.viewOnceMessageV2?.message?.interactiveMessage
|
|
1132
|
+
|
|
1133
|
+
const widget = interactive?.bloksWidget
|
|
1134
|
+
if (!widget) {
|
|
1135
|
+
return null
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
let params = null
|
|
1139
|
+
try {
|
|
1140
|
+
params = widget.data ? JSON.parse(widget.data) : null
|
|
1141
|
+
}
|
|
1142
|
+
catch {
|
|
1143
|
+
params = null
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
return {
|
|
1147
|
+
type: widget.type ?? '',
|
|
1148
|
+
uuid: widget.uuid ?? '',
|
|
1149
|
+
fallback: widget.fallback ?? '',
|
|
1150
|
+
data: widget.data ?? '',
|
|
1151
|
+
params
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
export const collectTypenames = (root, { limit = 20000 } = {}) => {
|
|
1156
|
+
const found = new Set()
|
|
1157
|
+
const stack = [root]
|
|
1158
|
+
let seen = 0
|
|
1159
|
+
|
|
1160
|
+
while (stack.length && seen < limit) {
|
|
1161
|
+
const node = stack.pop()
|
|
1162
|
+
seen++
|
|
1163
|
+
if (Array.isArray(node)) {
|
|
1164
|
+
for (const entry of node) {
|
|
1165
|
+
if (entry && typeof entry === 'object') {
|
|
1166
|
+
stack.push(entry)
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
continue
|
|
1170
|
+
}
|
|
1171
|
+
if (!node || typeof node !== 'object') {
|
|
1172
|
+
continue
|
|
1173
|
+
}
|
|
1174
|
+
if (typeof node.__typename === 'string' && node.__typename) {
|
|
1175
|
+
found.add(node.__typename)
|
|
1176
|
+
}
|
|
1177
|
+
for (const value of Object.values(node)) {
|
|
1178
|
+
if (value && typeof value === 'object') {
|
|
1179
|
+
stack.push(value)
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
return [...found].sort()
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
export const decodeAIRich = (msg) => {
|
|
1188
|
+
const message = msg?.message ?? msg
|
|
1189
|
+
const rich =
|
|
1190
|
+
message?.botForwardedMessage?.message?.richResponseMessage ??
|
|
1191
|
+
message?.botForwardedMessage?.richResponseMessage ??
|
|
1192
|
+
message?.richResponseMessage
|
|
1193
|
+
|
|
1194
|
+
if (!rich) {
|
|
1195
|
+
return null
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
let unified = null
|
|
1199
|
+
const data = rich.unifiedResponse?.data
|
|
1200
|
+
if (data) {
|
|
1201
|
+
try {
|
|
1202
|
+
unified = JSON.parse(Buffer.from(data, 'base64').toString('utf-8'))
|
|
1203
|
+
}
|
|
1204
|
+
catch {
|
|
1205
|
+
unified = null
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
const sections = Array.isArray(unified?.sections) ? unified.sections : []
|
|
1210
|
+
const footerSections = Array.isArray(unified?.footer_sections) ? unified.footer_sections : []
|
|
1211
|
+
const embeddedScreens = Array.isArray(unified?.embedded_screens) ? unified.embedded_screens : []
|
|
1212
|
+
|
|
1213
|
+
const readPrimitives = (section) => {
|
|
1214
|
+
const view = section?.view_model
|
|
1215
|
+
if (Array.isArray(view?.primitives)) return view.primitives
|
|
1216
|
+
if (view?.primitive) return [view.primitive]
|
|
1217
|
+
return []
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
return {
|
|
1221
|
+
responseId: unified?.response_id,
|
|
1222
|
+
layouts: sections.map(section => String(section?.view_model?.__typename ?? '').replace(/^GenAI(.*)LayoutViewModel$/, '$1')),
|
|
1223
|
+
typenames: [...new Set(sections.flatMap(section => readPrimitives(section).map(primitive => primitive?.__typename).filter(Boolean)))],
|
|
1224
|
+
footerTypenames: [...new Set(footerSections.flatMap(section => readPrimitives(section).map(primitive => primitive?.__typename).filter(Boolean)))],
|
|
1225
|
+
allTypenames: collectTypenames(unified),
|
|
1226
|
+
sections,
|
|
1227
|
+
footerSections,
|
|
1228
|
+
embeddedScreens,
|
|
1229
|
+
embeddedTabs: embeddedScreens.flatMap(readEmbeddedTabs),
|
|
1230
|
+
embeddedSections: embeddedScreens.flatMap(readEmbeddedSections),
|
|
1231
|
+
submessages: rich.submessages ?? [],
|
|
1232
|
+
unified
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
const A2UI_TEXT_KEYS = Object.freeze(['text', 'label', 'title'])
|
|
1237
|
+
|
|
1238
|
+
const readPrimitiveText = (primitive) => {
|
|
1239
|
+
if (!primitive || typeof primitive !== 'object') {
|
|
1240
|
+
return ''
|
|
1241
|
+
}
|
|
1242
|
+
if (typeof primitive.text === 'string') {
|
|
1243
|
+
return primitive.text
|
|
1244
|
+
}
|
|
1245
|
+
if (typeof primitive.title === 'string') {
|
|
1246
|
+
return primitive.title
|
|
1247
|
+
}
|
|
1248
|
+
return ''
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
const readA2UIText = (components) => components
|
|
1252
|
+
.filter(component => component?.component === 'Text' || component?.component === 'Heading')
|
|
1253
|
+
.map(component => A2UI_TEXT_KEYS.map(key => component[key]).find(value => typeof value === 'string') ?? '')
|
|
1254
|
+
.filter(Boolean)
|
|
1255
|
+
|
|
1256
|
+
const parseBloksParams = (data) => {
|
|
1257
|
+
if (!data || typeof data !== 'string') {
|
|
1258
|
+
return null
|
|
1259
|
+
}
|
|
1260
|
+
try {
|
|
1261
|
+
return JSON.parse(data)
|
|
1262
|
+
}
|
|
1263
|
+
catch {
|
|
1264
|
+
return null
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
export const readRichMessage = (msg) => {
|
|
1269
|
+
const raw = msg?.message ?? msg
|
|
1270
|
+
if (!raw || typeof raw !== 'object') {
|
|
1271
|
+
return null
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
const content = extractMessageContent(raw) ?? raw
|
|
1275
|
+
const rich = decodeAIRich(raw)
|
|
1276
|
+
const widget = decodeBloksWidget(raw)
|
|
1277
|
+
const interactive =
|
|
1278
|
+
content?.interactiveMessage ??
|
|
1279
|
+
raw?.interactiveMessage ??
|
|
1280
|
+
raw?.viewOnceMessage?.message?.interactiveMessage ??
|
|
1281
|
+
raw?.viewOnceMessageV2?.message?.interactiveMessage
|
|
1282
|
+
|
|
1283
|
+
if (!rich && !widget && !interactive) {
|
|
1284
|
+
return null
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
const buttons = (interactive?.nativeFlowMessage?.buttons ?? []).map(button => {
|
|
1288
|
+
let params = null
|
|
1289
|
+
try {
|
|
1290
|
+
params = button?.buttonParamsJson ? JSON.parse(button.buttonParamsJson) : null
|
|
1291
|
+
}
|
|
1292
|
+
catch {
|
|
1293
|
+
params = null
|
|
1294
|
+
}
|
|
1295
|
+
return { name: button?.name ?? '', params }
|
|
1296
|
+
})
|
|
1297
|
+
|
|
1298
|
+
const isA2UIWidget = widget?.type === BLOKS_A2UI_TYPE
|
|
1299
|
+
|
|
1300
|
+
const sectionPrimitives = sections => sections.flatMap(section => {
|
|
1301
|
+
const view = section?.view_model
|
|
1302
|
+
if (Array.isArray(view?.primitives)) return view.primitives
|
|
1303
|
+
return view?.primitive ? [view.primitive] : []
|
|
1304
|
+
})
|
|
1305
|
+
|
|
1306
|
+
const primitives = sectionPrimitives(rich?.sections ?? [])
|
|
1307
|
+
const embeddedPrimitives = sectionPrimitives(rich?.embeddedSections ?? [])
|
|
1308
|
+
|
|
1309
|
+
const richBloks = primitives.find(primitive => primitive?.__typename === 'FOABloksPrimitive' && primitive?.type === BLOKS_A2UI_TYPE)
|
|
1310
|
+
const a2uiParams = isA2UIWidget ? widget.params : parseBloksParams(richBloks?.data)
|
|
1311
|
+
const surface = a2uiParams?.createSurface
|
|
1312
|
+
const a2uiComponents = Array.isArray(surface?.components) ? surface.components : []
|
|
1313
|
+
const a2uiElement = !surface && a2uiParams && typeof a2uiParams === 'object' ? a2uiParams : undefined
|
|
1314
|
+
const isA2UI = isA2UIWidget || !!richBloks
|
|
1315
|
+
|
|
1316
|
+
const a2uiTextLines = readA2UIText(a2uiComponents)
|
|
1317
|
+
const bodyText = interactive?.body?.text ?? ''
|
|
1318
|
+
const widgetFallback = widget?.fallback ?? ''
|
|
1319
|
+
const bodyDuplicatesWidget = bodyText !== '' && bodyText === widgetFallback
|
|
1320
|
+
|
|
1321
|
+
const lines = [
|
|
1322
|
+
...primitives.filter(primitive => primitive !== richBloks).map(readPrimitiveText),
|
|
1323
|
+
...a2uiTextLines,
|
|
1324
|
+
bodyDuplicatesWidget && a2uiTextLines.length ? '' : bodyText,
|
|
1325
|
+
interactive?.footer?.text ?? ''
|
|
1326
|
+
].filter(Boolean)
|
|
1327
|
+
|
|
1328
|
+
const kind = isA2UI ? 'a2ui' : widget ? 'bloks' : rich ? 'airich' : 'interactive'
|
|
1329
|
+
|
|
1330
|
+
return trimEmpty({
|
|
1331
|
+
kind,
|
|
1332
|
+
text: lines.join('\n'),
|
|
1333
|
+
title: raw?.messageContextInfo?.botMetadata?.messageDisclaimerText || interactive?.header?.title || '',
|
|
1334
|
+
buttons,
|
|
1335
|
+
html: [...primitives, ...embeddedPrimitives].filter(p => typeof p?.payload === 'string').map(p => p.payload),
|
|
1336
|
+
typenames: rich?.typenames ?? [],
|
|
1337
|
+
sections: rich?.sections ?? [],
|
|
1338
|
+
footerSections: rich?.footerSections ?? [],
|
|
1339
|
+
embeddedScreens: rich?.embeddedScreens ?? [],
|
|
1340
|
+
embeddedTabs: rich?.embeddedTabs ?? [],
|
|
1341
|
+
submessages: rich?.submessages ?? [],
|
|
1342
|
+
responseId: rich?.responseId,
|
|
1343
|
+
a2ui: isA2UI
|
|
1344
|
+
? trimEmpty({
|
|
1345
|
+
surfaceId: surface?.surfaceId,
|
|
1346
|
+
root: surface?.root,
|
|
1347
|
+
catalogId: surface?.catalogId,
|
|
1348
|
+
version: a2uiParams?.version,
|
|
1349
|
+
type: a2uiParams?.type,
|
|
1350
|
+
components: a2uiComponents.length ? a2uiComponents : undefined,
|
|
1351
|
+
element: a2uiElement
|
|
1352
|
+
})
|
|
1353
|
+
: undefined,
|
|
1354
|
+
bloks: widget ? { type: widget.type, uuid: widget.uuid, fallback: widget.fallback, params: widget.params } : undefined
|
|
1355
|
+
})
|
|
1356
|
+
}
|