violetics 7.0.6-alpha → 7.0.8-alpha
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 -2
- package/README.md +456 -978
- package/WAProto/GenerateStatics.sh +3 -0
- package/WAProto/WAProto.proto +5479 -0
- package/WAProto/fix-imports.js +81 -0
- package/WAProto/index.d.ts +14017 -0
- package/WAProto/index.js +2728 -7894
- package/engine-requirements.js +7 -10
- package/index.cjs +196 -0
- package/index.d.ts +30 -0
- package/lib/Defaults/index.d.ts +74 -0
- package/lib/Defaults/index.d.ts.map +1 -0
- package/lib/Defaults/index.js +16 -25
- package/lib/Defaults/index.js.map +1 -0
- package/lib/Defaults/phonenumber-mcc.json +223 -0
- package/lib/Signal/Group/ciphertext-message.d.ts +10 -0
- package/lib/Signal/Group/ciphertext-message.d.ts.map +1 -0
- package/lib/Signal/Group/ciphertext-message.js +2 -1
- package/lib/Signal/Group/ciphertext-message.js.map +1 -0
- package/lib/Signal/Group/group-session-builder.d.ts +15 -0
- package/lib/Signal/Group/group-session-builder.d.ts.map +1 -0
- package/lib/Signal/Group/group-session-builder.js +2 -1
- package/lib/Signal/Group/group-session-builder.js.map +1 -0
- package/lib/Signal/Group/group_cipher.d.ts +17 -0
- package/lib/Signal/Group/group_cipher.d.ts.map +1 -0
- package/lib/Signal/Group/group_cipher.js +2 -1
- package/lib/Signal/Group/group_cipher.js.map +1 -0
- package/lib/Signal/Group/index.d.ts +12 -0
- package/lib/Signal/Group/index.d.ts.map +1 -0
- package/lib/Signal/Group/index.js +2 -1
- package/lib/Signal/Group/index.js.map +1 -0
- package/lib/Signal/Group/keyhelper.d.ts +11 -0
- package/lib/Signal/Group/keyhelper.d.ts.map +1 -0
- package/lib/Signal/Group/keyhelper.js +2 -1
- package/lib/Signal/Group/keyhelper.js.map +1 -0
- package/lib/Signal/Group/sender-chain-key.d.ts +14 -0
- package/lib/Signal/Group/sender-chain-key.d.ts.map +1 -0
- package/lib/Signal/Group/sender-chain-key.js +2 -1
- package/lib/Signal/Group/sender-chain-key.js.map +1 -0
- package/lib/Signal/Group/sender-key-distribution-message.d.ts +17 -0
- package/lib/Signal/Group/sender-key-distribution-message.d.ts.map +1 -0
- package/lib/Signal/Group/sender-key-distribution-message.js +2 -1
- package/lib/Signal/Group/sender-key-distribution-message.js.map +1 -0
- package/lib/Signal/Group/sender-key-message.d.ts +19 -0
- package/lib/Signal/Group/sender-key-message.d.ts.map +1 -0
- package/lib/Signal/Group/sender-key-message.js +2 -1
- package/lib/Signal/Group/sender-key-message.js.map +1 -0
- package/lib/Signal/Group/sender-key-name.d.ts +18 -0
- package/lib/Signal/Group/sender-key-name.d.ts.map +1 -0
- package/lib/Signal/Group/sender-key-name.js +2 -1
- package/lib/Signal/Group/sender-key-name.js.map +1 -0
- package/lib/Signal/Group/sender-key-record.d.ts +31 -0
- package/lib/Signal/Group/sender-key-record.d.ts.map +1 -0
- package/lib/Signal/Group/sender-key-record.js +2 -1
- package/lib/Signal/Group/sender-key-record.js.map +1 -0
- package/lib/Signal/Group/sender-key-state.d.ts +39 -0
- package/lib/Signal/Group/sender-key-state.d.ts.map +1 -0
- package/lib/Signal/Group/sender-key-state.js +2 -1
- package/lib/Signal/Group/sender-key-state.js.map +1 -0
- package/lib/Signal/Group/sender-message-key.d.ts +12 -0
- package/lib/Signal/Group/sender-message-key.d.ts.map +1 -0
- package/lib/Signal/Group/sender-message-key.js +2 -1
- package/lib/Signal/Group/sender-message-key.js.map +1 -0
- package/lib/Signal/libsignal.d.ts +5 -0
- package/lib/Signal/libsignal.d.ts.map +1 -0
- package/lib/Signal/libsignal.js +44 -8
- package/lib/Signal/libsignal.js.map +1 -0
- package/lib/Signal/lid-mapping.d.ts +19 -0
- package/lib/Signal/lid-mapping.d.ts.map +1 -0
- package/lib/Signal/lid-mapping.js +2 -1
- package/lib/Signal/lid-mapping.js.map +1 -0
- package/lib/Socket/Client/index.d.ts +3 -0
- package/lib/Socket/Client/index.d.ts.map +1 -0
- package/lib/Socket/Client/index.js +2 -1
- package/lib/Socket/Client/index.js.map +1 -0
- package/lib/Socket/Client/types.d.ts +16 -0
- package/lib/Socket/Client/types.d.ts.map +1 -0
- package/lib/Socket/Client/types.js +2 -1
- package/lib/Socket/Client/types.js.map +1 -0
- package/lib/Socket/Client/websocket.d.ts +13 -0
- package/lib/Socket/Client/websocket.d.ts.map +1 -0
- package/lib/Socket/Client/websocket.js +3 -11
- package/lib/Socket/Client/websocket.js.map +1 -0
- package/lib/Socket/business.d.ts +202 -0
- package/lib/Socket/business.d.ts.map +1 -0
- package/lib/Socket/business.js +2 -1
- package/lib/Socket/business.js.map +1 -0
- package/lib/Socket/chats.d.ts +111 -0
- package/lib/Socket/chats.d.ts.map +1 -0
- package/lib/Socket/chats.js +155 -71
- package/lib/Socket/chats.js.map +1 -0
- package/lib/Socket/communities.d.ts +258 -0
- package/lib/Socket/communities.d.ts.map +1 -0
- package/lib/Socket/communities.js +2 -1
- package/lib/Socket/communities.js.map +1 -0
- package/lib/Socket/community.js +361 -0
- package/lib/Socket/groups.d.ts +150 -0
- package/lib/Socket/groups.d.ts.map +1 -0
- package/lib/Socket/groups.js +140 -9
- package/lib/Socket/groups.js.map +1 -0
- package/lib/Socket/index.d.ts +245 -0
- package/lib/Socket/index.d.ts.map +1 -0
- package/lib/Socket/index.js +2 -1
- package/lib/Socket/index.js.map +1 -0
- package/lib/Socket/messages-recv.d.ts +187 -0
- package/lib/Socket/messages-recv.d.ts.map +1 -0
- package/lib/Socket/messages-recv.js +481 -204
- package/lib/Socket/messages-recv.js.map +1 -0
- package/lib/Socket/messages-send.d.ts +183 -0
- package/lib/Socket/messages-send.d.ts.map +1 -0
- package/lib/Socket/messages-send.js +622 -233
- 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 +2 -1
- package/lib/Socket/mex.js.map +1 -0
- package/lib/Socket/newsletter.d.ts +160 -0
- package/lib/Socket/newsletter.d.ts.map +1 -0
- package/lib/Socket/newsletter.js +107 -68
- package/lib/Socket/newsletter.js.map +1 -0
- package/lib/Socket/socket.d.ts +55 -0
- package/lib/Socket/socket.d.ts.map +1 -0
- package/lib/Socket/socket.js +25 -26
- package/lib/Socket/socket.js.map +1 -0
- package/lib/Socket/usync.js +76 -0
- package/lib/Store/index.js +2 -1
- package/lib/Store/make-cache-manager-store.js +77 -0
- package/lib/Store/make-in-memory-store.js +44 -65
- package/lib/Store/make-ordered-dictionary.js +1 -1
- package/lib/Store/object-repository.js +1 -1
- package/lib/Types/Auth.d.ts +116 -0
- package/lib/Types/Auth.d.ts.map +1 -0
- package/lib/Types/Auth.js +2 -1
- 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 -1
- package/lib/Types/Bussines.js.map +1 -0
- package/lib/Types/Call.d.ts +15 -0
- package/lib/Types/Call.d.ts.map +1 -0
- package/lib/Types/Call.js +2 -1
- package/lib/Types/Call.js.map +1 -0
- package/lib/Types/Chat.d.ts +123 -0
- package/lib/Types/Chat.d.ts.map +1 -0
- package/lib/Types/Chat.js +2 -1
- package/lib/Types/Chat.js.map +1 -0
- package/lib/Types/Contact.d.ts +24 -0
- package/lib/Types/Contact.d.ts.map +1 -0
- package/lib/Types/Contact.js +2 -1
- package/lib/Types/Contact.js.map +1 -0
- package/lib/Types/Events.d.ts +237 -0
- package/lib/Types/Events.d.ts.map +1 -0
- package/lib/Types/Events.js +2 -1
- package/lib/Types/Events.js.map +1 -0
- package/lib/Types/GroupMetadata.d.ts +67 -0
- package/lib/Types/GroupMetadata.d.ts.map +1 -0
- package/lib/Types/GroupMetadata.js +2 -1
- package/lib/Types/GroupMetadata.js.map +1 -0
- package/lib/Types/Label.d.ts +47 -0
- package/lib/Types/Label.d.ts.map +1 -0
- package/lib/Types/Label.js +2 -1
- package/lib/Types/Label.js.map +1 -0
- package/lib/Types/LabelAssociation.d.ts +30 -0
- package/lib/Types/LabelAssociation.d.ts.map +1 -0
- package/lib/Types/LabelAssociation.js +2 -1
- package/lib/Types/LabelAssociation.js.map +1 -0
- package/lib/Types/Message.d.ts +305 -0
- package/lib/Types/Message.d.ts.map +1 -0
- package/lib/Types/Message.js +2 -8
- package/lib/Types/Message.js.map +1 -0
- package/lib/Types/MexUpdates.js +9 -0
- package/lib/Types/Newsletter.d.ts +135 -0
- package/lib/Types/Newsletter.d.ts.map +1 -0
- package/lib/Types/Newsletter.js +14 -4
- package/lib/Types/Newsletter.js.map +1 -0
- package/lib/Types/Product.d.ts +79 -0
- package/lib/Types/Product.d.ts.map +1 -0
- package/lib/Types/Product.js +2 -1
- package/lib/Types/Product.js.map +1 -0
- package/lib/Types/Signal.d.ts +76 -0
- package/lib/Types/Signal.d.ts.map +1 -0
- package/lib/Types/Signal.js +2 -1
- package/lib/Types/Signal.js.map +1 -0
- package/lib/Types/Socket.d.ts +133 -0
- package/lib/Types/Socket.d.ts.map +1 -0
- package/lib/Types/Socket.js +2 -1
- package/lib/Types/Socket.js.map +1 -0
- package/lib/Types/State.d.ts +39 -0
- package/lib/Types/State.d.ts.map +1 -0
- package/lib/Types/State.js +2 -1
- package/lib/Types/State.js.map +1 -0
- package/lib/Types/USync.d.ts +26 -0
- package/lib/Types/USync.d.ts.map +1 -0
- package/lib/Types/USync.js +2 -1
- package/lib/Types/USync.js.map +1 -0
- package/lib/Types/index.d.ts +65 -0
- package/lib/Types/index.d.ts.map +1 -0
- package/lib/Types/index.js +3 -1
- package/lib/Types/index.js.map +1 -0
- package/lib/Utils/audioToBuffer.js +31 -0
- package/lib/Utils/auth-utils.d.ts +19 -0
- package/lib/Utils/auth-utils.d.ts.map +1 -0
- package/lib/Utils/auth-utils.js +2 -1
- package/lib/Utils/auth-utils.js.map +1 -0
- package/lib/Utils/baileys-event-stream.js +54 -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 +35 -22
- package/lib/Utils/browser-utils.js.map +1 -0
- package/lib/Utils/business.d.ts +23 -0
- package/lib/Utils/business.d.ts.map +1 -0
- package/lib/Utils/business.js +2 -1
- package/lib/Utils/business.js.map +1 -0
- package/lib/Utils/chat-utils.d.ts +70 -0
- package/lib/Utils/chat-utils.d.ts.map +1 -0
- package/lib/Utils/chat-utils.js +2 -1
- package/lib/Utils/chat-utils.js.map +1 -0
- package/lib/Utils/crypto.d.ts +37 -0
- package/lib/Utils/crypto.d.ts.map +1 -0
- package/lib/Utils/crypto.js +2 -1
- package/lib/Utils/crypto.js.map +1 -0
- package/lib/Utils/decode-wa-message.d.ts +48 -0
- package/lib/Utils/decode-wa-message.d.ts.map +1 -0
- package/lib/Utils/decode-wa-message.js +2 -1
- package/lib/Utils/decode-wa-message.js.map +1 -0
- package/lib/Utils/event-buffer.d.ts +34 -0
- package/lib/Utils/event-buffer.d.ts.map +1 -0
- package/lib/Utils/event-buffer.js +4 -3
- package/lib/Utils/event-buffer.js.map +1 -0
- package/lib/Utils/generics.d.ts +91 -0
- package/lib/Utils/generics.d.ts.map +1 -0
- package/lib/Utils/generics.js +34 -7
- package/lib/Utils/generics.js.map +1 -0
- package/lib/Utils/history.d.ts +22 -0
- package/lib/Utils/history.d.ts.map +1 -0
- package/lib/Utils/history.js +2 -1
- package/lib/Utils/history.js.map +1 -0
- package/lib/Utils/identity-change-handler.d.ts +37 -0
- package/lib/Utils/identity-change-handler.d.ts.map +1 -0
- package/lib/Utils/identity-change-handler.js +2 -1
- package/lib/Utils/identity-change-handler.js.map +1 -0
- package/lib/Utils/index.d.ts +22 -0
- package/lib/Utils/index.d.ts.map +1 -0
- package/lib/Utils/index.js +8 -1
- package/lib/Utils/index.js.map +1 -0
- package/lib/Utils/link-preview.d.ts +21 -0
- package/lib/Utils/link-preview.d.ts.map +1 -0
- package/lib/Utils/link-preview.js +2 -1
- package/lib/Utils/link-preview.js.map +1 -0
- package/lib/Utils/logger.d.ts +13 -0
- package/lib/Utils/logger.d.ts.map +1 -0
- package/lib/Utils/logger.js +2 -1
- package/lib/Utils/logger.js.map +1 -0
- package/lib/Utils/lt-hash.d.ts +8 -0
- package/lib/Utils/lt-hash.d.ts.map +1 -0
- package/lib/Utils/lt-hash.js +2 -1
- package/lib/Utils/lt-hash.js.map +1 -0
- package/lib/Utils/make-mutex.d.ts +9 -0
- package/lib/Utils/make-mutex.d.ts.map +1 -0
- package/lib/Utils/make-mutex.js +2 -1
- package/lib/Utils/make-mutex.js.map +1 -0
- package/lib/Utils/message-retry-manager.d.ts +110 -0
- package/lib/Utils/message-retry-manager.d.ts.map +1 -0
- package/lib/Utils/message-retry-manager.js +3 -2
- package/lib/Utils/message-retry-manager.js.map +1 -0
- package/lib/Utils/messages-media.d.ts +130 -0
- package/lib/Utils/messages-media.d.ts.map +1 -0
- package/lib/Utils/messages-media.js +186 -180
- package/lib/Utils/messages-media.js.map +1 -0
- package/lib/Utils/messages-newsletter.d.ts +84 -0
- package/lib/Utils/messages-newsletter.js +316 -0
- package/lib/Utils/messages.d.ts +92 -0
- package/lib/Utils/messages.d.ts.map +1 -0
- package/lib/Utils/messages.js +929 -1116
- package/lib/Utils/messages.js.map +1 -0
- package/lib/Utils/noise-handler.d.ts +20 -0
- package/lib/Utils/noise-handler.d.ts.map +1 -0
- package/lib/Utils/noise-handler.js +2 -7
- package/lib/Utils/noise-handler.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 +2 -1
- package/lib/Utils/pre-key-manager.js.map +1 -0
- package/lib/Utils/process-message.d.ts +60 -0
- package/lib/Utils/process-message.d.ts.map +1 -0
- package/lib/Utils/process-message.js +2 -1
- 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 +2 -1
- package/lib/Utils/reporting-utils.js.map +1 -0
- package/lib/Utils/resolve-jid.d.ts +43 -0
- package/lib/Utils/resolve-jid.js +101 -0
- package/lib/Utils/signal.d.ts +34 -0
- package/lib/Utils/signal.d.ts.map +1 -0
- package/lib/Utils/signal.js +2 -1
- package/lib/Utils/signal.js.map +1 -0
- package/lib/Utils/streamToBuffer.js +17 -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 +2 -1
- package/lib/Utils/sync-action-utils.js.map +1 -0
- package/lib/Utils/tc-token-utils.d.ts +12 -0
- package/lib/Utils/tc-token-utils.d.ts.map +1 -0
- package/lib/Utils/tc-token-utils.js +2 -1
- package/lib/Utils/tc-token-utils.js.map +1 -0
- package/lib/Utils/use-mongo-file-auth-state.js +77 -0
- package/lib/Utils/use-multi-file-auth-state.d.ts +13 -0
- package/lib/Utils/use-multi-file-auth-state.d.ts.map +1 -0
- package/lib/Utils/use-multi-file-auth-state.js +2 -1
- package/lib/Utils/use-multi-file-auth-state.js.map +1 -0
- package/lib/Utils/use-single-file-auth-state.js +74 -0
- package/lib/Utils/validate-connection.d.ts +11 -0
- package/lib/Utils/validate-connection.d.ts.map +1 -0
- package/lib/Utils/validate-connection.js +4 -10
- package/lib/Utils/validate-connection.js.map +1 -0
- package/lib/WABinary/constants.d.ts +28 -0
- package/lib/WABinary/constants.d.ts.map +1 -0
- package/lib/WABinary/constants.js +2 -1
- package/lib/WABinary/constants.js.map +1 -0
- package/lib/WABinary/decode.d.ts +7 -0
- package/lib/WABinary/decode.d.ts.map +1 -0
- package/lib/WABinary/decode.js +2 -1
- package/lib/WABinary/decode.js.map +1 -0
- package/lib/WABinary/encode.d.ts +3 -0
- package/lib/WABinary/encode.d.ts.map +1 -0
- package/lib/WABinary/encode.js +2 -1
- package/lib/WABinary/encode.js.map +1 -0
- package/lib/WABinary/generic-utils.d.ts +15 -0
- package/lib/WABinary/generic-utils.d.ts.map +1 -0
- package/lib/WABinary/generic-utils.js +15 -97
- package/lib/WABinary/generic-utils.js.map +1 -0
- package/lib/WABinary/index.d.ts +6 -0
- package/lib/WABinary/index.d.ts.map +1 -0
- package/lib/WABinary/index.js +3 -1
- package/lib/WABinary/index.js.map +1 -0
- package/lib/WABinary/jid-utils.d.ts +48 -0
- package/lib/WABinary/jid-utils.d.ts.map +1 -0
- package/lib/WABinary/jid-utils.js +4 -1
- package/lib/WABinary/jid-utils.js.map +1 -0
- package/lib/WABinary/types.d.ts +19 -0
- package/lib/WABinary/types.d.ts.map +1 -0
- package/lib/WABinary/types.js +2 -1
- package/lib/WABinary/types.js.map +1 -0
- package/lib/WAM/BinaryInfo.d.ts +9 -0
- package/lib/WAM/BinaryInfo.d.ts.map +1 -0
- package/lib/WAM/BinaryInfo.js +2 -1
- package/lib/WAM/BinaryInfo.js.map +1 -0
- package/lib/WAM/constants.d.ts +40 -0
- package/lib/WAM/constants.d.ts.map +1 -0
- package/lib/WAM/constants.js +2 -1
- package/lib/WAM/constants.js.map +1 -0
- package/lib/WAM/encode.d.ts +3 -0
- package/lib/WAM/encode.d.ts.map +1 -0
- package/lib/WAM/encode.js +2 -1
- package/lib/WAM/encode.js.map +1 -0
- package/lib/WAM/index.d.ts +4 -0
- package/lib/WAM/index.d.ts.map +1 -0
- package/lib/WAM/index.js +2 -1
- package/lib/WAM/index.js.map +1 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +10 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts.map +1 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +2 -1
- package/lib/WAUSync/Protocols/USyncContactProtocol.js.map +1 -0
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +23 -0
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts.map +1 -0
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +2 -1
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js.map +1 -0
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +13 -0
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts.map +1 -0
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +2 -1
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js.map +1 -0
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +13 -0
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts.map +1 -0
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +2 -1
- package/lib/WAUSync/Protocols/USyncStatusProtocol.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 +2 -1
- 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 +2 -1
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js.map +1 -0
- package/lib/WAUSync/Protocols/index.d.ts +5 -0
- package/lib/WAUSync/Protocols/index.d.ts.map +1 -0
- package/lib/WAUSync/Protocols/index.js +3 -1
- package/lib/WAUSync/Protocols/index.js.map +1 -0
- package/lib/WAUSync/USyncQuery.d.ts +29 -0
- package/lib/WAUSync/USyncQuery.d.ts.map +1 -0
- package/lib/WAUSync/USyncQuery.js +2 -1
- package/lib/WAUSync/USyncQuery.js.map +1 -0
- package/lib/WAUSync/USyncUser.d.ts +13 -0
- package/lib/WAUSync/USyncUser.d.ts.map +1 -0
- package/lib/WAUSync/USyncUser.js +2 -1
- package/lib/WAUSync/USyncUser.js.map +1 -0
- package/lib/WAUSync/index.d.ts +4 -0
- package/lib/WAUSync/index.d.ts.map +1 -0
- package/lib/WAUSync/index.js +2 -1
- package/lib/WAUSync/index.js.map +1 -0
- package/lib/index.d.ts +12 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +22 -2
- package/lib/index.js.map +1 -0
- package/package.json +75 -72
- package/lib/Utils/offline-node-processor.js +0 -39
- package/lib/Utils/stanza-ack.js +0 -37
package/lib/Utils/messages.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { Boom } from '@hapi/boom';
|
|
2
2
|
import { randomBytes } from 'crypto';
|
|
3
|
-
import { zip } from 'fflate';
|
|
4
3
|
import { promises as fs } from 'fs';
|
|
5
|
-
import {
|
|
4
|
+
import {} from 'stream';
|
|
6
5
|
import { proto } from '../../WAProto/index.js';
|
|
7
|
-
import { CALL_AUDIO_PREFIX, CALL_VIDEO_PREFIX,
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
6
|
+
import { CALL_AUDIO_PREFIX, CALL_VIDEO_PREFIX, MEDIA_KEYS, URL_REGEX, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
|
|
7
|
+
import { WAMessageStatus, WAProto } from '../Types/index.js';
|
|
8
|
+
import { isJidGroup, isJidNewsletter, isJidStatusBroadcast, jidNormalizedUser } from '../WABinary/index.js';
|
|
10
9
|
import { sha256 } from './crypto.js';
|
|
11
10
|
import { generateMessageIDV2, getKeyAuthor, unixTimestampSeconds } from './generics.js';
|
|
12
|
-
import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform,
|
|
11
|
+
import { downloadContentFromMessage, encryptedStream, prepareStream, generateThumbnail, getAudioDuration, getAudioWaveform, getStream, toBuffer } from './messages-media.js';
|
|
12
|
+
import { createRequire } from 'module';
|
|
13
|
+
const _require = createRequire(import.meta.url);
|
|
13
14
|
import { shouldIncludeReportingToken } from './reporting-utils.js';
|
|
14
15
|
const MIMETYPE_MAP = {
|
|
15
16
|
image: 'image/jpeg',
|
|
@@ -19,6 +20,92 @@ const MIMETYPE_MAP = {
|
|
|
19
20
|
sticker: 'image/webp',
|
|
20
21
|
'product-catalog-image': 'image/jpeg'
|
|
21
22
|
};
|
|
23
|
+
/** Map ekstensi audio ke mimetype */
|
|
24
|
+
const AUDIO_MIMETYPE_MAP = {
|
|
25
|
+
ogg: 'audio/ogg; codecs=opus',
|
|
26
|
+
oga: 'audio/ogg; codecs=opus',
|
|
27
|
+
opus: 'audio/ogg; codecs=opus',
|
|
28
|
+
mp3: 'audio/mpeg',
|
|
29
|
+
mpeg: 'audio/mpeg',
|
|
30
|
+
mp4: 'audio/mp4',
|
|
31
|
+
m4a: 'audio/mp4',
|
|
32
|
+
aac: 'audio/aac',
|
|
33
|
+
wav: 'audio/wav',
|
|
34
|
+
wave: 'audio/wav',
|
|
35
|
+
flac: 'audio/flac',
|
|
36
|
+
webm: 'audio/webm',
|
|
37
|
+
amr: 'audio/amr',
|
|
38
|
+
'3gp': 'audio/3gpp',
|
|
39
|
+
'3gpp': 'audio/3gpp',
|
|
40
|
+
wma: 'audio/x-ms-wma',
|
|
41
|
+
caf: 'audio/x-caf',
|
|
42
|
+
aiff: 'audio/aiff',
|
|
43
|
+
aif: 'audio/aiff',
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Deteksi mimetype audio dari magic bytes buffer.
|
|
47
|
+
* Return null jika tidak dikenali.
|
|
48
|
+
*/
|
|
49
|
+
const detectAudioMimetypeFromBuffer = (buf) => {
|
|
50
|
+
if (!buf || buf.length < 12) return null;
|
|
51
|
+
// OGG
|
|
52
|
+
if (buf[0] === 0x4F && buf[1] === 0x67 && buf[2] === 0x67 && buf[3] === 0x53)
|
|
53
|
+
return 'audio/ogg; codecs=opus';
|
|
54
|
+
// MP3 (ID3 tag atau sync bits)
|
|
55
|
+
if ((buf[0] === 0x49 && buf[1] === 0x44 && buf[2] === 0x33) ||
|
|
56
|
+
(buf[0] === 0xFF && (buf[1] & 0xE0) === 0xE0))
|
|
57
|
+
return 'audio/mpeg';
|
|
58
|
+
// MP4/M4A (ftyp box)
|
|
59
|
+
if (buf[4] === 0x66 && buf[5] === 0x74 && buf[6] === 0x79 && buf[7] === 0x70)
|
|
60
|
+
return 'audio/mp4';
|
|
61
|
+
// RIFF/WAV
|
|
62
|
+
if (buf[0] === 0x52 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x46 &&
|
|
63
|
+
buf[8] === 0x57 && buf[9] === 0x41 && buf[10] === 0x56 && buf[11] === 0x45)
|
|
64
|
+
return 'audio/wav';
|
|
65
|
+
// FLAC
|
|
66
|
+
if (buf[0] === 0x66 && buf[1] === 0x4C && buf[2] === 0x61 && buf[3] === 0x43)
|
|
67
|
+
return 'audio/flac';
|
|
68
|
+
// WEBM/MKV
|
|
69
|
+
if (buf[0] === 0x1A && buf[1] === 0x45 && buf[2] === 0xDF && buf[3] === 0xA3)
|
|
70
|
+
return 'audio/webm';
|
|
71
|
+
// AMR
|
|
72
|
+
if (buf[0] === 0x23 && buf[1] === 0x21 && buf[2] === 0x41 && buf[3] === 0x4D &&
|
|
73
|
+
buf[4] === 0x52)
|
|
74
|
+
return 'audio/amr';
|
|
75
|
+
return null;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Deteksi mimetype audio secara otomatis dari media input.
|
|
79
|
+
* Cek: 1) ekstensi URL/path, 2) magic bytes buffer, 3) fallback ke ogg/opus.
|
|
80
|
+
*/
|
|
81
|
+
const detectAudioMimetype = async (media) => {
|
|
82
|
+
// Cek ekstensi dari URL atau path string
|
|
83
|
+
if (typeof media === 'string' || (media && typeof media === 'object' && 'url' in media)) {
|
|
84
|
+
const urlStr = typeof media === 'string' ? media : media.url?.toString?.() ?? '';
|
|
85
|
+
// Ambil path tanpa query string, lalu cari semua ekstensi
|
|
86
|
+
const pathOnly = urlStr.split('?')[0];
|
|
87
|
+
// Cek ekstensi terakhir (.m4a, .mp3, dst)
|
|
88
|
+
const extMatch = pathOnly.match(/\.([a-zA-Z0-9]{2,5})(?:[^/]*)?$/);
|
|
89
|
+
if (extMatch) {
|
|
90
|
+
const ext = extMatch[1].toLowerCase();
|
|
91
|
+
if (AUDIO_MIMETYPE_MAP[ext]) return AUDIO_MIMETYPE_MAP[ext];
|
|
92
|
+
}
|
|
93
|
+
// Fallback: scan semua segmen path untuk ekstensi audio yang dikenal
|
|
94
|
+
// Contoh: ".plus.aac.ep.m4a" → cek tiap segment dari belakang
|
|
95
|
+
const segments = pathOnly.split('.');
|
|
96
|
+
for (let i = segments.length - 1; i >= 0; i--) {
|
|
97
|
+
const seg = segments[i].toLowerCase().split('/')[0].split('?')[0];
|
|
98
|
+
if (AUDIO_MIMETYPE_MAP[seg]) return AUDIO_MIMETYPE_MAP[seg];
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Cek magic bytes jika Buffer
|
|
102
|
+
if (Buffer.isBuffer(media)) {
|
|
103
|
+
const detected = detectAudioMimetypeFromBuffer(media);
|
|
104
|
+
if (detected) return detected;
|
|
105
|
+
}
|
|
106
|
+
// Fallback: default ogg/opus
|
|
107
|
+
return MIMETYPE_MAP.audio;
|
|
108
|
+
};
|
|
22
109
|
const MessageTypeProto = {
|
|
23
110
|
image: WAProto.Message.ImageMessage,
|
|
24
111
|
video: WAProto.Message.VideoMessage,
|
|
@@ -26,28 +113,6 @@ const MessageTypeProto = {
|
|
|
26
113
|
sticker: WAProto.Message.StickerMessage,
|
|
27
114
|
document: WAProto.Message.DocumentMessage
|
|
28
115
|
};
|
|
29
|
-
const mediaAnnotation = [
|
|
30
|
-
{
|
|
31
|
-
polygonVertices: [
|
|
32
|
-
{ x: 60.71664810180664, y: -36.39784622192383 },
|
|
33
|
-
{ x: -16.710189819335938, y: 49.263675689697266 },
|
|
34
|
-
{ x: -56.585853576660156, y: 37.85963439941406 },
|
|
35
|
-
{ x: 20.840980529785156, y: -47.80188751220703 }
|
|
36
|
-
],
|
|
37
|
-
newsletter: {
|
|
38
|
-
// Lia@Note 03-02-26 --- You can change jid, message id, and name via .env (≧▽≦)
|
|
39
|
-
newsletterJid: process.env.NEWSLETTER_ID ||
|
|
40
|
-
Buffer.from('313230333633343034303036363434313339406e6577736c6574746572', 'hex').toString(),
|
|
41
|
-
serverMessageId: process.env.NEWSLETTER_MESSAGE_ID ||
|
|
42
|
-
Buffer.from('313033', 'hex').toString(),
|
|
43
|
-
newsletterName: process.env.NEWSLETTER_NAME ||
|
|
44
|
-
Buffer.from('f09d96b2f09d978df09d96baf09d978bf09d96bff09d96baf09d9785f09d9785', 'hex').toString(),
|
|
45
|
-
contentType: proto.ContextInfo.ForwardedNewsletterMessageInfo.ContentType.UPDATE,
|
|
46
|
-
accessibilityText: process.env.NEWSLETTER_ACCESSIBILITY_TEXT ||
|
|
47
|
-
'violetics'
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
];
|
|
51
116
|
/**
|
|
52
117
|
* Uses a regex to test whether the string contains a URL, and returns the URL if it does.
|
|
53
118
|
* @param text eg. hello https://google.com
|
|
@@ -96,18 +161,15 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
96
161
|
...message,
|
|
97
162
|
media: message[mediaType]
|
|
98
163
|
};
|
|
99
|
-
if (uploadData.image || uploadData.video) {
|
|
100
|
-
uploadData.annotations = mediaAnnotation;
|
|
101
|
-
}
|
|
102
164
|
delete uploadData[mediaType];
|
|
103
165
|
// check if cacheable + generate cache key
|
|
104
166
|
const cacheableKey = typeof uploadData.media === 'object' &&
|
|
105
167
|
'url' in uploadData.media &&
|
|
106
168
|
!!uploadData.media.url &&
|
|
107
169
|
!!options.mediaCache &&
|
|
108
|
-
mediaType + ':' + uploadData.media.url;
|
|
170
|
+
mediaType + ':' + uploadData.media.url.toString();
|
|
109
171
|
if (mediaType === 'document' && !uploadData.fileName) {
|
|
110
|
-
uploadData.fileName =
|
|
172
|
+
uploadData.fileName = 'file';
|
|
111
173
|
}
|
|
112
174
|
if (!uploadData.mimetype) {
|
|
113
175
|
uploadData.mimetype = MIMETYPE_MAP[mediaType];
|
|
@@ -123,96 +185,45 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
123
185
|
}
|
|
124
186
|
}
|
|
125
187
|
const isNewsletter = !!options.jid && isJidNewsletter(options.jid);
|
|
188
|
+
if (isNewsletter) options.newsletter = true;
|
|
126
189
|
const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined';
|
|
127
190
|
const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData['jpegThumbnail'] === 'undefined';
|
|
128
|
-
const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true;
|
|
129
|
-
const requiresAudioBackground = options.backgroundColor && mediaType === 'audio'
|
|
191
|
+
const requiresWaveformProcessing = mediaType === 'audio' && (uploadData.ptt === true || !!options.backgroundColor);
|
|
192
|
+
const requiresAudioBackground = options.backgroundColor && mediaType === 'audio';
|
|
130
193
|
const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation || requiresWaveformProcessing;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
timeoutMs: options.mediaUploadTimeoutMs,
|
|
142
|
-
newsletter: isNewsletter
|
|
143
|
-
});
|
|
144
|
-
logger?.debug({ mediaType, cacheableKey }, 'uploaded media');
|
|
145
|
-
return result;
|
|
146
|
-
})(),
|
|
147
|
-
(async () => {
|
|
148
|
-
try {
|
|
149
|
-
if (requiresThumbnailComputation) {
|
|
150
|
-
const { thumbnail } = await generateThumbnail(filePath, mediaType, options);
|
|
151
|
-
uploadData.jpegThumbnail = thumbnail;
|
|
152
|
-
logger?.debug('generated thumbnail');
|
|
153
|
-
}
|
|
154
|
-
if (requiresDurationComputation) {
|
|
155
|
-
uploadData.seconds = await getAudioDuration(filePath);
|
|
156
|
-
logger?.debug('computed audio duration');
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
catch (error) {
|
|
160
|
-
logger?.warn({ trace: error.stack }, 'failed to obtain extra info');
|
|
161
|
-
}
|
|
162
|
-
})()
|
|
163
|
-
]).finally(async () => {
|
|
164
|
-
try {
|
|
165
|
-
await fs.unlink(filePath);
|
|
166
|
-
logger?.debug('removed tmp files');
|
|
167
|
-
}
|
|
168
|
-
catch (error) {
|
|
169
|
-
logger?.warn('failed to remove tmp file');
|
|
194
|
+
let streamResult;
|
|
195
|
+
try {
|
|
196
|
+
streamResult = await (options.newsletter ? prepareStream : encryptedStream)(
|
|
197
|
+
uploadData.media,
|
|
198
|
+
options.mediaTypeOverride || mediaType,
|
|
199
|
+
{
|
|
200
|
+
logger,
|
|
201
|
+
saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
|
|
202
|
+
opts: options.options,
|
|
203
|
+
isPtt: uploadData.ptt,
|
|
170
204
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
...uploadData
|
|
181
|
-
})
|
|
182
|
-
});
|
|
183
|
-
if (uploadData.ptv) {
|
|
184
|
-
obj.ptvMessage = obj.videoMessage;
|
|
185
|
-
delete obj.videoMessage;
|
|
186
|
-
}
|
|
187
|
-
if (obj.stickerMessage) {
|
|
188
|
-
obj.stickerMessage.stickerSentTs = Date.now();
|
|
189
|
-
}
|
|
190
|
-
if (cacheableKey) {
|
|
191
|
-
logger?.debug({ cacheableKey }, 'set cache');
|
|
192
|
-
await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
|
|
193
|
-
}
|
|
194
|
-
return obj;
|
|
195
|
-
}
|
|
196
|
-
const { mediaKey, encFilePath, originalFilePath, fileEncSha256, fileSha256, fileLength } = await encryptedStream(uploadData.media, options.mediaTypeOverride || mediaType, {
|
|
197
|
-
logger,
|
|
198
|
-
saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
|
|
199
|
-
opts: options.options
|
|
200
|
-
});
|
|
201
|
-
const fileEncSha256B64 = fileEncSha256.toString('base64');
|
|
202
|
-
const [{ mediaUrl, directPath }] = await Promise.all([
|
|
205
|
+
);
|
|
206
|
+
} catch (streamErr) {
|
|
207
|
+
throw streamErr;
|
|
208
|
+
}
|
|
209
|
+
const { mediaKey, encWriteStream, bodyPath, fileEncSha256, fileSha256, fileLength, didSaveToTmpPath } = streamResult;
|
|
210
|
+
|
|
211
|
+
const fileEncSha256B64 = (options.newsletter ? fileSha256 : fileEncSha256 ?? fileSha256).toString('base64');
|
|
212
|
+
|
|
213
|
+
const [{ mediaUrl, directPath, handle: uploadHandle }] = await Promise.all([
|
|
203
214
|
(async () => {
|
|
204
|
-
const result = await options.upload(
|
|
215
|
+
const result = await options.upload(encWriteStream, {
|
|
205
216
|
fileEncSha256B64,
|
|
206
217
|
mediaType,
|
|
207
|
-
timeoutMs: options.mediaUploadTimeoutMs
|
|
218
|
+
timeoutMs: options.mediaUploadTimeoutMs,
|
|
219
|
+
newsletter: !!options.newsletter
|
|
208
220
|
});
|
|
209
|
-
logger?.debug({ mediaType, cacheableKey }, 'uploaded media');
|
|
210
221
|
return result;
|
|
211
222
|
})(),
|
|
212
223
|
(async () => {
|
|
213
224
|
try {
|
|
214
225
|
if (requiresThumbnailComputation) {
|
|
215
|
-
const { thumbnail, originalImageDimensions } = await generateThumbnail(
|
|
226
|
+
const { thumbnail, originalImageDimensions } = await generateThumbnail(bodyPath, mediaType, options);
|
|
216
227
|
uploadData.jpegThumbnail = thumbnail;
|
|
217
228
|
if (!uploadData.width && originalImageDimensions) {
|
|
218
229
|
uploadData.width = originalImageDimensions.width;
|
|
@@ -222,53 +233,71 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
222
233
|
logger?.debug('generated thumbnail');
|
|
223
234
|
}
|
|
224
235
|
if (requiresDurationComputation) {
|
|
225
|
-
|
|
236
|
+
try {
|
|
237
|
+
if (bodyPath) {
|
|
238
|
+
uploadData.seconds = await getAudioDuration(bodyPath, uploadData.mimetype);
|
|
239
|
+
}
|
|
240
|
+
} catch (err) {
|
|
241
|
+
uploadData.seconds = 0;
|
|
242
|
+
}
|
|
243
|
+
// Pastikan seconds valid — NaN/undefined bikin WhatsApp tampilkan Loading...
|
|
244
|
+
if (typeof uploadData.seconds !== 'number' || isNaN(uploadData.seconds)) {
|
|
245
|
+
uploadData.seconds = 0;
|
|
246
|
+
}
|
|
226
247
|
logger?.debug('computed audio duration');
|
|
227
248
|
}
|
|
228
249
|
if (requiresWaveformProcessing) {
|
|
229
|
-
|
|
230
|
-
|
|
250
|
+
try {
|
|
251
|
+
uploadData.waveform = await getAudioWaveform(bodyPath || encWriteStream, logger);
|
|
252
|
+
} catch (err) {
|
|
253
|
+
}
|
|
254
|
+
if (!uploadData.waveform) {
|
|
255
|
+
uploadData.waveform = new Uint8Array([0,99,0,99,0,99,0,99,88,99,0,99,0,55,0,99,0,99,0,99,0,99,0,99,88,99,0,99,0,55,0,99]);
|
|
256
|
+
}
|
|
231
257
|
}
|
|
232
258
|
if (requiresAudioBackground) {
|
|
233
259
|
uploadData.backgroundArgb = await assertColor(options.backgroundColor);
|
|
234
260
|
logger?.debug('computed backgroundColor audio status');
|
|
235
261
|
}
|
|
236
|
-
}
|
|
237
|
-
catch (error) {
|
|
262
|
+
} catch (error) {
|
|
238
263
|
logger?.warn({ trace: error.stack }, 'failed to obtain extra info');
|
|
239
264
|
}
|
|
240
265
|
})()
|
|
241
266
|
]).finally(async () => {
|
|
242
267
|
try {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
await fs.unlink(originalFilePath);
|
|
268
|
+
if (!Buffer.isBuffer(encWriteStream)) {
|
|
269
|
+
encWriteStream.destroy?.();
|
|
246
270
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
271
|
+
if (didSaveToTmpPath && bodyPath) {
|
|
272
|
+
await fs.unlink(bodyPath).catch(() => {});
|
|
273
|
+
}
|
|
274
|
+
} catch (error) {
|
|
250
275
|
logger?.warn('failed to remove tmp file');
|
|
251
276
|
}
|
|
252
277
|
});
|
|
253
|
-
|
|
254
|
-
const obj = proto.Message.create({
|
|
278
|
+
const obj = WAProto.Message.fromObject({
|
|
255
279
|
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
|
256
|
-
url: mediaUrl,
|
|
280
|
+
url: uploadHandle ? undefined : mediaUrl,
|
|
257
281
|
directPath,
|
|
258
|
-
mediaKey,
|
|
259
|
-
fileEncSha256,
|
|
282
|
+
mediaKey: mediaKey,
|
|
283
|
+
fileEncSha256: fileEncSha256,
|
|
260
284
|
fileSha256,
|
|
261
285
|
fileLength,
|
|
262
|
-
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
263
|
-
...uploadData
|
|
286
|
+
mediaKeyTimestamp: uploadHandle ? undefined : unixTimestampSeconds(),
|
|
287
|
+
...uploadData,
|
|
288
|
+
media: undefined,
|
|
289
|
+
...(options?.contextInfo ? { contextInfo: options.contextInfo } : {})
|
|
264
290
|
})
|
|
265
291
|
});
|
|
266
292
|
if (uploadData.ptv) {
|
|
267
293
|
obj.ptvMessage = obj.videoMessage;
|
|
268
294
|
delete obj.videoMessage;
|
|
269
295
|
}
|
|
270
|
-
|
|
271
|
-
|
|
296
|
+
// Attach uploadHandle so sendMessage can use it as media_id
|
|
297
|
+
if (uploadHandle) {
|
|
298
|
+
obj._uploadHandle = uploadHandle;
|
|
299
|
+
}
|
|
300
|
+
if (mediaType === 'audio') {
|
|
272
301
|
}
|
|
273
302
|
if (cacheableKey) {
|
|
274
303
|
logger?.debug({ cacheableKey }, 'set cache');
|
|
@@ -282,335 +311,13 @@ export const prepareDisappearingMessageSettingContent = (ephemeralExpiration) =>
|
|
|
282
311
|
ephemeralMessage: {
|
|
283
312
|
message: {
|
|
284
313
|
protocolMessage: {
|
|
285
|
-
type:
|
|
314
|
+
type: WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
|
|
286
315
|
ephemeralExpiration
|
|
287
316
|
}
|
|
288
317
|
}
|
|
289
318
|
}
|
|
290
319
|
};
|
|
291
|
-
return content;
|
|
292
|
-
};
|
|
293
|
-
// vltcs@changes 31-01-26 --- Extract product message into a standalone function so it can also be reused as the header for interactive messages
|
|
294
|
-
const prepareProductMessage = async (message, options) => {
|
|
295
|
-
if (!message.businessOwnerJid) {
|
|
296
|
-
throw new Boom('"businessOwnerJid" is missing from the content', { statusCode: 400 });
|
|
297
|
-
}
|
|
298
|
-
const { imageMessage } = await prepareWAMessageMedia({ image: message.image || message.product.productImage }, options);
|
|
299
|
-
// vltcs@changes 01-02-26 --- Add product message default value
|
|
300
|
-
const content = {
|
|
301
|
-
...message,
|
|
302
|
-
product: {
|
|
303
|
-
currencyCode: 'IDR',
|
|
304
|
-
priceAmount1000: 1000,
|
|
305
|
-
title: LIBRARY_NAME,
|
|
306
|
-
...message.product,
|
|
307
|
-
productImage: imageMessage
|
|
308
|
-
}
|
|
309
|
-
};
|
|
310
|
-
delete content.image;
|
|
311
|
-
return content;
|
|
312
|
-
};
|
|
313
|
-
/**
|
|
314
|
-
* Lia@Note 30-01-26
|
|
315
|
-
* ---
|
|
316
|
-
* Credits: Work on ensuring stickerPackMessage fields are valid by @jlucaso1 (https://github.com/jlucaso1).
|
|
317
|
-
* based on https://github.com/WhiskeySockets/Baileys/pull/1561
|
|
318
|
-
*/
|
|
319
|
-
const prepareStickerPackMessage = async (message, options) => {
|
|
320
|
-
const { cover, stickers = [], name = '📦 Sticker Pack', publisher = 'GitHub: itsliaaa', description = '🏷️ itsliaaa/baileys' } = message;
|
|
321
|
-
if (stickers.length > 60) {
|
|
322
|
-
throw new Boom('Sticker pack exceeds the maximum limit of 60 stickers', { statusCode: 400 });
|
|
323
|
-
}
|
|
324
|
-
if (stickers.length === 0) {
|
|
325
|
-
throw new Boom('Sticker pack must contain at least one sticker', { statusCode: 400 });
|
|
326
|
-
}
|
|
327
|
-
if (!cover) {
|
|
328
|
-
throw new Boom('Sticker pack must contain a cover', { statusCode: 400 });
|
|
329
|
-
}
|
|
330
|
-
// vltcs@changes 01-02-26 --- Add caching for sticker pack (similiar to prepareWAMessageMedia)
|
|
331
|
-
const cacheableKey = Array.isArray(stickers) &&
|
|
332
|
-
stickers.length &&
|
|
333
|
-
!!options.mediaCache &&
|
|
334
|
-
'sticker:' + stickers
|
|
335
|
-
.reduce((acc, x) => {
|
|
336
|
-
const url = typeof x.data === 'object' &&
|
|
337
|
-
'url' in x.data &&
|
|
338
|
-
!!x.data.url &&
|
|
339
|
-
x.data.url;
|
|
340
|
-
if (url) acc.push(url);
|
|
341
|
-
return acc;
|
|
342
|
-
}, [])
|
|
343
|
-
.join('@');
|
|
344
|
-
if (cacheableKey) {
|
|
345
|
-
const mediaBuff = await options.mediaCache.get(cacheableKey);
|
|
346
|
-
if (mediaBuff) {
|
|
347
|
-
options.logger?.debug({ cacheableKey }, 'got media cache hit');
|
|
348
|
-
return proto.Message.StickerPackMessage.decode(mediaBuff);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
const lib = await getImageProcessingLibrary();
|
|
352
|
-
const stickerPackIdValue = generateMessageIDV2();
|
|
353
|
-
const stickerData = {};
|
|
354
|
-
const stickerPromises = stickers.map(async (sticker, i) => {
|
|
355
|
-
const { stream } = await getStream(sticker.data);
|
|
356
|
-
const buffer = await toBuffer(stream);
|
|
357
|
-
let webpBuffer,
|
|
358
|
-
isAnimated = false;
|
|
359
|
-
const isWebP = isWebPBuffer(buffer);
|
|
360
|
-
if (isWebP) {
|
|
361
|
-
// Already WebP - preserve original to keep exif metadata and animation
|
|
362
|
-
webpBuffer = buffer;
|
|
363
|
-
isAnimated = isAnimatedWebP(buffer);
|
|
364
|
-
}
|
|
365
|
-
else if ('sharp' in lib && lib.sharp?.default) {
|
|
366
|
-
// Convert to WebP, preserving metadata
|
|
367
|
-
webpBuffer = await lib
|
|
368
|
-
.sharp
|
|
369
|
-
.default(buffer)
|
|
370
|
-
.resize(512, 512, { fit: 'inside' })
|
|
371
|
-
.webp({ quality: 80 })
|
|
372
|
-
.toBuffer();
|
|
373
|
-
// Non-WebP inputs converted to WebP are not animated
|
|
374
|
-
isAnimated = false;
|
|
375
|
-
}
|
|
376
|
-
else if ('image' in lib && lib.image?.Transformer) {
|
|
377
|
-
webpBuffer = await new lib
|
|
378
|
-
.image
|
|
379
|
-
.Transformer(buffer)
|
|
380
|
-
.resize(512, 512)
|
|
381
|
-
.webp(80);
|
|
382
|
-
// Non-WebP inputs converted to WebP are not animated
|
|
383
|
-
isAnimated = false;
|
|
384
|
-
}
|
|
385
|
-
else {
|
|
386
|
-
throw new Boom('No image processing library (sharp or @napi-rs/image) available for converting sticker to WebP. Either install sharp or @napi-rs/image or provide stickers in WebP format.');
|
|
387
|
-
}
|
|
388
|
-
if (webpBuffer.length > 1024 * 1024) {
|
|
389
|
-
throw new Boom(`Sticker at index ${i} exceeds the 1MB size limit`, { statusCode: 400 });
|
|
390
|
-
}
|
|
391
|
-
const hash = sha256(webpBuffer).toString('base64').replace(/\//g, '-');
|
|
392
|
-
const fileName = hash + '.webp';
|
|
393
|
-
stickerData[fileName] = [new Uint8Array(webpBuffer), { level: 0 }];
|
|
394
|
-
return {
|
|
395
|
-
fileName,
|
|
396
|
-
mimetype: 'image/webp',
|
|
397
|
-
isAnimated,
|
|
398
|
-
emojis: sticker.emojis || ['✨'],
|
|
399
|
-
accessibilityLabel: sticker.accessibilityLabel || ''
|
|
400
|
-
};
|
|
401
|
-
});
|
|
402
|
-
const stickerMetadata = await Promise.all(stickerPromises);
|
|
403
|
-
// Process and add cover/tray icon to the ZIP
|
|
404
|
-
const trayIconFileName = stickerPackIdValue + '.webp';
|
|
405
|
-
const { stream: coverStream } = await getStream(cover);
|
|
406
|
-
const coverBuffer = await toBuffer(coverStream);
|
|
407
|
-
let coverWebpBuffer;
|
|
408
|
-
const isCoverWebP = isWebPBuffer(coverBuffer);
|
|
409
|
-
if (isCoverWebP) {
|
|
410
|
-
// Already WebP - preserve original to keep exif metadata
|
|
411
|
-
coverWebpBuffer = coverBuffer;
|
|
412
|
-
}
|
|
413
|
-
else if ('sharp' in lib && lib.sharp?.default) {
|
|
414
|
-
coverWebpBuffer = await lib
|
|
415
|
-
.sharp
|
|
416
|
-
.default(coverBuffer)
|
|
417
|
-
.resize(512, 512, { fit: 'inside' })
|
|
418
|
-
.webp({ quality: 80 })
|
|
419
|
-
.toBuffer();
|
|
420
|
-
}
|
|
421
|
-
else if ('image' in lib && lib.image?.Transformer) {
|
|
422
|
-
coverWebpBuffer = await new lib
|
|
423
|
-
.image
|
|
424
|
-
.Transformer(coverBuffer)
|
|
425
|
-
.resize(512, 512)
|
|
426
|
-
.webp(80);
|
|
427
|
-
}
|
|
428
|
-
else {
|
|
429
|
-
throw new Boom('No image processing library (sharp or @napi-rs/image) available for converting cover to WebP. Either install sharp or @napi-rs/image or provide cover in WebP format.');
|
|
430
|
-
}
|
|
431
|
-
// Add cover to ZIP data
|
|
432
|
-
stickerData[trayIconFileName] = [new Uint8Array(coverWebpBuffer), { level: 0 }];
|
|
433
|
-
const zipBuffer = await new Promise((resolve, reject) => {
|
|
434
|
-
zip(stickerData, (error, data) => {
|
|
435
|
-
if (error) {
|
|
436
|
-
reject(error);
|
|
437
|
-
} else {
|
|
438
|
-
resolve(Buffer.from(data));
|
|
439
|
-
}
|
|
440
|
-
});
|
|
441
|
-
});
|
|
442
|
-
const stickerPackSize = zipBuffer.length;
|
|
443
|
-
const stickerPackUpload = await encryptedStream(zipBuffer, 'sticker-pack', {
|
|
444
|
-
logger: options.logger,
|
|
445
|
-
opts: options.options
|
|
446
|
-
});
|
|
447
|
-
const stickerPackUploadResult = await options.upload(stickerPackUpload.encFilePath, {
|
|
448
|
-
fileEncSha256B64: stickerPackUpload.fileEncSha256.toString('base64'),
|
|
449
|
-
mediaType: 'sticker-pack',
|
|
450
|
-
timeoutMs: options.mediaUploadTimeoutMs
|
|
451
|
-
});
|
|
452
|
-
await fs.unlink(stickerPackUpload.encFilePath);
|
|
453
|
-
const obj = {
|
|
454
|
-
name: name,
|
|
455
|
-
publisher: publisher,
|
|
456
|
-
stickerPackId: stickerPackIdValue,
|
|
457
|
-
packDescription: description,
|
|
458
|
-
stickerPackOrigin: proto.Message.StickerPackMessage.StickerPackOrigin.USER_CREATED,
|
|
459
|
-
stickerPackSize: stickerPackSize,
|
|
460
|
-
stickers: stickerMetadata,
|
|
461
|
-
fileSha256: stickerPackUpload.fileSha256,
|
|
462
|
-
fileEncSha256: stickerPackUpload.fileEncSha256,
|
|
463
|
-
mediaKey: stickerPackUpload.mediaKey,
|
|
464
|
-
directPath: stickerPackUploadResult.directPath,
|
|
465
|
-
fileLength: stickerPackUpload.fileLength,
|
|
466
|
-
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
467
|
-
trayIconFileName: trayIconFileName
|
|
468
|
-
};
|
|
469
|
-
try {
|
|
470
|
-
// Reuse the cover buffer we already processed for thumbnail generation
|
|
471
|
-
let thumbnailBuffer;
|
|
472
|
-
if ('sharp' in lib && lib.sharp?.default) {
|
|
473
|
-
thumbnailBuffer = await lib
|
|
474
|
-
.sharp
|
|
475
|
-
.default(coverBuffer)
|
|
476
|
-
.resize(252, 252)
|
|
477
|
-
.jpeg()
|
|
478
|
-
.toBuffer();
|
|
479
|
-
}
|
|
480
|
-
if ('image' in lib && lib.image?.Transformer) {
|
|
481
|
-
thumbnailBuffer = await new lib
|
|
482
|
-
.image
|
|
483
|
-
.Transformer(coverBuffer)
|
|
484
|
-
.resize(252, 252)
|
|
485
|
-
.jpeg();
|
|
486
|
-
}
|
|
487
|
-
else if ('jimp' in lib && lib.jimp?.Jimp) {
|
|
488
|
-
const jimpImage = await lib.jimp.Jimp.read(coverBuffer);
|
|
489
|
-
thumbnailBuffer = await jimpImage
|
|
490
|
-
.resize({ w: 252, h: 252 })
|
|
491
|
-
.getBuffer('image/jpeg');
|
|
492
|
-
}
|
|
493
|
-
else {
|
|
494
|
-
throw new Error('No image processing library available for thumbnail generation');
|
|
495
|
-
}
|
|
496
|
-
if (!thumbnailBuffer || thumbnailBuffer.length === 0) {
|
|
497
|
-
throw new Error('Failed to generate thumbnail buffer');
|
|
498
|
-
}
|
|
499
|
-
const thumbUpload = await encryptedStream(thumbnailBuffer, 'thumbnail-sticker-pack', {
|
|
500
|
-
logger: options.logger,
|
|
501
|
-
opts: options.options,
|
|
502
|
-
mediaKey: stickerPackUpload.mediaKey // Use same mediaKey as the sticker pack ZIP
|
|
503
|
-
});
|
|
504
|
-
const thumbUploadResult = await options.upload(thumbUpload.encFilePath, {
|
|
505
|
-
fileEncSha256B64: thumbUpload.fileEncSha256.toString('base64'),
|
|
506
|
-
mediaType: 'thumbnail-sticker-pack',
|
|
507
|
-
timeoutMs: options.mediaUploadTimeoutMs
|
|
508
|
-
});
|
|
509
|
-
await fs.unlink(thumbUpload.encFilePath);
|
|
510
|
-
Object.assign(obj, {
|
|
511
|
-
thumbnailDirectPath: thumbUploadResult.directPath,
|
|
512
|
-
thumbnailSha256: thumbUpload.fileSha256,
|
|
513
|
-
thumbnailEncSha256: thumbUpload.fileEncSha256,
|
|
514
|
-
thumbnailHeight: 252,
|
|
515
|
-
thumbnailWidth: 252,
|
|
516
|
-
imageDataHash: sha256(thumbnailBuffer).toString('base64')
|
|
517
|
-
});
|
|
518
|
-
}
|
|
519
|
-
catch (error) {
|
|
520
|
-
options.logger?.warn?.(`Thumbnail generation failed: ${error}`);
|
|
521
|
-
}
|
|
522
|
-
const content = obj;
|
|
523
|
-
if (cacheableKey) {
|
|
524
|
-
options.logger?.debug({ cacheableKey }, 'set cache');
|
|
525
|
-
await options.mediaCache.set(cacheableKey, WAProto.Message.StickerPackMessage.encode(content).finish());
|
|
526
|
-
}
|
|
527
|
-
return WAProto.Message.StickerPackMessage.fromObject(content);
|
|
528
|
-
};
|
|
529
|
-
// vltcs@changes 30-01-26 --- Add native flow button helper for interactive message
|
|
530
|
-
const prepareNativeFlowButtons = (message) => {
|
|
531
|
-
const buttons = message.nativeFlow
|
|
532
|
-
const isButtonsFieldArray = Array.isArray(buttons);
|
|
533
|
-
const correctedField = isButtonsFieldArray ? buttons : buttons.buttons;
|
|
534
|
-
const messageParamsJson = {};
|
|
535
|
-
// vltcs@changes 31-01-26 --- Add coupon and options inside interactive message
|
|
536
|
-
if (hasOptionalProperty(message, 'couponCode') && !!message.couponCode) {
|
|
537
|
-
Object.assign(messageParamsJson, {
|
|
538
|
-
limited_time_offer: {
|
|
539
|
-
text: message.couponText || LIBRARY_NAME,
|
|
540
|
-
url: message.couponUrl || DONATE_URL, // Lia@Note 02-02-26 --- Apologies if this feels cheeky, just a fallback
|
|
541
|
-
copy_code: message.couponCode,
|
|
542
|
-
expiration_time: Date.now() * 2
|
|
543
|
-
}
|
|
544
|
-
});
|
|
545
|
-
}
|
|
546
|
-
if (hasOptionalProperty(message, 'optionText') && !!message.optionText) {
|
|
547
|
-
Object.assign(messageParamsJson, {
|
|
548
|
-
bottom_sheet: {
|
|
549
|
-
in_thread_buttons_limit: 1,
|
|
550
|
-
divider_indices: Array.from(
|
|
551
|
-
{ length: correctedField.length },
|
|
552
|
-
(_, index) => index
|
|
553
|
-
),
|
|
554
|
-
list_title: message.optionTitle || '📄 Select Options',
|
|
555
|
-
button_title: message.optionText
|
|
556
|
-
}
|
|
557
|
-
});
|
|
558
|
-
}
|
|
559
|
-
return {
|
|
560
|
-
buttons: correctedField.map(button => {
|
|
561
|
-
const buttonText = button.text || button.buttonText;
|
|
562
|
-
if (hasOptionalProperty(button, 'id') && !!button.id) {
|
|
563
|
-
return {
|
|
564
|
-
name: 'quick_reply',
|
|
565
|
-
buttonParamsJson: JSON.stringify({
|
|
566
|
-
display_text: buttonText || '👉🏻 Click',
|
|
567
|
-
id: button.id
|
|
568
|
-
})
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
else if (hasOptionalProperty(button, 'copy') && !!button.copy) {
|
|
572
|
-
return {
|
|
573
|
-
name: 'cta_copy',
|
|
574
|
-
buttonParamsJson: JSON.stringify({
|
|
575
|
-
display_text: buttonText || '📋 Copy',
|
|
576
|
-
copy_code: button.copy
|
|
577
|
-
})
|
|
578
|
-
};
|
|
579
|
-
}
|
|
580
|
-
else if (hasOptionalProperty(button, 'url') && !!button.url) {
|
|
581
|
-
return {
|
|
582
|
-
name: 'cta_url',
|
|
583
|
-
buttonParamsJson: JSON.stringify({
|
|
584
|
-
display_text: buttonText || '🌐 Visit',
|
|
585
|
-
url: button.url,
|
|
586
|
-
merchant_url: button.url
|
|
587
|
-
})
|
|
588
|
-
};
|
|
589
|
-
}
|
|
590
|
-
else if (hasOptionalProperty(button, 'call') && !!button.call) {
|
|
591
|
-
return {
|
|
592
|
-
name: 'cta_call',
|
|
593
|
-
buttonParamsJson: JSON.stringify({
|
|
594
|
-
display_text: buttonText || '📞 Call',
|
|
595
|
-
phone_number: button.call
|
|
596
|
-
})
|
|
597
|
-
};
|
|
598
|
-
}
|
|
599
|
-
// vltcs@changes 12-03-26 --- Add "single_select" shortcut \(°o°)/
|
|
600
|
-
else if (hasOptionalProperty(button, 'sections') && !!button.sections) {
|
|
601
|
-
return {
|
|
602
|
-
name: 'single_select',
|
|
603
|
-
buttonParamsJson: JSON.stringify({
|
|
604
|
-
title: buttonText || '📋 Select',
|
|
605
|
-
sections: button.sections
|
|
606
|
-
})
|
|
607
|
-
};
|
|
608
|
-
}
|
|
609
|
-
return button;
|
|
610
|
-
}),
|
|
611
|
-
messageParamsJson: JSON.stringify(messageParamsJson),
|
|
612
|
-
messageVersion: 1
|
|
613
|
-
};
|
|
320
|
+
return WAProto.Message.fromObject(content);
|
|
614
321
|
};
|
|
615
322
|
/**
|
|
616
323
|
* Generate forwarded message content like WA does
|
|
@@ -618,7 +325,7 @@ const prepareNativeFlowButtons = (message) => {
|
|
|
618
325
|
* @param options.forceForward will show the message as forwarded even if it is from you
|
|
619
326
|
*/
|
|
620
327
|
export const generateForwardMessageContent = (message, forceForward) => {
|
|
621
|
-
let content = message.message
|
|
328
|
+
let content = message.message;
|
|
622
329
|
if (!content) {
|
|
623
330
|
throw new Boom('no content in message', { statusCode: 400 });
|
|
624
331
|
}
|
|
@@ -634,24 +341,12 @@ export const generateForwardMessageContent = (message, forceForward) => {
|
|
|
634
341
|
key = 'extendedTextMessage';
|
|
635
342
|
}
|
|
636
343
|
const key_ = content?.[key];
|
|
637
|
-
const contextInfo = {};
|
|
638
344
|
if (score > 0) {
|
|
639
|
-
contextInfo
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
// so the server knows where to find the original media
|
|
644
|
-
const remoteJid = message.key?.remoteJid;
|
|
645
|
-
if (remoteJid && isJidNewsletter(remoteJid)) {
|
|
646
|
-
contextInfo.forwardedNewsletterMessageInfo = {
|
|
647
|
-
newsletterJid: remoteJid,
|
|
648
|
-
serverMessageId: message.key?.server_id ? parseInt(message.key.server_id) : null,
|
|
649
|
-
newsletterName: null
|
|
650
|
-
};
|
|
651
|
-
// strip messageContextInfo (contains messageSecret etc.) as WA Web does
|
|
652
|
-
delete content.messageContextInfo;
|
|
345
|
+
key_.contextInfo = { forwardingScore: score, isForwarded: true };
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
key_.contextInfo = {};
|
|
653
349
|
}
|
|
654
|
-
key_.contextInfo = contextInfo;
|
|
655
350
|
return content;
|
|
656
351
|
};
|
|
657
352
|
export const hasNonNullishProperty = (message, key) => {
|
|
@@ -661,39 +356,13 @@ export const hasNonNullishProperty = (message, key) => {
|
|
|
661
356
|
message[key] !== null &&
|
|
662
357
|
message[key] !== undefined);
|
|
663
358
|
};
|
|
664
|
-
|
|
665
|
-
return typeof obj === 'object' &&
|
|
666
|
-
|
|
667
|
-
key in obj &&
|
|
668
|
-
obj[key] !== null;
|
|
669
|
-
};
|
|
670
|
-
// vltcs@changes 06-02-26 --- Validate album message media to avoid bug 👀
|
|
671
|
-
export const hasValidAlbumMedia = (message) => {
|
|
672
|
-
return Boolean(message.imageMessage ||
|
|
673
|
-
message.videoMessage);
|
|
674
|
-
};
|
|
675
|
-
export const hasValidInteractiveHeader = (message) => {
|
|
676
|
-
return Boolean(message.imageMessage ||
|
|
677
|
-
message.videoMessage ||
|
|
678
|
-
message.documentMessage ||
|
|
679
|
-
message.productMessage ||
|
|
680
|
-
message.locationMessage);
|
|
681
|
-
};
|
|
682
|
-
// vltcs@changes 30-01-26 --- Validate carousel cards header to avoid bug 👀
|
|
683
|
-
export const hasValidCarouselHeader = (message) => {
|
|
684
|
-
return Boolean(message.imageMessage ||
|
|
685
|
-
message.videoMessage ||
|
|
686
|
-
message.productMessage);
|
|
687
|
-
};
|
|
359
|
+
function hasOptionalProperty(obj, key) {
|
|
360
|
+
return typeof obj === 'object' && obj !== null && key in obj && obj[key] !== null;
|
|
361
|
+
}
|
|
688
362
|
export const generateWAMessageContent = async (message, options) => {
|
|
689
363
|
var _a, _b;
|
|
690
364
|
let m = {};
|
|
691
|
-
|
|
692
|
-
if (hasNonNullishProperty(message, 'raw')) {
|
|
693
|
-
delete message.raw;
|
|
694
|
-
return proto.Message.create(message);
|
|
695
|
-
}
|
|
696
|
-
else if (hasNonNullishProperty(message, 'text')) {
|
|
365
|
+
if (hasNonNullishProperty(message, 'text')) {
|
|
697
366
|
const extContent = { text: message.text };
|
|
698
367
|
let urlInfo = message.linkPreview;
|
|
699
368
|
if (typeof urlInfo === 'undefined') {
|
|
@@ -725,58 +394,65 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
725
394
|
m.extendedTextMessage = extContent;
|
|
726
395
|
}
|
|
727
396
|
else if (hasNonNullishProperty(message, 'contacts')) {
|
|
728
|
-
const
|
|
729
|
-
const contactLen = contacts.contacts.length;
|
|
397
|
+
const contactLen = message.contacts.contacts.length;
|
|
730
398
|
if (!contactLen) {
|
|
731
399
|
throw new Boom('require atleast 1 contact', { statusCode: 400 });
|
|
732
400
|
}
|
|
733
401
|
if (contactLen === 1) {
|
|
734
|
-
m.contactMessage = contacts.contacts[0];
|
|
402
|
+
m.contactMessage = WAProto.Message.ContactMessage.create(message.contacts.contacts[0]);
|
|
735
403
|
}
|
|
736
404
|
else {
|
|
737
|
-
m.contactsArrayMessage = contacts;
|
|
405
|
+
m.contactsArrayMessage = WAProto.Message.ContactsArrayMessage.create(message.contacts);
|
|
738
406
|
}
|
|
739
407
|
}
|
|
740
408
|
else if (hasNonNullishProperty(message, 'location')) {
|
|
741
|
-
|
|
409
|
+
if (message.live) {
|
|
410
|
+
m.liveLocationMessage = WAProto.Message.LiveLocationMessage.create(message.location);
|
|
411
|
+
} else {
|
|
412
|
+
m.locationMessage = WAProto.Message.LocationMessage.create(message.location);
|
|
413
|
+
}
|
|
414
|
+
const locType = message.live ? 'liveLocationMessage' : 'locationMessage';
|
|
415
|
+
if (m[locType]) {
|
|
416
|
+
m[locType].contextInfo = {
|
|
417
|
+
...(message.contextInfo || {}),
|
|
418
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
419
|
+
};
|
|
420
|
+
}
|
|
742
421
|
}
|
|
743
422
|
else if (hasNonNullishProperty(message, 'react')) {
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
react.senderTimestampMs = Date.now();
|
|
423
|
+
if (!message.react.senderTimestampMs) {
|
|
424
|
+
message.react.senderTimestampMs = Date.now();
|
|
747
425
|
}
|
|
748
|
-
m.reactionMessage = react;
|
|
426
|
+
m.reactionMessage = WAProto.Message.ReactionMessage.create(message.react);
|
|
749
427
|
}
|
|
750
428
|
else if (hasNonNullishProperty(message, 'delete')) {
|
|
751
429
|
m.protocolMessage = {
|
|
752
430
|
key: message.delete,
|
|
753
|
-
type:
|
|
431
|
+
type: WAProto.Message.ProtocolMessage.Type.REVOKE
|
|
754
432
|
};
|
|
755
433
|
}
|
|
756
434
|
else if (hasNonNullishProperty(message, 'forward')) {
|
|
757
435
|
m = generateForwardMessageContent(message.forward, message.force);
|
|
758
436
|
}
|
|
759
437
|
else if (hasNonNullishProperty(message, 'disappearingMessagesInChat')) {
|
|
760
|
-
const
|
|
761
|
-
|
|
762
|
-
? disappearingMessagesInChat
|
|
438
|
+
const exp = typeof message.disappearingMessagesInChat === 'boolean'
|
|
439
|
+
? message.disappearingMessagesInChat
|
|
763
440
|
? WA_DEFAULT_EPHEMERAL
|
|
764
441
|
: 0
|
|
765
|
-
: disappearingMessagesInChat;
|
|
442
|
+
: message.disappearingMessagesInChat;
|
|
766
443
|
m = prepareDisappearingMessageSettingContent(exp);
|
|
767
444
|
}
|
|
768
445
|
else if (hasNonNullishProperty(message, 'groupInvite')) {
|
|
769
|
-
const { groupInvite } = message; // vltcs@changes 04-02-26 --- Destructured for readability & cleaner access (✷‿✷)
|
|
770
446
|
m.groupInviteMessage = {};
|
|
771
|
-
m.groupInviteMessage.inviteCode = groupInvite.inviteCode;
|
|
772
|
-
m.groupInviteMessage.inviteExpiration = groupInvite.inviteExpiration;
|
|
773
|
-
m.groupInviteMessage.caption = groupInvite.text;
|
|
774
|
-
m.groupInviteMessage.groupJid = groupInvite.jid;
|
|
775
|
-
m.groupInviteMessage.groupName = groupInvite.subject;
|
|
447
|
+
m.groupInviteMessage.inviteCode = message.groupInvite.inviteCode;
|
|
448
|
+
m.groupInviteMessage.inviteExpiration = message.groupInvite.inviteExpiration;
|
|
449
|
+
m.groupInviteMessage.caption = message.groupInvite.text;
|
|
450
|
+
m.groupInviteMessage.groupJid = message.groupInvite.jid;
|
|
451
|
+
m.groupInviteMessage.groupName = message.groupInvite.subject;
|
|
776
452
|
//TODO: use built-in interface and get disappearing mode info etc.
|
|
777
453
|
//TODO: cache / use store!?
|
|
778
454
|
if (options.getProfilePicUrl) {
|
|
779
|
-
const pfpUrl = await options.getProfilePicUrl(groupInvite.jid, 'preview');
|
|
455
|
+
const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid, 'preview');
|
|
780
456
|
if (pfpUrl) {
|
|
781
457
|
const resp = await fetch(pfpUrl, { method: 'GET', dispatcher: options?.options?.dispatcher });
|
|
782
458
|
if (resp.ok) {
|
|
@@ -786,137 +462,206 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
786
462
|
}
|
|
787
463
|
}
|
|
788
464
|
}
|
|
789
|
-
else if (hasNonNullishProperty(message, 'stickers')) {
|
|
790
|
-
m.stickerPackMessage = await prepareStickerPackMessage(message, options);
|
|
791
|
-
}
|
|
792
465
|
else if (hasNonNullishProperty(message, 'pin')) {
|
|
793
466
|
m.pinInChatMessage = {};
|
|
794
467
|
m.messageContextInfo = {};
|
|
795
|
-
m.pinInChatMessage.key = message.pin;
|
|
796
|
-
m.pinInChatMessage.type = message.type;
|
|
797
|
-
m.pinInChatMessage.senderTimestampMs = Date.now();
|
|
798
|
-
m.messageContextInfo.messageAddOnDurationInSecs = message.type === 1 ? message.time || 86400 : 0;
|
|
468
|
+
m.pinInChatMessage.key = message.pin.key;
|
|
469
|
+
m.pinInChatMessage.type = message.pin?.type || 1;
|
|
470
|
+
m.pinInChatMessage.senderTimestampMs = message.pin?.time || Date.now();
|
|
471
|
+
m.messageContextInfo.messageAddOnDurationInSecs = message.pin.type === 1 ? message.pin.time || 86400 : 0;
|
|
472
|
+
m.messageContextInfo.messageAddOnExpiryType = proto.MessageContextInfo.MessageAddonExpiryType.STATIC;
|
|
799
473
|
}
|
|
800
474
|
else if (hasNonNullishProperty(message, 'keep')) {
|
|
801
475
|
m.keepInChatMessage = {};
|
|
802
|
-
m.keepInChatMessage.key = message.keep;
|
|
803
|
-
m.keepInChatMessage.keepType = message.type;
|
|
804
|
-
m.keepInChatMessage.timestampMs = Date.now();
|
|
805
|
-
}
|
|
806
|
-
else if (hasNonNullishProperty(message, '
|
|
807
|
-
|
|
808
|
-
m.
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
},
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
476
|
+
m.keepInChatMessage.key = message.keep.key;
|
|
477
|
+
m.keepInChatMessage.keepType = message.keep?.type || 1;
|
|
478
|
+
m.keepInChatMessage.timestampMs = message.keep?.time || Date.now();
|
|
479
|
+
}
|
|
480
|
+
else if (hasNonNullishProperty(message, 'call')) {
|
|
481
|
+
m.scheduledCallCreationMessage = {};
|
|
482
|
+
m.scheduledCallCreationMessage.scheduledTimestampMs = message.call?.time || Date.now();
|
|
483
|
+
m.scheduledCallCreationMessage.callType = message.call?.type || 1;
|
|
484
|
+
m.scheduledCallCreationMessage.title = message.call?.name || 'Call Creation';
|
|
485
|
+
m.scheduledCallCreationMessage.contextInfo = {
|
|
486
|
+
...(message.contextInfo || {}),
|
|
487
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
else if (hasNonNullishProperty(message, 'paymentInvite')) {
|
|
491
|
+
m.messageContextInfo = {};
|
|
492
|
+
m.paymentInviteMessage = {};
|
|
493
|
+
m.paymentInviteMessage.expiryTimestamp = message.paymentInvite?.expiry || 0;
|
|
494
|
+
m.paymentInviteMessage.serviceType = message.paymentInvite?.type || 2;
|
|
495
|
+
m.paymentInviteMessage.contextInfo = {
|
|
496
|
+
...(message.contextInfo || {}),
|
|
497
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
818
498
|
};
|
|
819
499
|
}
|
|
820
500
|
else if (hasNonNullishProperty(message, 'buttonReply')) {
|
|
821
|
-
const { buttonReply } = message; // vltcs@changes 04-02-26 --- Destructured for readability & cleaner access (✷‿✷)
|
|
822
501
|
switch (message.type) {
|
|
502
|
+
case 'list':
|
|
503
|
+
m.listResponseMessage = {
|
|
504
|
+
title: message.buttonReply.title,
|
|
505
|
+
description: message.buttonReply.description,
|
|
506
|
+
singleSelectReply: {
|
|
507
|
+
selectedRowId: message.buttonReply.rowId
|
|
508
|
+
},
|
|
509
|
+
listType: proto.Message.ListResponseMessage.ListType.SINGLE_SELECT
|
|
510
|
+
};
|
|
511
|
+
break;
|
|
823
512
|
case 'template':
|
|
824
513
|
m.templateButtonReplyMessage = {
|
|
825
|
-
selectedDisplayText: buttonReply.displayText,
|
|
826
|
-
selectedId: buttonReply.id,
|
|
827
|
-
selectedIndex: buttonReply.index
|
|
514
|
+
selectedDisplayText: message.buttonReply.displayText,
|
|
515
|
+
selectedId: message.buttonReply.id,
|
|
516
|
+
selectedIndex: message.buttonReply.index
|
|
828
517
|
};
|
|
829
518
|
break;
|
|
830
519
|
case 'plain':
|
|
831
520
|
m.buttonsResponseMessage = {
|
|
832
|
-
selectedButtonId: buttonReply.id,
|
|
833
|
-
selectedDisplayText: buttonReply.displayText,
|
|
521
|
+
selectedButtonId: message.buttonReply.id,
|
|
522
|
+
selectedDisplayText: message.buttonReply.displayText,
|
|
834
523
|
type: proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
|
|
835
524
|
};
|
|
836
525
|
break;
|
|
526
|
+
case 'interactive':
|
|
527
|
+
m.interactiveResponseMessage = {
|
|
528
|
+
body: {
|
|
529
|
+
text: message.buttonReply.displayText,
|
|
530
|
+
format: proto.Message.InteractiveResponseMessage.Body.Format.EXTENSIONS_1
|
|
531
|
+
},
|
|
532
|
+
nativeFlowResponseMessage: {
|
|
533
|
+
name: message.buttonReply.nativeFlows.name,
|
|
534
|
+
paramsJson: message.buttonReply.nativeFlows.paramsJson,
|
|
535
|
+
version: message.buttonReply.nativeFlows.version
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
break;
|
|
837
539
|
}
|
|
838
540
|
}
|
|
839
|
-
else if (hasNonNullishProperty(message, '
|
|
840
|
-
const
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
541
|
+
else if (hasNonNullishProperty(message, 'album')) {
|
|
542
|
+
const imageMessages = message.album.filter(item => 'image' in item);
|
|
543
|
+
const videoMessages = message.album.filter(item => 'video' in item);
|
|
544
|
+
m.albumMessage = WAProto.Message.AlbumMessage.fromObject({
|
|
545
|
+
expectedImageCount: imageMessages.length,
|
|
546
|
+
expectedVideoCount: videoMessages.length
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
else if (hasNonNullishProperty(message, 'order')) {
|
|
550
|
+
m.orderMessage = WAProto.Message.OrderMessage.fromObject(message.order);
|
|
551
|
+
m.orderMessage.contextInfo = {
|
|
552
|
+
...(message.contextInfo || {}),
|
|
553
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
else if (hasNonNullishProperty(message, 'payment')) {
|
|
557
|
+
const requestPaymentMessage = {
|
|
558
|
+
amount: {
|
|
559
|
+
currencyCode: message.payment?.currency || 'IDR',
|
|
560
|
+
offset: message.payment?.offset || 0,
|
|
561
|
+
value: message.payment?.amount || 999999999
|
|
846
562
|
},
|
|
847
|
-
|
|
563
|
+
expiryTimestamp: message.payment?.expiry || 0,
|
|
564
|
+
amount1000: (message.payment?.amount || 999999999) * 1000,
|
|
565
|
+
currencyCodeIso4217: message.payment?.currency || 'IDR',
|
|
566
|
+
requestFrom: message.payment?.from || '0@s.whatsapp.net',
|
|
567
|
+
noteMessage: {
|
|
568
|
+
extendedTextMessage: {
|
|
569
|
+
text: message.payment?.note || 'Notes'
|
|
570
|
+
}
|
|
571
|
+
},
|
|
572
|
+
background: {
|
|
573
|
+
placeholderArgb: message.payment?.image?.placeholderArgb || 4278190080,
|
|
574
|
+
textArgb: message.payment?.image?.textArgb || 4294967295,
|
|
575
|
+
subtextArgb: message.payment?.image?.subtextArgb || 4294967295,
|
|
576
|
+
type: 1
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
requestPaymentMessage.noteMessage.extendedTextMessage.contextInfo = {
|
|
580
|
+
...(message.contextInfo || {}),
|
|
581
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
848
582
|
};
|
|
583
|
+
m.requestPaymentMessage = requestPaymentMessage;
|
|
584
|
+
}
|
|
585
|
+
else if (hasNonNullishProperty(message, 'pollResult')) {
|
|
586
|
+
if (!Array.isArray(message.pollResult.values)) {
|
|
587
|
+
throw new Boom('Invalid pollResult values', { statusCode: 400 });
|
|
588
|
+
}
|
|
589
|
+
const pollResultSnapshotMessage = {
|
|
590
|
+
name: message.pollResult.name,
|
|
591
|
+
pollVotes: message.pollResult.values.map(([optionName, optionVoteCount]) => ({
|
|
592
|
+
optionName,
|
|
593
|
+
optionVoteCount
|
|
594
|
+
}))
|
|
595
|
+
};
|
|
596
|
+
pollResultSnapshotMessage.contextInfo = {
|
|
597
|
+
...(message.contextInfo || {}),
|
|
598
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
599
|
+
};
|
|
600
|
+
m.pollResultSnapshotMessage = pollResultSnapshotMessage;
|
|
849
601
|
}
|
|
850
602
|
else if (hasOptionalProperty(message, 'ptv') && message.ptv) {
|
|
851
603
|
const { videoMessage } = await prepareWAMessageMedia({ video: message.video }, options);
|
|
852
604
|
m.ptvMessage = videoMessage;
|
|
853
605
|
}
|
|
854
606
|
else if (hasNonNullishProperty(message, 'product')) {
|
|
855
|
-
|
|
607
|
+
const { imageMessage } = await prepareWAMessageMedia({ image: message.product.productImage }, options);
|
|
608
|
+
m.productMessage = WAProto.Message.ProductMessage.create({
|
|
609
|
+
...message,
|
|
610
|
+
product: {
|
|
611
|
+
...message.product,
|
|
612
|
+
productImage: imageMessage
|
|
613
|
+
}
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
else if (hasNonNullishProperty(message, 'listReply')) {
|
|
617
|
+
m.listResponseMessage = { ...message.listReply };
|
|
856
618
|
}
|
|
857
619
|
else if (hasNonNullishProperty(message, 'event')) {
|
|
858
|
-
const { event } = message; // vltcs@changes 04-02-26 --- Destructured for readability & cleaner access (✷‿✷)
|
|
859
620
|
m.eventMessage = {};
|
|
860
|
-
const startTime = Math.floor(event.startDate.getTime() / 1000);
|
|
861
|
-
if (event.call && options.getCallLink) {
|
|
862
|
-
const token = await options.getCallLink(event.call, { startTime });
|
|
863
|
-
m.eventMessage.joinLink = (event.call === 'audio' ? CALL_AUDIO_PREFIX : CALL_VIDEO_PREFIX) + token;
|
|
621
|
+
const startTime = Math.floor(message.event.startDate.getTime() / 1000);
|
|
622
|
+
if (message.event.call && options.getCallLink) {
|
|
623
|
+
const token = await options.getCallLink(message.event.call, { startTime });
|
|
624
|
+
m.eventMessage.joinLink = (message.event.call === 'audio' ? CALL_AUDIO_PREFIX : CALL_VIDEO_PREFIX) + token;
|
|
864
625
|
}
|
|
865
626
|
m.messageContextInfo = {
|
|
866
627
|
// encKey
|
|
867
|
-
messageSecret: event.messageSecret || randomBytes(32)
|
|
628
|
+
messageSecret: message.event.messageSecret || randomBytes(32)
|
|
868
629
|
};
|
|
869
|
-
m.eventMessage.name = event.name;
|
|
870
|
-
m.eventMessage.description = event.description;
|
|
630
|
+
m.eventMessage.name = message.event.name;
|
|
631
|
+
m.eventMessage.description = message.event.description;
|
|
871
632
|
m.eventMessage.startTime = startTime;
|
|
872
|
-
m.eventMessage.endTime = event.endDate ? event.endDate.getTime() / 1000 : undefined;
|
|
873
|
-
m.eventMessage.isCanceled = event.isCancelled ?? false;
|
|
874
|
-
m.eventMessage.extraGuestsAllowed = event.extraGuestsAllowed;
|
|
875
|
-
m.eventMessage.isScheduleCall = event.isScheduleCall ?? false;
|
|
876
|
-
m.eventMessage.location = event.location;
|
|
633
|
+
m.eventMessage.endTime = message.event.endDate ? message.event.endDate.getTime() / 1000 : undefined;
|
|
634
|
+
m.eventMessage.isCanceled = message.event.isCancelled ?? false;
|
|
635
|
+
m.eventMessage.extraGuestsAllowed = message.event.extraGuestsAllowed;
|
|
636
|
+
m.eventMessage.isScheduleCall = message.event.isScheduleCall ?? false;
|
|
637
|
+
m.eventMessage.location = message.event.location;
|
|
877
638
|
}
|
|
878
639
|
else if (hasNonNullishProperty(message, 'poll')) {
|
|
879
|
-
|
|
880
|
-
(
|
|
881
|
-
(
|
|
882
|
-
if (!Array.isArray(poll.values)) {
|
|
640
|
+
(_a = message.poll).selectableCount || (_a.selectableCount = 0);
|
|
641
|
+
(_b = message.poll).toAnnouncementGroup || (_b.toAnnouncementGroup = false);
|
|
642
|
+
if (!Array.isArray(message.poll.values)) {
|
|
883
643
|
throw new Boom('Invalid poll values', { statusCode: 400 });
|
|
884
644
|
}
|
|
885
|
-
if (poll.selectableCount < 0 || poll.selectableCount > poll.values.length) {
|
|
886
|
-
throw new Boom(`poll.selectableCount in poll should be >= 0 and <= ${poll.values.length}`, {
|
|
645
|
+
if (message.poll.selectableCount < 0 || message.poll.selectableCount > message.poll.values.length) {
|
|
646
|
+
throw new Boom(`poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`, {
|
|
887
647
|
statusCode: 400
|
|
888
648
|
});
|
|
889
649
|
}
|
|
890
650
|
m.messageContextInfo = {
|
|
891
651
|
// encKey
|
|
892
|
-
messageSecret: poll.messageSecret || randomBytes(32)
|
|
652
|
+
messageSecret: message.poll.messageSecret || randomBytes(32)
|
|
893
653
|
};
|
|
894
654
|
const pollCreationMessage = {
|
|
895
|
-
name: poll.name,
|
|
896
|
-
selectableOptionsCount: poll.selectableCount,
|
|
897
|
-
options: poll.values.map(optionName => ({ optionName }))
|
|
655
|
+
name: message.poll.name,
|
|
656
|
+
selectableOptionsCount: message.poll.selectableCount,
|
|
657
|
+
options: message.poll.values.map(optionName => ({ optionName }))
|
|
898
658
|
};
|
|
899
|
-
if (poll.toAnnouncementGroup) {
|
|
659
|
+
if (message.poll.toAnnouncementGroup) {
|
|
900
660
|
// poll v2 is for community announcement groups (single select and multiple)
|
|
901
661
|
m.pollCreationMessageV2 = pollCreationMessage;
|
|
902
662
|
}
|
|
903
663
|
else {
|
|
904
|
-
|
|
905
|
-
if (poll.pollType === 1) {
|
|
906
|
-
if (!poll.correctAnswer) {
|
|
907
|
-
throw new Boom('No "correctAnswer" provided for quiz', { statusCode: 400 });
|
|
908
|
-
}
|
|
909
|
-
m.pollCreationMessageV5 = {
|
|
910
|
-
// Lia@Note 08-02-26 --- quiz for newsletter only
|
|
911
|
-
...pollCreationMessage,
|
|
912
|
-
correctAnswer: {
|
|
913
|
-
optionName: poll.correctAnswer.toString()
|
|
914
|
-
},
|
|
915
|
-
pollType: poll.pollType,
|
|
916
|
-
selectableOptionsCount: 1
|
|
917
|
-
};
|
|
918
|
-
}
|
|
919
|
-
else if (poll.selectableCount === 1) {
|
|
664
|
+
if (message.poll.selectableCount === 1) {
|
|
920
665
|
//poll v3 is for single select polls
|
|
921
666
|
m.pollCreationMessageV3 = pollCreationMessage;
|
|
922
667
|
}
|
|
@@ -926,44 +671,211 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
926
671
|
}
|
|
927
672
|
}
|
|
928
673
|
}
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
const {
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
674
|
+
else if (hasNonNullishProperty(message, 'stickerPack')) {
|
|
675
|
+
const { zip } = _require('fflate');
|
|
676
|
+
const { stickers, cover, name, publisher, packId, description } = message.stickerPack;
|
|
677
|
+
|
|
678
|
+
// ── Validasi jumlah sticker ───────────────────────────────────────────
|
|
679
|
+
if (stickers.length > 60) {
|
|
680
|
+
throw new Boom('Sticker pack exceeds the maximum limit of 60 stickers', { statusCode: 400 });
|
|
681
|
+
}
|
|
682
|
+
if (stickers.length === 0) {
|
|
683
|
+
throw new Boom('Sticker pack must contain at least one sticker', { statusCode: 400 });
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
const stickerPackId = packId || generateMessageIDV2();
|
|
687
|
+
const [_sharp, _jimp] = await Promise.all([import('sharp').catch(() => null), import('jimp').catch(() => null)]);
|
|
688
|
+
const lib = _sharp ? { sharp: _sharp } : _jimp ? { jimp: _jimp } : null;
|
|
689
|
+
if (!lib) throw new Boom('No image processing library available (install sharp or jimp)');
|
|
690
|
+
|
|
691
|
+
// ── Helper: deteksi WebP dari magic bytes ─────────────────────────────
|
|
692
|
+
const isWebPBuffer = (buf) => (
|
|
693
|
+
buf.length >= 12 &&
|
|
694
|
+
buf[0] === 0x52 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x46 &&
|
|
695
|
+
buf[8] === 0x57 && buf[9] === 0x45 && buf[10] === 0x42 && buf[11] === 0x50
|
|
696
|
+
);
|
|
697
|
+
|
|
698
|
+
// ── Helper: deteksi animasi WebP (VP8X/ANIM/ANMF chunks) ─────────────
|
|
699
|
+
const isAnimatedWebP = (buf) => {
|
|
700
|
+
if (!isWebPBuffer(buf)) return false;
|
|
701
|
+
let offset = 12;
|
|
702
|
+
while (offset < buf.length - 8) {
|
|
703
|
+
const fourCC = buf.toString('ascii', offset, offset + 4);
|
|
704
|
+
const chunkSize = buf.readUInt32LE(offset + 4);
|
|
705
|
+
if (fourCC === 'VP8X') {
|
|
706
|
+
const flagsOffset = offset + 8;
|
|
707
|
+
if (flagsOffset < buf.length && (buf[flagsOffset] & 0x02)) return true;
|
|
708
|
+
} else if (fourCC === 'ANIM' || fourCC === 'ANMF') {
|
|
709
|
+
return true;
|
|
710
|
+
}
|
|
711
|
+
offset += 8 + chunkSize + (chunkSize % 2);
|
|
712
|
+
}
|
|
713
|
+
return false;
|
|
938
714
|
};
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
715
|
+
|
|
716
|
+
// ── Step 1: proses & zip semua sticker ────────────────────────────────
|
|
717
|
+
const stickerData = {};
|
|
718
|
+
const stickerPromises = stickers.map(async (s, i) => {
|
|
719
|
+
const { stream } = await getStream(s.data || s.sticker);
|
|
720
|
+
const buffer = await toBuffer(stream);
|
|
721
|
+
|
|
722
|
+
let webpBuffer;
|
|
723
|
+
let isAnimated = false;
|
|
724
|
+
if (isWebPBuffer(buffer)) {
|
|
725
|
+
webpBuffer = buffer;
|
|
726
|
+
isAnimated = isAnimatedWebP(buffer);
|
|
727
|
+
} else if ('sharp' in lib && lib.sharp) {
|
|
728
|
+
webpBuffer = await lib.sharp.default(buffer).webp().toBuffer();
|
|
729
|
+
} else {
|
|
730
|
+
throw new Boom(
|
|
731
|
+
'No image processing library (sharp) available for converting sticker to WebP. ' +
|
|
732
|
+
'Either install sharp or provide stickers in WebP format.'
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
if (webpBuffer.length > 1024 * 1024) {
|
|
737
|
+
throw new Boom(`Sticker at index ${i} exceeds the 1MB size limit`, { statusCode: 400 });
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const hash = sha256(webpBuffer).toString('base64').replace(/\//g, '-');
|
|
741
|
+
const fileName = `${hash}.webp`;
|
|
742
|
+
stickerData[fileName] = [new Uint8Array(webpBuffer), { level: 0 }];
|
|
743
|
+
return {
|
|
744
|
+
fileName,
|
|
745
|
+
mimetype: 'image/webp',
|
|
746
|
+
isAnimated,
|
|
747
|
+
emojis: s.emojis || [],
|
|
748
|
+
accessibilityLabel: s.accessibilityLabel || ''
|
|
749
|
+
};
|
|
750
|
+
});
|
|
751
|
+
const stickerMetadata = await Promise.all(stickerPromises);
|
|
752
|
+
|
|
753
|
+
// ── Step 2: proses cover & masukkan ke dalam ZIP ──────────────────────
|
|
754
|
+
const trayIconFileName = `${stickerPackId}.webp`;
|
|
755
|
+
const coverBuffer = await toBuffer((await getStream(cover)).stream);
|
|
756
|
+
|
|
757
|
+
let coverWebpBuffer;
|
|
758
|
+
if (isWebPBuffer(coverBuffer)) {
|
|
759
|
+
coverWebpBuffer = coverBuffer;
|
|
760
|
+
} else if ('sharp' in lib && lib.sharp) {
|
|
761
|
+
coverWebpBuffer = await lib.sharp.default(coverBuffer).webp().toBuffer();
|
|
762
|
+
} else {
|
|
763
|
+
throw new Boom(
|
|
764
|
+
'No image processing library (sharp) available for converting cover to WebP. ' +
|
|
765
|
+
'Either install sharp or provide cover in WebP format.'
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
stickerData[trayIconFileName] = [new Uint8Array(coverWebpBuffer), { level: 0 }];
|
|
769
|
+
|
|
770
|
+
// ── Step 3: buat ZIP buffer ───────────────────────────────────────────
|
|
771
|
+
const zipBuffer = await new Promise((resolve, reject) => {
|
|
772
|
+
zip(stickerData, (err, data) => {
|
|
773
|
+
if (err) reject(err);
|
|
774
|
+
else resolve(Buffer.from(data));
|
|
775
|
+
});
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
// ── Step 4: encrypt ZIP (generate random mediaKey) ────────────────────
|
|
779
|
+
const stickerPackUpload = await encryptedStream(zipBuffer, 'sticker-pack', {
|
|
780
|
+
logger: options.logger,
|
|
781
|
+
opts: options.options
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
// ── Step 5: upload ZIP ────────────────────────────────────────────────
|
|
785
|
+
const stickerPackUploadResult = await options.upload(stickerPackUpload.encWriteStream, {
|
|
786
|
+
fileEncSha256B64: stickerPackUpload.fileEncSha256.toString('base64'),
|
|
787
|
+
mediaType: 'sticker-pack',
|
|
788
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
// ── Step 6: build stickerPackMessage ──────────────────────────────────
|
|
792
|
+
m.stickerPackMessage = {
|
|
793
|
+
name,
|
|
794
|
+
publisher,
|
|
795
|
+
stickerPackId,
|
|
796
|
+
packDescription: description,
|
|
797
|
+
stickerPackOrigin: WAProto.Message.StickerPackMessage.StickerPackOrigin.THIRD_PARTY,
|
|
798
|
+
stickerPackSize: zipBuffer.length,
|
|
799
|
+
stickers: stickerMetadata,
|
|
800
|
+
fileSha256: stickerPackUpload.fileSha256,
|
|
801
|
+
fileEncSha256: stickerPackUpload.fileEncSha256,
|
|
802
|
+
mediaKey: stickerPackUpload.mediaKey,
|
|
803
|
+
directPath: stickerPackUploadResult.directPath,
|
|
804
|
+
fileLength: stickerPackUpload.fileLength,
|
|
805
|
+
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
806
|
+
trayIconFileName
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
// ── Step 7: generate & upload thumbnail (pakai mediaKey yang sama) ────
|
|
810
|
+
try {
|
|
811
|
+
let thumbnailBuffer;
|
|
812
|
+
if ('sharp' in lib && lib.sharp) {
|
|
813
|
+
thumbnailBuffer = await lib.sharp.default(coverBuffer).resize(252, 252).jpeg().toBuffer();
|
|
814
|
+
} else if ('jimp' in lib && lib.jimp) {
|
|
815
|
+
const jimpImage = await (lib.jimp.Jimp || lib.jimp.default).read(coverBuffer);
|
|
816
|
+
thumbnailBuffer = await jimpImage.resize({ w: 252, h: 252 }).getBuffer('image/jpeg');
|
|
817
|
+
} else {
|
|
818
|
+
throw new Error('No image processing library available for thumbnail generation');
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
if (!thumbnailBuffer || thumbnailBuffer.length === 0) {
|
|
822
|
+
throw new Error('Failed to generate thumbnail buffer');
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
const thumbUpload = await encryptedStream(thumbnailBuffer, 'thumbnail-sticker-pack', {
|
|
826
|
+
logger: options.logger,
|
|
827
|
+
opts: options.options,
|
|
828
|
+
mediaKey: stickerPackUpload.mediaKey
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
const thumbUploadResult = await options.upload(thumbUpload.encWriteStream, {
|
|
832
|
+
fileEncSha256B64: thumbUpload.fileEncSha256.toString('base64'),
|
|
833
|
+
mediaType: 'thumbnail-sticker-pack',
|
|
834
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
Object.assign(m.stickerPackMessage, {
|
|
838
|
+
thumbnailDirectPath: thumbUploadResult.directPath,
|
|
839
|
+
thumbnailSha256: thumbUpload.fileSha256,
|
|
840
|
+
thumbnailEncSha256: thumbUpload.fileEncSha256,
|
|
841
|
+
thumbnailHeight: 252,
|
|
842
|
+
thumbnailWidth: 252,
|
|
843
|
+
imageDataHash: sha256(thumbnailBuffer).toString('base64')
|
|
844
|
+
});
|
|
845
|
+
} catch (e) {
|
|
846
|
+
options.logger?.warn?.(`Thumbnail generation failed: ${e}`);
|
|
946
847
|
}
|
|
848
|
+
|
|
849
|
+
m.stickerPackMessage.contextInfo = {
|
|
850
|
+
...(message.contextInfo || {}),
|
|
851
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
852
|
+
};
|
|
947
853
|
}
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
if (
|
|
955
|
-
|
|
854
|
+
else if (hasNonNullishProperty(message, 'adminInvite')) {
|
|
855
|
+
m.newsletterAdminInviteMessage = {};
|
|
856
|
+
m.newsletterAdminInviteMessage.newsletterJid = message.adminInvite.jid;
|
|
857
|
+
m.newsletterAdminInviteMessage.newsletterName = message.adminInvite.name;
|
|
858
|
+
m.newsletterAdminInviteMessage.caption = message.adminInvite.caption;
|
|
859
|
+
m.newsletterAdminInviteMessage.inviteExpiration = message.adminInvite.expiration;
|
|
860
|
+
if (message.adminInvite.jpegThumbnail) {
|
|
861
|
+
m.newsletterAdminInviteMessage.jpegThumbnail = message.adminInvite.jpegThumbnail;
|
|
862
|
+
} else if (options.getProfilePicUrl) {
|
|
863
|
+
try {
|
|
864
|
+
const pfpUrl = await options.getProfilePicUrl(message.adminInvite.jid);
|
|
865
|
+
if (pfpUrl) {
|
|
866
|
+
const { thumbnail } = await generateThumbnail(pfpUrl, 'image');
|
|
867
|
+
m.newsletterAdminInviteMessage.jpegThumbnail = thumbnail;
|
|
868
|
+
}
|
|
869
|
+
} catch (_) {}
|
|
956
870
|
}
|
|
957
|
-
m.
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
senderTimestampMs: Date.now(),
|
|
961
|
-
vote: pollUpdate.vote
|
|
871
|
+
m.newsletterAdminInviteMessage.contextInfo = {
|
|
872
|
+
...(message.contextInfo || {}),
|
|
873
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
962
874
|
};
|
|
963
875
|
}
|
|
964
876
|
else if (hasNonNullishProperty(message, 'sharePhoneNumber')) {
|
|
965
877
|
m.protocolMessage = {
|
|
966
|
-
type:
|
|
878
|
+
type: proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
|
|
967
879
|
};
|
|
968
880
|
}
|
|
969
881
|
else if (hasNonNullishProperty(message, 'requestPhoneNumber')) {
|
|
@@ -971,7 +883,7 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
971
883
|
}
|
|
972
884
|
else if (hasNonNullishProperty(message, 'limitSharing')) {
|
|
973
885
|
m.protocolMessage = {
|
|
974
|
-
type:
|
|
886
|
+
type: proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
|
|
975
887
|
limitSharing: {
|
|
976
888
|
sharingLimited: message.limitSharing === true,
|
|
977
889
|
trigger: 1,
|
|
@@ -980,444 +892,288 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
980
892
|
}
|
|
981
893
|
};
|
|
982
894
|
}
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
};
|
|
895
|
+
else if ('interactiveMessage' in message && !!message.interactiveMessage) {
|
|
896
|
+
// ── Passthrough interactiveMessage raw object ──────────────────────
|
|
897
|
+
// Must be BEFORE the else block to avoid hitting prepareWAMessageMedia
|
|
898
|
+
// which throws 'Invalid media type' for interactiveMessage keys.
|
|
899
|
+
m = { interactiveMessage: message.interactiveMessage };
|
|
989
900
|
}
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
if (!Buffer.isBuffer(message.thumbnail)) {
|
|
993
|
-
throw new Boom('Must provide thumbnail buffer in order message', { statusCode: 400 });
|
|
994
|
-
}
|
|
995
|
-
m.orderMessage = {
|
|
996
|
-
itemCount: 1,
|
|
997
|
-
messageVersion: 1,
|
|
998
|
-
orderTitle: LIBRARY_NAME,
|
|
999
|
-
status: proto.Message.OrderMessage.OrderStatus.INQUIRY,
|
|
1000
|
-
surface: proto.Message.OrderMessage.OrderSurface.CATALOG,
|
|
1001
|
-
token: generateMessageIDV2(),
|
|
1002
|
-
totalAmount1000: 1000,
|
|
1003
|
-
totalCurrencyCode: 'IDR',
|
|
1004
|
-
...message,
|
|
1005
|
-
message: message.orderText
|
|
1006
|
-
};
|
|
1007
|
-
delete m.orderMessage.orderText;
|
|
901
|
+
else {
|
|
902
|
+
m = await prepareWAMessageMedia(message, options);
|
|
1008
903
|
}
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
if (album[i].video) videoCount++;
|
|
1018
|
-
};
|
|
1019
|
-
let imageCount = 0;
|
|
1020
|
-
for (let i = 0; i < album.length; i++) {
|
|
1021
|
-
if (album[i].image) imageCount++;
|
|
904
|
+
if ('sections' in message && !!message.sections) {
|
|
905
|
+
const listMessage = {
|
|
906
|
+
title: message.title,
|
|
907
|
+
buttonText: message.buttonText,
|
|
908
|
+
footerText: message.footer,
|
|
909
|
+
description: message.text,
|
|
910
|
+
sections: message.sections,
|
|
911
|
+
listType: proto.Message.ListMessage.ListType.SINGLE_SELECT
|
|
1022
912
|
};
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
m.albumMessage = {
|
|
1027
|
-
expectedImageCount: imageCount,
|
|
1028
|
-
expectedVideoCount: videoCount
|
|
913
|
+
listMessage.contextInfo = {
|
|
914
|
+
...(message.contextInfo || {}),
|
|
915
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
1029
916
|
};
|
|
917
|
+
m = { listMessage };
|
|
1030
918
|
}
|
|
1031
|
-
else {
|
|
1032
|
-
|
|
919
|
+
else if ('productList' in message && !!message.productList) {
|
|
920
|
+
const thumbnail = message.thumbnail ? await generateThumbnail(message.thumbnail, 'image') : null;
|
|
921
|
+
const listMessage = {
|
|
922
|
+
title: message.title,
|
|
923
|
+
buttonText: message.buttonText,
|
|
924
|
+
footerText: message.footer,
|
|
925
|
+
description: message.text,
|
|
926
|
+
productListInfo: {
|
|
927
|
+
productSections: message.productList,
|
|
928
|
+
headerImage: {
|
|
929
|
+
productId: message.productList[0].products[0].productId,
|
|
930
|
+
jpegThumbnail: thumbnail?.thumbnail || null
|
|
931
|
+
},
|
|
932
|
+
businessOwnerJid: message.businessOwnerJid
|
|
933
|
+
},
|
|
934
|
+
listType: proto.Message.ListMessage.ListType.PRODUCT_LIST
|
|
935
|
+
};
|
|
936
|
+
listMessage.contextInfo = {
|
|
937
|
+
...(message.contextInfo || {}),
|
|
938
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
939
|
+
};
|
|
940
|
+
m = { listMessage };
|
|
1033
941
|
}
|
|
1034
|
-
|
|
1035
|
-
if (hasNonNullishProperty(message, 'buttons')) {
|
|
942
|
+
else if ('buttons' in message && !!message.buttons) {
|
|
1036
943
|
const buttonsMessage = {
|
|
1037
|
-
buttons: message.buttons.map(
|
|
1038
|
-
// vltcs@changes 12-03-26 --- Add "single_select" shortcut!
|
|
1039
|
-
const buttonText = button.text || button.buttonText
|
|
1040
|
-
if (hasOptionalProperty(button, 'sections')) {
|
|
1041
|
-
return {
|
|
1042
|
-
nativeFlowInfo: {
|
|
1043
|
-
name: 'single_select',
|
|
1044
|
-
paramsJson: JSON.stringify({
|
|
1045
|
-
title: buttonText,
|
|
1046
|
-
sections: button.sections
|
|
1047
|
-
})
|
|
1048
|
-
},
|
|
1049
|
-
type: ButtonType.NATIVE_FLOW
|
|
1050
|
-
};
|
|
1051
|
-
}
|
|
1052
|
-
else if (hasOptionalProperty(button, 'name')) {
|
|
1053
|
-
return {
|
|
1054
|
-
nativeFlowInfo: {
|
|
1055
|
-
name: button.name,
|
|
1056
|
-
paramsJson: button.paramsJson
|
|
1057
|
-
},
|
|
1058
|
-
type: ButtonType.NATIVE_FLOW
|
|
1059
|
-
};
|
|
1060
|
-
}
|
|
1061
|
-
return {
|
|
1062
|
-
buttonId: button.id || button.buttonId,
|
|
1063
|
-
buttonText: typeof buttonText === 'string' ? { displayText: buttonText } : buttonText,
|
|
1064
|
-
type: button.type || ButtonType.RESPONSE
|
|
1065
|
-
};
|
|
1066
|
-
})
|
|
944
|
+
buttons: message.buttons.map(b => ({ ...b, type: proto.Message.ButtonsMessage.Button.Type.RESPONSE }))
|
|
1067
945
|
};
|
|
1068
|
-
if (
|
|
946
|
+
if ('text' in message) {
|
|
1069
947
|
buttonsMessage.contentText = message.text;
|
|
1070
|
-
buttonsMessage.headerType =
|
|
948
|
+
buttonsMessage.headerType = proto.Message.ButtonsMessage.HeaderType.EMPTY;
|
|
1071
949
|
}
|
|
1072
950
|
else {
|
|
1073
|
-
if (
|
|
951
|
+
if ('caption' in message) {
|
|
1074
952
|
buttonsMessage.contentText = message.caption;
|
|
1075
953
|
}
|
|
1076
954
|
const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
|
|
1077
|
-
buttonsMessage.headerType =
|
|
955
|
+
buttonsMessage.headerType = proto.Message.ButtonsMessage.HeaderType[type];
|
|
1078
956
|
Object.assign(buttonsMessage, m);
|
|
1079
957
|
}
|
|
1080
|
-
if (
|
|
958
|
+
if ('footer' in message && !!message.footer) {
|
|
1081
959
|
buttonsMessage.footerText = message.footer;
|
|
1082
960
|
}
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
footerText: message.footer,
|
|
1091
|
-
description: message.text,
|
|
1092
|
-
listType: ListType.SINGLE_SELECT
|
|
961
|
+
if ('title' in message && !!message.title) {
|
|
962
|
+
buttonsMessage.text = message.title;
|
|
963
|
+
buttonsMessage.headerType = proto.Message.ButtonsMessage.HeaderType.TEXT;
|
|
964
|
+
}
|
|
965
|
+
buttonsMessage.contextInfo = {
|
|
966
|
+
...(message.contextInfo || {}),
|
|
967
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
1093
968
|
};
|
|
1094
|
-
m = {
|
|
969
|
+
m = { buttonsMessage };
|
|
1095
970
|
}
|
|
1096
|
-
|
|
1097
|
-
else if (hasNonNullishProperty(message, 'templateButtons')) {
|
|
971
|
+
else if ('templateButtons' in message && !!message.templateButtons) {
|
|
1098
972
|
const hydratedTemplate = {
|
|
1099
|
-
hydratedButtons: message.templateButtons
|
|
1100
|
-
if (hasOptionalProperty(button, 'id')) {
|
|
1101
|
-
return {
|
|
1102
|
-
index: i,
|
|
1103
|
-
quickReplyButton: {
|
|
1104
|
-
displayText: button.text || button.buttonText || '👉🏻 Click',
|
|
1105
|
-
id: button.id
|
|
1106
|
-
}
|
|
1107
|
-
};
|
|
1108
|
-
}
|
|
1109
|
-
else if (hasOptionalProperty(button, 'url')) {
|
|
1110
|
-
return {
|
|
1111
|
-
index: i,
|
|
1112
|
-
urlButton: {
|
|
1113
|
-
displayText: button.text || button.buttonText || '🌐 Visit',
|
|
1114
|
-
url: button.url
|
|
1115
|
-
}
|
|
1116
|
-
};
|
|
1117
|
-
}
|
|
1118
|
-
else if (hasOptionalProperty(button, 'call')) {
|
|
1119
|
-
return {
|
|
1120
|
-
index: i,
|
|
1121
|
-
callButton: {
|
|
1122
|
-
displayText: button.text || button.buttonText || '📞 Call',
|
|
1123
|
-
phoneNumber: button.call
|
|
1124
|
-
}
|
|
1125
|
-
};
|
|
1126
|
-
}
|
|
1127
|
-
button.index = button.index || i;
|
|
1128
|
-
return button;
|
|
1129
|
-
})
|
|
973
|
+
hydratedButtons: message.templateButtons
|
|
1130
974
|
};
|
|
1131
|
-
if (
|
|
975
|
+
if ('text' in message) {
|
|
1132
976
|
hydratedTemplate.hydratedContentText = message.text;
|
|
1133
977
|
}
|
|
1134
978
|
else {
|
|
1135
|
-
if (
|
|
1136
|
-
hydratedTemplate.hydratedTitleText = message.title;
|
|
979
|
+
if ('caption' in message) {
|
|
1137
980
|
hydratedTemplate.hydratedContentText = message.caption;
|
|
1138
|
-
}
|
|
981
|
+
}
|
|
1139
982
|
Object.assign(hydratedTemplate, m);
|
|
1140
983
|
}
|
|
1141
|
-
if (
|
|
984
|
+
if ('footer' in message && !!message.footer) {
|
|
1142
985
|
hydratedTemplate.hydratedFooterText = message.footer;
|
|
1143
986
|
}
|
|
1144
|
-
hydratedTemplate.
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
987
|
+
hydratedTemplate.contextInfo = {
|
|
988
|
+
...(message.contextInfo || {}),
|
|
989
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
990
|
+
};
|
|
991
|
+
m = { templateMessage: { fourRowTemplate: hydratedTemplate, hydratedTemplate } };
|
|
1151
992
|
}
|
|
1152
|
-
else if (
|
|
993
|
+
else if ('interactiveButtons' in message && !!message.interactiveButtons) {
|
|
1153
994
|
const interactiveMessage = {
|
|
1154
|
-
nativeFlowMessage:
|
|
995
|
+
nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.fromObject({
|
|
996
|
+
buttons: message.interactiveButtons,
|
|
997
|
+
})
|
|
1155
998
|
};
|
|
1156
|
-
if (
|
|
1157
|
-
interactiveMessage.
|
|
1158
|
-
bizJid: message.bizJid,
|
|
1159
|
-
id: message.id,
|
|
1160
|
-
messageVersion: 1
|
|
1161
|
-
};
|
|
999
|
+
if ('text' in message) {
|
|
1000
|
+
interactiveMessage.body = { text: message.text };
|
|
1162
1001
|
}
|
|
1163
|
-
else if (
|
|
1164
|
-
interactiveMessage.
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1002
|
+
else if ('caption' in message) {
|
|
1003
|
+
interactiveMessage.body = { text: message.caption };
|
|
1004
|
+
interactiveMessage.header = {
|
|
1005
|
+
title: message.title,
|
|
1006
|
+
subtitle: message.subtitle,
|
|
1007
|
+
hasMediaAttachment: message?.media ?? false,
|
|
1168
1008
|
};
|
|
1009
|
+
Object.assign(interactiveMessage.header, m);
|
|
1169
1010
|
}
|
|
1170
|
-
if (
|
|
1171
|
-
interactiveMessage.
|
|
1011
|
+
if ('footer' in message && !!message.footer) {
|
|
1012
|
+
interactiveMessage.footer = { text: message.footer };
|
|
1172
1013
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
interactiveMessage.header = {
|
|
1180
|
-
title: message.title || '',
|
|
1181
|
-
subtitle: message.subtitle || '',
|
|
1182
|
-
hasMediaAttachment: isValidHeader
|
|
1183
|
-
};
|
|
1184
|
-
interactiveMessage.body = { text: message.caption };
|
|
1185
|
-
}
|
|
1186
|
-
if (hasOptionalProperty(message, 'thumbnail') && !!message.thumbnail) {
|
|
1187
|
-
interactiveMessage.jpegThumbnail = message.thumbnail;
|
|
1188
|
-
}
|
|
1014
|
+
if ('title' in message && !!message.title) {
|
|
1015
|
+
interactiveMessage.header = {
|
|
1016
|
+
title: message.title,
|
|
1017
|
+
subtitle: message.subtitle,
|
|
1018
|
+
hasMediaAttachment: message?.media ?? false,
|
|
1019
|
+
};
|
|
1189
1020
|
Object.assign(interactiveMessage.header, m);
|
|
1190
1021
|
}
|
|
1191
|
-
if (
|
|
1192
|
-
|
|
1193
|
-
audio: message.audioFooter
|
|
1194
|
-
}, options);
|
|
1195
|
-
interactiveMessage.footer = {
|
|
1196
|
-
audioMessage: parseFooter.audioMessage,
|
|
1197
|
-
hasMediaAttachment: true
|
|
1198
|
-
};
|
|
1022
|
+
if ('contextInfo' in message && !!message.contextInfo) {
|
|
1023
|
+
interactiveMessage.contextInfo = message.contextInfo;
|
|
1199
1024
|
}
|
|
1200
|
-
|
|
1201
|
-
interactiveMessage.
|
|
1025
|
+
if ('mentions' in message && !!message.mentions) {
|
|
1026
|
+
interactiveMessage.contextInfo = { mentionedJid: message.mentions };
|
|
1202
1027
|
}
|
|
1203
1028
|
m = { interactiveMessage };
|
|
1204
1029
|
}
|
|
1205
|
-
else if (
|
|
1030
|
+
else if ('shop' in message && !!message.shop) {
|
|
1206
1031
|
const interactiveMessage = {
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
carouselHeader.productMessage = await prepareProductMessage(card, options);
|
|
1212
|
-
}
|
|
1213
|
-
else {
|
|
1214
|
-
carouselHeader = await prepareWAMessageMedia(card, options).catch(() => ({}));
|
|
1215
|
-
}
|
|
1216
|
-
const isValidHeader = hasValidCarouselHeader(carouselHeader)
|
|
1217
|
-
if (!isValidHeader) {
|
|
1218
|
-
throw new Boom('Invalid media type for carousel card', { statusCode: 400 });
|
|
1219
|
-
}
|
|
1220
|
-
const carouselCard = {
|
|
1221
|
-
nativeFlowMessage: prepareNativeFlowButtons(card.nativeFlow ? card : [])
|
|
1222
|
-
};
|
|
1223
|
-
if (hasOptionalProperty(card, 'text')) {
|
|
1224
|
-
carouselCard.body = { text: card.text };
|
|
1225
|
-
}
|
|
1226
|
-
else {
|
|
1227
|
-
if (hasOptionalProperty(card, 'caption')) {
|
|
1228
|
-
carouselCard.header = {
|
|
1229
|
-
title: card.title || '',
|
|
1230
|
-
subtitle: card.subtitle || '',
|
|
1231
|
-
hasMediaAttachment: isValidHeader
|
|
1232
|
-
};
|
|
1233
|
-
carouselCard.body = { text: card.caption };
|
|
1234
|
-
}
|
|
1235
|
-
if (hasOptionalProperty(card, 'thumbnail') && !!card.thumbnail) {
|
|
1236
|
-
carouselCard.jpegThumbnail = card.thumbnail;
|
|
1237
|
-
}
|
|
1238
|
-
Object.assign(carouselCard.header, carouselHeader);
|
|
1239
|
-
}
|
|
1240
|
-
if (hasOptionalProperty(card, 'audioFooter')) {
|
|
1241
|
-
const parseFooter = await prepareWAMessageMedia({
|
|
1242
|
-
audio: card.audioFooter
|
|
1243
|
-
}, options);
|
|
1244
|
-
carouselCard.footer = {
|
|
1245
|
-
audioMessage: parseFooter.audioMessage,
|
|
1246
|
-
hasMediaAttachment: true
|
|
1247
|
-
};
|
|
1248
|
-
}
|
|
1249
|
-
else if (hasOptionalProperty(card, 'footer')) {
|
|
1250
|
-
carouselCard.footer = { text: card.footer };
|
|
1251
|
-
}
|
|
1252
|
-
return carouselCard
|
|
1253
|
-
})),
|
|
1254
|
-
carouselCardType: CarouselCardType.UNKNOWN,
|
|
1255
|
-
messageVersion: 1
|
|
1256
|
-
}
|
|
1032
|
+
shopStorefrontMessage: proto.Message.InteractiveMessage.ShopMessage.fromObject({
|
|
1033
|
+
surface: message.shop,
|
|
1034
|
+
id: message.id
|
|
1035
|
+
})
|
|
1257
1036
|
};
|
|
1258
|
-
if (
|
|
1037
|
+
if ('text' in message) {
|
|
1259
1038
|
interactiveMessage.body = { text: message.text };
|
|
1260
1039
|
}
|
|
1261
|
-
if (
|
|
1262
|
-
interactiveMessage.
|
|
1040
|
+
else if ('caption' in message) {
|
|
1041
|
+
interactiveMessage.body = { text: message.caption };
|
|
1042
|
+
interactiveMessage.header = {
|
|
1043
|
+
title: message.title,
|
|
1044
|
+
subtitle: message.subtitle,
|
|
1045
|
+
hasMediaAttachment: message?.media ?? false,
|
|
1046
|
+
};
|
|
1047
|
+
Object.assign(interactiveMessage.header, m);
|
|
1263
1048
|
}
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
// vltcs@changes 01-02-26 --- Add request payment message
|
|
1267
|
-
else if (hasNonNullishProperty(message, 'requestPaymentFrom')) {
|
|
1268
|
-
const requestPaymentMessage = {
|
|
1269
|
-
amount: {
|
|
1270
|
-
currencyCode: 'IDR',
|
|
1271
|
-
offset: 1000,
|
|
1272
|
-
value: 1000
|
|
1273
|
-
},
|
|
1274
|
-
amount1000: 1000,
|
|
1275
|
-
currencyCodeIso4217: 'IDR',
|
|
1276
|
-
expiryTimestamp: Date.now(),
|
|
1277
|
-
noteMessage: m,
|
|
1278
|
-
requestFrom: message.requestPaymentFrom,
|
|
1279
|
-
...message
|
|
1280
|
-
};
|
|
1281
|
-
delete requestPaymentMessage.requestPaymentFrom;
|
|
1282
|
-
if (hasNonNullishProperty(m, 'extendedTextMessage') || hasNonNullishProperty(m, 'stickerMessage')) {
|
|
1283
|
-
Object.assign(requestPaymentMessage.noteMessage, m);
|
|
1049
|
+
if ('footer' in message && !!message.footer) {
|
|
1050
|
+
interactiveMessage.footer = { text: message.footer };
|
|
1284
1051
|
}
|
|
1285
|
-
|
|
1286
|
-
|
|
1052
|
+
if ('title' in message && !!message.title) {
|
|
1053
|
+
interactiveMessage.header = {
|
|
1054
|
+
title: message.title,
|
|
1055
|
+
subtitle: message.subtitle,
|
|
1056
|
+
hasMediaAttachment: message?.media ?? false,
|
|
1057
|
+
};
|
|
1058
|
+
Object.assign(interactiveMessage.header, m);
|
|
1287
1059
|
}
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
// vltcs@changes 01-02-26 --- Add invoice message
|
|
1291
|
-
else if (hasNonNullishProperty(message, 'invoiceNote')) {
|
|
1292
|
-
const attachment = m.imageMessage || m.documentMessage;
|
|
1293
|
-
const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
|
|
1294
|
-
const invoiceMessage = {
|
|
1295
|
-
attachmentType: proto.Message.InvoiceMessage.AttachmentType[type === 'DOCUMENT' ? 'PDF' : 'IMAGE'],
|
|
1296
|
-
note: message.invoiceNote
|
|
1297
|
-
};
|
|
1298
|
-
if (attachment) {
|
|
1299
|
-
const { directPath, fileEncSha256, fileSha256, jpegThumbnail = undefined, mediaKey, mediaKeyTimestamp, mimetype } = attachment;
|
|
1300
|
-
Object.assign(invoiceMessage, {
|
|
1301
|
-
attachmentDirectPath: directPath,
|
|
1302
|
-
attachmentFileEncSha256: fileEncSha256,
|
|
1303
|
-
attachmentFileSha256: fileSha256,
|
|
1304
|
-
attachmentJpegThumbnail: jpegThumbnail,
|
|
1305
|
-
attachmentMediaKey: mediaKey,
|
|
1306
|
-
attachmentMediaKeyTimestamp: mediaKeyTimestamp,
|
|
1307
|
-
attachmentMimetype: mimetype,
|
|
1308
|
-
token: generateMessageIDV2()
|
|
1309
|
-
});
|
|
1060
|
+
if ('contextInfo' in message && !!message.contextInfo) {
|
|
1061
|
+
interactiveMessage.contextInfo = message.contextInfo;
|
|
1310
1062
|
}
|
|
1311
|
-
|
|
1312
|
-
|
|
1063
|
+
if ('mentions' in message && !!message.mentions) {
|
|
1064
|
+
interactiveMessage.contextInfo = { mentionedJid: message.mentions };
|
|
1313
1065
|
}
|
|
1314
|
-
m = {
|
|
1066
|
+
m = { interactiveMessage };
|
|
1315
1067
|
}
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
}
|
|
1324
|
-
if (!content.url || typeof content.url !== 'string') {
|
|
1325
|
-
content.url = DONATE_URL; // Lia@Note 02-02-26 --- Apologies if this feels cheeky, just a fallback
|
|
1326
|
-
}
|
|
1327
|
-
const externalAdReply = {
|
|
1328
|
-
...content,
|
|
1329
|
-
body: content.body,
|
|
1330
|
-
mediaType: content.mediaType || 1,
|
|
1331
|
-
mediaUrl: content.url + '?update=' + Date.now(),
|
|
1332
|
-
renderLargerThumbnail: content.largeThumbnail,
|
|
1333
|
-
thumbnail: content.thumbnail,
|
|
1334
|
-
thumbnailUrl: content.url,
|
|
1335
|
-
title: content.title || LIBRARY_NAME
|
|
1068
|
+
else if ('collection' in message && !!message.collection) {
|
|
1069
|
+
const interactiveMessage = {
|
|
1070
|
+
collectionMessage: {
|
|
1071
|
+
bizJid: message.collection.bizJid,
|
|
1072
|
+
id: message.collection.id,
|
|
1073
|
+
messageVersion: message?.collection?.version
|
|
1074
|
+
}
|
|
1336
1075
|
};
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
else if (key) {
|
|
1344
|
-
key.contextInfo = { externalAdReply };
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
if (
|
|
1348
|
-
(hasOptionalProperty(message, 'mentions') && message.mentions?.length) ||
|
|
1349
|
-
(hasOptionalProperty(message, 'mentionAll') && message.mentionAll)
|
|
1350
|
-
) {
|
|
1351
|
-
const messageType = Object.keys(m)[0];
|
|
1352
|
-
const key = m[messageType];
|
|
1353
|
-
if ('contextInfo' in key && !!key.contextInfo) {
|
|
1354
|
-
key.contextInfo.mentionedJid = message.mentions || [];
|
|
1355
|
-
}
|
|
1356
|
-
else if (key) {
|
|
1357
|
-
key.contextInfo = {
|
|
1358
|
-
mentionedJid: message.mentions || []
|
|
1076
|
+
if ('text' in message) {
|
|
1077
|
+
interactiveMessage.body = { text: message.text };
|
|
1078
|
+
interactiveMessage.header = {
|
|
1079
|
+
title: message.title,
|
|
1080
|
+
subtitle: message.subtitle,
|
|
1081
|
+
hasMediaAttachment: false
|
|
1359
1082
|
};
|
|
1360
1083
|
}
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1084
|
+
else {
|
|
1085
|
+
if ('caption' in message) {
|
|
1086
|
+
interactiveMessage.body = { text: message.caption };
|
|
1087
|
+
interactiveMessage.header = {
|
|
1088
|
+
title: message.title,
|
|
1089
|
+
subtitle: message.subtitle,
|
|
1090
|
+
hasMediaAttachment: message.hasMediaAttachment ? message.hasMediaAttachment : false,
|
|
1091
|
+
...Object.assign(interactiveMessage, m)
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1371
1094
|
}
|
|
1372
|
-
|
|
1373
|
-
|
|
1095
|
+
if ('footer' in message && !!message.footer) {
|
|
1096
|
+
interactiveMessage.footer = { text: message.footer };
|
|
1374
1097
|
}
|
|
1098
|
+
interactiveMessage.contextInfo = {
|
|
1099
|
+
...(message.contextInfo || {}),
|
|
1100
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
1101
|
+
};
|
|
1102
|
+
m = { interactiveMessage };
|
|
1375
1103
|
}
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1104
|
+
else if ('cards' in message && !!message.cards) {
|
|
1105
|
+
const slides = await Promise.all(message.cards.map(async (slide) => {
|
|
1106
|
+
const { image, video, product, title, body, footer, buttons } = slide;
|
|
1107
|
+
let header;
|
|
1108
|
+
if (product) {
|
|
1109
|
+
const { imageMessage } = await prepareWAMessageMedia({ image: product.productImage, ...options }, options);
|
|
1110
|
+
header = {
|
|
1111
|
+
productMessage: {
|
|
1112
|
+
product: {
|
|
1113
|
+
...product,
|
|
1114
|
+
productImage: imageMessage,
|
|
1115
|
+
},
|
|
1116
|
+
...slide
|
|
1117
|
+
}
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
1120
|
+
else if (image) {
|
|
1121
|
+
header = await prepareWAMessageMedia({ image: image, ...options }, options);
|
|
1122
|
+
}
|
|
1123
|
+
else if (video) {
|
|
1124
|
+
header = await prepareWAMessageMedia({ video: video, ...options }, options);
|
|
1386
1125
|
}
|
|
1126
|
+
return {
|
|
1127
|
+
header: {
|
|
1128
|
+
title,
|
|
1129
|
+
hasMediaAttachment: true,
|
|
1130
|
+
...header
|
|
1131
|
+
},
|
|
1132
|
+
body: { text: body },
|
|
1133
|
+
footer: { text: footer },
|
|
1134
|
+
nativeFlowMessage: { buttons }
|
|
1135
|
+
};
|
|
1136
|
+
}));
|
|
1137
|
+
const interactiveMessage = {
|
|
1138
|
+
carouselMessage: { cards: slides }
|
|
1139
|
+
};
|
|
1140
|
+
if ('text' in message) {
|
|
1141
|
+
interactiveMessage.body = { text: message.text };
|
|
1142
|
+
interactiveMessage.header = {
|
|
1143
|
+
title: message.title,
|
|
1144
|
+
subtitle: message.subtitle,
|
|
1145
|
+
hasMediaAttachment: false
|
|
1146
|
+
};
|
|
1387
1147
|
}
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
}
|
|
1391
|
-
// vltcs@changes 02-02-26 --- Add "interactiveAsTemplate" boolean to wrap interactiveMessage into templateMessage
|
|
1392
|
-
else if (hasOptionalProperty(message, 'interactiveAsTemplate') && !!message.interactiveAsTemplate) {
|
|
1393
|
-
if (!m.interactiveMessage) {
|
|
1394
|
-
throw new Boom('Invalid message type for template', { statusCode: 400 }); // Lia@Note 02-02-26 --- To avoid bug 👀
|
|
1148
|
+
if ('footer' in message && !!message.footer) {
|
|
1149
|
+
interactiveMessage.footer = { text: message.footer };
|
|
1395
1150
|
}
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
templateId: message.id || 'template-' + Date.now() // Lia@Note 04-02-26 --- Minimal templateId to satisfy WhatsApp ( ꈍᴗꈍ)
|
|
1400
|
-
}
|
|
1151
|
+
interactiveMessage.contextInfo = {
|
|
1152
|
+
...(message.contextInfo || {}),
|
|
1153
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
1401
1154
|
};
|
|
1402
|
-
|
|
1155
|
+
m = { interactiveMessage };
|
|
1403
1156
|
}
|
|
1404
|
-
// vltcs@changes 30-01-26 --- Add "ephemeral" boolean to wrap message into ephemeralMessage like "viewOnce"
|
|
1405
1157
|
if (hasOptionalProperty(message, 'ephemeral') && !!message.ephemeral) {
|
|
1406
1158
|
m = { ephemeralMessage: { message: m } };
|
|
1407
|
-
delete message.ephemeral;
|
|
1408
|
-
}
|
|
1409
|
-
else if (hasOptionalProperty(message, 'viewOnce') && !!message.viewOnce) {
|
|
1410
|
-
m = { viewOnceMessage: { message: m } };
|
|
1411
1159
|
}
|
|
1412
|
-
|
|
1413
|
-
else if (hasOptionalProperty(message, 'viewOnceV2') && !!message.viewOnceV2) {
|
|
1160
|
+
if (hasOptionalProperty(message, 'viewOnce') && !!message.viewOnce) {
|
|
1414
1161
|
m = { viewOnceMessageV2: { message: m } };
|
|
1415
|
-
delete message.viewOnceV2;
|
|
1416
1162
|
}
|
|
1417
|
-
|
|
1418
|
-
else if (hasOptionalProperty(message, 'viewOnceV2Extension') && !!message.viewOnceV2Extension) {
|
|
1163
|
+
if (hasOptionalProperty(message, 'viewOnceExt') && !!message.viewOnceExt) {
|
|
1419
1164
|
m = { viewOnceMessageV2Extension: { message: m } };
|
|
1420
|
-
|
|
1165
|
+
}
|
|
1166
|
+
if (hasOptionalProperty(message, 'mentions') && message.mentions?.length) {
|
|
1167
|
+
const messageType = Object.keys(m)[0];
|
|
1168
|
+
const key = m[messageType];
|
|
1169
|
+
if ('contextInfo' in key && !!key.contextInfo) {
|
|
1170
|
+
key.contextInfo.mentionedJid = message.mentions;
|
|
1171
|
+
}
|
|
1172
|
+
else if (key) {
|
|
1173
|
+
key.contextInfo = {
|
|
1174
|
+
mentionedJid: message.mentions
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1421
1177
|
}
|
|
1422
1178
|
if (hasOptionalProperty(message, 'edit')) {
|
|
1423
1179
|
m = {
|
|
@@ -1425,31 +1181,39 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1425
1181
|
key: message.edit,
|
|
1426
1182
|
editedMessage: m,
|
|
1427
1183
|
timestampMs: Date.now(),
|
|
1428
|
-
type:
|
|
1184
|
+
type: WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
|
|
1429
1185
|
}
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
if (hasOptionalProperty(message, 'contextInfo') && !!message.contextInfo) {
|
|
1189
|
+
const messageType = Object.keys(m)[0];
|
|
1190
|
+
const key = m[messageType];
|
|
1191
|
+
if ('contextInfo' in key && !!key.contextInfo) {
|
|
1192
|
+
key.contextInfo = { ...key.contextInfo, ...message.contextInfo };
|
|
1193
|
+
}
|
|
1194
|
+
else if (key) {
|
|
1195
|
+
key.contextInfo = message.contextInfo;
|
|
1430
1196
|
}
|
|
1431
1197
|
}
|
|
1432
|
-
if (shouldIncludeReportingToken(m)) {
|
|
1198
|
+
if (shouldIncludeReportingToken(m) && !options.newsletter) {
|
|
1433
1199
|
m.messageContextInfo = m.messageContextInfo || {};
|
|
1434
1200
|
if (!m.messageContextInfo.messageSecret) {
|
|
1435
1201
|
m.messageContextInfo.messageSecret = randomBytes(32);
|
|
1436
1202
|
}
|
|
1437
1203
|
}
|
|
1438
|
-
return
|
|
1204
|
+
return WAProto.Message.create(m);
|
|
1439
1205
|
};
|
|
1440
1206
|
export const generateWAMessageFromContent = (jid, message, options) => {
|
|
1441
1207
|
// set timestamp to now
|
|
1442
1208
|
// if not specified
|
|
1443
1209
|
if (!options.timestamp) {
|
|
1444
|
-
options.timestamp = Date
|
|
1210
|
+
options.timestamp = new Date();
|
|
1445
1211
|
}
|
|
1446
|
-
const messageContextInfo = message.messageContextInfo
|
|
1447
1212
|
const innerMessage = normalizeMessageContent(message);
|
|
1448
1213
|
const key = getContentType(innerMessage);
|
|
1449
1214
|
const timestamp = unixTimestampSeconds(options.timestamp);
|
|
1450
|
-
const isNewsletter = isJidNewsletter(jid);
|
|
1451
1215
|
const { quoted, userJid } = options;
|
|
1452
|
-
if (quoted && !
|
|
1216
|
+
if (quoted && !isJidNewsletter(jid)) {
|
|
1453
1217
|
const participant = quoted.key.fromMe
|
|
1454
1218
|
? userJid // TODO: Add support for LIDs
|
|
1455
1219
|
: quoted.participant || quoted.key.participant || quoted.key.remoteJid;
|
|
@@ -1476,14 +1240,14 @@ export const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
1476
1240
|
}
|
|
1477
1241
|
}
|
|
1478
1242
|
if (
|
|
1479
|
-
|
|
1480
|
-
|
|
1243
|
+
// if we want to send a disappearing message
|
|
1244
|
+
!!options?.ephemeralExpiration &&
|
|
1481
1245
|
// and it's not a protocol message -- delete, toggle disappear message
|
|
1482
1246
|
key !== 'protocolMessage' &&
|
|
1483
1247
|
// already not converted to disappearing message
|
|
1484
1248
|
key !== 'ephemeralMessage' &&
|
|
1485
1249
|
// newsletters don't support ephemeral messages
|
|
1486
|
-
!
|
|
1250
|
+
!isJidNewsletter(jid)) {
|
|
1487
1251
|
/* @ts-ignore */
|
|
1488
1252
|
innerMessage[key].contextInfo = {
|
|
1489
1253
|
...(innerMessage[key].contextInfo || {}),
|
|
@@ -1491,15 +1255,7 @@ export const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
1491
1255
|
//ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
|
|
1492
1256
|
};
|
|
1493
1257
|
}
|
|
1494
|
-
|
|
1495
|
-
else if (messageContextInfo?.messageSecret && (isPnUser(jid) || isLidUser(jid))) {
|
|
1496
|
-
messageContextInfo.deviceListMetadata = {
|
|
1497
|
-
recipientKeyHash: randomBytes(10),
|
|
1498
|
-
recipientTimestamp: unixTimestampSeconds()
|
|
1499
|
-
};
|
|
1500
|
-
messageContextInfo.deviceListMetadataVersion = 2
|
|
1501
|
-
}
|
|
1502
|
-
message = proto.Message.create(message);
|
|
1258
|
+
message = WAProto.Message.create(message);
|
|
1503
1259
|
const messageJSON = {
|
|
1504
1260
|
key: {
|
|
1505
1261
|
remoteJid: jid,
|
|
@@ -1514,14 +1270,12 @@ export const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
1514
1270
|
};
|
|
1515
1271
|
return WAProto.WebMessageInfo.fromObject(messageJSON);
|
|
1516
1272
|
};
|
|
1517
|
-
export const generateWAMessage = async (jid, content, options
|
|
1273
|
+
export const generateWAMessage = async (jid, content, options) => {
|
|
1518
1274
|
// ensure msg ID is with every log
|
|
1519
1275
|
options.logger = options?.logger?.child({ msgId: options.messageId });
|
|
1520
|
-
// Pass jid
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
}
|
|
1524
|
-
return generateWAMessageFromContent(jid, await generateWAMessageContent(content, options), options);
|
|
1276
|
+
// Pass jid + newsletter flag to generateWAMessageContent (like wiley)
|
|
1277
|
+
const _isNewsletter = typeof jid === 'string' && jid.endsWith('@newsletter');
|
|
1278
|
+
return generateWAMessageFromContent(jid, await generateWAMessageContent(content, { newsletter: _isNewsletter, ...options, jid }), options);
|
|
1525
1279
|
};
|
|
1526
1280
|
/** Get the key to access the true type of content */
|
|
1527
1281
|
export const getContentType = (content) => {
|
|
@@ -1531,6 +1285,21 @@ export const getContentType = (content) => {
|
|
|
1531
1285
|
return key;
|
|
1532
1286
|
}
|
|
1533
1287
|
};
|
|
1288
|
+
/**
|
|
1289
|
+
* Maps a message content type key to its MediaType string.
|
|
1290
|
+
* Handles ptvMessage → 'ptv', audioMessage ptt → 'ptt', etc.
|
|
1291
|
+
*/
|
|
1292
|
+
export const getMediaTypeFromContentType = (contentType, content) => {
|
|
1293
|
+
if (!contentType)
|
|
1294
|
+
return undefined;
|
|
1295
|
+
if (contentType === 'ptvMessage')
|
|
1296
|
+
return 'ptv';
|
|
1297
|
+
if (contentType === 'audioMessage' && content?.[contentType]?.ptt)
|
|
1298
|
+
return 'ptt';
|
|
1299
|
+
if (contentType === 'videoMessage' && content?.[contentType]?.gifPlayback)
|
|
1300
|
+
return 'gif';
|
|
1301
|
+
return contentType.replace('Message', '');
|
|
1302
|
+
};
|
|
1534
1303
|
/**
|
|
1535
1304
|
* Normalizes ephemeral, view once messages to regular message content
|
|
1536
1305
|
* Eg. image messages in ephemeral messages, in view once messages etc.
|
|
@@ -1550,33 +1319,16 @@ export const normalizeMessageContent = (content) => {
|
|
|
1550
1319
|
content = inner.message;
|
|
1551
1320
|
}
|
|
1552
1321
|
return content;
|
|
1553
|
-
// vltcs@changes 03-02-26 --- Add all futureProofMessage into getFutureProofMessage()
|
|
1554
1322
|
function getFutureProofMessage(message) {
|
|
1555
|
-
return (
|
|
1556
|
-
message?.
|
|
1557
|
-
message?.botForwardedMessage ||
|
|
1558
|
-
message?.botInvokeMessage ||
|
|
1559
|
-
message?.botTaskMessage ||
|
|
1323
|
+
return (message?.ephemeralMessage ||
|
|
1324
|
+
message?.viewOnceMessage ||
|
|
1560
1325
|
message?.documentWithCaptionMessage ||
|
|
1326
|
+
message?.viewOnceMessageV2 ||
|
|
1327
|
+
message?.viewOnceMessageV2Extension ||
|
|
1561
1328
|
message?.editedMessage ||
|
|
1562
|
-
message?.
|
|
1563
|
-
message?.eventCoverImage ||
|
|
1564
|
-
message?.groupMentionedMessage ||
|
|
1565
|
-
message?.groupStatusMentionMessage ||
|
|
1329
|
+
message?.associatedChildMessage ||
|
|
1566
1330
|
message?.groupStatusMessage ||
|
|
1567
|
-
message?.groupStatusMessageV2
|
|
1568
|
-
message?.limitSharingMessage ||
|
|
1569
|
-
message?.lottieStickerMessage ||
|
|
1570
|
-
message?.pollCreationMessageV4 ||
|
|
1571
|
-
message?.pollCreationOptionImageMessage ||
|
|
1572
|
-
message?.questionMessage ||
|
|
1573
|
-
message?.questionReplyMessage ||
|
|
1574
|
-
message?.statusAddYours ||
|
|
1575
|
-
message?.statusMentionMessage ||
|
|
1576
|
-
message?.viewOnceMessage ||
|
|
1577
|
-
message?.viewOnceMessageV2 ||
|
|
1578
|
-
message?.viewOnceMessageV2Extension
|
|
1579
|
-
);
|
|
1331
|
+
message?.groupStatusMessageV2);
|
|
1580
1332
|
}
|
|
1581
1333
|
};
|
|
1582
1334
|
/**
|
|
@@ -1769,7 +1521,7 @@ export const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
|
1769
1521
|
throw new Boom('No message present', { statusCode: 400, data: message });
|
|
1770
1522
|
}
|
|
1771
1523
|
const contentType = getContentType(mContent);
|
|
1772
|
-
let mediaType = contentType
|
|
1524
|
+
let mediaType = getMediaTypeFromContentType(contentType, mContent);
|
|
1773
1525
|
const media = mContent[contentType];
|
|
1774
1526
|
if (!media || typeof media !== 'object' || (!('url' in media) && !('thumbnailDirectPath' in media))) {
|
|
1775
1527
|
throw new Boom(`"${contentType}" message is not a media message`);
|
|
@@ -1809,77 +1561,138 @@ export const assertMediaContent = (content) => {
|
|
|
1809
1561
|
}
|
|
1810
1562
|
return mediaContent;
|
|
1811
1563
|
};
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
if (
|
|
1819
|
-
buffer.length < 12 ||
|
|
1820
|
-
buffer[0] !== 0x52 ||
|
|
1821
|
-
buffer[1] !== 0x49 ||
|
|
1822
|
-
buffer[2] !== 0x46 ||
|
|
1823
|
-
buffer[3] !== 0x46 ||
|
|
1824
|
-
buffer[8] !== 0x57 ||
|
|
1825
|
-
buffer[9] !== 0x45 ||
|
|
1826
|
-
buffer[10] !== 0x42 ||
|
|
1827
|
-
buffer[11] !== 0x50
|
|
1564
|
+
|
|
1565
|
+
export const patchMessageForMdIfRequired = (message) => {
|
|
1566
|
+
if (message?.buttonsMessage ||
|
|
1567
|
+
message?.templateMessage ||
|
|
1568
|
+
message?.listMessage ||
|
|
1569
|
+
message?.interactiveMessage?.nativeFlowMessage
|
|
1828
1570
|
) {
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
if (flagsOffset < buffer.length) {
|
|
1840
|
-
const flags = buffer[flagsOffset];
|
|
1841
|
-
if (flags & 0x02) {
|
|
1842
|
-
return true;
|
|
1843
|
-
};
|
|
1844
|
-
};
|
|
1845
|
-
} else if (chunkFourCC === 'ANIM' || chunkFourCC === 'ANMF') {
|
|
1846
|
-
// ANIM or ANMF chunks indicate animation
|
|
1847
|
-
return true;
|
|
1571
|
+
message = {
|
|
1572
|
+
viewOnceMessageV2Extension: {
|
|
1573
|
+
message: {
|
|
1574
|
+
messageContextInfo: {
|
|
1575
|
+
deviceListMetadataVersion: 2,
|
|
1576
|
+
deviceListMetadata: {}
|
|
1577
|
+
},
|
|
1578
|
+
...message
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1848
1581
|
};
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
};
|
|
1852
|
-
return false;
|
|
1582
|
+
}
|
|
1583
|
+
return message;
|
|
1853
1584
|
};
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
const
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1585
|
+
export const prepareAlbumMessageContent = async (jid, albums, options) => {
|
|
1586
|
+
let mediaHandle;
|
|
1587
|
+
let mediaMsg;
|
|
1588
|
+
const message = [];
|
|
1589
|
+
const albumMsg = generateWAMessageFromContent(jid, {
|
|
1590
|
+
albumMessage: {
|
|
1591
|
+
expectedImageCount: albums.filter(item => 'image' in item).length,
|
|
1592
|
+
expectedVideoCount: albums.filter(item => 'video' in item).length
|
|
1593
|
+
}
|
|
1594
|
+
}, options);
|
|
1595
|
+
await options.sock.relayMessage(jid, albumMsg.message, { messageId: albumMsg.key.id });
|
|
1596
|
+
for (const i in albums) {
|
|
1597
|
+
const media = albums[i];
|
|
1598
|
+
if ('image' in media) {
|
|
1599
|
+
mediaMsg = await generateWAMessage(jid, { image: media.image, ...media, ...options }, {
|
|
1600
|
+
userJid: options.userJid,
|
|
1601
|
+
upload: async (encFilePath, opts) => {
|
|
1602
|
+
const up = await options.sock.waUploadToServer(encFilePath, { ...opts, newsletter: isJidNewsletter(jid) });
|
|
1603
|
+
mediaHandle = up.handle;
|
|
1604
|
+
return up;
|
|
1605
|
+
},
|
|
1606
|
+
...options
|
|
1607
|
+
});
|
|
1608
|
+
} else if ('video' in media) {
|
|
1609
|
+
mediaMsg = await generateWAMessage(jid, { video: media.video, ...media, ...options }, {
|
|
1610
|
+
userJid: options.userJid,
|
|
1611
|
+
upload: async (encFilePath, opts) => {
|
|
1612
|
+
const up = await options.sock.waUploadToServer(encFilePath, { ...opts, newsletter: isJidNewsletter(jid) });
|
|
1613
|
+
mediaHandle = up.handle;
|
|
1614
|
+
return up;
|
|
1615
|
+
},
|
|
1616
|
+
...options
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
if (mediaMsg) {
|
|
1620
|
+
mediaMsg.message.messageContextInfo = {
|
|
1621
|
+
messageSecret: randomBytes(32),
|
|
1622
|
+
messageAssociation: {
|
|
1623
|
+
associationType: 1,
|
|
1624
|
+
parentMessageKey: albumMsg.key
|
|
1625
|
+
}
|
|
1626
|
+
};
|
|
1627
|
+
}
|
|
1628
|
+
message.push(mediaMsg);
|
|
1629
|
+
}
|
|
1630
|
+
return message;
|
|
1869
1631
|
};
|
|
1870
1632
|
/**
|
|
1871
|
-
*
|
|
1872
|
-
*
|
|
1873
|
-
*
|
|
1874
|
-
*
|
|
1875
|
-
*
|
|
1633
|
+
* Ekstrak quoted message dari contextInfo pesan.
|
|
1634
|
+
* Return object yang sudah dinormalisasi, termasuk debug info LID/PN.
|
|
1635
|
+
*
|
|
1636
|
+
* @param {object} msg - WAMessage object dari event messages.upsert
|
|
1637
|
+
* @param {object} [options]
|
|
1638
|
+
* @param {boolean} [options.debug] - jika true, log info ke console
|
|
1639
|
+
* @returns {object|null} quoted message info atau null jika tidak ada
|
|
1876
1640
|
*/
|
|
1877
|
-
export const
|
|
1878
|
-
const
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
);
|
|
1885
|
-
|
|
1641
|
+
export const getQuotedMsg = (msg, options = {}) => {
|
|
1642
|
+
const { debug = false } = options;
|
|
1643
|
+
const msgContent = normalizeMessageContent(msg?.message);
|
|
1644
|
+
if (!msgContent) return null;
|
|
1645
|
+
const msgType = getContentType(msgContent);
|
|
1646
|
+
if (!msgType) return null;
|
|
1647
|
+
const innerMsg = msgContent[msgType];
|
|
1648
|
+
if (!innerMsg) return null;
|
|
1649
|
+
const contextInfo = innerMsg.contextInfo;
|
|
1650
|
+
if (!contextInfo?.stanzaId || !contextInfo?.quotedMessage) return null;
|
|
1651
|
+
|
|
1652
|
+
// Normalisasi participant (sender quoted message)
|
|
1653
|
+
const participant = contextInfo.participant || msg.key?.participant || msg.key?.remoteJid || '';
|
|
1654
|
+
|
|
1655
|
+
// Normalisasi mentionedJid di quoted message
|
|
1656
|
+
const quotedMsgContent = normalizeMessageContent(contextInfo.quotedMessage);
|
|
1657
|
+
const quotedMsgType = getContentType(quotedMsgContent);
|
|
1658
|
+
const quotedInnerMsg = quotedMsgType ? quotedMsgContent?.[quotedMsgType] : null;
|
|
1659
|
+
const quotedMentionedJid = quotedInnerMsg?.contextInfo?.mentionedJid || [];
|
|
1660
|
+
const quotedText = quotedInnerMsg?.text || quotedInnerMsg?.caption || quotedInnerMsg?.conversation || '';
|
|
1661
|
+
|
|
1662
|
+
const result = {
|
|
1663
|
+
key: {
|
|
1664
|
+
id: contextInfo.stanzaId,
|
|
1665
|
+
remoteJid: contextInfo.remoteJid || msg.key?.remoteJid || '',
|
|
1666
|
+
participant: participant,
|
|
1667
|
+
fromMe: false
|
|
1668
|
+
},
|
|
1669
|
+
message: contextInfo.quotedMessage,
|
|
1670
|
+
participant,
|
|
1671
|
+
sender: participant,
|
|
1672
|
+
text: quotedText,
|
|
1673
|
+
type: quotedMsgType || '',
|
|
1674
|
+
mentionedJid: quotedMentionedJid,
|
|
1675
|
+
// Debug info
|
|
1676
|
+
_rawContextInfo: debug ? contextInfo : undefined
|
|
1677
|
+
};
|
|
1678
|
+
|
|
1679
|
+
if (debug) {
|
|
1680
|
+
const hasLidInText = typeof quotedText === 'string' && /@\d{13,20}/.test(quotedText);
|
|
1681
|
+
const hasLidInMentioned = quotedMentionedJid.some(j => j?.endsWith('@lid'));
|
|
1682
|
+
const hasLidParticipant = participant?.endsWith('@lid');
|
|
1683
|
+
console.log('[getQuotedMsg DEBUG]', JSON.stringify({
|
|
1684
|
+
stanzaId: contextInfo.stanzaId,
|
|
1685
|
+
participant,
|
|
1686
|
+
participantIsLid: hasLidParticipant,
|
|
1687
|
+
quotedText,
|
|
1688
|
+
textHasLid: hasLidInText,
|
|
1689
|
+
mentionedJid: quotedMentionedJid,
|
|
1690
|
+
mentionedHasLid: hasLidInMentioned,
|
|
1691
|
+
quotedMsgType
|
|
1692
|
+
}, null, 2));
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
return result;
|
|
1696
|
+
};
|
|
1697
|
+
|
|
1698
|
+
//# sourceMappingURL=messages.js.map
|