@pontalabs/baileys 1.0.4 → 1.0.6
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/LICENSE +1 -1
- package/README.md +267 -839
- package/WAProto/GenerateStatics.sh +3 -4
- package/WAProto/WAProto.proto +1215 -511
- package/WAProto/fix-imports.js +85 -0
- package/WAProto/index.d.ts +14017 -55
- package/WAProto/index.js +97689 -29
- package/engine-requirements.js +10 -0
- package/lib/Defaults/index.d.ts +72 -74
- package/lib/Defaults/index.d.ts.map +1 -0
- package/lib/Defaults/index.js +94 -108
- package/lib/Defaults/index.js.map +1 -0
- package/lib/Signal/Group/ciphertext-message.d.ts +8 -7
- package/lib/Signal/Group/ciphertext-message.d.ts.map +1 -0
- package/lib/Signal/Group/ciphertext-message.js +9 -16
- package/lib/Signal/Group/ciphertext-message.js.map +1 -0
- package/lib/Signal/Group/group-session-builder.d.ts +11 -13
- package/lib/Signal/Group/group-session-builder.d.ts.map +1 -0
- package/lib/Signal/Group/group-session-builder.js +19 -61
- package/lib/Signal/Group/group-session-builder.js.map +1 -0
- package/lib/Signal/Group/group_cipher.d.ts +14 -16
- package/lib/Signal/Group/group_cipher.d.ts.map +1 -0
- package/lib/Signal/Group/group_cipher.js +41 -70
- package/lib/Signal/Group/group_cipher.js.map +1 -0
- package/lib/Signal/Group/index.d.ts +12 -11
- package/lib/Signal/Group/index.d.ts.map +1 -0
- package/lib/Signal/Group/index.js +12 -61
- package/lib/Signal/Group/index.js.map +1 -0
- package/lib/Signal/Group/keyhelper.d.ts +9 -14
- package/lib/Signal/Group/keyhelper.d.ts.map +1 -0
- package/lib/Signal/Group/keyhelper.js +10 -58
- package/lib/Signal/Group/keyhelper.js.map +1 -0
- package/lib/Signal/Group/sender-chain-key.d.ts +13 -13
- package/lib/Signal/Group/sender-chain-key.d.ts.map +1 -0
- package/lib/Signal/Group/sender-chain-key.js +13 -34
- package/lib/Signal/Group/sender-chain-key.js.map +1 -0
- package/lib/Signal/Group/sender-key-distribution-message.d.ts +16 -16
- package/lib/Signal/Group/sender-key-distribution-message.d.ts.map +1 -0
- package/lib/Signal/Group/sender-key-distribution-message.js +28 -36
- package/lib/Signal/Group/sender-key-distribution-message.js.map +1 -0
- package/lib/Signal/Group/sender-key-message.d.ts +18 -18
- package/lib/Signal/Group/sender-key-message.d.ts.map +1 -0
- package/lib/Signal/Group/sender-key-message.js +38 -45
- package/lib/Signal/Group/sender-key-message.js.map +1 -0
- package/lib/Signal/Group/sender-key-name.d.ts +14 -15
- package/lib/Signal/Group/sender-key-name.d.ts.map +1 -0
- package/lib/Signal/Group/sender-key-name.js +20 -31
- package/lib/Signal/Group/sender-key-name.js.map +1 -0
- package/lib/Signal/Group/sender-key-record.d.ts +24 -25
- package/lib/Signal/Group/sender-key-record.d.ts.map +1 -0
- package/lib/Signal/Group/sender-key-record.js +18 -35
- package/lib/Signal/Group/sender-key-record.js.map +1 -0
- package/lib/Signal/Group/sender-key-state.d.ts +29 -34
- package/lib/Signal/Group/sender-key-state.d.ts.map +1 -0
- package/lib/Signal/Group/sender-key-state.js +37 -100
- package/lib/Signal/Group/sender-key-state.js.map +1 -0
- package/lib/Signal/Group/sender-message-key.d.ts +11 -10
- package/lib/Signal/Group/sender-message-key.d.ts.map +1 -0
- package/lib/Signal/Group/sender-message-key.js +15 -22
- package/lib/Signal/Group/sender-message-key.js.map +1 -0
- package/lib/Signal/libsignal.d.ts +5 -8
- package/lib/Signal/libsignal.d.ts.map +1 -0
- package/lib/Signal/libsignal.js +279 -121
- package/lib/Signal/libsignal.js.map +1 -0
- package/lib/Signal/lid-mapping.d.ts +23 -28
- package/lib/Signal/lid-mapping.d.ts.map +1 -0
- package/lib/Signal/lid-mapping.js +277 -184
- package/lib/Signal/lid-mapping.js.map +1 -0
- package/lib/Socket/Client/index.d.ts +3 -2
- package/lib/Socket/Client/index.d.ts.map +1 -0
- package/lib/Socket/Client/index.js +3 -22
- package/lib/Socket/Client/index.js.map +1 -0
- package/lib/Socket/Client/types.d.ts +15 -15
- package/lib/Socket/Client/types.d.ts.map +1 -0
- package/lib/Socket/Client/types.js +8 -15
- package/lib/Socket/Client/types.js.map +1 -0
- package/lib/Socket/Client/websocket.d.ts +12 -12
- package/lib/Socket/Client/websocket.d.ts.map +1 -0
- package/lib/Socket/Client/websocket.js +29 -37
- package/lib/Socket/Client/websocket.js.map +1 -0
- package/lib/Socket/business.d.ts +208 -178
- package/lib/Socket/business.d.ts.map +1 -0
- package/lib/Socket/business.js +91 -127
- package/lib/Socket/business.js.map +1 -0
- package/lib/Socket/chats.d.ts +120 -93
- package/lib/Socket/chats.d.ts.map +1 -0
- package/lib/Socket/chats.js +727 -652
- package/lib/Socket/chats.js.map +1 -0
- package/lib/Socket/communities.d.ts +273 -0
- package/lib/Socket/communities.d.ts.map +1 -0
- package/lib/Socket/communities.js +431 -0
- package/lib/Socket/communities.js.map +1 -0
- package/lib/Socket/dugong.js +261 -0
- package/lib/Socket/dugong.js.bak +204 -0
- package/lib/Socket/groups.d.ts +143 -111
- package/lib/Socket/groups.d.ts.map +1 -0
- package/lib/Socket/groups.js +165 -177
- package/lib/Socket/groups.js.map +1 -0
- package/lib/Socket/index.d.ts +252 -183
- package/lib/Socket/index.d.ts.map +1 -0
- package/lib/Socket/index.js +38 -18
- package/lib/Socket/index.js.bak +44 -0
- package/lib/Socket/index.js.map +1 -0
- package/lib/Socket/messages-recv.d.ts +196 -167
- package/lib/Socket/messages-recv.d.ts.map +1 -0
- package/lib/Socket/messages-recv.js +1443 -1563
- package/lib/Socket/messages-recv.js.map +1 -0
- package/lib/Socket/messages-send.d.ts +192 -158
- package/lib/Socket/messages-send.d.ts.map +1 -0
- package/lib/Socket/messages-send.js +1102 -1208
- package/lib/Socket/messages-send.js.bak +1400 -0
- package/lib/Socket/messages-send.js.map +1 -0
- package/lib/Socket/mex.d.ts +3 -0
- package/lib/Socket/mex.d.ts.map +1 -0
- package/lib/Socket/mex.js +42 -0
- package/lib/Socket/mex.js.map +1 -0
- package/lib/Socket/newsletter.d.ts +163 -138
- package/lib/Socket/newsletter.d.ts.map +1 -0
- package/lib/Socket/newsletter.js +153 -249
- package/lib/Socket/newsletter.js.map +1 -0
- package/lib/Socket/socket.d.ts +50 -36
- package/lib/Socket/socket.d.ts.map +1 -0
- package/lib/Socket/socket.js +749 -820
- package/lib/Socket/socket.js.map +1 -0
- package/lib/Store/index.d.ts +4 -4
- package/lib/Store/index.d.ts.map +1 -0
- package/lib/Store/index.js +4 -24
- package/lib/Store/index.js.map +1 -0
- package/lib/Store/make-in-memory-store.d.ts +44 -104
- package/lib/Store/make-in-memory-store.d.ts.map +1 -0
- package/lib/Store/make-in-memory-store.js +199 -213
- package/lib/Store/make-in-memory-store.js.map +1 -0
- package/lib/Store/make-ordered-dictionary.d.ts +13 -12
- package/lib/Store/make-ordered-dictionary.d.ts.map +1 -0
- package/lib/Store/make-ordered-dictionary.js +38 -45
- package/lib/Store/make-ordered-dictionary.js.map +1 -0
- package/lib/Store/object-repository.d.ts +11 -10
- package/lib/Store/object-repository.d.ts.map +1 -0
- package/lib/Store/object-repository.js +9 -16
- package/lib/Store/object-repository.js.map +1 -0
- package/lib/Types/Auth.d.ts +93 -97
- package/lib/Types/Auth.d.ts.map +1 -0
- package/lib/Types/Auth.js +2 -3
- package/lib/Types/Auth.js.map +1 -0
- package/lib/Types/Bussines.d.ts +25 -0
- package/lib/Types/Bussines.d.ts.map +1 -0
- package/lib/Types/Bussines.js +2 -3
- package/lib/Types/Bussines.js.map +1 -0
- package/lib/Types/Call.d.ts +14 -13
- package/lib/Types/Call.d.ts.map +1 -0
- package/lib/Types/Call.js +2 -3
- package/lib/Types/Call.js.map +1 -0
- package/lib/Types/Chat.d.ts +78 -97
- package/lib/Types/Chat.d.ts.map +1 -0
- package/lib/Types/Chat.js +8 -9
- package/lib/Types/Chat.js.map +1 -0
- package/lib/Types/Contact.d.ts +12 -9
- package/lib/Types/Contact.d.ts.map +1 -0
- package/lib/Types/Contact.js +2 -3
- package/lib/Types/Contact.js.map +1 -0
- package/lib/Types/Events.d.ts +206 -176
- package/lib/Types/Events.d.ts.map +1 -0
- package/lib/Types/Events.js +2 -3
- package/lib/Types/Events.js.map +1 -0
- package/lib/Types/GroupMetadata.d.ts +53 -48
- package/lib/Types/GroupMetadata.d.ts.map +1 -0
- package/lib/Types/GroupMetadata.js +2 -3
- package/lib/Types/GroupMetadata.js.map +1 -0
- package/lib/Types/Label.d.ts +13 -14
- package/lib/Types/Label.d.ts.map +1 -0
- package/lib/Types/Label.js +24 -30
- package/lib/Types/Label.js.map +1 -0
- package/lib/Types/LabelAssociation.d.ts +15 -20
- package/lib/Types/LabelAssociation.d.ts.map +1 -0
- package/lib/Types/LabelAssociation.js +6 -12
- package/lib/Types/LabelAssociation.js.map +1 -0
- package/lib/Types/Message.d.ts +248 -412
- package/lib/Types/Message.d.ts.map +1 -0
- package/lib/Types/Message.js +17 -19
- package/lib/Types/Message.js.map +1 -0
- package/lib/Types/Mex.d.ts +141 -0
- package/lib/Types/Mex.d.ts.map +1 -0
- package/lib/Types/Mex.js +37 -0
- package/lib/Types/Mex.js.map +1 -0
- package/lib/Types/Product.d.ts +58 -71
- package/lib/Types/Product.d.ts.map +1 -0
- package/lib/Types/Product.js +2 -3
- package/lib/Types/Product.js.map +1 -0
- package/lib/Types/RichType.d.ts +3 -0
- package/lib/Types/RichType.d.ts.map +1 -0
- package/lib/Types/RichType.js +23 -0
- package/lib/Types/RichType.js.bak +23 -0
- package/lib/Types/RichType.js.map +1 -0
- package/lib/Types/Signal.d.ts +71 -82
- package/lib/Types/Signal.d.ts.map +1 -0
- package/lib/Types/Signal.js +2 -3
- package/lib/Types/Signal.js.map +1 -0
- package/lib/Types/Socket.d.ts +76 -81
- package/lib/Types/Socket.d.ts.map +1 -0
- package/lib/Types/Socket.js +3 -3
- package/lib/Types/Socket.js.map +1 -0
- package/lib/Types/State.d.ts +75 -19
- package/lib/Types/State.d.ts.map +1 -0
- package/lib/Types/State.js +56 -14
- package/lib/Types/State.js.map +1 -0
- package/lib/Types/USync.d.ts +8 -8
- package/lib/Types/USync.d.ts.map +1 -0
- package/lib/Types/USync.js +2 -3
- package/lib/Types/USync.js.map +1 -0
- package/lib/Types/index.d.ts +47 -62
- package/lib/Types/index.d.ts.map +1 -0
- package/lib/Types/index.js +26 -50
- package/lib/Types/index.js.map +1 -0
- package/lib/Utils/auth-utils.d.ts +13 -10
- package/lib/Utils/auth-utils.d.ts.map +1 -0
- package/lib/Utils/auth-utils.js +213 -454
- package/lib/Utils/auth-utils.js.map +1 -0
- package/lib/Utils/browser-utils.d.ts +4 -0
- package/lib/Utils/browser-utils.d.ts.map +1 -0
- package/lib/Utils/browser-utils.js +28 -0
- package/lib/Utils/browser-utils.js.map +1 -0
- package/lib/Utils/business.d.ts +14 -20
- package/lib/Utils/business.d.ts.map +1 -0
- package/lib/Utils/business.js +110 -134
- package/lib/Utils/business.js.map +1 -0
- package/lib/Utils/chat-utils.d.ts +88 -70
- package/lib/Utils/chat-utils.d.ts.map +1 -0
- package/lib/Utils/chat-utils.js +466 -403
- package/lib/Utils/chat-utils.js.map +1 -0
- package/lib/Utils/companion-reg-client-utils.d.ts +17 -0
- package/lib/Utils/companion-reg-client-utils.d.ts.map +1 -0
- package/lib/Utils/companion-reg-client-utils.js +35 -0
- package/lib/Utils/companion-reg-client-utils.js.map +1 -0
- package/lib/Utils/crypto.d.ts +27 -46
- package/lib/Utils/crypto.d.ts.map +1 -0
- package/lib/Utils/crypto.js +117 -188
- package/lib/Utils/crypto.js.map +1 -0
- package/lib/Utils/decode-wa-message.d.ts +58 -45
- package/lib/Utils/decode-wa-message.d.ts.map +1 -0
- package/lib/Utils/decode-wa-message.js +213 -303
- package/lib/Utils/decode-wa-message.js.map +1 -0
- package/lib/Utils/event-buffer.d.ts +14 -17
- package/lib/Utils/event-buffer.d.ts.map +1 -0
- package/lib/Utils/event-buffer.js +321 -296
- package/lib/Utils/event-buffer.js.map +1 -0
- package/lib/Utils/generics.d.ts +66 -92
- package/lib/Utils/generics.d.ts.map +1 -0
- package/lib/Utils/generics.js +270 -436
- package/lib/Utils/generics.js.map +1 -0
- package/lib/Utils/history.d.ts +24 -23
- package/lib/Utils/history.d.ts.map +1 -0
- package/lib/Utils/history.js +106 -76
- package/lib/Utils/history.js.map +1 -0
- package/lib/Utils/identity-change-handler.d.ts +44 -0
- package/lib/Utils/identity-change-handler.d.ts.map +1 -0
- package/lib/Utils/identity-change-handler.js +50 -0
- package/lib/Utils/identity-change-handler.js.map +1 -0
- package/lib/Utils/index.d.ts +24 -21
- package/lib/Utils/index.d.ts.map +1 -0
- package/lib/Utils/index.js +24 -41
- package/lib/Utils/index.js.map +1 -0
- package/lib/Utils/link-preview.d.ts +12 -14
- package/lib/Utils/link-preview.d.ts.map +1 -0
- package/lib/Utils/link-preview.js +40 -75
- package/lib/Utils/link-preview.js.map +1 -0
- package/lib/Utils/logger.d.ts +10 -11
- package/lib/Utils/logger.d.ts.map +1 -0
- package/lib/Utils/logger.js +3 -7
- package/lib/Utils/logger.js.map +1 -0
- package/lib/Utils/lt-hash.d.ts +8 -14
- package/lib/Utils/lt-hash.d.ts.map +1 -0
- package/lib/Utils/lt-hash.js +3 -53
- package/lib/Utils/lt-hash.js.map +1 -0
- package/lib/Utils/make-mutex.d.ts +7 -7
- package/lib/Utils/make-mutex.d.ts.map +1 -0
- package/lib/Utils/make-mutex.js +28 -44
- package/lib/Utils/make-mutex.js.map +1 -0
- package/lib/Utils/message-retry-manager.d.ts +115 -88
- package/lib/Utils/message-retry-manager.d.ts.map +1 -0
- package/lib/Utils/message-retry-manager.js +265 -160
- package/lib/Utils/message-retry-manager.js.map +1 -0
- package/lib/Utils/messages-media.d.ts +105 -107
- package/lib/Utils/messages-media.d.ts.map +1 -0
- package/lib/Utils/messages-media.js +548 -619
- package/lib/Utils/messages-media.js.bak +786 -0
- package/lib/Utils/messages-media.js.map +1 -0
- package/lib/Utils/messages.d.ts +52 -66
- package/lib/Utils/messages.d.ts.map +1 -0
- package/lib/Utils/messages.js +1730 -1558
- package/lib/Utils/messages.js.backup +1768 -0
- package/lib/Utils/messages.js.bak +1809 -0
- package/lib/Utils/messages.js.bak2 +893 -0
- package/lib/Utils/messages.js.bak3 +1183 -0
- package/lib/Utils/messages.js.map +1 -0
- package/lib/Utils/noise-handler.d.ts +18 -18
- package/lib/Utils/noise-handler.d.ts.map +1 -0
- package/lib/Utils/noise-handler.js +176 -130
- package/lib/Utils/noise-handler.js.map +1 -0
- package/lib/Utils/offline-node-processor.d.ts +17 -0
- package/lib/Utils/offline-node-processor.d.ts.map +1 -0
- package/lib/Utils/offline-node-processor.js +40 -0
- package/lib/Utils/offline-node-processor.js.map +1 -0
- package/lib/Utils/pre-key-manager.d.ts +28 -0
- package/lib/Utils/pre-key-manager.d.ts.map +1 -0
- package/lib/Utils/pre-key-manager.js +106 -0
- package/lib/Utils/pre-key-manager.js.map +1 -0
- package/lib/Utils/process-message.d.ts +43 -32
- package/lib/Utils/process-message.d.ts.map +1 -0
- package/lib/Utils/process-message.js +484 -282
- package/lib/Utils/process-message.js.map +1 -0
- package/lib/Utils/reporting-utils.d.ts +11 -0
- package/lib/Utils/reporting-utils.d.ts.map +1 -0
- package/lib/Utils/reporting-utils.js +258 -0
- package/lib/Utils/reporting-utils.js.map +1 -0
- package/lib/Utils/rich-message-utils.d.ts +49 -0
- package/lib/Utils/rich-message-utils.d.ts.map +1 -0
- package/lib/Utils/rich-message-utils.js +1220 -724
- package/lib/Utils/rich-message-utils.js.backup +706 -0
- package/lib/Utils/rich-message-utils.js.bak +246 -0
- package/lib/Utils/rich-message-utils.js.map +1 -0
- package/lib/Utils/rich-message-utils.js.original +1332 -0
- package/lib/Utils/signal.d.ts +37 -32
- package/lib/Utils/signal.d.ts.map +1 -0
- package/lib/Utils/signal.js +140 -105
- package/lib/Utils/signal.js.map +1 -0
- package/lib/Utils/stanza-ack.d.ts +11 -0
- package/lib/Utils/stanza-ack.d.ts.map +1 -0
- package/lib/Utils/stanza-ack.js +38 -0
- package/lib/Utils/stanza-ack.js.map +1 -0
- package/lib/Utils/sync-action-utils.d.ts +19 -0
- package/lib/Utils/sync-action-utils.d.ts.map +1 -0
- package/lib/Utils/sync-action-utils.js +49 -0
- package/lib/Utils/sync-action-utils.js.map +1 -0
- package/lib/Utils/tc-token-utils.d.ts +37 -0
- package/lib/Utils/tc-token-utils.d.ts.map +1 -0
- package/lib/Utils/tc-token-utils.js +163 -0
- package/lib/Utils/tc-token-utils.js.map +1 -0
- package/lib/Utils/use-multi-file-auth-state.d.ts +5 -5
- package/lib/Utils/use-multi-file-auth-state.d.ts.map +1 -0
- package/lib/Utils/use-multi-file-auth-state.js +70 -120
- package/lib/Utils/use-multi-file-auth-state.js.map +1 -0
- package/lib/Utils/use-single-file-auth-state.d.ts +11 -13
- package/lib/Utils/use-single-file-auth-state.d.ts.map +1 -0
- package/lib/Utils/use-single-file-auth-state.js +92 -66
- package/lib/Utils/use-single-file-auth-state.js.map +1 -0
- package/lib/Utils/use-sqlite-auth-state.d.ts +11 -0
- package/lib/Utils/use-sqlite-auth-state.d.ts.map +1 -0
- package/lib/Utils/use-sqlite-auth-state.js +109 -0
- package/lib/Utils/use-sqlite-auth-state.js.map +1 -0
- package/lib/Utils/validate-connection.d.ts +11 -13
- package/lib/Utils/validate-connection.d.ts.map +1 -0
- package/lib/Utils/validate-connection.js +128 -113
- package/lib/Utils/validate-connection.js.map +1 -0
- package/lib/WABinary/constants.d.ts +25 -27
- package/lib/WABinary/constants.d.ts.map +1 -0
- package/lib/WABinary/constants.js +1451 -1292
- package/lib/WABinary/constants.js.bak +1373 -0
- package/lib/WABinary/constants.js.map +1 -0
- package/lib/WABinary/decode.d.ts +7 -9
- package/lib/WABinary/decode.d.ts.map +1 -0
- package/lib/WABinary/decode.js +163 -189
- package/lib/WABinary/decode.js.map +1 -0
- package/lib/WABinary/encode.d.ts +3 -3
- package/lib/WABinary/encode.d.ts.map +1 -0
- package/lib/WABinary/encode.js +110 -155
- package/lib/WABinary/encode.js.map +1 -0
- package/lib/WABinary/generic-utils.d.ts +14 -27
- package/lib/WABinary/generic-utils.d.ts.map +1 -0
- package/lib/WABinary/generic-utils.js +177 -105
- package/lib/WABinary/generic-utils.js.map +1 -0
- package/lib/WABinary/index.d.ts +6 -5
- package/lib/WABinary/index.d.ts.map +1 -0
- package/lib/WABinary/index.js +6 -25
- package/lib/WABinary/index.js.map +1 -0
- package/lib/WABinary/jid-utils.d.ts +41 -51
- package/lib/WABinary/jid-utils.d.ts.map +1 -0
- package/lib/WABinary/jid-utils.js +83 -120
- package/lib/WABinary/jid-utils.js.map +1 -0
- package/lib/WABinary/types.d.ts +10 -13
- package/lib/WABinary/types.d.ts.map +1 -0
- package/lib/WABinary/types.js +2 -3
- package/lib/WABinary/types.js.map +1 -0
- package/lib/WAM/BinaryInfo.d.ts +8 -15
- package/lib/WAM/BinaryInfo.d.ts.map +1 -0
- package/lib/WAM/BinaryInfo.js +7 -14
- package/lib/WAM/BinaryInfo.js.map +1 -0
- package/lib/WAM/constants.d.ts +30 -37
- package/lib/WAM/constants.d.ts.map +1 -0
- package/lib/WAM/constants.js +19193 -11711
- package/lib/WAM/constants.js.map +1 -0
- package/lib/WAM/encode.d.ts +3 -3
- package/lib/WAM/encode.d.ts.map +1 -0
- package/lib/WAM/encode.js +95 -109
- package/lib/WAM/encode.js.map +1 -0
- package/lib/WAM/index.d.ts +4 -3
- package/lib/WAM/index.d.ts.map +1 -0
- package/lib/WAM/index.js +4 -23
- package/lib/WAM/index.js.map +1 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +9 -9
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts.map +1 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +37 -21
- package/lib/WAUSync/Protocols/USyncContactProtocol.js.map +1 -0
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +19 -22
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts.map +1 -0
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +18 -26
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js.map +1 -0
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +11 -12
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts.map +1 -0
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +12 -20
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js.map +1 -0
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +11 -12
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts.map +1 -0
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +16 -24
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js.map +1 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +10 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts.map +1 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js.map +1 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +26 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts.map +1 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +51 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js.map +1 -0
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +10 -0
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts.map +1 -0
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +29 -0
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js.map +1 -0
- package/lib/WAUSync/Protocols/index.d.ts +6 -6
- package/lib/WAUSync/Protocols/index.d.ts.map +1 -0
- package/lib/WAUSync/Protocols/index.js +6 -26
- package/lib/WAUSync/Protocols/index.js.map +1 -0
- package/lib/WAUSync/USyncQuery.d.ts +27 -28
- package/lib/WAUSync/USyncQuery.d.ts.map +1 -0
- package/lib/WAUSync/USyncQuery.js +68 -62
- package/lib/WAUSync/USyncQuery.js.map +1 -0
- package/lib/WAUSync/USyncUser.d.ts +15 -10
- package/lib/WAUSync/USyncUser.d.ts.map +1 -0
- package/lib/WAUSync/USyncUser.js +20 -19
- package/lib/WAUSync/USyncUser.js.map +1 -0
- package/lib/WAUSync/index.d.ts +4 -3
- package/lib/WAUSync/index.d.ts.map +1 -0
- package/lib/WAUSync/index.js +4 -23
- package/lib/WAUSync/index.js.map +1 -0
- package/lib/index.d.ts +11 -12
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +12 -39
- package/lib/index.js.map +1 -0
- package/package.json +86 -42
package/lib/Utils/messages.js
CHANGED
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const messages_media_1 = require("./messages-media")
|
|
17
|
-
const rich_message_utils_1 = require("./rich-message-utils")
|
|
18
|
-
|
|
1
|
+
import { Boom } from '@hapi/boom';
|
|
2
|
+
import { randomBytes } from 'crypto';
|
|
3
|
+
import { zip } from 'fflate';
|
|
4
|
+
import { promises as fs } from 'fs';
|
|
5
|
+
import {} from 'stream';
|
|
6
|
+
import { proto } from '../../WAProto/index.js';
|
|
7
|
+
import { CALL_AUDIO_PREFIX, CALL_VIDEO_PREFIX, DONATE_URL, LIBRARY_NAME, MEDIA_KEYS, URL_REGEX, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
|
|
8
|
+
import { AssociationType, ButtonHeaderType, ButtonType, CarouselCardType, ListType, ProtocolType, WAMessageStatus, WAProto } from '../Types/index.js';
|
|
9
|
+
import { isJidGroup, isJidNewsletter, isJidStatusBroadcast, isLidUser, isPnUser, jidNormalizedUser } from '../WABinary/index.js';
|
|
10
|
+
import { sha256 } from './crypto.js';
|
|
11
|
+
import { generateMessageIDV2, getKeyAuthor, unixTimestampSeconds } from './generics.js';
|
|
12
|
+
import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getImageProcessingLibrary, getRawMediaUploadData, getStream, toBuffer } from './messages-media.js';
|
|
13
|
+
import { prepareRichResponseMessage } from './rich-message-utils.js';
|
|
14
|
+
import { shouldIncludeReportingToken } from './reporting-utils.js';
|
|
15
|
+
const CONCURRENCY_LIMIT = 10;
|
|
19
16
|
const MIMETYPE_MAP = {
|
|
20
17
|
image: 'image/jpeg',
|
|
21
18
|
video: 'video/mp4',
|
|
@@ -23,117 +20,111 @@ const MIMETYPE_MAP = {
|
|
|
23
20
|
audio: 'audio/ogg; codecs=opus',
|
|
24
21
|
sticker: 'image/webp',
|
|
25
22
|
'product-catalog-image': 'image/jpeg'
|
|
26
|
-
}
|
|
27
|
-
|
|
23
|
+
};
|
|
28
24
|
const MessageTypeProto = {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
25
|
+
image: WAProto.Message.ImageMessage,
|
|
26
|
+
video: WAProto.Message.VideoMessage,
|
|
27
|
+
audio: WAProto.Message.AudioMessage,
|
|
28
|
+
sticker: WAProto.Message.StickerMessage,
|
|
29
|
+
document: WAProto.Message.DocumentMessage
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Uses a regex to test whether the string contains a URL, and returns the URL if it does.
|
|
33
|
+
* @param text eg. hello https://google.com
|
|
34
|
+
* @returns the URL, eg. https://google.com
|
|
35
|
+
*/
|
|
36
|
+
export const extractUrlFromText = (text) => text.match(URL_REGEX)?.[0];
|
|
37
|
+
export const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
|
|
38
|
+
const url = extractUrlFromText(text);
|
|
40
39
|
if (!!getUrlInfo && url) {
|
|
41
40
|
try {
|
|
42
|
-
const urlInfo = await getUrlInfo(url)
|
|
43
|
-
return urlInfo
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}, 'url generation failed')
|
|
41
|
+
const urlInfo = await getUrlInfo(url);
|
|
42
|
+
return urlInfo;
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
// ignore if fails
|
|
46
|
+
logger?.warn({ trace: error.stack }, 'url generation failed');
|
|
48
47
|
}
|
|
49
48
|
}
|
|
50
|
-
}
|
|
51
|
-
|
|
49
|
+
};
|
|
52
50
|
const assertColor = async (color) => {
|
|
53
|
-
let assertedColor
|
|
51
|
+
let assertedColor;
|
|
54
52
|
if (typeof color === 'number') {
|
|
55
|
-
assertedColor = color > 0 ? color : 0xffffffff + Number(color) + 1
|
|
56
|
-
}
|
|
57
|
-
|
|
53
|
+
assertedColor = color > 0 ? color : 0xffffffff + Number(color) + 1;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
let hex = color.trim().replace('#', '');
|
|
58
57
|
if (hex.length <= 6) {
|
|
59
|
-
hex = 'FF' + hex.padStart(6, '0')
|
|
58
|
+
hex = 'FF' + hex.padStart(6, '0');
|
|
60
59
|
}
|
|
61
|
-
assertedColor = parseInt(hex, 16)
|
|
62
|
-
return assertedColor
|
|
60
|
+
assertedColor = parseInt(hex, 16);
|
|
61
|
+
return assertedColor;
|
|
63
62
|
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
for (const key of Defaults_1.MEDIA_KEYS) {
|
|
63
|
+
};
|
|
64
|
+
export const prepareWAMessageMedia = async (message, options) => {
|
|
65
|
+
const logger = options.logger;
|
|
66
|
+
let mediaType;
|
|
67
|
+
for (const key of MEDIA_KEYS) {
|
|
71
68
|
if (key in message) {
|
|
72
|
-
mediaType = key
|
|
69
|
+
mediaType = key;
|
|
73
70
|
}
|
|
74
71
|
}
|
|
75
|
-
|
|
76
72
|
if (!mediaType) {
|
|
77
|
-
throw new
|
|
78
|
-
statusCode: 400
|
|
79
|
-
});
|
|
73
|
+
throw new Boom('Invalid media type', { statusCode: 400 });
|
|
80
74
|
}
|
|
81
|
-
|
|
82
75
|
const uploadData = {
|
|
83
76
|
...message,
|
|
84
77
|
media: message[mediaType]
|
|
85
|
-
}
|
|
86
|
-
delete uploadData[mediaType]
|
|
87
|
-
|
|
88
|
-
|
|
78
|
+
};
|
|
79
|
+
delete uploadData[mediaType];
|
|
80
|
+
// check if cacheable + generate cache key
|
|
81
|
+
const cacheableKey = typeof uploadData.media === 'object' &&
|
|
82
|
+
'url' in uploadData.media &&
|
|
83
|
+
!!uploadData.media.url &&
|
|
84
|
+
!!options.mediaCache &&
|
|
85
|
+
mediaType + ':' + uploadData.media.url.toString();
|
|
89
86
|
if (mediaType === 'document' && !uploadData.fileName) {
|
|
90
|
-
uploadData.fileName = 'file'
|
|
87
|
+
uploadData.fileName = 'file';
|
|
91
88
|
}
|
|
92
|
-
|
|
93
89
|
if (!uploadData.mimetype) {
|
|
94
|
-
uploadData.mimetype = MIMETYPE_MAP[mediaType]
|
|
90
|
+
uploadData.mimetype = MIMETYPE_MAP[mediaType];
|
|
95
91
|
}
|
|
96
|
-
|
|
97
92
|
if (cacheableKey) {
|
|
98
|
-
const mediaBuff = await options.mediaCache.get(cacheableKey)
|
|
93
|
+
const mediaBuff = await options.mediaCache.get(cacheableKey);
|
|
99
94
|
if (mediaBuff) {
|
|
100
|
-
logger?.debug({
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
Object.assign(obj[key], {
|
|
106
|
-
...uploadData,
|
|
107
|
-
media: undefined
|
|
108
|
-
})
|
|
109
|
-
return obj
|
|
95
|
+
logger?.debug({ cacheableKey }, 'got media cache hit');
|
|
96
|
+
const obj = proto.Message.decode(mediaBuff);
|
|
97
|
+
const key = `${mediaType}Message`;
|
|
98
|
+
Object.assign(obj[key], { ...uploadData, media: undefined });
|
|
99
|
+
return obj;
|
|
110
100
|
}
|
|
111
101
|
}
|
|
112
|
-
|
|
113
|
-
const isNewsletter = !!options.jid && WABinary_1.isJidNewsletter(options.jid)
|
|
114
|
-
|
|
102
|
+
const isNewsletter = !!options.jid && isJidNewsletter(options.jid);
|
|
115
103
|
if (isNewsletter) {
|
|
116
|
-
logger?.info({
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
104
|
+
logger?.info({ key: cacheableKey }, 'Preparing raw media for newsletter');
|
|
105
|
+
const { filePath, fileSha256, fileLength } = await getRawMediaUploadData(uploadData.media, options.mediaTypeOverride || mediaType, logger);
|
|
106
|
+
const fileSha256B64 = fileSha256.toString('base64');
|
|
107
|
+
// Generate thumbnail for image/video before uploading (filePath still exists)
|
|
108
|
+
if ((mediaType === 'image' || mediaType === 'video') && !uploadData.jpegThumbnail) {
|
|
109
|
+
try {
|
|
110
|
+
const { thumbnail, originalImageDimensions } = await generateThumbnail(filePath, mediaType, options);
|
|
111
|
+
uploadData.jpegThumbnail = thumbnail;
|
|
112
|
+
if (!uploadData.width && originalImageDimensions) {
|
|
113
|
+
uploadData.width = originalImageDimensions.width;
|
|
114
|
+
uploadData.height = originalImageDimensions.height;
|
|
115
|
+
}
|
|
116
|
+
} catch (e) {
|
|
117
|
+
logger?.warn({ err: e }, 'failed to generate thumbnail for newsletter media');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const { mediaUrl, directPath } = await options.upload(filePath, {
|
|
129
121
|
fileEncSha256B64: fileSha256B64,
|
|
130
122
|
mediaType: mediaType,
|
|
131
123
|
timeoutMs: options.mediaUploadTimeoutMs
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const obj = Types_1.WAProto.Message.fromObject({
|
|
124
|
+
});
|
|
125
|
+
await fs.unlink(filePath);
|
|
126
|
+
const obj = WAProto.Message.fromObject({
|
|
127
|
+
// todo: add more support here
|
|
137
128
|
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
|
138
129
|
url: mediaUrl,
|
|
139
130
|
directPath,
|
|
@@ -142,114 +133,83 @@ const prepareWAMessageMedia = async (message, options) => {
|
|
|
142
133
|
...uploadData,
|
|
143
134
|
media: undefined
|
|
144
135
|
})
|
|
145
|
-
})
|
|
146
|
-
|
|
136
|
+
});
|
|
147
137
|
if (uploadData.ptv) {
|
|
148
|
-
obj.ptvMessage = obj.videoMessage
|
|
149
|
-
delete obj.videoMessage
|
|
138
|
+
obj.ptvMessage = obj.videoMessage;
|
|
139
|
+
delete obj.videoMessage;
|
|
140
|
+
}
|
|
141
|
+
if (obj.stickerMessage) {
|
|
142
|
+
obj.stickerMessage.stickerSentTs = Date.now();
|
|
150
143
|
}
|
|
151
|
-
|
|
152
144
|
if (cacheableKey) {
|
|
153
|
-
logger?.debug({
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const
|
|
163
|
-
const
|
|
164
|
-
const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true && typeof uploadData.waveform === 'undefined'
|
|
165
|
-
const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true
|
|
166
|
-
const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation
|
|
167
|
-
|
|
168
|
-
const {
|
|
169
|
-
mediaKey,
|
|
170
|
-
encFilePath,
|
|
171
|
-
originalFilePath,
|
|
172
|
-
fileEncSha256,
|
|
173
|
-
fileSha256,
|
|
174
|
-
fileLength
|
|
175
|
-
} = await messages_media_1.encryptedStream(uploadData.media, options.mediaTypeOverride || mediaType, {
|
|
145
|
+
logger?.debug({ cacheableKey }, 'set cache');
|
|
146
|
+
await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
|
|
147
|
+
}
|
|
148
|
+
return obj;
|
|
149
|
+
}
|
|
150
|
+
const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined';
|
|
151
|
+
const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData['jpegThumbnail'] === 'undefined';
|
|
152
|
+
const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true && typeof uploadData.waveform === 'undefined';
|
|
153
|
+
const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true;
|
|
154
|
+
const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation;
|
|
155
|
+
const { mediaKey, encFilePath, originalFilePath, fileEncSha256, fileSha256, fileLength } = await encryptedStream(uploadData.media, options.mediaTypeOverride || mediaType, {
|
|
176
156
|
logger,
|
|
177
157
|
saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
|
|
178
158
|
opts: options.options
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
const [{
|
|
184
|
-
mediaUrl,
|
|
185
|
-
directPath
|
|
186
|
-
}] = await Promise.all([
|
|
159
|
+
});
|
|
160
|
+
const fileEncSha256B64 = fileEncSha256.toString('base64');
|
|
161
|
+
const [{ mediaUrl, directPath }] = await Promise.all([
|
|
187
162
|
(async () => {
|
|
188
163
|
const result = await options.upload(encFilePath, {
|
|
189
164
|
fileEncSha256B64,
|
|
190
165
|
mediaType,
|
|
191
166
|
timeoutMs: options.mediaUploadTimeoutMs
|
|
192
|
-
})
|
|
193
|
-
logger?.debug({
|
|
194
|
-
|
|
195
|
-
cacheableKey
|
|
196
|
-
}, 'uploaded media')
|
|
197
|
-
return result
|
|
167
|
+
});
|
|
168
|
+
logger?.debug({ mediaType, cacheableKey }, 'uploaded media');
|
|
169
|
+
return result;
|
|
198
170
|
})(),
|
|
199
|
-
|
|
200
171
|
(async () => {
|
|
201
172
|
try {
|
|
202
173
|
if (requiresThumbnailComputation) {
|
|
203
|
-
const {
|
|
204
|
-
|
|
205
|
-
originalImageDimensions
|
|
206
|
-
} = await messages_media_1.generateThumbnail(originalFilePath, mediaType, options)
|
|
207
|
-
uploadData.jpegThumbnail = thumbnail
|
|
174
|
+
const { thumbnail, originalImageDimensions } = await generateThumbnail(originalFilePath, mediaType, options);
|
|
175
|
+
uploadData.jpegThumbnail = thumbnail;
|
|
208
176
|
if (!uploadData.width && originalImageDimensions) {
|
|
209
|
-
uploadData.width = originalImageDimensions.width
|
|
210
|
-
uploadData.height = originalImageDimensions.height
|
|
211
|
-
|
|
212
|
-
logger?.debug('set dimensions')
|
|
177
|
+
uploadData.width = originalImageDimensions.width;
|
|
178
|
+
uploadData.height = originalImageDimensions.height;
|
|
179
|
+
logger?.debug('set dimensions');
|
|
213
180
|
}
|
|
214
|
-
|
|
215
181
|
logger?.debug('generated thumbnail');
|
|
216
182
|
}
|
|
217
|
-
|
|
218
183
|
if (requiresDurationComputation) {
|
|
219
|
-
uploadData.seconds = await
|
|
220
|
-
logger?.debug('computed audio duration')
|
|
184
|
+
uploadData.seconds = await getAudioDuration(originalFilePath);
|
|
185
|
+
logger?.debug('computed audio duration');
|
|
221
186
|
}
|
|
222
|
-
|
|
223
187
|
if (requiresWaveformProcessing) {
|
|
224
|
-
uploadData.waveform = await
|
|
225
|
-
logger?.debug('processed waveform')
|
|
188
|
+
uploadData.waveform = await getAudioWaveform(originalFilePath, logger);
|
|
189
|
+
logger?.debug('processed waveform');
|
|
226
190
|
}
|
|
227
|
-
|
|
228
191
|
if (requiresAudioBackground) {
|
|
229
|
-
uploadData.backgroundArgb = await assertColor(options.backgroundColor)
|
|
230
|
-
logger?.debug('computed backgroundColor audio status')
|
|
192
|
+
uploadData.backgroundArgb = await assertColor(options.backgroundColor);
|
|
193
|
+
logger?.debug('computed backgroundColor audio status');
|
|
231
194
|
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
}, 'failed to obtain extra info')
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
logger?.warn({ trace: error.stack }, 'failed to obtain extra info');
|
|
236
198
|
}
|
|
237
199
|
})()
|
|
238
200
|
]).finally(async () => {
|
|
239
201
|
try {
|
|
240
|
-
await
|
|
241
|
-
|
|
202
|
+
await fs.unlink(encFilePath);
|
|
242
203
|
if (originalFilePath) {
|
|
243
|
-
await
|
|
204
|
+
await fs.unlink(originalFilePath);
|
|
244
205
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
logger?.warn('failed to remove tmp file')
|
|
249
|
-
}
|
|
250
|
-
})
|
|
251
|
-
|
|
252
|
-
const obj = Types_1.WAProto.Message.fromObject({
|
|
206
|
+
logger?.debug('removed tmp files');
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
logger?.warn('failed to remove tmp file');
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
const obj = WAProto.Message.fromObject({
|
|
253
213
|
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
|
254
214
|
url: mediaUrl,
|
|
255
215
|
directPath,
|
|
@@ -257,850 +217,827 @@ const prepareWAMessageMedia = async (message, options) => {
|
|
|
257
217
|
fileEncSha256,
|
|
258
218
|
fileSha256,
|
|
259
219
|
fileLength,
|
|
260
|
-
mediaKeyTimestamp:
|
|
220
|
+
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
261
221
|
...uploadData,
|
|
262
222
|
media: undefined
|
|
263
223
|
})
|
|
264
|
-
})
|
|
265
|
-
|
|
224
|
+
});
|
|
266
225
|
if (uploadData.ptv) {
|
|
267
|
-
obj.ptvMessage = obj.videoMessage
|
|
268
|
-
delete obj.videoMessage
|
|
226
|
+
obj.ptvMessage = obj.videoMessage;
|
|
227
|
+
delete obj.videoMessage;
|
|
269
228
|
}
|
|
270
|
-
|
|
271
229
|
if (cacheableKey) {
|
|
272
|
-
logger?.debug({
|
|
273
|
-
|
|
274
|
-
}, 'set cache');
|
|
275
|
-
await options.mediaCache.set(cacheableKey, Types_1.WAProto.Message.encode(obj).finish())
|
|
230
|
+
logger?.debug({ cacheableKey }, 'set cache');
|
|
231
|
+
await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
|
|
276
232
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
const prepareAlbumMessageContent = async (jid, albums, options) => {
|
|
282
|
-
let mediaHandle
|
|
283
|
-
let mediaMsg
|
|
284
|
-
let message = []
|
|
285
|
-
|
|
286
|
-
const albumMsg = generateWAMessageFromContent(jid, {
|
|
287
|
-
albumMessage: {
|
|
288
|
-
expectedImageCount: albums.filter(item => 'image' in item).length,
|
|
289
|
-
expectedVideoCount: albums.filter(item => 'video' in item).length
|
|
290
|
-
}
|
|
291
|
-
}, options)
|
|
292
|
-
|
|
293
|
-
await options.sock.relayMessage(jid, albumMsg.message, {
|
|
294
|
-
messageId: albumMsg.key.id
|
|
295
|
-
})
|
|
296
|
-
|
|
297
|
-
for (const i in albums) {
|
|
298
|
-
const media = albums[i]
|
|
299
|
-
|
|
300
|
-
if ('image' in media) {
|
|
301
|
-
mediaMsg = await generateWAMessage(jid, {
|
|
302
|
-
image: media.image,
|
|
303
|
-
...media,
|
|
304
|
-
...options
|
|
305
|
-
}, {
|
|
306
|
-
userJid: options.userJid,
|
|
307
|
-
upload: async (encFilePath, opts) => {
|
|
308
|
-
const up = await options.sock.waUploadToServer(encFilePath, {
|
|
309
|
-
...opts,
|
|
310
|
-
newsletter: WABinary_1.isJidNewsletter(jid)
|
|
311
|
-
})
|
|
312
|
-
mediaHandle = up.handle
|
|
313
|
-
return up
|
|
314
|
-
},
|
|
315
|
-
...options
|
|
316
|
-
})
|
|
317
|
-
} else if ('video' in media) {
|
|
318
|
-
mediaMsg = await generateWAMessage(jid, {
|
|
319
|
-
video: media.video,
|
|
320
|
-
...media,
|
|
321
|
-
...options
|
|
322
|
-
}, {
|
|
323
|
-
userJid: options.userJid,
|
|
324
|
-
upload: async (encFilePath, opts) => {
|
|
325
|
-
const up = await options.sock.waUploadToServer(encFilePath, {
|
|
326
|
-
...opts,
|
|
327
|
-
newsletter: WABinary_1.isJidNewsletter(jid)
|
|
328
|
-
})
|
|
329
|
-
mediaHandle = up.handle
|
|
330
|
-
return up
|
|
331
|
-
},
|
|
332
|
-
...options
|
|
333
|
-
})
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
if (mediaMsg) {
|
|
337
|
-
mediaMsg.message.messageContextInfo = {
|
|
338
|
-
messageSecret: crypto_1.randomBytes(32),
|
|
339
|
-
messageAssociation: {
|
|
340
|
-
associationType: 1,
|
|
341
|
-
parentMessageKey: albumMsg.key
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
message.push(mediaMsg)
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
return message
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
const prepareDisappearingMessageSettingContent = (expiration) => {
|
|
233
|
+
return obj;
|
|
234
|
+
};
|
|
235
|
+
export const prepareDisappearingMessageSettingContent = (ephemeralExpiration) => {
|
|
236
|
+
ephemeralExpiration = ephemeralExpiration || 0;
|
|
353
237
|
const content = {
|
|
354
238
|
ephemeralMessage: {
|
|
355
239
|
message: {
|
|
356
240
|
protocolMessage: {
|
|
357
|
-
type:
|
|
358
|
-
ephemeralExpiration
|
|
241
|
+
type: WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
|
|
242
|
+
ephemeralExpiration
|
|
359
243
|
}
|
|
360
244
|
}
|
|
361
245
|
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
246
|
+
};
|
|
247
|
+
return WAProto.Message.fromObject(content);
|
|
248
|
+
};
|
|
249
|
+
/**
|
|
250
|
+
* Generate forwarded message content like WA does
|
|
251
|
+
* @param message the message to forward
|
|
252
|
+
* @param options.forceForward will show the message as forwarded even if it is from you
|
|
253
|
+
*/
|
|
254
|
+
export const generateForwardMessageContent = (message, forceForward) => {
|
|
255
|
+
let content = message.message;
|
|
369
256
|
if (!content) {
|
|
370
|
-
throw new
|
|
371
|
-
statusCode: 400
|
|
372
|
-
})
|
|
257
|
+
throw new Boom('no content in message', { statusCode: 400 });
|
|
373
258
|
}
|
|
374
|
-
|
|
375
|
-
content = normalizeMessageContent(content)
|
|
376
|
-
content =
|
|
377
|
-
|
|
378
|
-
let
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
score += message.key.fromMe && !forceForward ? 0 : 1
|
|
382
|
-
|
|
259
|
+
// hacky copy
|
|
260
|
+
content = normalizeMessageContent(content);
|
|
261
|
+
content = proto.Message.decode(proto.Message.encode(content).finish());
|
|
262
|
+
let key = Object.keys(content)[0];
|
|
263
|
+
let score = content?.[key]?.contextInfo?.forwardingScore || 0;
|
|
264
|
+
score += message.key.fromMe && !forceForward ? 0 : 1;
|
|
383
265
|
if (key === 'conversation') {
|
|
384
|
-
content.extendedTextMessage = {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
delete content.conversation
|
|
388
|
-
key = 'extendedTextMessage'
|
|
266
|
+
content.extendedTextMessage = { text: content[key] };
|
|
267
|
+
delete content.conversation;
|
|
268
|
+
key = 'extendedTextMessage';
|
|
389
269
|
}
|
|
390
|
-
|
|
270
|
+
const key_ = content?.[key];
|
|
391
271
|
if (score > 0) {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
272
|
+
key_.contextInfo = { forwardingScore: score, isForwarded: true };
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
key_.contextInfo = {};
|
|
276
|
+
}
|
|
277
|
+
return content;
|
|
278
|
+
};
|
|
279
|
+
export const hasNonNullishProperty = (message, key) => {
|
|
280
|
+
return message != null &&
|
|
281
|
+
typeof message === 'object' &&
|
|
282
|
+
key in message &&
|
|
283
|
+
message[key] != null;
|
|
284
|
+
};
|
|
285
|
+
export const hasOptionalProperty = (obj, key) => {
|
|
286
|
+
return obj != null &&
|
|
287
|
+
typeof obj === 'object' &&
|
|
288
|
+
key in obj &&
|
|
289
|
+
obj[key] != null;
|
|
290
|
+
};
|
|
291
|
+
// Validate album message media to avoid bug
|
|
292
|
+
export const hasValidAlbumMedia = (message) => {
|
|
293
|
+
return message.imageMessage ||
|
|
294
|
+
message.videoMessage;
|
|
295
|
+
};
|
|
296
|
+
export const hasValidInteractiveHeader = (message) => {
|
|
297
|
+
return message.imageMessage ||
|
|
298
|
+
message.videoMessage ||
|
|
299
|
+
message.documentMessage ||
|
|
300
|
+
message.productMessage ||
|
|
301
|
+
message.locationMessage;
|
|
302
|
+
};
|
|
303
|
+
// Validate carousel cards header to avoid bug
|
|
304
|
+
export const hasValidCarouselHeader = (message) => {
|
|
305
|
+
return message.imageMessage ||
|
|
306
|
+
message.videoMessage ||
|
|
307
|
+
message.productMessage;
|
|
308
|
+
};
|
|
309
|
+
// Extract product message into a standalone function so it can also be reused as the header for interactive messages
|
|
310
|
+
const prepareProductMessage = async (message, options) => {
|
|
311
|
+
if (!message.businessOwnerJid) {
|
|
312
|
+
throw new Boom('"businessOwnerJid" is missing from the content', { statusCode: 400 });
|
|
313
|
+
}
|
|
314
|
+
const { imageMessage } = await prepareWAMessageMedia({ image: message.image || message.product.productImage }, options);
|
|
315
|
+
const content = {
|
|
316
|
+
...message,
|
|
317
|
+
product: {
|
|
318
|
+
currencyCode: 'IDR',
|
|
319
|
+
priceAmount1000: 1000,
|
|
320
|
+
title: LIBRARY_NAME,
|
|
321
|
+
...message.product,
|
|
322
|
+
productImage: imageMessage
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
delete content.image;
|
|
326
|
+
return content;
|
|
327
|
+
};
|
|
328
|
+
const prepareStickerPackMessage = async (message, options) => {
|
|
329
|
+
const { cover, stickers = [], name = '📦 Sticker Pack', publisher = 'GitHub: pontalabs', description = '🏷️ pontalabs/baileys' } = message;
|
|
330
|
+
if (stickers.length > 60) {
|
|
331
|
+
throw new Boom('Sticker pack exceeds the maximum limit of 60 stickers', { statusCode: 400 });
|
|
332
|
+
}
|
|
333
|
+
if (stickers.length === 0) {
|
|
334
|
+
throw new Boom('Sticker pack must contain at least one sticker', { statusCode: 400 });
|
|
335
|
+
}
|
|
336
|
+
if (!cover) {
|
|
337
|
+
throw new Boom('Sticker pack must contain a cover', { statusCode: 400 });
|
|
338
|
+
}
|
|
339
|
+
const logger = options.logger;
|
|
340
|
+
let cacheableKey = false;
|
|
341
|
+
if (Array.isArray(stickers) && stickers.length && options.mediaCache) {
|
|
342
|
+
const urls = [];
|
|
343
|
+
for (let i = 0; i < stickers.length; i++) {
|
|
344
|
+
const data = stickers[i].data;
|
|
345
|
+
if (typeof data === 'object' && data?.url) {
|
|
346
|
+
urls.push(data.url);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (urls.length > 0) {
|
|
350
|
+
cacheableKey = 'sticker:' + urls.join('@');
|
|
395
351
|
}
|
|
396
|
-
} else {
|
|
397
|
-
content[key].contextInfo = {}
|
|
398
352
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
353
|
+
if (cacheableKey) {
|
|
354
|
+
const mediaBuff = await options.mediaCache.get(cacheableKey);
|
|
355
|
+
if (mediaBuff) {
|
|
356
|
+
logger?.debug({ cacheableKey }, 'got media cache hit');
|
|
357
|
+
return proto.Message.StickerPackMessage.decode(mediaBuff);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const lib = await getImageProcessingLibrary();
|
|
361
|
+
const hasSharp = 'sharp' in lib && !!lib.sharp?.default;
|
|
362
|
+
const hasImage = 'image' in lib && !!lib.image?.Transformer;
|
|
363
|
+
const hasJimp = 'jimp' in lib && !!lib.jimp?.Jimp;
|
|
364
|
+
if (!hasSharp && !hasImage) {
|
|
365
|
+
throw new Boom('No image processing library (sharp or @napi-rs/image) available for converting sticker to WebP.');
|
|
366
|
+
}
|
|
367
|
+
const stickerPackIdValue = generateMessageIDV2();
|
|
368
|
+
const stickerData = {};
|
|
369
|
+
const stickerMetadata = new Array(stickers.length);
|
|
370
|
+
for (let i = 0; i < stickers.length; i += CONCURRENCY_LIMIT) {
|
|
371
|
+
const promises = [];
|
|
372
|
+
const chunkEnd = Math.min(i + CONCURRENCY_LIMIT, stickers.length);
|
|
373
|
+
for (let j = i; j < chunkEnd; j++) {
|
|
374
|
+
promises.push((async (index) => {
|
|
375
|
+
const sticker = stickers[index];
|
|
376
|
+
const { stream } = await getStream(sticker.data);
|
|
377
|
+
const buffer = await toBuffer(stream);
|
|
378
|
+
let webpBuffer;
|
|
379
|
+
let isAnimated = false;
|
|
380
|
+
if (isWebPBuffer(buffer)) {
|
|
381
|
+
webpBuffer = buffer;
|
|
382
|
+
isAnimated = isAnimatedWebP(buffer);
|
|
383
|
+
}
|
|
384
|
+
else if (hasSharp) {
|
|
385
|
+
webpBuffer = await lib.sharp.default(buffer)
|
|
386
|
+
.resize(512, 512, { fit: 'inside' })
|
|
387
|
+
.webp({ quality: 80 })
|
|
388
|
+
.toBuffer();
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
webpBuffer = await new lib.image.Transformer(buffer)
|
|
392
|
+
.resize(512, 512)
|
|
393
|
+
.webp(80);
|
|
394
|
+
}
|
|
395
|
+
if (webpBuffer.length > 1024 * 1024) {
|
|
396
|
+
throw new Boom(`Sticker at index ${index} exceeds the 1MB size limit`, { statusCode: 400 });
|
|
397
|
+
}
|
|
398
|
+
const hash = sha256(webpBuffer).toString('base64').replace(/\//g, '-');
|
|
399
|
+
const fileName = `${hash}.webp`;
|
|
400
|
+
stickerData[fileName] = [new Uint8Array(webpBuffer), { level: 0 }];
|
|
401
|
+
stickerMetadata[index] = {
|
|
402
|
+
fileName,
|
|
403
|
+
mimetype: 'image/webp',
|
|
404
|
+
isAnimated,
|
|
405
|
+
emojis: sticker.emojis || ['✨'],
|
|
406
|
+
accessibilityLabel: sticker.accessibilityLabel || ''
|
|
407
|
+
};
|
|
408
|
+
})(j));
|
|
409
|
+
}
|
|
410
|
+
await Promise.all(promises);
|
|
411
|
+
}
|
|
412
|
+
const trayIconFileName = `${stickerPackIdValue}.webp`;
|
|
413
|
+
const { stream: coverStream } = await getStream(cover);
|
|
414
|
+
const coverBuffer = await toBuffer(coverStream);
|
|
415
|
+
let coverWebpBuffer;
|
|
416
|
+
if (isWebPBuffer(coverBuffer)) {
|
|
417
|
+
coverWebpBuffer = coverBuffer;
|
|
418
|
+
}
|
|
419
|
+
else if (hasSharp) {
|
|
420
|
+
coverWebpBuffer = await lib.sharp.default(coverBuffer)
|
|
421
|
+
.resize(512, 512, { fit: 'inside' })
|
|
422
|
+
.webp({ quality: 80 })
|
|
423
|
+
.toBuffer();
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
coverWebpBuffer = await new lib.image.Transformer(coverBuffer)
|
|
427
|
+
.resize(512, 512)
|
|
428
|
+
.webp(80);
|
|
429
|
+
}
|
|
430
|
+
stickerData[trayIconFileName] = [new Uint8Array(coverWebpBuffer), { level: 0 }];
|
|
431
|
+
const zipBuffer = await new Promise((resolve, reject) => {
|
|
432
|
+
zip(stickerData, (error, data) => error ? reject(error) : resolve(Buffer.from(data)));
|
|
433
|
+
});
|
|
434
|
+
const stickerPackUpload = await encryptedStream(zipBuffer, 'sticker-pack', {
|
|
435
|
+
logger,
|
|
436
|
+
opts: options.options
|
|
437
|
+
});
|
|
438
|
+
let stickerPackUploadResult;
|
|
439
|
+
try {
|
|
440
|
+
stickerPackUploadResult = await options.upload(stickerPackUpload.encFilePath, {
|
|
441
|
+
fileEncSha256B64: stickerPackUpload.fileEncSha256.toString('base64'),
|
|
442
|
+
mediaType: 'sticker-pack',
|
|
443
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
finally {
|
|
447
|
+
fs.unlink(stickerPackUpload.encFilePath).catch(() => logger?.warn('failed to remove tmp file'));
|
|
448
|
+
}
|
|
449
|
+
const obj = {
|
|
450
|
+
name,
|
|
451
|
+
publisher,
|
|
452
|
+
stickerPackId: stickerPackIdValue,
|
|
453
|
+
packDescription: description,
|
|
454
|
+
stickerPackOrigin: proto.Message.StickerPackMessage.StickerPackOrigin.USER_CREATED,
|
|
455
|
+
stickerPackSize: zipBuffer.length,
|
|
456
|
+
stickers: stickerMetadata,
|
|
457
|
+
fileSha256: stickerPackUpload.fileSha256,
|
|
458
|
+
fileEncSha256: stickerPackUpload.fileEncSha256,
|
|
459
|
+
mediaKey: stickerPackUpload.mediaKey,
|
|
460
|
+
directPath: stickerPackUploadResult.directPath,
|
|
461
|
+
fileLength: stickerPackUpload.fileLength,
|
|
462
|
+
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
463
|
+
trayIconFileName
|
|
464
|
+
};
|
|
465
|
+
try {
|
|
466
|
+
let thumbnailBuffer;
|
|
467
|
+
if (hasSharp) {
|
|
468
|
+
thumbnailBuffer = await lib.sharp.default(coverBuffer).resize(252, 252).jpeg().toBuffer();
|
|
469
|
+
}
|
|
470
|
+
else if (hasImage) {
|
|
471
|
+
thumbnailBuffer = await new lib.image.Transformer(coverBuffer).resize(252, 252).jpeg();
|
|
472
|
+
}
|
|
473
|
+
else if (hasJimp) {
|
|
474
|
+
const jimpImage = await lib.jimp.Jimp.read(coverBuffer);
|
|
475
|
+
thumbnailBuffer = await jimpImage.resize({ w: 252, h: 252 }).getBuffer('image/jpeg');
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
throw new Error('No image processing library available for thumbnail generation');
|
|
479
|
+
}
|
|
480
|
+
if (!thumbnailBuffer || thumbnailBuffer.length === 0) {
|
|
481
|
+
throw new Error('Failed to generate thumbnail buffer');
|
|
482
|
+
}
|
|
483
|
+
const thumbUpload = await encryptedStream(thumbnailBuffer, 'thumbnail-sticker-pack', {
|
|
484
|
+
logger,
|
|
485
|
+
opts: options.options,
|
|
486
|
+
mediaKey: stickerPackUpload.mediaKey
|
|
487
|
+
});
|
|
488
|
+
let thumbUploadResult;
|
|
489
|
+
try {
|
|
490
|
+
thumbUploadResult = await options.upload(thumbUpload.encFilePath, {
|
|
491
|
+
fileEncSha256B64: thumbUpload.fileEncSha256.toString('base64'),
|
|
492
|
+
mediaType: 'thumbnail-sticker-pack',
|
|
493
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
finally {
|
|
497
|
+
fs.unlink(thumbUpload.encFilePath).catch(() => logger?.warn('failed to remove tmp file'));
|
|
498
|
+
}
|
|
499
|
+
Object.assign(obj, {
|
|
500
|
+
thumbnailDirectPath: thumbUploadResult.directPath,
|
|
501
|
+
thumbnailSha256: thumbUpload.fileSha256,
|
|
502
|
+
thumbnailEncSha256: thumbUpload.fileEncSha256,
|
|
503
|
+
thumbnailHeight: 252,
|
|
504
|
+
thumbnailWidth: 252,
|
|
505
|
+
imageDataHash: sha256(thumbnailBuffer).toString('base64')
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
catch (error) {
|
|
509
|
+
logger?.warn(`Thumbnail generation failed: ${error}`);
|
|
510
|
+
}
|
|
511
|
+
if (cacheableKey) {
|
|
512
|
+
logger?.debug({ cacheableKey }, 'set cache (background)');
|
|
513
|
+
options.mediaCache.set(cacheableKey, WAProto.Message.StickerPackMessage.encode(obj).finish());
|
|
514
|
+
}
|
|
515
|
+
return WAProto.Message.StickerPackMessage.fromObject(obj);
|
|
516
|
+
};
|
|
517
|
+
const prepareNativeFlowButtons = (message) => {
|
|
518
|
+
const buttons = message.nativeFlow;
|
|
519
|
+
const isButtonsFieldArray = Array.isArray(buttons);
|
|
520
|
+
const correctedField = isButtonsFieldArray ? buttons : buttons.buttons;
|
|
521
|
+
const messageParamsJson = {};
|
|
522
|
+
if (hasOptionalProperty(message, 'offerText') && !!message.offerText) {
|
|
523
|
+
Object.assign(messageParamsJson, {
|
|
524
|
+
limited_time_offer: {
|
|
525
|
+
text: message.offerText || LIBRARY_NAME,
|
|
526
|
+
url: (message.offerUrl && /^https?:\/\//i.test(message.offerUrl)) ? message.offerUrl : DONATE_URL,
|
|
527
|
+
copy_code: message.offerCode,
|
|
528
|
+
expiration_time: message.offerExpiration
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
if (hasOptionalProperty(message, 'optionText') && !!message.optionText) {
|
|
533
|
+
Object.assign(messageParamsJson, {
|
|
534
|
+
bottom_sheet: {
|
|
535
|
+
in_thread_buttons_limit: 1,
|
|
536
|
+
divider_indices: Array.from({ length: correctedField.length }, (_, index) => index),
|
|
537
|
+
list_title: message.optionTitle || '📄 Select Options',
|
|
538
|
+
button_title: message.optionText
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
return {
|
|
543
|
+
buttons: correctedField.map(button => {
|
|
544
|
+
const buttonText = button.text || button.buttonText;
|
|
545
|
+
const buttonIcon = button.icon?.toUpperCase();
|
|
546
|
+
if (hasOptionalProperty(button, 'id') && !!button.id) {
|
|
547
|
+
return { name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: buttonText || '👉🏻 Click', id: button.id, icon: buttonIcon }) };
|
|
548
|
+
}
|
|
549
|
+
else if (hasOptionalProperty(button, 'copy') && !!button.copy) {
|
|
550
|
+
return { name: 'cta_copy', buttonParamsJson: JSON.stringify({ display_text: buttonText || '📋 Copy', copy_code: button.copy, icon: buttonIcon }) };
|
|
551
|
+
}
|
|
552
|
+
else if (hasOptionalProperty(button, 'url') && !!button.url) {
|
|
553
|
+
return { name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: buttonText || '🌐 Visit', url: button.url, merchant_url: button.url, webview_interaction: button.useWebview, icon: buttonIcon }) };
|
|
554
|
+
}
|
|
555
|
+
else if (hasOptionalProperty(button, 'call') && !!button.call) {
|
|
556
|
+
return { name: 'cta_call', buttonParamsJson: JSON.stringify({ display_text: buttonText || '📞 Call', phone_number: button.call, icon: buttonIcon }) };
|
|
557
|
+
}
|
|
558
|
+
else if (hasOptionalProperty(button, 'sections') && !!button.sections) {
|
|
559
|
+
return { name: 'single_select', buttonParamsJson: JSON.stringify({ title: buttonText || '📋 Select', sections: button.sections, icon: buttonIcon }) };
|
|
560
|
+
}
|
|
561
|
+
return button;
|
|
562
|
+
}),
|
|
563
|
+
messageParamsJson: JSON.stringify(messageParamsJson),
|
|
564
|
+
messageVersion: 3
|
|
565
|
+
};
|
|
566
|
+
};
|
|
567
|
+
export const generateWAMessageContent = async (message, options) => {
|
|
568
|
+
var _a, _b;
|
|
569
|
+
let m = {};
|
|
570
|
+
if (hasNonNullishProperty(message, 'raw')) {
|
|
571
|
+
delete message.raw;
|
|
572
|
+
return message;
|
|
573
|
+
}
|
|
574
|
+
else if (hasNonNullishProperty(message, 'code') ||
|
|
575
|
+
hasNonNullishProperty(message, 'links') ||
|
|
576
|
+
hasNonNullishProperty(message, 'table') ||
|
|
577
|
+
hasNonNullishProperty(message, 'aiBlocks') ||
|
|
578
|
+
hasNonNullishProperty(message, 'latex') ||
|
|
579
|
+
hasNonNullishProperty(message, 'gridImage') ||
|
|
580
|
+
hasNonNullishProperty(message, 'inlineImage') ||
|
|
581
|
+
hasNonNullishProperty(message, 'dynamic') ||
|
|
582
|
+
hasNonNullishProperty(message, 'map') ||
|
|
583
|
+
hasNonNullishProperty(message, 'contentItems') ||
|
|
584
|
+
// tipe baru (nama prefixed 'rich' agar tidak konflik dengan media biasa)
|
|
585
|
+
hasNonNullishProperty(message, 'richImage') ||
|
|
586
|
+
hasNonNullishProperty(message, 'richVideo') ||
|
|
587
|
+
hasNonNullishProperty(message, 'reels') ||
|
|
588
|
+
hasNonNullishProperty(message, 'source') ||
|
|
589
|
+
hasNonNullishProperty(message, 'richProduct') ||
|
|
590
|
+
hasNonNullishProperty(message, 'richPost') ||
|
|
591
|
+
hasNonNullishProperty(message, 'tip') ||
|
|
592
|
+
hasNonNullishProperty(message, 'suggest')) {
|
|
593
|
+
m = prepareRichResponseMessage(message);
|
|
594
|
+
}
|
|
595
|
+
else if (hasNonNullishProperty(message, 'text')) {
|
|
596
|
+
const extContent = { text: message.text };
|
|
597
|
+
let urlInfo = message.linkPreview;
|
|
413
598
|
if (typeof urlInfo === 'undefined') {
|
|
414
|
-
urlInfo = await generateLinkPreviewIfRequired(message.text, options.getUrlInfo, options.logger)
|
|
599
|
+
urlInfo = await generateLinkPreviewIfRequired(message.text, options.getUrlInfo, options.logger);
|
|
415
600
|
}
|
|
416
|
-
|
|
417
601
|
if (urlInfo) {
|
|
418
|
-
extContent.
|
|
419
|
-
extContent.
|
|
420
|
-
extContent.
|
|
421
|
-
extContent.
|
|
422
|
-
extContent.
|
|
423
|
-
|
|
424
|
-
const img = urlInfo.highQualityThumbnail
|
|
425
|
-
|
|
602
|
+
extContent.matchedText = urlInfo['matched-text'];
|
|
603
|
+
extContent.jpegThumbnail = urlInfo.jpegThumbnail;
|
|
604
|
+
extContent.description = urlInfo.description;
|
|
605
|
+
extContent.title = urlInfo.title;
|
|
606
|
+
extContent.previewType = 0;
|
|
607
|
+
const img = urlInfo.highQualityThumbnail;
|
|
426
608
|
if (img) {
|
|
427
|
-
extContent.thumbnailDirectPath = img.directPath
|
|
428
|
-
extContent.mediaKey = img.mediaKey
|
|
429
|
-
extContent.mediaKeyTimestamp = img.mediaKeyTimestamp
|
|
430
|
-
extContent.thumbnailWidth = img.width
|
|
431
|
-
extContent.thumbnailHeight = img.height
|
|
432
|
-
extContent.thumbnailSha256 = img.fileSha256
|
|
433
|
-
extContent.thumbnailEncSha256 = img.fileEncSha256
|
|
609
|
+
extContent.thumbnailDirectPath = img.directPath;
|
|
610
|
+
extContent.mediaKey = img.mediaKey;
|
|
611
|
+
extContent.mediaKeyTimestamp = img.mediaKeyTimestamp;
|
|
612
|
+
extContent.thumbnailWidth = img.width;
|
|
613
|
+
extContent.thumbnailHeight = img.height;
|
|
614
|
+
extContent.thumbnailSha256 = img.fileSha256;
|
|
615
|
+
extContent.thumbnailEncSha256 = img.fileEncSha256;
|
|
434
616
|
}
|
|
435
617
|
}
|
|
436
|
-
|
|
437
618
|
if (options.backgroundColor) {
|
|
438
|
-
extContent.backgroundArgb = await assertColor(options.backgroundColor)
|
|
619
|
+
extContent.backgroundArgb = await assertColor(options.backgroundColor);
|
|
439
620
|
}
|
|
440
|
-
|
|
441
|
-
if (options.textColor) {
|
|
442
|
-
extContent.textArgb = await assertColor(options.textColor)
|
|
443
|
-
}
|
|
444
|
-
|
|
445
621
|
if (options.font) {
|
|
446
|
-
extContent.font = options.font
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
mentionedJid: message.mentions
|
|
453
|
-
} : {})
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
m.extendedTextMessage = extContent
|
|
457
|
-
} else if ('contacts' in message) {
|
|
458
|
-
const contactLen = message.contacts.contacts.length
|
|
459
|
-
|
|
460
|
-
let contactMessage
|
|
461
|
-
|
|
622
|
+
extContent.font = options.font;
|
|
623
|
+
}
|
|
624
|
+
m.extendedTextMessage = extContent;
|
|
625
|
+
}
|
|
626
|
+
else if (hasNonNullishProperty(message, 'contacts')) {
|
|
627
|
+
const contactLen = message.contacts.contacts.length;
|
|
462
628
|
if (!contactLen) {
|
|
463
|
-
throw new
|
|
464
|
-
statusCode: 400
|
|
465
|
-
})
|
|
629
|
+
throw new Boom('require atleast 1 contact', { statusCode: 400 });
|
|
466
630
|
}
|
|
467
|
-
|
|
468
631
|
if (contactLen === 1) {
|
|
469
|
-
contactMessage =
|
|
470
|
-
contactMessage: Types_1.WAProto.Message.ContactMessage.fromObject(message.contacts.contacts[0])
|
|
471
|
-
}
|
|
472
|
-
} else {
|
|
473
|
-
contactMessage = {
|
|
474
|
-
contactsArrayMessage: Types_1.WAProto.Message.ContactsArrayMessage.fromObject(message.contacts)
|
|
475
|
-
}
|
|
632
|
+
m.contactMessage = WAProto.Message.ContactMessage.create(message.contacts.contacts[0]);
|
|
476
633
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
contactMessage[type].contextInfo = {
|
|
481
|
-
...(message.contextInfo || {}),
|
|
482
|
-
...(message.mentions ? {
|
|
483
|
-
mentionedJid: message.mentions
|
|
484
|
-
} : {})
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
m = contactMessage
|
|
488
|
-
} else if ('location' in message) {
|
|
489
|
-
let locationMessage
|
|
490
|
-
|
|
491
|
-
if (message.live) {
|
|
492
|
-
locationMessage = {
|
|
493
|
-
liveLocationMessage: Types_1.WAProto.Message.LiveLocationMessage.fromObject(message.location)
|
|
494
|
-
}
|
|
495
|
-
} else {
|
|
496
|
-
locationMessage = {
|
|
497
|
-
locationMessage: Types_1.WAProto.Message.LocationMessage.fromObject(message.location)
|
|
498
|
-
}
|
|
634
|
+
else {
|
|
635
|
+
m.contactsArrayMessage = WAProto.Message.ContactsArrayMessage.create(message.contacts);
|
|
499
636
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
...(message.mentions ? {
|
|
506
|
-
mentionedJid: message.mentions
|
|
507
|
-
} : {})
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
m = locationMessage
|
|
511
|
-
} else if ('react' in message) {
|
|
637
|
+
}
|
|
638
|
+
else if (hasNonNullishProperty(message, 'location')) {
|
|
639
|
+
m.locationMessage = WAProto.Message.LocationMessage.create(message.location);
|
|
640
|
+
}
|
|
641
|
+
else if (hasNonNullishProperty(message, 'react')) {
|
|
512
642
|
if (!message.react.senderTimestampMs) {
|
|
513
|
-
message.react.senderTimestampMs = Date.now()
|
|
643
|
+
message.react.senderTimestampMs = Date.now();
|
|
514
644
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
645
|
+
m.reactionMessage = WAProto.Message.ReactionMessage.create(message.react);
|
|
646
|
+
}
|
|
647
|
+
else if (hasNonNullishProperty(message, 'delete')) {
|
|
518
648
|
m.protocolMessage = {
|
|
519
649
|
key: message.delete,
|
|
520
|
-
type:
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
m =
|
|
537
|
-
|
|
538
|
-
m.groupInviteMessage =
|
|
539
|
-
|
|
540
|
-
m.groupInviteMessage.
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
m.groupInviteMessage.groupJid = message.groupInvite.jid
|
|
544
|
-
m.groupInviteMessage.groupName = message.groupInvite.name
|
|
545
|
-
m.groupInviteMessage.contextInfo = message.contextInfo
|
|
546
|
-
|
|
547
|
-
if (options.getProfilePicUrl) {
|
|
548
|
-
const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid)
|
|
549
|
-
const { thumbnail } = await messages_media_1.generateThumbnail(pfpUrl, 'image')
|
|
550
|
-
m.groupInviteMessage.jpegThumbnail = thumbnail
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
m.groupInviteMessage.contextInfo = {
|
|
554
|
-
...(message.contextInfo || {}),
|
|
555
|
-
...(message.mentions ? {
|
|
556
|
-
mentionedJid: message.mentions
|
|
557
|
-
} : {})
|
|
558
|
-
}
|
|
559
|
-
} else if ('adminInvite' in message) {
|
|
560
|
-
m.newsletterAdminInviteMessage = {}
|
|
561
|
-
|
|
562
|
-
m.newsletterAdminInviteMessage.newsletterJid = message.adminInvite.jid
|
|
563
|
-
m.newsletterAdminInviteMessage.newsletterName = message.adminInvite.name
|
|
564
|
-
m.newsletterAdminInviteMessage.caption = message.adminInvite.caption
|
|
565
|
-
m.newsletterAdminInviteMessage.inviteExpiration = message.adminInvite.expiration
|
|
566
|
-
m.newsletterAdminInviteMessage.contextInfo = message.contextInfo
|
|
567
|
-
|
|
650
|
+
type: WAProto.Message.ProtocolMessage.Type.REVOKE
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
else if (hasNonNullishProperty(message, 'forward')) {
|
|
654
|
+
m = generateForwardMessageContent(message.forward, message.force);
|
|
655
|
+
}
|
|
656
|
+
else if (hasNonNullishProperty(message, 'disappearingMessagesInChat')) {
|
|
657
|
+
const exp = typeof message.disappearingMessagesInChat === 'boolean'
|
|
658
|
+
? message.disappearingMessagesInChat
|
|
659
|
+
? WA_DEFAULT_EPHEMERAL
|
|
660
|
+
: 0
|
|
661
|
+
: message.disappearingMessagesInChat;
|
|
662
|
+
m = prepareDisappearingMessageSettingContent(exp);
|
|
663
|
+
}
|
|
664
|
+
else if (hasNonNullishProperty(message, 'groupInvite')) {
|
|
665
|
+
m.groupInviteMessage = {};
|
|
666
|
+
m.groupInviteMessage.inviteCode = message.groupInvite.inviteCode;
|
|
667
|
+
m.groupInviteMessage.inviteExpiration = message.groupInvite.inviteExpiration;
|
|
668
|
+
m.groupInviteMessage.caption = message.groupInvite.text;
|
|
669
|
+
m.groupInviteMessage.groupJid = message.groupInvite.jid;
|
|
670
|
+
m.groupInviteMessage.groupName = message.groupInvite.subject;
|
|
671
|
+
//TODO: use built-in interface and get disappearing mode info etc.
|
|
672
|
+
//TODO: cache / use store!?
|
|
568
673
|
if (options.getProfilePicUrl) {
|
|
569
|
-
const pfpUrl = await options.getProfilePicUrl(message.
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
m.newsletterAdminInviteMessage.contextInfo = {
|
|
577
|
-
...(message.contextInfo || {}),
|
|
578
|
-
...(message.mentions ? {
|
|
579
|
-
mentionedJid: message.mentions
|
|
580
|
-
} : {})
|
|
581
|
-
}
|
|
582
|
-
} else if ('pin' in message) {
|
|
583
|
-
m.pinInChatMessage = {}
|
|
584
|
-
m.messageContextInfo = {}
|
|
585
|
-
|
|
586
|
-
m.pinInChatMessage.key = message.pin.key
|
|
587
|
-
m.pinInChatMessage.type = message.pin?.type || 1
|
|
588
|
-
m.pinInChatMessage.senderTimestampMs = message.pin?.time || Date.now()
|
|
589
|
-
m.messageContextInfo.messageAddOnDurationInSecs = message.pin.type === 1 ? message.pin.time || 86400 : 0
|
|
590
|
-
m.messageContextInfo.messageAddOnExpiryType = WAProto_1.proto.MessageContextInfo.MessageAddonExpiryType.STATIC
|
|
591
|
-
} else if ('keep' in message) {
|
|
592
|
-
m.keepInChatMessage = {}
|
|
593
|
-
|
|
594
|
-
m.keepInChatMessage.key = message.keep.key
|
|
595
|
-
m.keepInChatMessage.keepType = message.keep?.type || 1
|
|
596
|
-
m.keepInChatMessage.timestampMs = message.keep?.time || Date.now()
|
|
597
|
-
} else if ('call' in message) {
|
|
598
|
-
m.scheduledCallCreationMessage = {}
|
|
599
|
-
|
|
600
|
-
m.scheduledCallCreationMessage.scheduledTimestampMs = message.call?.time || Date.now()
|
|
601
|
-
m.scheduledCallCreationMessage.callType = message.call?.type || 1
|
|
602
|
-
m.scheduledCallCreationMessage.title = message.call?.name || 'Call Creation'
|
|
603
|
-
|
|
604
|
-
m.scheduledCallCreationMessage.contextInfo = {
|
|
605
|
-
...(message.contextInfo || {}),
|
|
606
|
-
...(message.mentions ? {
|
|
607
|
-
mentionedJid: message.mentions
|
|
608
|
-
} : {})
|
|
609
|
-
}
|
|
610
|
-
} else if ('paymentInvite' in message) {
|
|
611
|
-
m.messageContextInfo = {}
|
|
612
|
-
m.paymentInviteMessage = {}
|
|
613
|
-
|
|
614
|
-
m.paymentInviteMessage.expiryTimestamp = message.paymentInvite?.expiry || 0
|
|
615
|
-
m.paymentInviteMessage.serviceType = message.paymentInvite?.type || 2
|
|
616
|
-
|
|
617
|
-
m.paymentInviteMessage.contextInfo = {
|
|
618
|
-
...(message.contextInfo || {}),
|
|
619
|
-
...(message.mentions ? {
|
|
620
|
-
mentionedJid: message.mentions
|
|
621
|
-
} : {})
|
|
622
|
-
}
|
|
623
|
-
} else if ('buttonLocation' in message) {
|
|
624
|
-
const bl = message.buttonLocation
|
|
625
|
-
const thumbInput = bl.jpegThumbnail || bl.thumbnail
|
|
626
|
-
if (!thumbInput) throw new Error('buttonLocation requires jpegThumbnail or thumbnail')
|
|
627
|
-
let thumb
|
|
628
|
-
if (Buffer.isBuffer(thumbInput)) {
|
|
629
|
-
thumb = thumbInput
|
|
630
|
-
} else if (typeof thumbInput === 'string' && (thumbInput.startsWith('http://') || thumbInput.startsWith('https://'))) {
|
|
631
|
-
const { stream } = await messages_media_1.getStream(thumbInput)
|
|
632
|
-
thumb = await messages_media_1.toBuffer(stream)
|
|
633
|
-
} else if (typeof thumbInput === 'string') {
|
|
634
|
-
thumb = await fs_1.promises.readFile(thumbInput)
|
|
635
|
-
} else {
|
|
636
|
-
throw new Error('buttonLocation thumbnail must be Buffer, URL, or file path')
|
|
637
|
-
}
|
|
638
|
-
let listButton = []
|
|
639
|
-
if (bl.listMenu && bl.listMenu.length) {
|
|
640
|
-
const nativeParams = JSON.stringify({
|
|
641
|
-
title: bl.listButtonText || 'Menu',
|
|
642
|
-
sections: [{
|
|
643
|
-
title: bl.listSectionTitle || 'Pilihan',
|
|
644
|
-
highlight_label: '',
|
|
645
|
-
rows: bl.listMenu.map(row => ({
|
|
646
|
-
header: row.header || '',
|
|
647
|
-
title: row.title || '',
|
|
648
|
-
description: row.description || '',
|
|
649
|
-
id: row.id || ''
|
|
650
|
-
}))
|
|
651
|
-
}]
|
|
652
|
-
})
|
|
653
|
-
listButton.push({
|
|
654
|
-
buttonId: 'list_menu',
|
|
655
|
-
buttonText: { displayText: bl.listButtonText || 'Pilih Menu' },
|
|
656
|
-
type: 1,
|
|
657
|
-
nativeFlowInfo: {
|
|
658
|
-
name: 'single_select',
|
|
659
|
-
paramsJson: nativeParams,
|
|
660
|
-
buttonParamsJson: nativeParams
|
|
674
|
+
const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid, 'preview');
|
|
675
|
+
if (pfpUrl) {
|
|
676
|
+
const resp = await fetch(pfpUrl, { method: 'GET', dispatcher: options?.options?.dispatcher });
|
|
677
|
+
if (resp.ok) {
|
|
678
|
+
const buf = Buffer.from(await resp.arrayBuffer());
|
|
679
|
+
m.groupInviteMessage.jpegThumbnail = buf;
|
|
661
680
|
}
|
|
662
|
-
})
|
|
663
|
-
}
|
|
664
|
-
const extraButtons = (bl.extraButtons || []).map(btn => ({
|
|
665
|
-
buttonId: btn.id || btn.buttonId || 'btn',
|
|
666
|
-
buttonText: { displayText: btn.displayText || btn.name || 'Button' },
|
|
667
|
-
type: 1
|
|
668
|
-
}))
|
|
669
|
-
m = {
|
|
670
|
-
buttonsMessage: {
|
|
671
|
-
locationMessage: {
|
|
672
|
-
degreesLatitude: bl.latitude || 0,
|
|
673
|
-
degreesLongitude: bl.longitude || 0,
|
|
674
|
-
name: bl.name || 'Location',
|
|
675
|
-
address: bl.address || '',
|
|
676
|
-
jpegThumbnail: thumb
|
|
677
|
-
},
|
|
678
|
-
contentText: bl.text || '',
|
|
679
|
-
footerText: bl.footer || '',
|
|
680
|
-
buttons: [...listButton, ...extraButtons],
|
|
681
|
-
headerType: 6,
|
|
682
|
-
viewOnce: true
|
|
683
681
|
}
|
|
684
682
|
}
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
messageContent.audioMessage || messageContent.documentMessage
|
|
714
|
-
) {
|
|
715
|
-
const key = Object.keys(messageContent)[0]
|
|
716
|
-
if (!messageContent[key].contextInfo) messageContent[key].contextInfo = {}
|
|
717
|
-
messageContent[key].contextInfo.statusAudienceMetadata = { audienceType }
|
|
718
|
-
}
|
|
719
|
-
m = { groupStatusMessageV2: { message: messageContent } }
|
|
720
|
-
} else if ('orderStatus' in message) {
|
|
721
|
-
const os = message.orderStatus
|
|
722
|
-
if (!os.image) throw new Error('orderStatus requires image')
|
|
723
|
-
let imageInput = os.image
|
|
724
|
-
if (typeof imageInput === 'string' && !imageInput.startsWith('http://') && !imageInput.startsWith('https://')) {
|
|
725
|
-
imageInput = await fs_1.promises.readFile(imageInput)
|
|
726
|
-
}
|
|
727
|
-
const media = await prepareWAMessageMedia({ image: imageInput }, { upload: options.upload, ...options })
|
|
728
|
-
m = {
|
|
729
|
-
viewOnceMessage: {
|
|
730
|
-
message: {
|
|
731
|
-
messageContextInfo: {
|
|
732
|
-
deviceListMetadata: {},
|
|
733
|
-
deviceListMetadataVersion: 2
|
|
734
|
-
},
|
|
735
|
-
interactiveMessage: WAProto_1.proto.Message.InteractiveMessage.create({
|
|
736
|
-
header: {
|
|
737
|
-
title: os.title || 'Order Status',
|
|
738
|
-
hasMediaAttachment: true,
|
|
739
|
-
...media
|
|
740
|
-
},
|
|
741
|
-
body: { text: os.text || 'Silakan cek status pesanan Anda.' },
|
|
742
|
-
footer: { text: os.footer || 'Powered by PontaCT' },
|
|
743
|
-
nativeFlowMessage: {
|
|
744
|
-
buttons: [{
|
|
745
|
-
name: 'order_status',
|
|
746
|
-
buttonParamsJson: JSON.stringify({
|
|
747
|
-
reference_id: os.referenceId || 'PT-001',
|
|
748
|
-
order: {
|
|
749
|
-
status: os.status || 'PROCESSING',
|
|
750
|
-
subtotal: {
|
|
751
|
-
value: os.subtotalValue || 0,
|
|
752
|
-
offset: os.subtotalOffset || 100
|
|
753
|
-
},
|
|
754
|
-
tax: {
|
|
755
|
-
value: os.taxValue || 0,
|
|
756
|
-
offset: os.taxOffset || 100
|
|
757
|
-
},
|
|
758
|
-
currency: os.currency || 'IDR'
|
|
759
|
-
}
|
|
760
|
-
})
|
|
761
|
-
}]
|
|
762
|
-
}
|
|
763
|
-
})
|
|
764
|
-
}
|
|
683
|
+
}
|
|
684
|
+
else if (hasNonNullishProperty(message, 'stickers')) {
|
|
685
|
+
m.stickerPackMessage = await prepareStickerPackMessage(message, options);
|
|
686
|
+
}
|
|
687
|
+
else if (hasNonNullishProperty(message, 'pin')) {
|
|
688
|
+
m.pinInChatMessage = {};
|
|
689
|
+
m.messageContextInfo = {};
|
|
690
|
+
m.pinInChatMessage.key = message.pin;
|
|
691
|
+
m.pinInChatMessage.type = message.type;
|
|
692
|
+
m.pinInChatMessage.senderTimestampMs = Date.now();
|
|
693
|
+
m.messageContextInfo.messageAddOnDurationInSecs = message.type === 1 ? message.time || 86400 : 0;
|
|
694
|
+
}
|
|
695
|
+
else if (hasNonNullishProperty(message, 'keep')) {
|
|
696
|
+
m.keepInChatMessage = {};
|
|
697
|
+
m.keepInChatMessage.key = message.keep;
|
|
698
|
+
m.keepInChatMessage.keepType = message.type;
|
|
699
|
+
m.keepInChatMessage.timestampMs = Date.now();
|
|
700
|
+
}
|
|
701
|
+
else if (hasNonNullishProperty(message, 'flowReply')) {
|
|
702
|
+
m.interactiveResponseMessage = {
|
|
703
|
+
body: {
|
|
704
|
+
format: message.flowReply.format || proto.Message.InteractiveResponseMessage.Body.Format.DEFAULT,
|
|
705
|
+
text: message.flowReply.text
|
|
706
|
+
},
|
|
707
|
+
nativeFlowResponseMessage: {
|
|
708
|
+
name: message.flowReply.name,
|
|
709
|
+
paramsJson: message.flowReply.paramsJson || '{}',
|
|
710
|
+
version: message.flowReply.version || 1
|
|
765
711
|
}
|
|
766
|
-
}
|
|
767
|
-
}
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
else if (hasNonNullishProperty(message, 'buttonReply')) {
|
|
768
715
|
switch (message.type) {
|
|
769
|
-
case 'list':
|
|
770
|
-
m.listResponseMessage = {
|
|
771
|
-
title: message.buttonReply.title,
|
|
772
|
-
description: message.buttonReply.description,
|
|
773
|
-
singleSelectReply: {
|
|
774
|
-
selectedRowId: message.buttonReply.rowId
|
|
775
|
-
},
|
|
776
|
-
lisType: WAProto_1.proto.Message.ListResponseMessage.ListType.SINGLE_SELECT
|
|
777
|
-
}
|
|
778
|
-
break
|
|
779
716
|
case 'template':
|
|
780
717
|
m.templateButtonReplyMessage = {
|
|
781
718
|
selectedDisplayText: message.buttonReply.displayText,
|
|
782
719
|
selectedId: message.buttonReply.id,
|
|
783
720
|
selectedIndex: message.buttonReply.index
|
|
784
|
-
}
|
|
785
|
-
break
|
|
721
|
+
};
|
|
722
|
+
break;
|
|
786
723
|
case 'plain':
|
|
787
724
|
m.buttonsResponseMessage = {
|
|
788
725
|
selectedButtonId: message.buttonReply.id,
|
|
789
726
|
selectedDisplayText: message.buttonReply.displayText,
|
|
790
|
-
type:
|
|
791
|
-
}
|
|
792
|
-
break
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
break
|
|
806
|
-
}
|
|
807
|
-
} else if ('ptv' in message && message.ptv) {
|
|
808
|
-
const {
|
|
809
|
-
videoMessage
|
|
810
|
-
} = await prepareWAMessageMedia({
|
|
811
|
-
video: message.video
|
|
812
|
-
}, options)
|
|
813
|
-
|
|
814
|
-
m.ptvMessage = videoMessage
|
|
815
|
-
} else if ('album' in message) {
|
|
816
|
-
const imageMessages = message.album.filter(item => 'image' in item)
|
|
817
|
-
const videoMessages = message.album.filter(item => 'video' in item)
|
|
818
|
-
|
|
819
|
-
m.albumMessage = Types_1.WAProto.Message.AlbumMessage.fromObject({
|
|
820
|
-
expectedImageCount: imageMessages.length,
|
|
821
|
-
expectedVideoCount: videoMessages.length
|
|
822
|
-
})
|
|
823
|
-
} else if ('order' in message) {
|
|
824
|
-
m.orderMessage = Types_1.WAProto.Message.OrderMessage.fromObject(message.order)
|
|
825
|
-
|
|
826
|
-
m.orderMessage.contextInfo = {
|
|
827
|
-
...(message.contextInfo || {}),
|
|
828
|
-
...(message.mentions ? {
|
|
829
|
-
mentionedJid: message.mentions
|
|
830
|
-
} : {})
|
|
831
|
-
}
|
|
832
|
-
} else if ('event' in message) {
|
|
833
|
-
m.eventMessage = Types_1.WAProto.Message.EventMessage.fromObject(message.event)
|
|
834
|
-
|
|
835
|
-
if (!message.event.startTime) {
|
|
836
|
-
m.eventMessage.startTime = generics_1.unixTimestampSeconds() + 86400
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
if (options.getCallLink && message.event.call) {
|
|
840
|
-
const link = await options.getCallLink(message.event.call, m.eventMessage.startTime)
|
|
841
|
-
m.eventMessage.joinLink = link
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
m.eventMessage.contextInfo = {
|
|
845
|
-
...(message.contextInfo || {}),
|
|
846
|
-
...(message.mentions ? {
|
|
847
|
-
mentionedJid: message.mentions
|
|
848
|
-
} : {})
|
|
849
|
-
}
|
|
850
|
-
} else if ('product' in message) {
|
|
851
|
-
const { imageMessage } = await prepareWAMessageMedia({
|
|
852
|
-
image: message.product.productImage
|
|
853
|
-
}, options)
|
|
854
|
-
|
|
855
|
-
m.productMessage = Types_1.WAProto.Message.ProductMessage.fromObject({
|
|
727
|
+
type: proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
|
|
728
|
+
};
|
|
729
|
+
break;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
else if (hasOptionalProperty(message, 'ptv') && message.ptv) {
|
|
733
|
+
const { videoMessage } = await prepareWAMessageMedia({ video: message.video }, options);
|
|
734
|
+
m.ptvMessage = videoMessage;
|
|
735
|
+
}
|
|
736
|
+
else if (hasNonNullishProperty(message, 'product')) {
|
|
737
|
+
if (!message.businessOwnerJid) {
|
|
738
|
+
throw new Boom('"businessOwnerJid" is missing from the content', { statusCode: 400 });
|
|
739
|
+
}
|
|
740
|
+
const { imageMessage } = await prepareWAMessageMedia({ image: message.image || message.product.productImage }, options);
|
|
741
|
+
const content = {
|
|
856
742
|
...message,
|
|
857
743
|
product: {
|
|
744
|
+
currencyCode: 'IDR',
|
|
745
|
+
priceAmount1000: 1000,
|
|
746
|
+
title: LIBRARY_NAME,
|
|
858
747
|
...message.product,
|
|
859
|
-
productImage: imageMessage
|
|
748
|
+
productImage: imageMessage
|
|
860
749
|
}
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
m.productMessage
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
m.
|
|
892
|
-
|
|
750
|
+
};
|
|
751
|
+
delete content.image;
|
|
752
|
+
m.productMessage = WAProto.Message.ProductMessage.create(content);
|
|
753
|
+
}
|
|
754
|
+
else if (hasNonNullishProperty(message, 'listReply')) {
|
|
755
|
+
m.listResponseMessage = {
|
|
756
|
+
description: message.listReply.description,
|
|
757
|
+
listType: proto.Message.ListResponseMessage.ListType.SINGLE_SELECT,
|
|
758
|
+
singleSelectReply: {
|
|
759
|
+
selectedRowId: message.listReply.id
|
|
760
|
+
},
|
|
761
|
+
title: message.listReply.title
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
else if (hasNonNullishProperty(message, 'event')) {
|
|
765
|
+
m.eventMessage = {};
|
|
766
|
+
const startTime = Math.floor(message.event.startDate.getTime() / 1000);
|
|
767
|
+
if (message.event.call && options.getCallLink) {
|
|
768
|
+
const token = await options.getCallLink(message.event.call, { startTime });
|
|
769
|
+
m.eventMessage.joinLink = (message.event.call === 'audio' ? CALL_AUDIO_PREFIX : CALL_VIDEO_PREFIX) + token;
|
|
770
|
+
}
|
|
771
|
+
m.messageContextInfo = {
|
|
772
|
+
// encKey
|
|
773
|
+
messageSecret: message.event.messageSecret || randomBytes(32)
|
|
774
|
+
};
|
|
775
|
+
m.eventMessage.name = message.event.name;
|
|
776
|
+
m.eventMessage.description = message.event.description;
|
|
777
|
+
m.eventMessage.startTime = startTime;
|
|
778
|
+
m.eventMessage.endTime = message.event.endDate ? message.event.endDate.getTime() / 1000 : undefined;
|
|
779
|
+
m.eventMessage.isCanceled = message.event.isCancelled ?? false;
|
|
780
|
+
m.eventMessage.extraGuestsAllowed = message.event.extraGuestsAllowed;
|
|
781
|
+
m.eventMessage.isScheduleCall = message.event.isScheduleCall ?? false;
|
|
782
|
+
m.eventMessage.location = message.event.location;
|
|
783
|
+
}
|
|
784
|
+
else if (hasNonNullishProperty(message, 'poll')) {
|
|
785
|
+
(_a = message.poll).selectableCount || (_a.selectableCount = 0);
|
|
786
|
+
(_b = message.poll).toAnnouncementGroup || (_b.toAnnouncementGroup = false);
|
|
893
787
|
if (!Array.isArray(message.poll.values)) {
|
|
894
|
-
throw new
|
|
895
|
-
statusCode: 400
|
|
896
|
-
})
|
|
788
|
+
throw new Boom('Invalid poll values', { statusCode: 400 });
|
|
897
789
|
}
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
message.poll.selectableCount > message.poll.values.length) {
|
|
901
|
-
throw new boom_1.Boom(`poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`, {
|
|
790
|
+
if (message.poll.selectableCount < 0 || message.poll.selectableCount > message.poll.values.length) {
|
|
791
|
+
throw new Boom(`poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`, {
|
|
902
792
|
statusCode: 400
|
|
903
|
-
})
|
|
793
|
+
});
|
|
904
794
|
}
|
|
905
|
-
|
|
906
795
|
const pollCreationMessage = {
|
|
907
796
|
name: message.poll.name,
|
|
908
|
-
selectableOptionsCount: message.poll
|
|
909
|
-
options: message.poll.values.map(optionName => ({
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
} else {
|
|
924
|
-
if (message.poll.selectableCount > 0) {
|
|
925
|
-
m.pollCreationMessageV3 = pollCreationMessage
|
|
926
|
-
} else {
|
|
927
|
-
m.pollCreationMessage = pollCreationMessage
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
} else if ('payment' in message) {
|
|
931
|
-
const requestPaymentMessage = {
|
|
932
|
-
amount: {
|
|
933
|
-
currencyCode: message.payment?.currency || 'IDR',
|
|
934
|
-
offset: message.payment?.offset || 0,
|
|
935
|
-
value: message.payment?.amount || 999999999
|
|
936
|
-
},
|
|
937
|
-
expiryTimestamp: message.payment?.expiry || 0,
|
|
938
|
-
amount1000: message.payment?.amount || 999999999 * 1000,
|
|
939
|
-
currencyCodeIso4217: message.payment?.currency || 'IDR',
|
|
940
|
-
requestFrom: message.payment?.from || '0@s.whatsapp.net',
|
|
941
|
-
noteMessage: {
|
|
942
|
-
extendedTextMessage: {
|
|
943
|
-
text: message.payment?.note || 'Notes'
|
|
797
|
+
selectableOptionsCount: message.poll.selectableCount,
|
|
798
|
+
options: message.poll.values.map(optionName => ({ optionName })),
|
|
799
|
+
endTime: message.poll.endDate ? message.poll.endDate.getTime() : undefined,
|
|
800
|
+
hideParticipantName: message.poll.hideVoter ?? false,
|
|
801
|
+
allowAddOption: message.poll.canAddOption ?? false
|
|
802
|
+
};
|
|
803
|
+
if (message.poll.toAnnouncementGroup) {
|
|
804
|
+
// poll v2 is for community announcement groups (single select and multiple)
|
|
805
|
+
m.pollCreationMessageV2 = pollCreationMessage;
|
|
806
|
+
}
|
|
807
|
+
else {
|
|
808
|
+
// Add quiz message support
|
|
809
|
+
if (message.poll.pollType === 1) {
|
|
810
|
+
if (!message.poll.correctAnswer) {
|
|
811
|
+
throw new Boom('No "correctAnswer" provided for quiz', { statusCode: 400 });
|
|
944
812
|
}
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
813
|
+
m.pollCreationMessageV5 = {
|
|
814
|
+
// quiz for newsletter only
|
|
815
|
+
...pollCreationMessage,
|
|
816
|
+
correctAnswer: {
|
|
817
|
+
optionName: message.poll.correctAnswer.toString()
|
|
818
|
+
},
|
|
819
|
+
pollType: 1,
|
|
820
|
+
selectableOptionsCount: 1
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
else if (message.poll.selectableCount === 1) {
|
|
824
|
+
//poll v3 is for single select polls
|
|
825
|
+
m.pollCreationMessageV3 = pollCreationMessage;
|
|
826
|
+
}
|
|
827
|
+
else {
|
|
828
|
+
// poll for multiple choice polls
|
|
829
|
+
m.pollCreationMessage = pollCreationMessage;
|
|
951
830
|
}
|
|
952
831
|
}
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
832
|
+
m.messageContextInfo = {
|
|
833
|
+
// encKey
|
|
834
|
+
messageSecret: message.poll.messageSecret || randomBytes(32)
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
else if (hasNonNullishProperty(message, 'pollResult')) {
|
|
838
|
+
const pollResultSnapshotMessage = {
|
|
839
|
+
name: message.pollResult.name,
|
|
840
|
+
pollVotes: message.pollResult.votes.map(vote => ({
|
|
841
|
+
optionName: vote.name,
|
|
842
|
+
optionVoteCount: parseInt(vote.voteCount)
|
|
843
|
+
}))
|
|
844
|
+
};
|
|
845
|
+
if (message.pollResult.pollType === 1) {
|
|
846
|
+
pollResultSnapshotMessage.pollType = proto.Message.PollType.QUIZ;
|
|
847
|
+
m.pollResultSnapshotMessageV3 = pollResultSnapshotMessage;
|
|
848
|
+
}
|
|
849
|
+
else {
|
|
850
|
+
pollResultSnapshotMessage.pollType = proto.Message.PollType.POLL;
|
|
851
|
+
m.pollResultSnapshotMessage = pollResultSnapshotMessage;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
else if (hasNonNullishProperty(message, 'pollUpdate')) {
|
|
855
|
+
if (!message.pollUpdate.key) {
|
|
856
|
+
throw new Boom('Message key is required', { statusCode: 400 });
|
|
857
|
+
}
|
|
858
|
+
if (!message.pollUpdate.vote) {
|
|
859
|
+
throw new Boom('Encrypted vote payload is required', { statusCode: 400 });
|
|
860
|
+
}
|
|
861
|
+
m.pollUpdateMessage = {
|
|
862
|
+
metadata: message.pollUpdate.metadata,
|
|
863
|
+
pollCreationMessageKey: message.pollUpdate.key,
|
|
864
|
+
senderTimestampMs: Date.now(),
|
|
865
|
+
vote: message.pollUpdate.vote
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
else if (hasNonNullishProperty(message, 'paymentInviteServiceType')) {
|
|
869
|
+
m.paymentInviteMessage = {
|
|
870
|
+
expiryTimestamp: Date.now(),
|
|
871
|
+
serviceType: message.paymentInviteServiceType
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
else if (hasNonNullishProperty(message, 'orderText')) {
|
|
875
|
+
if (!Buffer.isBuffer(message.thumbnail)) {
|
|
876
|
+
throw new Boom('Must provide thumbnail buffer in order message', { statusCode: 400 });
|
|
877
|
+
}
|
|
878
|
+
m.orderMessage = {
|
|
879
|
+
itemCount: 1,
|
|
880
|
+
messageVersion: 1,
|
|
881
|
+
orderTitle: LIBRARY_NAME,
|
|
882
|
+
status: proto.Message.OrderMessage.OrderStatus.INQUIRY,
|
|
883
|
+
surface: proto.Message.OrderMessage.OrderSurface.CATALOG,
|
|
884
|
+
token: generateMessageIDV2(),
|
|
885
|
+
totalAmount1000: 1000,
|
|
886
|
+
totalCurrencyCode: 'IDR',
|
|
887
|
+
...message,
|
|
888
|
+
message: message.orderText
|
|
889
|
+
};
|
|
890
|
+
delete m.orderMessage.orderText;
|
|
891
|
+
}
|
|
892
|
+
else if (hasNonNullishProperty(message, 'album')) {
|
|
893
|
+
if (!Array.isArray(message.album)) {
|
|
894
|
+
throw new Boom('Invalid album type. Expected an array.', { statusCode: 400 });
|
|
895
|
+
}
|
|
896
|
+
let videoCount = 0;
|
|
897
|
+
for (let i = 0; i < message.album.length; i++) {
|
|
898
|
+
if (message.album[i].video)
|
|
899
|
+
videoCount++;
|
|
900
|
+
}
|
|
901
|
+
;
|
|
902
|
+
let imageCount = 0;
|
|
903
|
+
for (let i = 0; i < message.album.length; i++) {
|
|
904
|
+
if (message.album[i].image)
|
|
905
|
+
imageCount++;
|
|
906
|
+
}
|
|
907
|
+
;
|
|
908
|
+
if ((videoCount + imageCount) < 2) {
|
|
909
|
+
throw new Boom('Minimum provide 2 media to upload album message', { statusCode: 400 });
|
|
910
|
+
}
|
|
911
|
+
m.albumMessage = {
|
|
912
|
+
expectedImageCount: imageCount,
|
|
913
|
+
expectedVideoCount: videoCount
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
else if (hasNonNullishProperty(message, 'sharePhoneNumber')) {
|
|
917
|
+
m.protocolMessage = {
|
|
918
|
+
type: proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
else if (hasNonNullishProperty(message, 'requestPhoneNumber')) {
|
|
922
|
+
m.requestPhoneNumberMessage = {};
|
|
923
|
+
}
|
|
924
|
+
else if (hasNonNullishProperty(message, 'limitSharing')) {
|
|
925
|
+
m.protocolMessage = {
|
|
926
|
+
type: proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
|
|
927
|
+
limitSharing: {
|
|
928
|
+
sharingLimited: message.limitSharing === true,
|
|
929
|
+
trigger: 1,
|
|
930
|
+
limitSharingSettingTimestamp: Date.now(),
|
|
931
|
+
initiatedByMe: true
|
|
990
932
|
}
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
const
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
else if (hasNonNullishProperty(message, 'buttonsMessage')) {
|
|
936
|
+
// Direct buttonsMessage passthrough — supports headerType 6 (locationMessage), etc.
|
|
937
|
+
const btnMsg = { ...message.buttonsMessage };
|
|
938
|
+
if (btnMsg.locationMessage?.jpegThumbnail && !Buffer.isBuffer(btnMsg.locationMessage.jpegThumbnail)) {
|
|
939
|
+
const lib = await getImageProcessingLibrary();
|
|
940
|
+
const hasSharp = 'sharp' in lib && !!lib.sharp?.default;
|
|
941
|
+
if (hasSharp) {
|
|
942
|
+
const rawBuf = typeof btnMsg.locationMessage.jpegThumbnail === 'string'
|
|
943
|
+
? (await fs.readFile(btnMsg.locationMessage.jpegThumbnail))
|
|
944
|
+
: btnMsg.locationMessage.jpegThumbnail;
|
|
945
|
+
btnMsg.locationMessage.jpegThumbnail = await lib.sharp.default(rawBuf)
|
|
946
|
+
.resize(300, 300, { fit: 'inside', withoutEnlargement: true })
|
|
947
|
+
.jpeg({ quality: 80 })
|
|
948
|
+
.toBuffer();
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
m = { buttonsMessage: btnMsg };
|
|
952
|
+
}
|
|
953
|
+
else {
|
|
954
|
+
m = await prepareWAMessageMedia(message, options);
|
|
955
|
+
}
|
|
956
|
+
if (hasNonNullishProperty(message, 'buttons')) {
|
|
957
|
+
const buttonsMessage = {
|
|
958
|
+
buttons: message.buttons.map(button => {
|
|
959
|
+
const buttonText = button.text || button.buttonText;
|
|
960
|
+
if (hasOptionalProperty(button, 'sections')) {
|
|
961
|
+
return {
|
|
962
|
+
nativeFlowInfo: {
|
|
963
|
+
name: 'single_select',
|
|
964
|
+
paramsJson: JSON.stringify({
|
|
965
|
+
title: buttonText,
|
|
966
|
+
sections: button.sections
|
|
967
|
+
})
|
|
968
|
+
},
|
|
969
|
+
type: ButtonType.NATIVE_FLOW
|
|
970
|
+
};
|
|
1001
971
|
}
|
|
972
|
+
else if (hasOptionalProperty(button, 'name')) {
|
|
973
|
+
return {
|
|
974
|
+
nativeFlowInfo: {
|
|
975
|
+
name: button.name,
|
|
976
|
+
paramsJson: button.paramsJson
|
|
977
|
+
},
|
|
978
|
+
type: ButtonType.NATIVE_FLOW
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
return {
|
|
982
|
+
buttonId: button.id || button.buttonId,
|
|
983
|
+
buttonText: typeof buttonText === 'string' ? { displayText: buttonText } : buttonText,
|
|
984
|
+
type: button.type || ButtonType.RESPONSE
|
|
985
|
+
};
|
|
1002
986
|
})
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
mediaType: 'sticker-pack',
|
|
1023
|
-
timeoutMs: options.mediaUploadTimeoutMs
|
|
1024
|
-
})
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
const coverImage = coverUpload.imageMessage
|
|
1028
|
-
const imageDataHash = crypto_2.sha256(coverBuffer).toString('base64')
|
|
1029
|
-
const stickerPackId = packId || generics_1.generateMessageID()
|
|
1030
|
-
|
|
1031
|
-
m.stickerPackMessage = {
|
|
1032
|
-
name,
|
|
1033
|
-
publisher,
|
|
1034
|
-
stickerPackId,
|
|
1035
|
-
packDescription: description,
|
|
1036
|
-
stickerPackOrigin: WAProto_1.proto.Message.StickerPackMessage.StickerPackOrigin.THIRD_PARTY,
|
|
1037
|
-
stickerPackSize: stickerPackUpload.fileLength,
|
|
1038
|
-
stickers: stickerMetadata,
|
|
1039
|
-
fileSha256: stickerPackUpload.fileSha256,
|
|
1040
|
-
fileEncSha256: stickerPackUpload.fileEncSha256,
|
|
1041
|
-
mediaKey: stickerPackUpload.mediaKey,
|
|
1042
|
-
directPath: stickerPackUploadResult.directPath,
|
|
1043
|
-
fileLength: stickerPackUpload.fileLength,
|
|
1044
|
-
mediaKeyTimestamp: generics_1.unixTimestampSeconds(),
|
|
1045
|
-
trayIconFileName: `${stickerPackId}.webp`,
|
|
1046
|
-
imageDataHash,
|
|
1047
|
-
thumbnailDirectPath: coverImage.directPath,
|
|
1048
|
-
thumbnailFileSha256: coverImage.fileSha256,
|
|
1049
|
-
thumbnailFileEncSha256: coverImage.fileEncSha256,
|
|
1050
|
-
thumbnailHeight: coverImage.height,
|
|
1051
|
-
thumbnailWidth: coverImage.width
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
m.stickerPackMessage.contextInfo = {
|
|
1055
|
-
...(message.contextInfo || {}),
|
|
1056
|
-
...(message.mentions ? {
|
|
1057
|
-
mentionedJid: message.mentions
|
|
1058
|
-
} : {})
|
|
1059
|
-
}
|
|
1060
|
-
} else if ('sharePhoneNumber' in message) {
|
|
1061
|
-
m.protocolMessage = {
|
|
1062
|
-
type: Types_1.WAProto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
|
|
1063
|
-
}
|
|
1064
|
-
} else if ('requestPhoneNumber' in message) {
|
|
1065
|
-
m.requestPhoneNumberMessage = {}
|
|
1066
|
-
} else {
|
|
1067
|
-
const mess = await prepareWAMessageMedia(message, options)
|
|
1068
|
-
const [type] = Object.keys(mess)
|
|
1069
|
-
|
|
1070
|
-
mess[type].contextInfo = {
|
|
1071
|
-
...(message.contextInfo || {}),
|
|
1072
|
-
...(message.mentions ? {
|
|
1073
|
-
mentionedJid: message.mentions
|
|
1074
|
-
} : {})
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
m = mess
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
if ('sections' in message && !!message.sections) {
|
|
987
|
+
};
|
|
988
|
+
if (hasOptionalProperty(message, 'text')) {
|
|
989
|
+
buttonsMessage.contentText = message.text;
|
|
990
|
+
buttonsMessage.headerType = ButtonHeaderType.EMPTY;
|
|
991
|
+
}
|
|
992
|
+
else {
|
|
993
|
+
if (hasOptionalProperty(message, 'caption')) {
|
|
994
|
+
buttonsMessage.contentText = message.caption;
|
|
995
|
+
}
|
|
996
|
+
const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
|
|
997
|
+
buttonsMessage.headerType = ButtonHeaderType[type];
|
|
998
|
+
Object.assign(buttonsMessage, m);
|
|
999
|
+
}
|
|
1000
|
+
if (hasOptionalProperty(message, 'footer')) {
|
|
1001
|
+
buttonsMessage.footerText = message.footer;
|
|
1002
|
+
}
|
|
1003
|
+
m = { buttonsMessage };
|
|
1004
|
+
}
|
|
1005
|
+
else if (hasNonNullishProperty(message, 'sections')) {
|
|
1081
1006
|
const listMessage = {
|
|
1082
|
-
|
|
1007
|
+
sections: message.sections,
|
|
1083
1008
|
buttonText: message.buttonText,
|
|
1009
|
+
title: message.title,
|
|
1084
1010
|
footerText: message.footer,
|
|
1085
1011
|
description: message.text,
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
} : {})
|
|
1012
|
+
listType: ListType.SINGLE_SELECT
|
|
1013
|
+
};
|
|
1014
|
+
m = { listMessage };
|
|
1015
|
+
}
|
|
1016
|
+
else if (hasNonNullishProperty(message, 'productList')) {
|
|
1017
|
+
// --- validasi 100% ---
|
|
1018
|
+
if (!message.businessOwnerJid) {
|
|
1019
|
+
throw new Boom('"businessOwnerJid" wajib diisi untuk mengirim productList', { statusCode: 400 });
|
|
1095
1020
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
listMessage
|
|
1021
|
+
if (!Array.isArray(message.productList) || message.productList.length === 0) {
|
|
1022
|
+
throw new Boom('"productList" harus berupa array section yang tidak kosong', { statusCode: 400 });
|
|
1099
1023
|
}
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1024
|
+
message.productList.forEach((section, i) => {
|
|
1025
|
+
if (!section || typeof section !== 'object') {
|
|
1026
|
+
throw new Boom(`productList[${i}] tidak valid, harus berupa object section`, { statusCode: 400 });
|
|
1027
|
+
}
|
|
1028
|
+
if (!Array.isArray(section.products) || section.products.length === 0) {
|
|
1029
|
+
throw new Boom(`productList[${i}].products harus berupa array produk yang tidak kosong`, { statusCode: 400 });
|
|
1030
|
+
}
|
|
1031
|
+
section.products.forEach((product, j) => {
|
|
1032
|
+
if (!product || !product.productId) {
|
|
1033
|
+
throw new Boom(`productList[${i}].products[${j}].productId wajib diisi`, { statusCode: 400 });
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
});
|
|
1037
|
+
const thumbnail = message.thumbnail
|
|
1038
|
+
? await generateThumbnail(message.thumbnail, 'image')
|
|
1039
|
+
: null;
|
|
1040
|
+
m.listMessage = {
|
|
1104
1041
|
title: message.title,
|
|
1105
1042
|
buttonText: message.buttonText,
|
|
1106
1043
|
footerText: message.footer,
|
|
@@ -1113,763 +1050,998 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
1113
1050
|
},
|
|
1114
1051
|
businessOwnerJid: message.businessOwnerJid
|
|
1115
1052
|
},
|
|
1116
|
-
listType:
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
m = {
|
|
1127
|
-
listMessage
|
|
1128
|
-
}
|
|
1129
|
-
} else if ('buttons' in message && !!message.buttons) {
|
|
1130
|
-
const buttonsMessage = {
|
|
1131
|
-
buttons: message.buttons.map(b => ({
|
|
1132
|
-
...b,
|
|
1133
|
-
type: WAProto_1.proto.Message.ButtonsMessage.Button.Type.RESPONSE
|
|
1134
|
-
}))
|
|
1053
|
+
listType: proto.Message.ListMessage.ListType.PRODUCT_LIST
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
else if (hasNonNullishProperty(message, 'payment')) {
|
|
1057
|
+
// --- validasi 100% ---
|
|
1058
|
+
const p = message.payment;
|
|
1059
|
+
if (!p || typeof p !== 'object') {
|
|
1060
|
+
throw new Boom('"payment" harus berupa object', { statusCode: 400 });
|
|
1135
1061
|
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
buttonsMessage.contentText = message.text
|
|
1139
|
-
buttonsMessage.headerType = WAProto_1.proto.Message.ButtonsMessage.HeaderType.EMPTY
|
|
1140
|
-
} else {
|
|
1141
|
-
if ('caption' in message) {
|
|
1142
|
-
buttonsMessage.contentText = message.caption
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
const type = Object.keys(m)[0].replace('Message', '').toUpperCase()
|
|
1146
|
-
|
|
1147
|
-
buttonsMessage.headerType = WAProto_1.proto.Message.ButtonsMessage.HeaderType[type]
|
|
1148
|
-
|
|
1149
|
-
Object.assign(buttonsMessage, m)
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
if ('footer' in message && !!message.footer) {
|
|
1153
|
-
buttonsMessage.footerText = message.footer
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
if ('title' in message && !!message.title) {
|
|
1157
|
-
buttonsMessage.text = message.title
|
|
1158
|
-
buttonsMessage.headerType = WAProto_1.proto.Message.ButtonsMessage.HeaderType.TEXT
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
buttonsMessage.contextInfo = {
|
|
1162
|
-
...(message.contextInfo || {}),
|
|
1163
|
-
...(message.mentions ? {
|
|
1164
|
-
mentionedJid: message.mentions
|
|
1165
|
-
} : {})
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
m = {
|
|
1169
|
-
buttonsMessage
|
|
1062
|
+
if (!p.from) {
|
|
1063
|
+
throw new Boom('"payment.from" (JID tujuan pembayaran) wajib diisi', { statusCode: 400 });
|
|
1170
1064
|
}
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
hydratedButtons: message.templateButtons
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
if ('text' in message) {
|
|
1177
|
-
hydratedTemplate.hydratedContentText = message.text
|
|
1178
|
-
} else {
|
|
1179
|
-
if ('caption' in message) {
|
|
1180
|
-
hydratedTemplate.hydratedContentText = message.caption
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
|
-
Object.assign(msg, m)
|
|
1065
|
+
if (typeof p.amount !== 'number' || !(p.amount > 0)) {
|
|
1066
|
+
throw new Boom('"payment.amount" wajib berupa angka lebih dari 0', { statusCode: 400 });
|
|
1184
1067
|
}
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
hydratedTemplate.hydratedFooterText = message.footer
|
|
1068
|
+
if (p.currency !== undefined && typeof p.currency !== 'string') {
|
|
1069
|
+
throw new Boom('"payment.currency" harus berupa string (mis. "IDR")', { statusCode: 400 });
|
|
1188
1070
|
}
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
...(message.contextInfo || {}),
|
|
1192
|
-
...(message.mentions ? {
|
|
1193
|
-
mentionedJid: message.mentions
|
|
1194
|
-
} : {})
|
|
1071
|
+
if (p.offset !== undefined && typeof p.offset !== 'number') {
|
|
1072
|
+
throw new Boom('"payment.offset" harus berupa angka', { statusCode: 400 });
|
|
1195
1073
|
}
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
templateMessage: {
|
|
1199
|
-
hydratedTemplate
|
|
1200
|
-
}
|
|
1074
|
+
if (p.expiry !== undefined && typeof p.expiry !== 'number') {
|
|
1075
|
+
throw new Boom('"payment.expiry" harus berupa angka (unix timestamp)', { statusCode: 400 });
|
|
1201
1076
|
}
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1077
|
+
const currency = p.currency || 'IDR';
|
|
1078
|
+
m.requestPaymentMessage = {
|
|
1079
|
+
amount: {
|
|
1080
|
+
currencyCode: currency,
|
|
1081
|
+
offset: p.offset ?? 0,
|
|
1082
|
+
value: p.amount
|
|
1083
|
+
},
|
|
1084
|
+
expiryTimestamp: p.expiry ?? 0,
|
|
1085
|
+
amount1000: p.amount * 1000,
|
|
1086
|
+
currencyCodeIso4217: currency,
|
|
1087
|
+
requestFrom: p.from,
|
|
1088
|
+
noteMessage: {
|
|
1089
|
+
extendedTextMessage: { text: p.note || '' }
|
|
1090
|
+
},
|
|
1091
|
+
background: {
|
|
1092
|
+
placeholderArgb: p.image?.placeholderArgb ?? 4278190080,
|
|
1093
|
+
textArgb: p.image?.textArgb ?? 4294967295,
|
|
1094
|
+
subtextArgb: p.image?.subtextArgb ?? 4294967295,
|
|
1095
|
+
type: 1
|
|
1206
1096
|
}
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
else if (hasNonNullishProperty(message, 'buttonLocation')) {
|
|
1100
|
+
// --- validasi 100% ---
|
|
1101
|
+
const bl = message.buttonLocation;
|
|
1102
|
+
if (!bl || typeof bl !== 'object') {
|
|
1103
|
+
throw new Boom('"buttonLocation" harus berupa object', { statusCode: 400 });
|
|
1104
|
+
}
|
|
1105
|
+
if (typeof bl.latitude !== 'number' || typeof bl.longitude !== 'number') {
|
|
1106
|
+
throw new Boom('"buttonLocation.latitude" dan "buttonLocation.longitude" wajib berupa angka', { statusCode: 400 });
|
|
1107
|
+
}
|
|
1108
|
+
if (bl.sections !== undefined && !Array.isArray(bl.sections)) {
|
|
1109
|
+
throw new Boom('"buttonLocation.sections" harus berupa array', { statusCode: 400 });
|
|
1110
|
+
}
|
|
1111
|
+
if (bl.extraButtons !== undefined && !Array.isArray(bl.extraButtons)) {
|
|
1112
|
+
throw new Boom('"buttonLocation.extraButtons" harus berupa array', { statusCode: 400 });
|
|
1113
|
+
}
|
|
1114
|
+
let thumb;
|
|
1115
|
+
if (bl.thumbnail) {
|
|
1116
|
+
const t = await generateThumbnail(bl.thumbnail, 'image');
|
|
1117
|
+
thumb = t?.thumbnail;
|
|
1118
|
+
}
|
|
1119
|
+
const listButton = [];
|
|
1120
|
+
if (Array.isArray(bl.sections) && bl.sections.length > 0) {
|
|
1121
|
+
bl.sections.forEach((section, i) => {
|
|
1122
|
+
if (!Array.isArray(section.rows) || section.rows.length === 0) {
|
|
1123
|
+
throw new Boom(`buttonLocation.sections[${i}].rows harus berupa array yang tidak kosong`, { statusCode: 400 });
|
|
1222
1124
|
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1125
|
+
section.rows.forEach((row, j) => {
|
|
1126
|
+
if (!row || !row.id || !row.title) {
|
|
1127
|
+
throw new Boom(`buttonLocation.sections[${i}].rows[${j}] wajib punya "id" dan "title"`, { statusCode: 400 });
|
|
1128
|
+
}
|
|
1129
|
+
});
|
|
1130
|
+
});
|
|
1131
|
+
const nativeParams = JSON.stringify({
|
|
1132
|
+
sections: bl.sections.map(section => ({
|
|
1133
|
+
title: section.title || '',
|
|
1134
|
+
rows: section.rows.map(row => ({
|
|
1135
|
+
title: row.title || '',
|
|
1136
|
+
description: row.description || '',
|
|
1137
|
+
id: row.id || ''
|
|
1138
|
+
}))
|
|
1139
|
+
}))
|
|
1140
|
+
});
|
|
1141
|
+
listButton.push({
|
|
1142
|
+
buttonId: 'list_menu',
|
|
1143
|
+
buttonText: { displayText: bl.listButtonText || '📄 Pilih Menu' },
|
|
1144
|
+
type: 1,
|
|
1145
|
+
nativeFlowInfo: {
|
|
1146
|
+
name: 'single_select',
|
|
1147
|
+
paramsJson: nativeParams,
|
|
1148
|
+
buttonParamsJson: nativeParams
|
|
1229
1149
|
}
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
const extraButtons = (bl.extraButtons || []).map((btn, i) => {
|
|
1153
|
+
if (!btn || (!btn.id && !btn.buttonId)) {
|
|
1154
|
+
throw new Boom(`buttonLocation.extraButtons[${i}].id wajib diisi`, { statusCode: 400 });
|
|
1230
1155
|
}
|
|
1156
|
+
return {
|
|
1157
|
+
buttonId: btn.id || btn.buttonId,
|
|
1158
|
+
buttonText: { displayText: btn.displayText || btn.name || 'Button' },
|
|
1159
|
+
type: 1
|
|
1160
|
+
};
|
|
1161
|
+
});
|
|
1162
|
+
if (listButton.length === 0 && extraButtons.length === 0) {
|
|
1163
|
+
throw new Boom('"buttonLocation" membutuhkan minimal 1 "sections" atau 1 "extraButtons"', { statusCode: 400 });
|
|
1231
1164
|
}
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1165
|
+
m = {
|
|
1166
|
+
buttonsMessage: {
|
|
1167
|
+
locationMessage: {
|
|
1168
|
+
degreesLatitude: bl.latitude,
|
|
1169
|
+
degreesLongitude: bl.longitude,
|
|
1170
|
+
name: bl.name || 'Location',
|
|
1171
|
+
address: bl.address || '',
|
|
1172
|
+
jpegThumbnail: thumb
|
|
1173
|
+
},
|
|
1174
|
+
contentText: bl.text || '',
|
|
1175
|
+
footerText: bl.footer || '',
|
|
1176
|
+
buttons: [...listButton, ...extraButtons],
|
|
1177
|
+
headerType: ButtonHeaderType.LOCATION,
|
|
1178
|
+
viewOnce: true
|
|
1236
1179
|
}
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
else if (hasNonNullishProperty(message, 'orderStatus')) {
|
|
1183
|
+
// --- validasi 100% ---
|
|
1184
|
+
const os = message.orderStatus;
|
|
1185
|
+
if (!os || typeof os !== 'object') {
|
|
1186
|
+
throw new Boom('"orderStatus" harus berupa object', { statusCode: 400 });
|
|
1237
1187
|
}
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
...(message.contextInfo || {}),
|
|
1241
|
-
...(message.mentions ? {
|
|
1242
|
-
mentionedJid: message.mentions
|
|
1243
|
-
} : {})
|
|
1188
|
+
if (!os.image) {
|
|
1189
|
+
throw new Boom('"orderStatus.image" wajib diisi (path file, URL, atau Buffer)', { statusCode: 400 });
|
|
1244
1190
|
}
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
interactiveMessage
|
|
1191
|
+
if (!os.referenceId) {
|
|
1192
|
+
throw new Boom('"orderStatus.referenceId" wajib diisi', { statusCode: 400 });
|
|
1248
1193
|
}
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
shopStorefrontMessage: {
|
|
1252
|
-
surface: message.shop.surface,
|
|
1253
|
-
id: message.shop.id
|
|
1254
|
-
}
|
|
1194
|
+
if (os.status !== undefined && typeof os.status !== 'string') {
|
|
1195
|
+
throw new Boom('"orderStatus.status" harus berupa string', { statusCode: 400 });
|
|
1255
1196
|
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
interactiveMessage.body = {
|
|
1259
|
-
text: message.text
|
|
1260
|
-
},
|
|
1261
|
-
interactiveMessage.header = {
|
|
1262
|
-
title: message.title,
|
|
1263
|
-
subtitle: message.subtitle,
|
|
1264
|
-
hasMediaAttachment: false
|
|
1265
|
-
}
|
|
1266
|
-
} else {
|
|
1267
|
-
if ('caption' in message) {
|
|
1268
|
-
interactiveMessage.body = {
|
|
1269
|
-
text: message.caption
|
|
1270
|
-
}
|
|
1271
|
-
|
|
1272
|
-
interactiveMessage.header = {
|
|
1273
|
-
title: message.title,
|
|
1274
|
-
subtitle: message.subtitle,
|
|
1275
|
-
hasMediaAttachment: message.hasMediaAttachment ? message.hasMediaAttachment : false,
|
|
1276
|
-
...Object.assign(interactiveMessage, m)
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
1197
|
+
if (os.subtotalValue !== undefined && typeof os.subtotalValue !== 'number') {
|
|
1198
|
+
throw new Boom('"orderStatus.subtotalValue" harus berupa angka', { statusCode: 400 });
|
|
1279
1199
|
}
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
interactiveMessage.footer = {
|
|
1283
|
-
text: message.footer
|
|
1284
|
-
}
|
|
1200
|
+
if (os.taxValue !== undefined && typeof os.taxValue !== 'number') {
|
|
1201
|
+
throw new Boom('"orderStatus.taxValue" harus berupa angka', { statusCode: 400 });
|
|
1285
1202
|
}
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
...(message.mentions ? {
|
|
1290
|
-
mentionedJid: message.mentions
|
|
1291
|
-
} : {})
|
|
1203
|
+
let imageInput = os.image;
|
|
1204
|
+
if (typeof imageInput === 'string' && !/^https?:\/\//i.test(imageInput)) {
|
|
1205
|
+
imageInput = await fs.readFile(imageInput);
|
|
1292
1206
|
}
|
|
1293
|
-
|
|
1207
|
+
const media = await prepareWAMessageMedia({ image: imageInput }, options);
|
|
1294
1208
|
m = {
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1209
|
+
viewOnceMessage: {
|
|
1210
|
+
message: {
|
|
1211
|
+
messageContextInfo: {
|
|
1212
|
+
deviceListMetadata: {},
|
|
1213
|
+
deviceListMetadataVersion: 2
|
|
1214
|
+
},
|
|
1215
|
+
interactiveMessage: proto.Message.InteractiveMessage.create({
|
|
1216
|
+
header: {
|
|
1217
|
+
title: os.title || 'Order Status',
|
|
1218
|
+
hasMediaAttachment: true,
|
|
1219
|
+
...media
|
|
1220
|
+
},
|
|
1221
|
+
body: {
|
|
1222
|
+
text: os.text || 'Silakan cek status pesanan Anda.'
|
|
1223
|
+
},
|
|
1224
|
+
footer: {
|
|
1225
|
+
text: os.footer || 'Powered by @pontalabs/baileys'
|
|
1226
|
+
},
|
|
1227
|
+
nativeFlowMessage: {
|
|
1228
|
+
buttons: [
|
|
1229
|
+
{
|
|
1230
|
+
name: 'order_status',
|
|
1231
|
+
buttonParamsJson: JSON.stringify({
|
|
1232
|
+
reference_id: os.referenceId,
|
|
1233
|
+
order: {
|
|
1234
|
+
status: os.status || 'PROCESSING',
|
|
1235
|
+
subtotal: {
|
|
1236
|
+
value: os.subtotalValue || 0,
|
|
1237
|
+
offset: os.subtotalOffset || 100
|
|
1238
|
+
},
|
|
1239
|
+
tax: {
|
|
1240
|
+
value: os.taxValue || 0,
|
|
1241
|
+
offset: os.taxOffset || 100
|
|
1242
|
+
},
|
|
1243
|
+
currency: os.currency || 'IDR'
|
|
1244
|
+
}
|
|
1245
|
+
})
|
|
1246
|
+
}
|
|
1247
|
+
]
|
|
1248
|
+
}
|
|
1249
|
+
})
|
|
1250
|
+
}
|
|
1303
1251
|
}
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
else if (hasNonNullishProperty(message, 'templateButtons')) {
|
|
1255
|
+
const hydratedTemplate = {
|
|
1256
|
+
hydratedButtons: message.templateButtons.map((button, i) => {
|
|
1257
|
+
const buttonText = button.text || button.buttonText;
|
|
1258
|
+
if (hasOptionalProperty(button, 'id')) {
|
|
1259
|
+
return {
|
|
1260
|
+
index: i,
|
|
1261
|
+
quickReplyButton: {
|
|
1262
|
+
displayText: buttonText || '👉🏻 Click',
|
|
1263
|
+
id: button.id
|
|
1264
|
+
}
|
|
1265
|
+
};
|
|
1314
1266
|
}
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1267
|
+
else if (hasOptionalProperty(button, 'url')) {
|
|
1268
|
+
return {
|
|
1269
|
+
index: i,
|
|
1270
|
+
urlButton: {
|
|
1271
|
+
displayText: buttonText || '🌐 Visit',
|
|
1272
|
+
url: button.url
|
|
1273
|
+
}
|
|
1274
|
+
};
|
|
1319
1275
|
}
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1276
|
+
else if (hasOptionalProperty(button, 'call')) {
|
|
1277
|
+
return {
|
|
1278
|
+
index: i,
|
|
1279
|
+
callButton: {
|
|
1280
|
+
displayText: buttonText || '📞 Call',
|
|
1281
|
+
phoneNumber: button.call
|
|
1282
|
+
}
|
|
1283
|
+
};
|
|
1325
1284
|
}
|
|
1285
|
+
button.index = button.index || i;
|
|
1286
|
+
return button;
|
|
1287
|
+
})
|
|
1288
|
+
};
|
|
1289
|
+
if (hasOptionalProperty(message, 'text')) {
|
|
1290
|
+
hydratedTemplate.hydratedContentText = message.text;
|
|
1291
|
+
}
|
|
1292
|
+
else {
|
|
1293
|
+
if (hasOptionalProperty(message, 'caption')) {
|
|
1294
|
+
hydratedTemplate.hydratedTitleText = message.title;
|
|
1295
|
+
hydratedTemplate.hydratedContentText = message.caption;
|
|
1326
1296
|
}
|
|
1297
|
+
;
|
|
1298
|
+
Object.assign(hydratedTemplate, m);
|
|
1327
1299
|
}
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
interactiveMessage.footer = {
|
|
1331
|
-
text: message.footer
|
|
1332
|
-
}
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
interactiveMessage.contextInfo = {
|
|
1336
|
-
...(message.contextInfo || {}),
|
|
1337
|
-
...(message.mentions ? {
|
|
1338
|
-
mentionedJid: message.mentions
|
|
1339
|
-
} : {})
|
|
1300
|
+
if (hasOptionalProperty(message, 'footer')) {
|
|
1301
|
+
hydratedTemplate.hydratedFooterText = message.footer;
|
|
1340
1302
|
}
|
|
1341
|
-
|
|
1303
|
+
hydratedTemplate.templateId = message.id || 'template-' + Date.now();
|
|
1342
1304
|
m = {
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
const slides = await Promise.all(message.cards.map(async (slide) => {
|
|
1347
|
-
const {
|
|
1348
|
-
image,
|
|
1349
|
-
video,
|
|
1350
|
-
product,
|
|
1351
|
-
title,
|
|
1352
|
-
body,
|
|
1353
|
-
footer,
|
|
1354
|
-
buttons
|
|
1355
|
-
} = slide
|
|
1356
|
-
let header
|
|
1357
|
-
|
|
1358
|
-
if (product) {
|
|
1359
|
-
const { imageMessage } = await prepareWAMessageMedia({
|
|
1360
|
-
image: product.productImage,
|
|
1361
|
-
...options
|
|
1362
|
-
}, options)
|
|
1363
|
-
header = {
|
|
1364
|
-
productMessage: {
|
|
1365
|
-
product: {
|
|
1366
|
-
...product,
|
|
1367
|
-
productImage: imageMessage,
|
|
1368
|
-
},
|
|
1369
|
-
...slide
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
} else if (image) {
|
|
1373
|
-
header = await prepareWAMessageMedia({
|
|
1374
|
-
image: image,
|
|
1375
|
-
...options
|
|
1376
|
-
}, options)
|
|
1377
|
-
} else if (video) {
|
|
1378
|
-
header = await prepareWAMessageMedia({
|
|
1379
|
-
video: video,
|
|
1380
|
-
...options
|
|
1381
|
-
}, options)
|
|
1305
|
+
templateMessage: {
|
|
1306
|
+
hydratedFourRowTemplate: hydratedTemplate,
|
|
1307
|
+
hydratedTemplate: hydratedTemplate
|
|
1382
1308
|
}
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
else if (hasNonNullishProperty(message, 'nativeFlow')) {
|
|
1312
|
+
const interactiveMessage = {
|
|
1313
|
+
nativeFlowMessage: prepareNativeFlowButtons(message)
|
|
1314
|
+
};
|
|
1315
|
+
if (hasOptionalProperty(message, 'bizJid')) {
|
|
1316
|
+
interactiveMessage.collectionMessage = {
|
|
1317
|
+
bizJid: message.bizJid,
|
|
1318
|
+
id: message.id,
|
|
1319
|
+
messageVersion: 1
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
else if (hasOptionalProperty(message, 'shopSurface')) {
|
|
1323
|
+
interactiveMessage.shopStorefrontMessage = {
|
|
1324
|
+
surface: message.shopSurface,
|
|
1325
|
+
id: message.id,
|
|
1326
|
+
messageVersion: 1
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
if (hasOptionalProperty(message, 'text')) {
|
|
1330
|
+
interactiveMessage.body = { text: message.text };
|
|
1331
|
+
}
|
|
1332
|
+
else {
|
|
1333
|
+
if (hasOptionalProperty(message, 'caption')) {
|
|
1334
|
+
const isValidHeader = hasValidInteractiveHeader(m);
|
|
1335
|
+
if (!isValidHeader) {
|
|
1336
|
+
throw new Boom('Invalid media type for interactive message header', { statusCode: 400 });
|
|
1398
1337
|
}
|
|
1338
|
+
interactiveMessage.header = {
|
|
1339
|
+
title: message.title || '',
|
|
1340
|
+
subtitle: message.subtitle || '',
|
|
1341
|
+
hasMediaAttachment: isValidHeader
|
|
1342
|
+
};
|
|
1343
|
+
interactiveMessage.body = { text: message.caption };
|
|
1399
1344
|
}
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
}))
|
|
1403
|
-
|
|
1404
|
-
const interactiveMessage = {
|
|
1405
|
-
carouselMessage: {
|
|
1406
|
-
cards: slides
|
|
1345
|
+
if (hasOptionalProperty(message, 'thumbnail') && !!message.thumbnail) {
|
|
1346
|
+
interactiveMessage.jpegThumbnail = message.thumbnail;
|
|
1407
1347
|
}
|
|
1348
|
+
Object.assign(interactiveMessage.header, m);
|
|
1408
1349
|
}
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
},
|
|
1414
|
-
interactiveMessage.header = {
|
|
1415
|
-
title: message.title,
|
|
1416
|
-
subtitle: message.subtitle,
|
|
1417
|
-
hasMediaAttachment: false
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
|
-
if ('footer' in message && !!message.footer) {
|
|
1350
|
+
if (hasOptionalProperty(message, 'audioFooter')) {
|
|
1351
|
+
const { audioMessage } = await prepareWAMessageMedia({
|
|
1352
|
+
audio: message.audioFooter
|
|
1353
|
+
}, options);
|
|
1422
1354
|
interactiveMessage.footer = {
|
|
1423
|
-
|
|
1355
|
+
audioMessage,
|
|
1356
|
+
hasMediaAttachment: true
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
else if (hasOptionalProperty(message, 'footer')) {
|
|
1360
|
+
interactiveMessage.footer = { text: message.footer };
|
|
1361
|
+
}
|
|
1362
|
+
m = { interactiveMessage };
|
|
1363
|
+
}
|
|
1364
|
+
else if (hasNonNullishProperty(message, 'cards')) {
|
|
1365
|
+
const interactiveMessage = {
|
|
1366
|
+
carouselMessage: {
|
|
1367
|
+
cards: await Promise.all(message.cards.map(async (card) => {
|
|
1368
|
+
let carouselHeader = {};
|
|
1369
|
+
if (hasNonNullishProperty(card, 'product')) {
|
|
1370
|
+
carouselHeader.productMessage = await prepareProductMessage(card, options);
|
|
1371
|
+
}
|
|
1372
|
+
else {
|
|
1373
|
+
carouselHeader = await prepareWAMessageMedia(card, options).catch(() => ({}));
|
|
1374
|
+
}
|
|
1375
|
+
const isValidHeader = hasValidCarouselHeader(carouselHeader);
|
|
1376
|
+
if (!isValidHeader) {
|
|
1377
|
+
throw new Boom('Invalid media type for carousel card', { statusCode: 400 });
|
|
1378
|
+
}
|
|
1379
|
+
const carouselCard = {
|
|
1380
|
+
nativeFlowMessage: prepareNativeFlowButtons(card.nativeFlow ? card : [])
|
|
1381
|
+
};
|
|
1382
|
+
if (hasOptionalProperty(card, 'text')) {
|
|
1383
|
+
carouselCard.body = { text: card.text };
|
|
1384
|
+
}
|
|
1385
|
+
else {
|
|
1386
|
+
if (hasOptionalProperty(card, 'caption')) {
|
|
1387
|
+
carouselCard.header = {
|
|
1388
|
+
title: card.title || '',
|
|
1389
|
+
subtitle: card.subtitle || '',
|
|
1390
|
+
hasMediaAttachment: isValidHeader
|
|
1391
|
+
};
|
|
1392
|
+
carouselCard.body = { text: card.caption };
|
|
1393
|
+
}
|
|
1394
|
+
if (hasOptionalProperty(card, 'thumbnail') && !!card.thumbnail) {
|
|
1395
|
+
carouselCard.jpegThumbnail = card.thumbnail;
|
|
1396
|
+
}
|
|
1397
|
+
Object.assign(carouselCard.header, carouselHeader);
|
|
1398
|
+
}
|
|
1399
|
+
if (hasOptionalProperty(card, 'audioFooter')) {
|
|
1400
|
+
const { audioMessage } = await prepareWAMessageMedia({
|
|
1401
|
+
audio: card.audioFooter
|
|
1402
|
+
}, options);
|
|
1403
|
+
carouselCard.footer = {
|
|
1404
|
+
audioMessage,
|
|
1405
|
+
hasMediaAttachment: true
|
|
1406
|
+
};
|
|
1407
|
+
}
|
|
1408
|
+
else if (hasOptionalProperty(card, 'footer')) {
|
|
1409
|
+
carouselCard.footer = { text: card.footer };
|
|
1410
|
+
}
|
|
1411
|
+
return carouselCard;
|
|
1412
|
+
})),
|
|
1413
|
+
carouselCardType: CarouselCardType.UNKNOWN,
|
|
1414
|
+
messageVersion: 1
|
|
1424
1415
|
}
|
|
1416
|
+
};
|
|
1417
|
+
if (hasOptionalProperty(message, 'text')) {
|
|
1418
|
+
interactiveMessage.body = { text: message.text };
|
|
1425
1419
|
}
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
...(message.contextInfo || {}),
|
|
1429
|
-
...(message.mentions ? {
|
|
1430
|
-
mentionedJid: message.mentions
|
|
1431
|
-
} : {})
|
|
1420
|
+
if (hasOptionalProperty(message, 'footer')) {
|
|
1421
|
+
interactiveMessage.footer = { text: message.footer };
|
|
1432
1422
|
}
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1423
|
+
m = { interactiveMessage };
|
|
1424
|
+
}
|
|
1425
|
+
else if (hasNonNullishProperty(message, 'requestPaymentFrom')) {
|
|
1426
|
+
const requestPaymentMessage = {
|
|
1427
|
+
amount: {
|
|
1428
|
+
currencyCode: 'IDR',
|
|
1429
|
+
offset: 1000,
|
|
1430
|
+
value: 1000
|
|
1431
|
+
},
|
|
1432
|
+
amount1000: 1000,
|
|
1433
|
+
currencyCodeIso4217: 'IDR',
|
|
1434
|
+
expiryTimestamp: Date.now(),
|
|
1435
|
+
noteMessage: m,
|
|
1436
|
+
requestFrom: message.requestPaymentFrom,
|
|
1437
|
+
...message
|
|
1438
|
+
};
|
|
1439
|
+
delete requestPaymentMessage.requestPaymentFrom;
|
|
1440
|
+
if (hasNonNullishProperty(m, 'extendedTextMessage') || hasNonNullishProperty(m, 'stickerMessage')) {
|
|
1441
|
+
Object.assign(requestPaymentMessage.noteMessage, m);
|
|
1442
|
+
}
|
|
1443
|
+
else {
|
|
1444
|
+
throw new Boom('Invalid message type for request payment note message', { statusCode: 400 });
|
|
1445
|
+
}
|
|
1446
|
+
m = { requestPaymentMessage };
|
|
1447
|
+
}
|
|
1448
|
+
else if (hasNonNullishProperty(message, 'invoiceNote')) {
|
|
1449
|
+
const attachment = m.imageMessage || m.documentMessage;
|
|
1450
|
+
const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
|
|
1451
|
+
const invoiceMessage = {
|
|
1452
|
+
attachmentType: proto.Message.InvoiceMessage.AttachmentType[type === 'DOCUMENT' ? 'PDF' : 'IMAGE'],
|
|
1453
|
+
note: message.invoiceNote
|
|
1454
|
+
};
|
|
1455
|
+
if (attachment) {
|
|
1456
|
+
const { directPath, fileEncSha256, fileSha256, jpegThumbnail = undefined, mediaKey, mediaKeyTimestamp, mimetype } = attachment;
|
|
1457
|
+
Object.assign(invoiceMessage, {
|
|
1458
|
+
attachmentDirectPath: directPath,
|
|
1459
|
+
attachmentFileEncSha256: fileEncSha256,
|
|
1460
|
+
attachmentFileSha256: fileSha256,
|
|
1461
|
+
attachmentJpegThumbnail: jpegThumbnail,
|
|
1462
|
+
attachmentMediaKey: mediaKey,
|
|
1463
|
+
attachmentMediaKeyTimestamp: mediaKeyTimestamp,
|
|
1464
|
+
attachmentMimetype: mimetype,
|
|
1465
|
+
token: generateMessageIDV2()
|
|
1466
|
+
});
|
|
1467
|
+
}
|
|
1468
|
+
else {
|
|
1469
|
+
throw new Boom('Invalid media type for invoice message', { statusCode: 400 });
|
|
1470
|
+
}
|
|
1471
|
+
m = { invoiceMessage };
|
|
1472
|
+
}
|
|
1473
|
+
if (hasOptionalProperty(message, 'externalAdReply') && !!message.externalAdReply) {
|
|
1474
|
+
const messageType = Object.keys(m)[0];
|
|
1475
|
+
const key = m[messageType];
|
|
1476
|
+
const content = message.externalAdReply;
|
|
1477
|
+
if ('thumbnail' in content && !Buffer.isBuffer(content.thumbnail)) {
|
|
1478
|
+
throw new Boom('Thumbnail must in buffer type', { statusCode: 400 });
|
|
1479
|
+
}
|
|
1480
|
+
if (!content.url || typeof content.url !== 'string' || !/^https?:\/\//i.test(content.url)) {
|
|
1481
|
+
options.logger?.warn({ givenUrl: content.url }, 'externalAdReply: field "url" kosong/tidak valid (harus diawali http:// atau https://) — fallback ke DONATE_URL, link produk asli TIDAK terkirim. Cek kode yang mengisi `url` di externalAdReply.');
|
|
1482
|
+
content.url = DONATE_URL;
|
|
1483
|
+
}
|
|
1484
|
+
const externalAdReply = {
|
|
1485
|
+
...content,
|
|
1486
|
+
body: content.body,
|
|
1487
|
+
mediaType: content.mediaType || 1,
|
|
1488
|
+
mediaUrl: content.url,
|
|
1489
|
+
renderLargerThumbnail: content.largeThumbnail,
|
|
1490
|
+
sourceUrl: content.url,
|
|
1491
|
+
thumbnail: content.thumbnail,
|
|
1492
|
+
thumbnailUrl: content.url + '?update=' + Date.now(),
|
|
1493
|
+
title: content.title || LIBRARY_NAME
|
|
1494
|
+
};
|
|
1495
|
+
delete externalAdReply.subTitle;
|
|
1496
|
+
delete externalAdReply.largeThumbnail;
|
|
1497
|
+
delete externalAdReply.url;
|
|
1498
|
+
if ('contextInfo' in key && !!key.contextInfo) {
|
|
1499
|
+
key.contextInfo.externalAdReply = { ...key.contextInfo.externalAdReply, ...externalAdReply };
|
|
1500
|
+
}
|
|
1501
|
+
else if (key) {
|
|
1502
|
+
key.contextInfo = { externalAdReply };
|
|
1436
1503
|
}
|
|
1437
1504
|
}
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1505
|
+
if ((hasOptionalProperty(message, 'mentions') && message.mentions?.length) ||
|
|
1506
|
+
(hasOptionalProperty(message, 'mentionAll') && message.mentionAll)) {
|
|
1507
|
+
const messageType = Object.keys(m)[0];
|
|
1508
|
+
const key = m[messageType];
|
|
1509
|
+
if (key && 'contextInfo' in key) {
|
|
1510
|
+
key.contextInfo = key.contextInfo || {};
|
|
1511
|
+
if (message.mentions?.length) {
|
|
1512
|
+
key.contextInfo.mentionedJid = message.mentions;
|
|
1443
1513
|
}
|
|
1514
|
+
if (message.mentionAll) {
|
|
1515
|
+
key.contextInfo.nonJidMentions = 1;
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
else if (key) {
|
|
1519
|
+
key.contextInfo = {
|
|
1520
|
+
mentionedJid: message.mentions,
|
|
1521
|
+
nonJidMentions: message.mentionAll ? 1 : 0
|
|
1522
|
+
};
|
|
1444
1523
|
}
|
|
1445
1524
|
}
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1525
|
+
if (hasOptionalProperty(message, 'contextInfo') && !!message.contextInfo) {
|
|
1526
|
+
const messageType = Object.keys(m)[0];
|
|
1527
|
+
const key = m[messageType];
|
|
1528
|
+
if ('contextInfo' in key && !!key.contextInfo) {
|
|
1529
|
+
key.contextInfo = { ...key.contextInfo, ...message.contextInfo };
|
|
1530
|
+
}
|
|
1531
|
+
else if (key) {
|
|
1532
|
+
key.contextInfo = message.contextInfo;
|
|
1452
1533
|
}
|
|
1453
1534
|
}
|
|
1454
|
-
|
|
1455
|
-
|
|
1535
|
+
if (hasOptionalProperty(message, 'groupStatus') && !!message.groupStatus) {
|
|
1536
|
+
const messageType = Object.keys(m)[0];
|
|
1537
|
+
const key = m[messageType];
|
|
1538
|
+
if ('contextInfo' in key && !!key.contextInfo) {
|
|
1539
|
+
key.contextInfo.isGroupStatus = message.groupStatus;
|
|
1540
|
+
}
|
|
1541
|
+
else if (key) {
|
|
1542
|
+
key.contextInfo = {
|
|
1543
|
+
isGroupStatus: message.groupStatus
|
|
1544
|
+
};
|
|
1545
|
+
}
|
|
1546
|
+
m = { groupStatusMessageV2: { message: m } };
|
|
1547
|
+
delete message.groupStatus;
|
|
1548
|
+
}
|
|
1549
|
+
if (hasOptionalProperty(message, 'spoiler') && !!message.spoiler) {
|
|
1550
|
+
const messageType = Object.keys(m)[0];
|
|
1551
|
+
const key = m[messageType];
|
|
1552
|
+
if ('contextInfo' in key && !!key.contextInfo) {
|
|
1553
|
+
key.contextInfo.isSpoiler = message.spoiler;
|
|
1554
|
+
}
|
|
1555
|
+
else if (key) {
|
|
1556
|
+
key.contextInfo = {
|
|
1557
|
+
isSpoiler: message.spoiler
|
|
1558
|
+
};
|
|
1559
|
+
}
|
|
1560
|
+
m = { spoilerMessage: { message: m } };
|
|
1561
|
+
delete message.spoiler;
|
|
1562
|
+
}
|
|
1563
|
+
else if (hasOptionalProperty(message, 'interactiveAsTemplate') && !!message.interactiveAsTemplate) {
|
|
1564
|
+
if (!m.interactiveMessage) {
|
|
1565
|
+
throw new Boom('Invalid message type for template', { statusCode: 400 });
|
|
1566
|
+
}
|
|
1456
1567
|
m = {
|
|
1457
|
-
|
|
1458
|
-
|
|
1568
|
+
templateMessage: {
|
|
1569
|
+
interactiveMessageTemplate: m.interactiveMessage,
|
|
1570
|
+
templateId: message.id || 'template-' + Date.now()
|
|
1459
1571
|
}
|
|
1460
|
-
}
|
|
1572
|
+
};
|
|
1573
|
+
delete message.interactiveAsTemplate;
|
|
1574
|
+
}
|
|
1575
|
+
if (hasOptionalProperty(message, 'ephemeral') && !!message.ephemeral) {
|
|
1576
|
+
m = { ephemeralMessage: { message: m } };
|
|
1577
|
+
delete message.ephemeral;
|
|
1578
|
+
}
|
|
1579
|
+
if (hasOptionalProperty(message, 'isLottie') && !!message.isLottie) {
|
|
1580
|
+
m = { lottieStickerMessage: { message: m } };
|
|
1461
1581
|
}
|
|
1462
|
-
|
|
1463
|
-
|
|
1582
|
+
else if (hasOptionalProperty(message, 'viewOnce') && !!message.viewOnce) {
|
|
1583
|
+
m = { viewOnceMessage: { message: m } };
|
|
1584
|
+
}
|
|
1585
|
+
else if (hasOptionalProperty(message, 'viewOnceV2') && !!message.viewOnceV2) {
|
|
1586
|
+
m = { viewOnceMessageV2: { message: m } };
|
|
1587
|
+
delete message.viewOnceV2;
|
|
1588
|
+
}
|
|
1589
|
+
else if (hasOptionalProperty(message, 'viewOnceV2Extension') && !!message.viewOnceV2Extension) {
|
|
1590
|
+
m = { viewOnceMessageV2Extension: { message: m } };
|
|
1591
|
+
delete message.viewOnceV2Extension;
|
|
1592
|
+
}
|
|
1593
|
+
if (hasOptionalProperty(message, 'edit')) {
|
|
1464
1594
|
m = {
|
|
1465
1595
|
protocolMessage: {
|
|
1466
1596
|
key: message.edit,
|
|
1467
1597
|
editedMessage: m,
|
|
1468
1598
|
timestampMs: Date.now(),
|
|
1469
|
-
type:
|
|
1599
|
+
type: WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
|
|
1470
1600
|
}
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
if (shouldIncludeReportingToken(m)) {
|
|
1604
|
+
m.messageContextInfo = m.messageContextInfo || {};
|
|
1605
|
+
if (!m.messageContextInfo.messageSecret) {
|
|
1606
|
+
m.messageContextInfo.messageSecret = randomBytes(32);
|
|
1471
1607
|
}
|
|
1472
1608
|
}
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1609
|
+
return WAProto.Message.create(m);
|
|
1610
|
+
};
|
|
1611
|
+
export const generateWAMessageFromContent = (jid, message, options) => {
|
|
1612
|
+
// set timestamp to now
|
|
1613
|
+
// if not specified
|
|
1478
1614
|
if (!options.timestamp) {
|
|
1479
|
-
options.timestamp = new Date()
|
|
1480
|
-
}
|
|
1481
|
-
|
|
1482
|
-
const
|
|
1483
|
-
const key = getContentType(innerMessage)
|
|
1484
|
-
const timestamp =
|
|
1485
|
-
const
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
[msgType]: quotedMsg[msgType]
|
|
1497
|
-
})
|
|
1498
|
-
|
|
1499
|
-
const quotedContent = quotedMsg[msgType]
|
|
1615
|
+
options.timestamp = new Date();
|
|
1616
|
+
}
|
|
1617
|
+
const innerMessage = normalizeMessageContent(message);
|
|
1618
|
+
const messageContextInfo = message.messageContextInfo;
|
|
1619
|
+
const key = getContentType(innerMessage);
|
|
1620
|
+
const timestamp = unixTimestampSeconds(options.timestamp);
|
|
1621
|
+
const isNewsletter = isJidNewsletter(jid);
|
|
1622
|
+
const { quoted, userJid } = options;
|
|
1623
|
+
if (quoted) {
|
|
1624
|
+
const participant = quoted.key.fromMe
|
|
1625
|
+
? userJid // TODO: Add support for LIDs
|
|
1626
|
+
: quoted.participant || quoted.key.participant || quoted.key.remoteJid;
|
|
1627
|
+
let quotedMsg = normalizeMessageContent(quoted.message);
|
|
1628
|
+
const msgType = getContentType(quotedMsg);
|
|
1629
|
+
// strip any redundant properties
|
|
1630
|
+
quotedMsg = proto.Message.create({ [msgType]: quotedMsg[msgType] });
|
|
1631
|
+
const quotedContent = quotedMsg[msgType];
|
|
1500
1632
|
if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
|
|
1501
|
-
delete quotedContent.contextInfo
|
|
1502
|
-
}
|
|
1503
|
-
|
|
1504
|
-
let requestPayment
|
|
1505
|
-
if (key === 'requestPaymentMessage') {
|
|
1506
|
-
if (innerMessage?.requestPaymentMessage && innerMessage?.requestPaymentMessage?.noteMessage?.extendedTextMessage) {
|
|
1507
|
-
requestPayment = innerMessage?.requestPaymentMessage?.noteMessage?.extendedTextMessage
|
|
1508
|
-
} else if (innerMessage?.requestPaymentMessage && innerMessage?.requestPaymentMessage?.noteMessage?.stickerMessage) {
|
|
1509
|
-
requestPayment = innerMessage.requestPaymentMessage?.noteMessage?.stickerMessage
|
|
1510
|
-
}
|
|
1633
|
+
delete quotedContent.contextInfo;
|
|
1511
1634
|
}
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
contextInfo.
|
|
1515
|
-
contextInfo.
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
if (jid !== quoted.key.remoteJid) {
|
|
1519
|
-
contextInfo.remoteJid = quoted.key.remoteJid
|
|
1635
|
+
const contextInfo = ('contextInfo' in innerMessage[key] && innerMessage[key]?.contextInfo) || {};
|
|
1636
|
+
contextInfo.participant = jidNormalizedUser(participant);
|
|
1637
|
+
contextInfo.stanzaId = quoted.key.id;
|
|
1638
|
+
contextInfo.quotedMessage = quotedMsg;
|
|
1639
|
+
// if a participant is quoted, then it must be a group
|
|
1640
|
+
// hence, remoteJid of group must also be entered
|
|
1641
|
+
if (!isNewsletter && jid !== quoted.key.remoteJid) {
|
|
1642
|
+
contextInfo.remoteJid = quoted.key.remoteJid;
|
|
1520
1643
|
}
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
contextInfo
|
|
1524
|
-
}
|
|
1525
|
-
|
|
1526
|
-
if (key === 'requestPaymentMessage' && requestPayment) {
|
|
1527
|
-
requestPayment.contextInfo = contextInfo
|
|
1528
|
-
} else {
|
|
1529
|
-
innerMessage[key].contextInfo = contextInfo
|
|
1530
|
-
}
|
|
1531
|
-
}
|
|
1532
|
-
|
|
1533
|
-
if (key !== 'protocolMessage' && key !== 'ephemeralMessage' && !WABinary_1.isJidNewsletter(jid)) {
|
|
1534
|
-
message.messageContextInfo = {
|
|
1535
|
-
messageSecret: crypto_1.randomBytes(32),
|
|
1536
|
-
...message.messageContextInfo
|
|
1644
|
+
if (contextInfo && innerMessage[key]) {
|
|
1645
|
+
/* @ts-ignore */
|
|
1646
|
+
innerMessage[key].contextInfo = contextInfo;
|
|
1537
1647
|
}
|
|
1538
1648
|
}
|
|
1539
|
-
|
|
1540
|
-
if
|
|
1649
|
+
if (
|
|
1650
|
+
// if we want to send a disappearing message
|
|
1651
|
+
!!options?.ephemeralExpiration &&
|
|
1652
|
+
// and it's not a protocol message -- delete, toggle disappear message
|
|
1653
|
+
key !== 'protocolMessage' &&
|
|
1654
|
+
// already not converted to disappearing message
|
|
1655
|
+
key !== 'ephemeralMessage' &&
|
|
1656
|
+
// newsletters don't support ephemeral messages
|
|
1657
|
+
!isNewsletter) {
|
|
1658
|
+
/* @ts-ignore */
|
|
1541
1659
|
innerMessage[key].contextInfo = {
|
|
1542
1660
|
...(innerMessage[key].contextInfo || {}),
|
|
1543
|
-
expiration: options.ephemeralExpiration
|
|
1544
|
-
|
|
1661
|
+
expiration: options.ephemeralExpiration || WA_DEFAULT_EPHEMERAL
|
|
1662
|
+
//ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
|
|
1663
|
+
};
|
|
1545
1664
|
}
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1665
|
+
if (messageContextInfo?.messageSecret && (isPnUser(jid) || isLidUser(jid))) {
|
|
1666
|
+
messageContextInfo.deviceListMetadata = {
|
|
1667
|
+
recipientKeyHash: randomBytes(10),
|
|
1668
|
+
recipientTimestamp: unixTimestampSeconds()
|
|
1669
|
+
};
|
|
1670
|
+
messageContextInfo.deviceListMetadataVersion = 2;
|
|
1671
|
+
}
|
|
1672
|
+
message = WAProto.Message.create(message);
|
|
1549
1673
|
const messageJSON = {
|
|
1550
1674
|
key: {
|
|
1551
1675
|
remoteJid: jid,
|
|
1552
1676
|
fromMe: true,
|
|
1553
|
-
id: options?.messageId ||
|
|
1677
|
+
id: options?.messageId || generateMessageIDV2()
|
|
1554
1678
|
},
|
|
1555
1679
|
message: message,
|
|
1556
1680
|
messageTimestamp: timestamp,
|
|
1557
1681
|
messageStubParameters: [],
|
|
1558
|
-
participant:
|
|
1559
|
-
status:
|
|
1560
|
-
}
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
options
|
|
1567
|
-
msgId: options.messageId
|
|
1568
|
-
})
|
|
1569
|
-
|
|
1682
|
+
participant: isJidGroup(jid) || isJidStatusBroadcast(jid) ? userJid : undefined, // TODO: Add support for LIDs
|
|
1683
|
+
status: WAMessageStatus.PENDING
|
|
1684
|
+
};
|
|
1685
|
+
return WAProto.WebMessageInfo.fromObject(messageJSON);
|
|
1686
|
+
};
|
|
1687
|
+
export const generateWAMessage = async (jid, content, options) => {
|
|
1688
|
+
// ensure msg ID is with every log
|
|
1689
|
+
options.logger = options?.logger?.child({ msgId: options.messageId });
|
|
1690
|
+
// Pass jid in the options to generateWAMessageContent
|
|
1570
1691
|
if (jid) {
|
|
1571
|
-
options.jid = jid
|
|
1692
|
+
options.jid = jid;
|
|
1572
1693
|
}
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
}), options)
|
|
1578
|
-
}
|
|
1579
|
-
|
|
1580
|
-
const getContentType = (content) => {
|
|
1694
|
+
return generateWAMessageFromContent(jid, await generateWAMessageContent(content, options), options);
|
|
1695
|
+
};
|
|
1696
|
+
/** Get the key to access the true type of content */
|
|
1697
|
+
export const getContentType = (content) => {
|
|
1581
1698
|
if (content) {
|
|
1582
|
-
const keys = Object.keys(content)
|
|
1583
|
-
const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage'
|
|
1584
|
-
|
|
1585
|
-
return key
|
|
1699
|
+
const keys = Object.keys(content);
|
|
1700
|
+
const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage');
|
|
1701
|
+
return key;
|
|
1586
1702
|
}
|
|
1587
|
-
}
|
|
1588
|
-
|
|
1589
|
-
const normalizeMessageContent = (content) => {
|
|
1703
|
+
};
|
|
1704
|
+
export const normalizeMessageContent = (content) => {
|
|
1590
1705
|
if (!content) {
|
|
1591
|
-
return undefined
|
|
1706
|
+
return undefined;
|
|
1592
1707
|
}
|
|
1593
|
-
|
|
1708
|
+
// set max iterations to prevent an infinite loop
|
|
1594
1709
|
for (let i = 0; i < 5; i++) {
|
|
1595
|
-
const inner = getFutureProofMessage(content)
|
|
1710
|
+
const inner = getFutureProofMessage(content);
|
|
1596
1711
|
if (!inner) {
|
|
1597
|
-
break
|
|
1712
|
+
break;
|
|
1598
1713
|
}
|
|
1599
|
-
|
|
1600
|
-
content = inner.message
|
|
1714
|
+
content = inner.message;
|
|
1601
1715
|
}
|
|
1602
|
-
|
|
1603
|
-
return content
|
|
1604
|
-
|
|
1716
|
+
return content;
|
|
1605
1717
|
function getFutureProofMessage(message) {
|
|
1606
|
-
return (
|
|
1718
|
+
return (message?.associatedChildMessage ||
|
|
1719
|
+
message?.botForwardedMessage ||
|
|
1720
|
+
message?.botInvokeMessage ||
|
|
1721
|
+
message?.botTaskMessage ||
|
|
1722
|
+
message?.documentWithCaptionMessage ||
|
|
1723
|
+
message?.editedMessage ||
|
|
1724
|
+
message?.ephemeralMessage ||
|
|
1725
|
+
message?.eventCoverImage ||
|
|
1726
|
+
message?.groupMentionedMessage ||
|
|
1727
|
+
message?.groupStatusMentionMessage ||
|
|
1728
|
+
message?.groupStatusMessage ||
|
|
1729
|
+
message?.groupStatusMessageV2 ||
|
|
1730
|
+
message?.limitSharingMessage ||
|
|
1731
|
+
message?.lottieStickerMessage ||
|
|
1732
|
+
message?.newsletterAdminProfileMessage ||
|
|
1733
|
+
message?.newsletterAdminProfileMessageV2 ||
|
|
1734
|
+
message?.newsletterAdminProfileStatusMessage ||
|
|
1735
|
+
message?.pollCreationMessageV4 ||
|
|
1736
|
+
message?.pollCreationOptionImageMessage ||
|
|
1737
|
+
message?.questionMessage ||
|
|
1738
|
+
message?.questionReplyMessage ||
|
|
1739
|
+
message?.spoilerMessage ||
|
|
1740
|
+
message?.statusAddYours ||
|
|
1741
|
+
message?.statusMentionMessage ||
|
|
1742
|
+
message?.viewOnceMessage ||
|
|
1743
|
+
message?.viewOnceMessageV2 ||
|
|
1744
|
+
message?.viewOnceMessageV2Extension);
|
|
1607
1745
|
}
|
|
1608
|
-
}
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
return {
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1746
|
+
};
|
|
1747
|
+
/**
|
|
1748
|
+
* Extract the true message content from a message
|
|
1749
|
+
* Eg. extracts the inner message from a disappearing message/view once message
|
|
1750
|
+
*/
|
|
1751
|
+
export const extractMessageContent = (content) => {
|
|
1752
|
+
const extractFromTemplateMessage = (msg) => {
|
|
1753
|
+
if (msg.imageMessage) {
|
|
1754
|
+
return { imageMessage: msg.imageMessage };
|
|
1755
|
+
}
|
|
1756
|
+
else if (msg.documentMessage) {
|
|
1757
|
+
return { documentMessage: msg.documentMessage };
|
|
1758
|
+
}
|
|
1759
|
+
else if (msg.videoMessage) {
|
|
1760
|
+
return { videoMessage: msg.videoMessage };
|
|
1761
|
+
}
|
|
1762
|
+
else if (msg.locationMessage) {
|
|
1763
|
+
return { locationMessage: msg.locationMessage };
|
|
1764
|
+
}
|
|
1765
|
+
else {
|
|
1623
1766
|
return {
|
|
1624
|
-
|
|
1625
|
-
}
|
|
1626
|
-
} else if (header ? msg.header?.locationMessage : msg.locationMessage) {
|
|
1627
|
-
return {
|
|
1628
|
-
locationMessage: header ? msg.header.locationMessage : msg.locationMessage
|
|
1629
|
-
}
|
|
1630
|
-
} else if (header ? msg.header?.productMessage : msg.productMessage) {
|
|
1631
|
-
return {
|
|
1632
|
-
productMessage: header ? msg.header.productMessage : msg.productMessage
|
|
1633
|
-
}
|
|
1634
|
-
} else {
|
|
1635
|
-
return {
|
|
1636
|
-
conversation: 'contentText' in msg ? msg.contentText : ('hydratedContentText' in msg ? msg.hydratedContentText : 'body' in msg ? msg.body.text : '')
|
|
1637
|
-
}
|
|
1767
|
+
conversation: 'contentText' in msg ? msg.contentText : 'hydratedContentText' in msg ? msg.hydratedContentText : ''
|
|
1768
|
+
};
|
|
1638
1769
|
}
|
|
1639
|
-
}
|
|
1640
|
-
|
|
1641
|
-
content = normalizeMessageContent(content)
|
|
1770
|
+
};
|
|
1771
|
+
content = normalizeMessageContent(content);
|
|
1642
1772
|
if (content?.buttonsMessage) {
|
|
1643
|
-
return
|
|
1644
|
-
}
|
|
1645
|
-
if (content?.interactiveMessage) {
|
|
1646
|
-
return extractFromButtonsMessage(content.interactiveMessage)
|
|
1647
|
-
}
|
|
1648
|
-
if (content?.templateMessage?.interactiveMessageTemplate) {
|
|
1649
|
-
return extractFromButtonsMessage(content?.templateMessage?.interactiveMessageTemplate)
|
|
1773
|
+
return extractFromTemplateMessage(content.buttonsMessage);
|
|
1650
1774
|
}
|
|
1651
1775
|
if (content?.templateMessage?.hydratedFourRowTemplate) {
|
|
1652
|
-
return
|
|
1776
|
+
return extractFromTemplateMessage(content?.templateMessage?.hydratedFourRowTemplate);
|
|
1653
1777
|
}
|
|
1654
1778
|
if (content?.templateMessage?.hydratedTemplate) {
|
|
1655
|
-
return
|
|
1779
|
+
return extractFromTemplateMessage(content?.templateMessage?.hydratedTemplate);
|
|
1656
1780
|
}
|
|
1657
1781
|
if (content?.templateMessage?.fourRowTemplate) {
|
|
1658
|
-
return
|
|
1782
|
+
return extractFromTemplateMessage(content?.templateMessage?.fourRowTemplate);
|
|
1659
1783
|
}
|
|
1660
|
-
return content
|
|
1661
|
-
}
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
const
|
|
1666
|
-
|
|
1667
|
-
|
|
1784
|
+
return content;
|
|
1785
|
+
};
|
|
1786
|
+
/**
|
|
1787
|
+
* Returns the device predicted by message ID
|
|
1788
|
+
*/
|
|
1789
|
+
export const getDevice = (id) => /^3A.{18}$/.test(id)
|
|
1790
|
+
? 'ios'
|
|
1791
|
+
: /^3E.{20}$/.test(id)
|
|
1792
|
+
? 'web'
|
|
1793
|
+
: /^(.{21}|.{32})$/.test(id)
|
|
1794
|
+
? 'android'
|
|
1795
|
+
: /^(3F|.{18}$)/.test(id)
|
|
1796
|
+
? 'desktop'
|
|
1797
|
+
: 'unknown';
|
|
1798
|
+
/** Upserts a receipt in the message */
|
|
1799
|
+
export const updateMessageWithReceipt = (msg, receipt) => {
|
|
1800
|
+
msg.userReceipt = msg.userReceipt || [];
|
|
1801
|
+
const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid);
|
|
1668
1802
|
if (recp) {
|
|
1669
|
-
Object.assign(recp, receipt)
|
|
1670
|
-
} else {
|
|
1671
|
-
msg.userReceipt.push(receipt)
|
|
1803
|
+
Object.assign(recp, receipt);
|
|
1672
1804
|
}
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
msg.reactions
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1805
|
+
else {
|
|
1806
|
+
msg.userReceipt.push(receipt);
|
|
1807
|
+
}
|
|
1808
|
+
};
|
|
1809
|
+
/** Update the message with a new reaction */
|
|
1810
|
+
export const updateMessageWithReaction = (msg, reaction) => {
|
|
1811
|
+
const authorID = getKeyAuthor(reaction.key);
|
|
1812
|
+
const reactions = (msg.reactions || []).filter(r => getKeyAuthor(r.key) !== authorID);
|
|
1813
|
+
reaction.text = reaction.text || '';
|
|
1814
|
+
reactions.push(reaction);
|
|
1815
|
+
msg.reactions = reactions;
|
|
1816
|
+
};
|
|
1817
|
+
/** Update the message with a new poll update */
|
|
1818
|
+
export const updateMessageWithPollUpdate = (msg, update) => {
|
|
1819
|
+
const authorID = getKeyAuthor(update.pollUpdateMessageKey);
|
|
1820
|
+
const reactions = (msg.pollUpdates || []).filter(r => getKeyAuthor(r.pollUpdateMessageKey) !== authorID);
|
|
1687
1821
|
if (update.vote?.selectedOptions?.length) {
|
|
1688
|
-
|
|
1822
|
+
reactions.push(update);
|
|
1689
1823
|
}
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1824
|
+
msg.pollUpdates = reactions;
|
|
1825
|
+
};
|
|
1826
|
+
/** Update the message with a new event response */
|
|
1827
|
+
export const updateMessageWithEventResponse = (msg, update) => {
|
|
1828
|
+
const authorID = getKeyAuthor(update.eventResponseMessageKey);
|
|
1829
|
+
const responses = (msg.eventResponses || []).filter(r => getKeyAuthor(r.eventResponseMessageKey) !== authorID);
|
|
1830
|
+
responses.push(update);
|
|
1831
|
+
msg.eventResponses = responses;
|
|
1832
|
+
};
|
|
1833
|
+
/**
|
|
1834
|
+
* Aggregates all poll updates in a poll.
|
|
1835
|
+
* @param msg the poll creation message
|
|
1836
|
+
* @param meId your jid
|
|
1837
|
+
* @returns A list of options & their voters
|
|
1838
|
+
*/
|
|
1839
|
+
export function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
1840
|
+
const opts = message?.pollCreationMessage?.options ||
|
|
1841
|
+
message?.pollCreationMessageV2?.options ||
|
|
1842
|
+
message?.pollCreationMessageV3?.options ||
|
|
1843
|
+
[];
|
|
1702
1844
|
const voteHashMap = opts.reduce((acc, opt) => {
|
|
1703
|
-
const hash =
|
|
1845
|
+
const hash = sha256(Buffer.from(opt.optionName || '')).toString();
|
|
1704
1846
|
acc[hash] = {
|
|
1705
1847
|
name: opt.optionName || '',
|
|
1706
1848
|
voters: []
|
|
1707
|
-
}
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
}, {})
|
|
1711
|
-
|
|
1849
|
+
};
|
|
1850
|
+
return acc;
|
|
1851
|
+
}, {});
|
|
1712
1852
|
for (const update of pollUpdates || []) {
|
|
1713
|
-
const { vote } = update
|
|
1853
|
+
const { vote } = update;
|
|
1714
1854
|
if (!vote) {
|
|
1715
|
-
continue
|
|
1855
|
+
continue;
|
|
1716
1856
|
}
|
|
1717
|
-
|
|
1718
1857
|
for (const option of vote.selectedOptions || []) {
|
|
1719
|
-
const hash = option.toString()
|
|
1720
|
-
let data = voteHashMap[hash]
|
|
1721
|
-
|
|
1858
|
+
const hash = option.toString();
|
|
1859
|
+
let data = voteHashMap[hash];
|
|
1722
1860
|
if (!data) {
|
|
1723
1861
|
voteHashMap[hash] = {
|
|
1724
1862
|
name: 'Unknown',
|
|
1725
1863
|
voters: []
|
|
1726
|
-
}
|
|
1727
|
-
|
|
1728
|
-
data = voteHashMap[hash]
|
|
1864
|
+
};
|
|
1865
|
+
data = voteHashMap[hash];
|
|
1729
1866
|
}
|
|
1730
|
-
|
|
1731
|
-
voteHashMap[hash].voters.push(generics_1.getKeyAuthor(update.pollUpdateMessageKey, meId))
|
|
1867
|
+
voteHashMap[hash].voters.push(getKeyAuthor(update.pollUpdateMessageKey, meId));
|
|
1732
1868
|
}
|
|
1733
1869
|
}
|
|
1734
|
-
|
|
1735
|
-
return Object.values(voteHashMap)
|
|
1870
|
+
return Object.values(voteHashMap);
|
|
1736
1871
|
}
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1872
|
+
/**
|
|
1873
|
+
* Aggregates all event responses in an event message.
|
|
1874
|
+
* @param msg the event creation message
|
|
1875
|
+
* @param meId your jid
|
|
1876
|
+
* @returns A list of response types & their responders
|
|
1877
|
+
*/
|
|
1878
|
+
export function getAggregateResponsesInEventMessage({ eventResponses }, meId) {
|
|
1879
|
+
const responseTypes = ['GOING', 'NOT_GOING', 'MAYBE'];
|
|
1880
|
+
const responseMap = {};
|
|
1881
|
+
for (const type of responseTypes) {
|
|
1882
|
+
responseMap[type] = {
|
|
1883
|
+
response: type,
|
|
1884
|
+
responders: []
|
|
1885
|
+
};
|
|
1886
|
+
}
|
|
1887
|
+
for (const update of eventResponses || []) {
|
|
1888
|
+
const responseType = update.eventResponse || 'UNKNOWN';
|
|
1889
|
+
if (responseType !== 'UNKNOWN' && responseMap[responseType]) {
|
|
1890
|
+
responseMap[responseType].responders.push(getKeyAuthor(update.eventResponseMessageKey, meId));
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
return Object.values(responseMap);
|
|
1894
|
+
}
|
|
1895
|
+
/** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
|
|
1896
|
+
export const aggregateMessageKeysNotFromMe = (keys) => {
|
|
1897
|
+
const keyMap = {};
|
|
1741
1898
|
for (const { remoteJid, id, participant, fromMe } of keys) {
|
|
1742
1899
|
if (!fromMe) {
|
|
1743
|
-
const uqKey = `${remoteJid}:${participant || ''}
|
|
1744
|
-
|
|
1900
|
+
const uqKey = `${remoteJid}:${participant || ''}`;
|
|
1745
1901
|
if (!keyMap[uqKey]) {
|
|
1746
1902
|
keyMap[uqKey] = {
|
|
1747
1903
|
jid: remoteJid,
|
|
1748
1904
|
participant: participant,
|
|
1749
1905
|
messageIds: []
|
|
1750
|
-
}
|
|
1906
|
+
};
|
|
1751
1907
|
}
|
|
1752
|
-
|
|
1753
|
-
keyMap[uqKey].messageIds.push(id)
|
|
1908
|
+
keyMap[uqKey].messageIds.push(id);
|
|
1754
1909
|
}
|
|
1755
1910
|
}
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
1911
|
+
return Object.values(keyMap);
|
|
1912
|
+
};
|
|
1913
|
+
const REUPLOAD_REQUIRED_STATUS = [410, 404];
|
|
1914
|
+
/**
|
|
1915
|
+
* Downloads the given message. Throws an error if it's not a media message
|
|
1916
|
+
*/
|
|
1917
|
+
export const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
1763
1918
|
const result = await downloadMsg().catch(async (error) => {
|
|
1764
|
-
if (ctx &&
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
}, 'sending reupload media request...')
|
|
1768
|
-
|
|
1769
|
-
message = await ctx.reuploadRequest(message)
|
|
1770
|
-
const result = await downloadMsg()
|
|
1771
|
-
return result
|
|
1772
|
-
}
|
|
1773
|
-
throw error
|
|
1774
|
-
})
|
|
1775
|
-
|
|
1776
|
-
return result
|
|
1777
|
-
|
|
1919
|
+
if (ctx &&
|
|
1920
|
+
typeof error?.status === 'number' && // treat errors with status as HTTP failures requiring reupload
|
|
1921
|
+
REUPLOAD_REQUIRED_STATUS.includes(error.status)) {
|
|
1922
|
+
ctx.logger.info({ key: message.key }, 'sending reupload media request...');
|
|
1923
|
+
// request reupload
|
|
1924
|
+
message = await ctx.reuploadRequest(message);
|
|
1925
|
+
const result = await downloadMsg();
|
|
1926
|
+
return result;
|
|
1927
|
+
}
|
|
1928
|
+
throw error;
|
|
1929
|
+
});
|
|
1930
|
+
return result;
|
|
1778
1931
|
async function downloadMsg() {
|
|
1779
|
-
const mContent = extractMessageContent(message.message)
|
|
1932
|
+
const mContent = extractMessageContent(message.message);
|
|
1780
1933
|
if (!mContent) {
|
|
1781
|
-
throw new
|
|
1782
|
-
statusCode: 400,
|
|
1783
|
-
data: message
|
|
1784
|
-
})
|
|
1934
|
+
throw new Boom('No message present', { statusCode: 400, data: message });
|
|
1785
1935
|
}
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
const media = contentType === 'productMessage' ? mContent[contentType]?.product?.productImage : mContent[contentType]
|
|
1790
|
-
|
|
1936
|
+
const contentType = getContentType(mContent);
|
|
1937
|
+
let mediaType = contentType?.replace('Message', '');
|
|
1938
|
+
const media = mContent[contentType];
|
|
1791
1939
|
if (!media || typeof media !== 'object' || (!('url' in media) && !('thumbnailDirectPath' in media))) {
|
|
1792
|
-
throw new
|
|
1940
|
+
throw new Boom(`"${contentType}" message is not a media message`);
|
|
1793
1941
|
}
|
|
1794
|
-
|
|
1795
|
-
let download
|
|
1796
|
-
|
|
1942
|
+
let download;
|
|
1797
1943
|
if ('thumbnailDirectPath' in media && !('url' in media)) {
|
|
1798
1944
|
download = {
|
|
1799
1945
|
directPath: media.thumbnailDirectPath,
|
|
1800
1946
|
mediaKey: media.mediaKey
|
|
1801
|
-
}
|
|
1802
|
-
mediaType = 'thumbnail-link'
|
|
1803
|
-
}
|
|
1804
|
-
|
|
1947
|
+
};
|
|
1948
|
+
mediaType = 'thumbnail-link';
|
|
1949
|
+
}
|
|
1950
|
+
else {
|
|
1951
|
+
download = media;
|
|
1805
1952
|
}
|
|
1806
|
-
|
|
1807
|
-
const stream = await messages_media_1.downloadContentFromMessage(download, mediaType, options)
|
|
1808
|
-
|
|
1953
|
+
const stream = await downloadContentFromMessage(download, mediaType, options);
|
|
1809
1954
|
if (type === 'buffer') {
|
|
1810
|
-
const bufferArray = []
|
|
1811
|
-
|
|
1955
|
+
const bufferArray = [];
|
|
1812
1956
|
for await (const chunk of stream) {
|
|
1813
|
-
bufferArray.push(chunk)
|
|
1957
|
+
bufferArray.push(chunk);
|
|
1814
1958
|
}
|
|
1815
|
-
|
|
1816
|
-
return Buffer.concat(bufferArray)
|
|
1959
|
+
return Buffer.concat(bufferArray);
|
|
1817
1960
|
}
|
|
1818
|
-
|
|
1819
|
-
return stream
|
|
1961
|
+
return stream;
|
|
1820
1962
|
}
|
|
1821
|
-
}
|
|
1822
|
-
|
|
1823
|
-
const assertMediaContent = (content) => {
|
|
1824
|
-
content = extractMessageContent(content)
|
|
1825
|
-
|
|
1826
|
-
|
|
1963
|
+
};
|
|
1964
|
+
/** Checks whether the given message is a media message; if it is returns the inner content */
|
|
1965
|
+
export const assertMediaContent = (content) => {
|
|
1966
|
+
content = extractMessageContent(content);
|
|
1967
|
+
const mediaContent = content?.documentMessage ||
|
|
1968
|
+
content?.imageMessage ||
|
|
1969
|
+
content?.videoMessage ||
|
|
1970
|
+
content?.audioMessage ||
|
|
1971
|
+
content?.stickerMessage;
|
|
1827
1972
|
if (!mediaContent) {
|
|
1828
|
-
throw new
|
|
1829
|
-
statusCode: 400,
|
|
1830
|
-
data: content
|
|
1831
|
-
})
|
|
1973
|
+
throw new Boom('given message is not a media message', { statusCode: 400, data: content });
|
|
1832
1974
|
}
|
|
1833
|
-
return mediaContent
|
|
1834
|
-
}
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1975
|
+
return mediaContent;
|
|
1976
|
+
};
|
|
1977
|
+
/**
|
|
1978
|
+
* Checks if a WebP buffer is animated by looking for VP8X chunk with animation flag
|
|
1979
|
+
* or ANIM/ANMF chunks
|
|
1980
|
+
*/
|
|
1981
|
+
const isAnimatedWebP = (buffer) => {
|
|
1982
|
+
// WebP must start with RIFF....WEBP
|
|
1983
|
+
if (buffer.length < 12 ||
|
|
1984
|
+
buffer[0] !== 0x52 ||
|
|
1985
|
+
buffer[1] !== 0x49 ||
|
|
1986
|
+
buffer[2] !== 0x46 ||
|
|
1987
|
+
buffer[3] !== 0x46 ||
|
|
1988
|
+
buffer[8] !== 0x57 ||
|
|
1989
|
+
buffer[9] !== 0x45 ||
|
|
1990
|
+
buffer[10] !== 0x42 ||
|
|
1991
|
+
buffer[11] !== 0x50) {
|
|
1992
|
+
return false;
|
|
1993
|
+
}
|
|
1994
|
+
;
|
|
1995
|
+
// Parse chunks starting after RIFF header (12 bytes)
|
|
1996
|
+
let offset = 12;
|
|
1997
|
+
while (offset < buffer.length - 8) {
|
|
1998
|
+
const chunkFourCC = buffer.toString('ascii', offset, offset + 4);
|
|
1999
|
+
const chunkSize = buffer.readUInt32LE(offset + 4);
|
|
2000
|
+
if (chunkFourCC === 'VP8X') {
|
|
2001
|
+
// VP8X extended header, check animation flag (bit 1 at offset+8)
|
|
2002
|
+
const flagsOffset = offset + 8;
|
|
2003
|
+
if (flagsOffset < buffer.length) {
|
|
2004
|
+
const flags = buffer[flagsOffset];
|
|
2005
|
+
if (flags & 0x02) {
|
|
2006
|
+
return true;
|
|
1846
2007
|
}
|
|
2008
|
+
;
|
|
1847
2009
|
}
|
|
2010
|
+
;
|
|
1848
2011
|
}
|
|
2012
|
+
else if (chunkFourCC === 'ANIM' || chunkFourCC === 'ANMF') {
|
|
2013
|
+
// ANIM or ANMF chunks indicate animation
|
|
2014
|
+
return true;
|
|
2015
|
+
}
|
|
2016
|
+
;
|
|
2017
|
+
// Move to next chunk (chunk size + 8 bytes header, padded to even)
|
|
2018
|
+
offset += 8 + chunkSize + (chunkSize % 2);
|
|
1849
2019
|
}
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
2020
|
+
;
|
|
2021
|
+
return false;
|
|
2022
|
+
};
|
|
2023
|
+
/**
|
|
2024
|
+
* Checks if a buffer is a WebP file
|
|
2025
|
+
*/
|
|
2026
|
+
const isWebPBuffer = (buffer) => {
|
|
2027
|
+
return (buffer.length >= 12 &&
|
|
2028
|
+
buffer[0] === 0x52 &&
|
|
2029
|
+
buffer[1] === 0x49 &&
|
|
2030
|
+
buffer[2] === 0x46 &&
|
|
2031
|
+
buffer[3] === 0x46 &&
|
|
2032
|
+
buffer[8] === 0x57 &&
|
|
2033
|
+
buffer[9] === 0x45 &&
|
|
2034
|
+
buffer[10] === 0x42 &&
|
|
2035
|
+
buffer[11] === 0x50);
|
|
2036
|
+
};
|
|
2037
|
+
export const shouldIncludeBizBinaryNode = (message) => {
|
|
2038
|
+
const hasValidInteractive = message.interactiveMessage &&
|
|
2039
|
+
!message.interactiveMessage.carouselMessage &&
|
|
2040
|
+
!message.interactiveMessage.collectionMessage &&
|
|
2041
|
+
!message.interactiveMessage.shopStorefrontMessage;
|
|
2042
|
+
return (message.buttonsMessage ||
|
|
2043
|
+
message.interactiveMessage ||
|
|
2044
|
+
message.listMessage ||
|
|
2045
|
+
hasValidInteractive);
|
|
2046
|
+
};
|
|
2047
|
+
//# sourceMappingURL=messages.js.map
|