violetics 7.0.7-alpha → 7.0.9-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 +493 -980
- 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 +2670 -9199
- 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 +14 -22
- 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 +2 -1
- 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 +154 -69
- 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 +471 -253
- 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 +620 -226
- 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 +109 -62
- 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 +12 -3
- 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 +2 -1
- 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 -6
- 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 +7 -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 +2 -1
- 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 +185 -185
- 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 +932 -1120
- 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 -1
- 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 +55 -77
- 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 +14 -113
- 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 +5 -2
- 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 -53
- package/.github/workflows/publish.yml +0 -75
- package/.pnp.cjs +0 -8572
- package/.pnp.loader.mjs +0 -2126
- package/.yarn/install-state.gz +0 -0
- 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
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
|
-
'@itsliaaa/baileys'
|
|
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];
|
|
@@ -122,99 +184,46 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
122
184
|
return obj;
|
|
123
185
|
}
|
|
124
186
|
}
|
|
125
|
-
const isNewsletter = isJidNewsletter(options.jid);
|
|
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
|
-
const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData
|
|
128
|
-
const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true
|
|
129
|
-
const requiresAudioBackground = options.backgroundColor && mediaType === 'audio'
|
|
190
|
+
const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData['jpegThumbnail'] === 'undefined';
|
|
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
|
-
thumbnailDirectPath,
|
|
181
|
-
thumbnailSha256,
|
|
182
|
-
...uploadData
|
|
183
|
-
})
|
|
184
|
-
});
|
|
185
|
-
if (uploadData.ptv) {
|
|
186
|
-
obj.ptvMessage = obj.videoMessage;
|
|
187
|
-
delete obj.videoMessage;
|
|
188
|
-
}
|
|
189
|
-
if (obj.stickerMessage) {
|
|
190
|
-
obj.stickerMessage.stickerSentTs = Date.now();
|
|
191
|
-
}
|
|
192
|
-
if (cacheableKey) {
|
|
193
|
-
logger?.debug({ cacheableKey }, 'set cache');
|
|
194
|
-
await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
|
|
195
|
-
}
|
|
196
|
-
return obj;
|
|
197
|
-
}
|
|
198
|
-
const { mediaKey, encFilePath, originalFilePath, fileEncSha256, fileSha256, fileLength } = await encryptedStream(uploadData.media, options.mediaTypeOverride || mediaType, {
|
|
199
|
-
logger,
|
|
200
|
-
saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
|
|
201
|
-
opts: options.options
|
|
202
|
-
});
|
|
203
|
-
const fileEncSha256B64 = fileEncSha256.toString('base64');
|
|
204
|
-
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([
|
|
205
214
|
(async () => {
|
|
206
|
-
const result = await options.upload(
|
|
215
|
+
const result = await options.upload(encWriteStream, {
|
|
207
216
|
fileEncSha256B64,
|
|
208
217
|
mediaType,
|
|
209
|
-
timeoutMs: options.mediaUploadTimeoutMs
|
|
218
|
+
timeoutMs: options.mediaUploadTimeoutMs,
|
|
219
|
+
newsletter: !!options.newsletter
|
|
210
220
|
});
|
|
211
|
-
logger?.debug({ mediaType, cacheableKey }, 'uploaded media');
|
|
212
221
|
return result;
|
|
213
222
|
})(),
|
|
214
223
|
(async () => {
|
|
215
224
|
try {
|
|
216
225
|
if (requiresThumbnailComputation) {
|
|
217
|
-
const { thumbnail, originalImageDimensions } = await generateThumbnail(
|
|
226
|
+
const { thumbnail, originalImageDimensions } = await generateThumbnail(bodyPath, mediaType, options);
|
|
218
227
|
uploadData.jpegThumbnail = thumbnail;
|
|
219
228
|
if (!uploadData.width && originalImageDimensions) {
|
|
220
229
|
uploadData.width = originalImageDimensions.width;
|
|
@@ -224,53 +233,71 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
224
233
|
logger?.debug('generated thumbnail');
|
|
225
234
|
}
|
|
226
235
|
if (requiresDurationComputation) {
|
|
227
|
-
|
|
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
|
+
}
|
|
228
247
|
logger?.debug('computed audio duration');
|
|
229
248
|
}
|
|
230
249
|
if (requiresWaveformProcessing) {
|
|
231
|
-
|
|
232
|
-
|
|
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
|
+
}
|
|
233
257
|
}
|
|
234
258
|
if (requiresAudioBackground) {
|
|
235
259
|
uploadData.backgroundArgb = await assertColor(options.backgroundColor);
|
|
236
260
|
logger?.debug('computed backgroundColor audio status');
|
|
237
261
|
}
|
|
238
|
-
}
|
|
239
|
-
catch (error) {
|
|
262
|
+
} catch (error) {
|
|
240
263
|
logger?.warn({ trace: error.stack }, 'failed to obtain extra info');
|
|
241
264
|
}
|
|
242
265
|
})()
|
|
243
266
|
]).finally(async () => {
|
|
244
267
|
try {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
await fs.unlink(originalFilePath);
|
|
268
|
+
if (!Buffer.isBuffer(encWriteStream)) {
|
|
269
|
+
encWriteStream.destroy?.();
|
|
248
270
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
271
|
+
if (didSaveToTmpPath && bodyPath) {
|
|
272
|
+
await fs.unlink(bodyPath).catch(() => {});
|
|
273
|
+
}
|
|
274
|
+
} catch (error) {
|
|
252
275
|
logger?.warn('failed to remove tmp file');
|
|
253
276
|
}
|
|
254
277
|
});
|
|
255
|
-
|
|
256
|
-
const obj = proto.Message.create({
|
|
278
|
+
const obj = WAProto.Message.fromObject({
|
|
257
279
|
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
|
258
|
-
url: mediaUrl,
|
|
280
|
+
url: uploadHandle ? undefined : mediaUrl,
|
|
259
281
|
directPath,
|
|
260
|
-
mediaKey,
|
|
261
|
-
fileEncSha256,
|
|
282
|
+
mediaKey: mediaKey,
|
|
283
|
+
fileEncSha256: fileEncSha256,
|
|
262
284
|
fileSha256,
|
|
263
285
|
fileLength,
|
|
264
|
-
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
265
|
-
...uploadData
|
|
286
|
+
mediaKeyTimestamp: uploadHandle ? undefined : unixTimestampSeconds(),
|
|
287
|
+
...uploadData,
|
|
288
|
+
media: undefined,
|
|
289
|
+
...(options?.contextInfo ? { contextInfo: options.contextInfo } : {})
|
|
266
290
|
})
|
|
267
291
|
});
|
|
268
292
|
if (uploadData.ptv) {
|
|
269
293
|
obj.ptvMessage = obj.videoMessage;
|
|
270
294
|
delete obj.videoMessage;
|
|
271
295
|
}
|
|
272
|
-
|
|
273
|
-
|
|
296
|
+
// Attach uploadHandle so sendMessage can use it as media_id
|
|
297
|
+
if (uploadHandle) {
|
|
298
|
+
obj._uploadHandle = uploadHandle;
|
|
299
|
+
}
|
|
300
|
+
if (mediaType === 'audio') {
|
|
274
301
|
}
|
|
275
302
|
if (cacheableKey) {
|
|
276
303
|
logger?.debug({ cacheableKey }, 'set cache');
|
|
@@ -284,335 +311,13 @@ export const prepareDisappearingMessageSettingContent = (ephemeralExpiration) =>
|
|
|
284
311
|
ephemeralMessage: {
|
|
285
312
|
message: {
|
|
286
313
|
protocolMessage: {
|
|
287
|
-
type:
|
|
314
|
+
type: WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
|
|
288
315
|
ephemeralExpiration
|
|
289
316
|
}
|
|
290
317
|
}
|
|
291
318
|
}
|
|
292
319
|
};
|
|
293
|
-
return content;
|
|
294
|
-
};
|
|
295
|
-
// Lia@Changes 31-01-26 --- Extract product message into a standalone function so it can also be reused as the header for interactive messages
|
|
296
|
-
const prepareProductMessage = async (message, options) => {
|
|
297
|
-
if (!message.businessOwnerJid) {
|
|
298
|
-
throw new Boom('"businessOwnerJid" is missing from the content', { statusCode: 400 });
|
|
299
|
-
}
|
|
300
|
-
const { imageMessage } = await prepareWAMessageMedia({ image: message.image || message.product.productImage }, options);
|
|
301
|
-
// Lia@Changes 01-02-26 --- Add product message default value
|
|
302
|
-
const content = {
|
|
303
|
-
...message,
|
|
304
|
-
product: {
|
|
305
|
-
currencyCode: 'IDR',
|
|
306
|
-
priceAmount1000: 1000,
|
|
307
|
-
title: LIBRARY_NAME,
|
|
308
|
-
...message.product,
|
|
309
|
-
productImage: imageMessage
|
|
310
|
-
}
|
|
311
|
-
};
|
|
312
|
-
delete content.image;
|
|
313
|
-
return content;
|
|
314
|
-
};
|
|
315
|
-
/**
|
|
316
|
-
* Lia@Note 30-01-26
|
|
317
|
-
* ---
|
|
318
|
-
* Credits: Work on ensuring stickerPackMessage fields are valid by @jlucaso1 (https://github.com/jlucaso1).
|
|
319
|
-
* based on https://github.com/WhiskeySockets/Baileys/pull/1561
|
|
320
|
-
*/
|
|
321
|
-
const prepareStickerPackMessage = async (message, options) => {
|
|
322
|
-
const { cover, stickers = [], name = '📦 Sticker Pack', publisher = 'GitHub: itsliaaa', description = '🏷️ itsliaaa/baileys' } = message;
|
|
323
|
-
if (stickers.length > 60) {
|
|
324
|
-
throw new Boom('Sticker pack exceeds the maximum limit of 60 stickers', { statusCode: 400 });
|
|
325
|
-
}
|
|
326
|
-
if (stickers.length === 0) {
|
|
327
|
-
throw new Boom('Sticker pack must contain at least one sticker', { statusCode: 400 });
|
|
328
|
-
}
|
|
329
|
-
if (!cover) {
|
|
330
|
-
throw new Boom('Sticker pack must contain a cover', { statusCode: 400 });
|
|
331
|
-
}
|
|
332
|
-
// Lia@Changes 01-02-26 --- Add caching for sticker pack (similiar to prepareWAMessageMedia)
|
|
333
|
-
const cacheableKey = Array.isArray(stickers) &&
|
|
334
|
-
stickers.length &&
|
|
335
|
-
!!options.mediaCache &&
|
|
336
|
-
'sticker:' + stickers
|
|
337
|
-
.reduce((acc, x) => {
|
|
338
|
-
const url = typeof x.data === 'object' &&
|
|
339
|
-
'url' in x.data &&
|
|
340
|
-
!!x.data.url &&
|
|
341
|
-
x.data.url;
|
|
342
|
-
if (url) acc.push(url);
|
|
343
|
-
return acc;
|
|
344
|
-
}, [])
|
|
345
|
-
.join('@');
|
|
346
|
-
if (cacheableKey) {
|
|
347
|
-
const mediaBuff = await options.mediaCache.get(cacheableKey);
|
|
348
|
-
if (mediaBuff) {
|
|
349
|
-
options.logger?.debug({ cacheableKey }, 'got media cache hit');
|
|
350
|
-
return proto.Message.StickerPackMessage.decode(mediaBuff);
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
const lib = await getImageProcessingLibrary();
|
|
354
|
-
const stickerPackIdValue = generateMessageIDV2();
|
|
355
|
-
const stickerData = {};
|
|
356
|
-
const stickerPromises = stickers.map(async (sticker, i) => {
|
|
357
|
-
const { stream } = await getStream(sticker.data);
|
|
358
|
-
const buffer = await toBuffer(stream);
|
|
359
|
-
let webpBuffer,
|
|
360
|
-
isAnimated = false;
|
|
361
|
-
const isWebP = isWebPBuffer(buffer);
|
|
362
|
-
if (isWebP) {
|
|
363
|
-
// Already WebP - preserve original to keep exif metadata and animation
|
|
364
|
-
webpBuffer = buffer;
|
|
365
|
-
isAnimated = isAnimatedWebP(buffer);
|
|
366
|
-
}
|
|
367
|
-
else if ('sharp' in lib && lib.sharp?.default) {
|
|
368
|
-
// Convert to WebP, preserving metadata
|
|
369
|
-
webpBuffer = await lib
|
|
370
|
-
.sharp
|
|
371
|
-
.default(buffer)
|
|
372
|
-
.resize(512, 512, { fit: 'inside' })
|
|
373
|
-
.webp({ quality: 80 })
|
|
374
|
-
.toBuffer();
|
|
375
|
-
// Non-WebP inputs converted to WebP are not animated
|
|
376
|
-
isAnimated = false;
|
|
377
|
-
}
|
|
378
|
-
else if ('image' in lib && lib.image?.Transformer) {
|
|
379
|
-
webpBuffer = await new lib
|
|
380
|
-
.image
|
|
381
|
-
.Transformer(buffer)
|
|
382
|
-
.resize(512, 512)
|
|
383
|
-
.webp(80);
|
|
384
|
-
// Non-WebP inputs converted to WebP are not animated
|
|
385
|
-
isAnimated = false;
|
|
386
|
-
}
|
|
387
|
-
else {
|
|
388
|
-
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.');
|
|
389
|
-
}
|
|
390
|
-
if (webpBuffer.length > 1024 * 1024) {
|
|
391
|
-
throw new Boom(`Sticker at index ${i} exceeds the 1MB size limit`, { statusCode: 400 });
|
|
392
|
-
}
|
|
393
|
-
const hash = sha256(webpBuffer).toString('base64').replace(/\//g, '-');
|
|
394
|
-
const fileName = hash + '.webp';
|
|
395
|
-
stickerData[fileName] = [new Uint8Array(webpBuffer), { level: 0 }];
|
|
396
|
-
return {
|
|
397
|
-
fileName,
|
|
398
|
-
mimetype: 'image/webp',
|
|
399
|
-
isAnimated,
|
|
400
|
-
emojis: sticker.emojis || ['✨'],
|
|
401
|
-
accessibilityLabel: sticker.accessibilityLabel || ''
|
|
402
|
-
};
|
|
403
|
-
});
|
|
404
|
-
const stickerMetadata = await Promise.all(stickerPromises);
|
|
405
|
-
// Process and add cover/tray icon to the ZIP
|
|
406
|
-
const trayIconFileName = stickerPackIdValue + '.webp';
|
|
407
|
-
const { stream: coverStream } = await getStream(cover);
|
|
408
|
-
const coverBuffer = await toBuffer(coverStream);
|
|
409
|
-
let coverWebpBuffer;
|
|
410
|
-
const isCoverWebP = isWebPBuffer(coverBuffer);
|
|
411
|
-
if (isCoverWebP) {
|
|
412
|
-
// Already WebP - preserve original to keep exif metadata
|
|
413
|
-
coverWebpBuffer = coverBuffer;
|
|
414
|
-
}
|
|
415
|
-
else if ('sharp' in lib && lib.sharp?.default) {
|
|
416
|
-
coverWebpBuffer = await lib
|
|
417
|
-
.sharp
|
|
418
|
-
.default(coverBuffer)
|
|
419
|
-
.resize(512, 512, { fit: 'inside' })
|
|
420
|
-
.webp({ quality: 80 })
|
|
421
|
-
.toBuffer();
|
|
422
|
-
}
|
|
423
|
-
else if ('image' in lib && lib.image?.Transformer) {
|
|
424
|
-
coverWebpBuffer = await new lib
|
|
425
|
-
.image
|
|
426
|
-
.Transformer(coverBuffer)
|
|
427
|
-
.resize(512, 512)
|
|
428
|
-
.webp(80);
|
|
429
|
-
}
|
|
430
|
-
else {
|
|
431
|
-
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.');
|
|
432
|
-
}
|
|
433
|
-
// Add cover to ZIP data
|
|
434
|
-
stickerData[trayIconFileName] = [new Uint8Array(coverWebpBuffer), { level: 0 }];
|
|
435
|
-
const zipBuffer = await new Promise((resolve, reject) => {
|
|
436
|
-
zip(stickerData, (error, data) => {
|
|
437
|
-
if (error) {
|
|
438
|
-
reject(error);
|
|
439
|
-
} else {
|
|
440
|
-
resolve(Buffer.from(data));
|
|
441
|
-
}
|
|
442
|
-
});
|
|
443
|
-
});
|
|
444
|
-
const stickerPackSize = zipBuffer.length;
|
|
445
|
-
const stickerPackUpload = await encryptedStream(zipBuffer, 'sticker-pack', {
|
|
446
|
-
logger: options.logger,
|
|
447
|
-
opts: options.options
|
|
448
|
-
});
|
|
449
|
-
const stickerPackUploadResult = await options.upload(stickerPackUpload.encFilePath, {
|
|
450
|
-
fileEncSha256B64: stickerPackUpload.fileEncSha256.toString('base64'),
|
|
451
|
-
mediaType: 'sticker-pack',
|
|
452
|
-
timeoutMs: options.mediaUploadTimeoutMs
|
|
453
|
-
});
|
|
454
|
-
await fs.unlink(stickerPackUpload.encFilePath);
|
|
455
|
-
const obj = {
|
|
456
|
-
name: name,
|
|
457
|
-
publisher: publisher,
|
|
458
|
-
stickerPackId: stickerPackIdValue,
|
|
459
|
-
packDescription: description,
|
|
460
|
-
stickerPackOrigin: proto.Message.StickerPackMessage.StickerPackOrigin.USER_CREATED,
|
|
461
|
-
stickerPackSize: stickerPackSize,
|
|
462
|
-
stickers: stickerMetadata,
|
|
463
|
-
fileSha256: stickerPackUpload.fileSha256,
|
|
464
|
-
fileEncSha256: stickerPackUpload.fileEncSha256,
|
|
465
|
-
mediaKey: stickerPackUpload.mediaKey,
|
|
466
|
-
directPath: stickerPackUploadResult.directPath,
|
|
467
|
-
fileLength: stickerPackUpload.fileLength,
|
|
468
|
-
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
469
|
-
trayIconFileName: trayIconFileName
|
|
470
|
-
};
|
|
471
|
-
try {
|
|
472
|
-
// Reuse the cover buffer we already processed for thumbnail generation
|
|
473
|
-
let thumbnailBuffer;
|
|
474
|
-
if ('sharp' in lib && lib.sharp?.default) {
|
|
475
|
-
thumbnailBuffer = await lib
|
|
476
|
-
.sharp
|
|
477
|
-
.default(coverBuffer)
|
|
478
|
-
.resize(252, 252)
|
|
479
|
-
.jpeg()
|
|
480
|
-
.toBuffer();
|
|
481
|
-
}
|
|
482
|
-
if ('image' in lib && lib.image?.Transformer) {
|
|
483
|
-
thumbnailBuffer = await new lib
|
|
484
|
-
.image
|
|
485
|
-
.Transformer(coverBuffer)
|
|
486
|
-
.resize(252, 252)
|
|
487
|
-
.jpeg();
|
|
488
|
-
}
|
|
489
|
-
else if ('jimp' in lib && lib.jimp?.Jimp) {
|
|
490
|
-
const jimpImage = await lib.jimp.Jimp.read(coverBuffer);
|
|
491
|
-
thumbnailBuffer = await jimpImage
|
|
492
|
-
.resize({ w: 252, h: 252 })
|
|
493
|
-
.getBuffer('image/jpeg');
|
|
494
|
-
}
|
|
495
|
-
else {
|
|
496
|
-
throw new Error('No image processing library available for thumbnail generation');
|
|
497
|
-
}
|
|
498
|
-
if (!thumbnailBuffer || thumbnailBuffer.length === 0) {
|
|
499
|
-
throw new Error('Failed to generate thumbnail buffer');
|
|
500
|
-
}
|
|
501
|
-
const thumbUpload = await encryptedStream(thumbnailBuffer, 'thumbnail-sticker-pack', {
|
|
502
|
-
logger: options.logger,
|
|
503
|
-
opts: options.options,
|
|
504
|
-
mediaKey: stickerPackUpload.mediaKey // Use same mediaKey as the sticker pack ZIP
|
|
505
|
-
});
|
|
506
|
-
const thumbUploadResult = await options.upload(thumbUpload.encFilePath, {
|
|
507
|
-
fileEncSha256B64: thumbUpload.fileEncSha256.toString('base64'),
|
|
508
|
-
mediaType: 'thumbnail-sticker-pack',
|
|
509
|
-
timeoutMs: options.mediaUploadTimeoutMs
|
|
510
|
-
});
|
|
511
|
-
await fs.unlink(thumbUpload.encFilePath);
|
|
512
|
-
Object.assign(obj, {
|
|
513
|
-
thumbnailDirectPath: thumbUploadResult.directPath,
|
|
514
|
-
thumbnailSha256: thumbUpload.fileSha256,
|
|
515
|
-
thumbnailEncSha256: thumbUpload.fileEncSha256,
|
|
516
|
-
thumbnailHeight: 252,
|
|
517
|
-
thumbnailWidth: 252,
|
|
518
|
-
imageDataHash: sha256(thumbnailBuffer).toString('base64')
|
|
519
|
-
});
|
|
520
|
-
}
|
|
521
|
-
catch (error) {
|
|
522
|
-
options.logger?.warn?.(`Thumbnail generation failed: ${error}`);
|
|
523
|
-
}
|
|
524
|
-
const content = obj;
|
|
525
|
-
if (cacheableKey) {
|
|
526
|
-
options.logger?.debug({ cacheableKey }, 'set cache');
|
|
527
|
-
await options.mediaCache.set(cacheableKey, WAProto.Message.StickerPackMessage.encode(content).finish());
|
|
528
|
-
}
|
|
529
|
-
return WAProto.Message.StickerPackMessage.fromObject(content);
|
|
530
|
-
};
|
|
531
|
-
// Lia@Changes 30-01-26 --- Add native flow button helper for interactive message
|
|
532
|
-
const prepareNativeFlowButtons = (message) => {
|
|
533
|
-
const buttons = message.nativeFlow
|
|
534
|
-
const isButtonsFieldArray = Array.isArray(buttons);
|
|
535
|
-
const correctedField = isButtonsFieldArray ? buttons : buttons.buttons;
|
|
536
|
-
const messageParamsJson = {};
|
|
537
|
-
// Lia@Changes 31-01-26 --- Add coupon and options inside interactive message
|
|
538
|
-
if (hasOptionalProperty(message, 'couponCode') && !!message.couponCode) {
|
|
539
|
-
Object.assign(messageParamsJson, {
|
|
540
|
-
limited_time_offer: {
|
|
541
|
-
text: message.couponText || LIBRARY_NAME,
|
|
542
|
-
url: message.couponUrl || DONATE_URL, // Lia@Note 02-02-26 --- Apologies if this feels cheeky, just a fallback
|
|
543
|
-
copy_code: message.couponCode,
|
|
544
|
-
expiration_time: Date.now() * 2
|
|
545
|
-
}
|
|
546
|
-
});
|
|
547
|
-
}
|
|
548
|
-
if (hasOptionalProperty(message, 'optionText') && !!message.optionText) {
|
|
549
|
-
Object.assign(messageParamsJson, {
|
|
550
|
-
bottom_sheet: {
|
|
551
|
-
in_thread_buttons_limit: 1,
|
|
552
|
-
divider_indices: Array.from(
|
|
553
|
-
{ length: correctedField.length },
|
|
554
|
-
(_, index) => index
|
|
555
|
-
),
|
|
556
|
-
list_title: message.optionTitle || '📄 Select Options',
|
|
557
|
-
button_title: message.optionText
|
|
558
|
-
}
|
|
559
|
-
});
|
|
560
|
-
}
|
|
561
|
-
return {
|
|
562
|
-
buttons: correctedField.map(button => {
|
|
563
|
-
const buttonText = button.text || button.buttonText;
|
|
564
|
-
if (hasOptionalProperty(button, 'id') && !!button.id) {
|
|
565
|
-
return {
|
|
566
|
-
name: 'quick_reply',
|
|
567
|
-
buttonParamsJson: JSON.stringify({
|
|
568
|
-
display_text: buttonText || '👉🏻 Click',
|
|
569
|
-
id: button.id
|
|
570
|
-
})
|
|
571
|
-
};
|
|
572
|
-
}
|
|
573
|
-
else if (hasOptionalProperty(button, 'copy') && !!button.copy) {
|
|
574
|
-
return {
|
|
575
|
-
name: 'cta_copy',
|
|
576
|
-
buttonParamsJson: JSON.stringify({
|
|
577
|
-
display_text: buttonText || '📋 Copy',
|
|
578
|
-
copy_code: button.copy
|
|
579
|
-
})
|
|
580
|
-
};
|
|
581
|
-
}
|
|
582
|
-
else if (hasOptionalProperty(button, 'url') && !!button.url) {
|
|
583
|
-
return {
|
|
584
|
-
name: 'cta_url',
|
|
585
|
-
buttonParamsJson: JSON.stringify({
|
|
586
|
-
display_text: buttonText || '🌐 Visit',
|
|
587
|
-
url: button.url,
|
|
588
|
-
merchant_url: button.url
|
|
589
|
-
})
|
|
590
|
-
};
|
|
591
|
-
}
|
|
592
|
-
else if (hasOptionalProperty(button, 'call') && !!button.call) {
|
|
593
|
-
return {
|
|
594
|
-
name: 'cta_call',
|
|
595
|
-
buttonParamsJson: JSON.stringify({
|
|
596
|
-
display_text: buttonText || '📞 Call',
|
|
597
|
-
phone_number: button.call
|
|
598
|
-
})
|
|
599
|
-
};
|
|
600
|
-
}
|
|
601
|
-
// Lia@Changes 12-03-26 --- Add "single_select" shortcut \(°o°)/
|
|
602
|
-
else if (hasOptionalProperty(button, 'sections') && !!button.sections) {
|
|
603
|
-
return {
|
|
604
|
-
name: 'single_select',
|
|
605
|
-
buttonParamsJson: JSON.stringify({
|
|
606
|
-
title: buttonText || '📋 Select',
|
|
607
|
-
sections: button.sections
|
|
608
|
-
})
|
|
609
|
-
};
|
|
610
|
-
}
|
|
611
|
-
return button;
|
|
612
|
-
}),
|
|
613
|
-
messageParamsJson: JSON.stringify(messageParamsJson),
|
|
614
|
-
messageVersion: 1
|
|
615
|
-
};
|
|
320
|
+
return WAProto.Message.fromObject(content);
|
|
616
321
|
};
|
|
617
322
|
/**
|
|
618
323
|
* Generate forwarded message content like WA does
|
|
@@ -620,7 +325,7 @@ const prepareNativeFlowButtons = (message) => {
|
|
|
620
325
|
* @param options.forceForward will show the message as forwarded even if it is from you
|
|
621
326
|
*/
|
|
622
327
|
export const generateForwardMessageContent = (message, forceForward) => {
|
|
623
|
-
let content = message.message
|
|
328
|
+
let content = message.message;
|
|
624
329
|
if (!content) {
|
|
625
330
|
throw new Boom('no content in message', { statusCode: 400 });
|
|
626
331
|
}
|
|
@@ -636,65 +341,28 @@ export const generateForwardMessageContent = (message, forceForward) => {
|
|
|
636
341
|
key = 'extendedTextMessage';
|
|
637
342
|
}
|
|
638
343
|
const key_ = content?.[key];
|
|
639
|
-
const contextInfo = {};
|
|
640
344
|
if (score > 0) {
|
|
641
|
-
contextInfo
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
// so the server knows where to find the original media
|
|
646
|
-
const remoteJid = message.key?.remoteJid;
|
|
647
|
-
if (remoteJid && isJidNewsletter(remoteJid)) {
|
|
648
|
-
contextInfo.forwardedNewsletterMessageInfo = {
|
|
649
|
-
newsletterJid: remoteJid,
|
|
650
|
-
serverMessageId: message.key?.server_id ? parseInt(message.key.server_id) : null,
|
|
651
|
-
newsletterName: null
|
|
652
|
-
};
|
|
653
|
-
// strip messageContextInfo (contains messageSecret etc.) as WA Web does
|
|
654
|
-
delete content.messageContextInfo;
|
|
345
|
+
key_.contextInfo = { forwardingScore: score, isForwarded: true };
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
key_.contextInfo = {};
|
|
655
349
|
}
|
|
656
|
-
key_.contextInfo = contextInfo;
|
|
657
350
|
return content;
|
|
658
351
|
};
|
|
659
352
|
export const hasNonNullishProperty = (message, key) => {
|
|
660
|
-
return message
|
|
661
|
-
|
|
353
|
+
return (typeof message === 'object' &&
|
|
354
|
+
message !== null &&
|
|
662
355
|
key in message &&
|
|
663
|
-
message[key]
|
|
664
|
-
|
|
665
|
-
export const hasOptionalProperty = (obj, key) => {
|
|
666
|
-
return obj != null &&
|
|
667
|
-
typeof obj === 'object' &&
|
|
668
|
-
key in obj &&
|
|
669
|
-
obj[key] != null;
|
|
670
|
-
};
|
|
671
|
-
// Lia@Changes 06-02-26 --- Validate album message media to avoid bug 👀
|
|
672
|
-
export const hasValidAlbumMedia = (message) => {
|
|
673
|
-
return message.imageMessage ||
|
|
674
|
-
message.videoMessage;
|
|
675
|
-
};
|
|
676
|
-
export const hasValidInteractiveHeader = (message) => {
|
|
677
|
-
return message.imageMessage ||
|
|
678
|
-
message.videoMessage ||
|
|
679
|
-
message.documentMessage ||
|
|
680
|
-
message.productMessage ||
|
|
681
|
-
message.locationMessage;
|
|
682
|
-
};
|
|
683
|
-
// Lia@Changes 30-01-26 --- Validate carousel cards header to avoid bug 👀
|
|
684
|
-
export const hasValidCarouselHeader = (message) => {
|
|
685
|
-
return message.imageMessage ||
|
|
686
|
-
message.videoMessage ||
|
|
687
|
-
message.productMessage;
|
|
356
|
+
message[key] !== null &&
|
|
357
|
+
message[key] !== undefined);
|
|
688
358
|
};
|
|
359
|
+
function hasOptionalProperty(obj, key) {
|
|
360
|
+
return typeof obj === 'object' && obj !== null && key in obj && obj[key] !== null;
|
|
361
|
+
}
|
|
689
362
|
export const generateWAMessageContent = async (message, options) => {
|
|
690
363
|
var _a, _b;
|
|
691
364
|
let m = {};
|
|
692
|
-
|
|
693
|
-
if (hasNonNullishProperty(message, 'raw')) {
|
|
694
|
-
delete message.raw;
|
|
695
|
-
return proto.Message.create(message);
|
|
696
|
-
}
|
|
697
|
-
else if (hasNonNullishProperty(message, 'text')) {
|
|
365
|
+
if (hasNonNullishProperty(message, 'text')) {
|
|
698
366
|
const extContent = { text: message.text };
|
|
699
367
|
let urlInfo = message.linkPreview;
|
|
700
368
|
if (typeof urlInfo === 'undefined') {
|
|
@@ -726,58 +394,65 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
726
394
|
m.extendedTextMessage = extContent;
|
|
727
395
|
}
|
|
728
396
|
else if (hasNonNullishProperty(message, 'contacts')) {
|
|
729
|
-
const
|
|
730
|
-
const contactLen = contacts.contacts.length;
|
|
397
|
+
const contactLen = message.contacts.contacts.length;
|
|
731
398
|
if (!contactLen) {
|
|
732
399
|
throw new Boom('require atleast 1 contact', { statusCode: 400 });
|
|
733
400
|
}
|
|
734
401
|
if (contactLen === 1) {
|
|
735
|
-
m.contactMessage = contacts.contacts[0];
|
|
402
|
+
m.contactMessage = WAProto.Message.ContactMessage.create(message.contacts.contacts[0]);
|
|
736
403
|
}
|
|
737
404
|
else {
|
|
738
|
-
m.contactsArrayMessage = contacts;
|
|
405
|
+
m.contactsArrayMessage = WAProto.Message.ContactsArrayMessage.create(message.contacts);
|
|
739
406
|
}
|
|
740
407
|
}
|
|
741
408
|
else if (hasNonNullishProperty(message, 'location')) {
|
|
742
|
-
|
|
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
|
+
}
|
|
743
421
|
}
|
|
744
422
|
else if (hasNonNullishProperty(message, 'react')) {
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
react.senderTimestampMs = Date.now();
|
|
423
|
+
if (!message.react.senderTimestampMs) {
|
|
424
|
+
message.react.senderTimestampMs = Date.now();
|
|
748
425
|
}
|
|
749
|
-
m.reactionMessage = react;
|
|
426
|
+
m.reactionMessage = WAProto.Message.ReactionMessage.create(message.react);
|
|
750
427
|
}
|
|
751
428
|
else if (hasNonNullishProperty(message, 'delete')) {
|
|
752
429
|
m.protocolMessage = {
|
|
753
430
|
key: message.delete,
|
|
754
|
-
type:
|
|
431
|
+
type: WAProto.Message.ProtocolMessage.Type.REVOKE
|
|
755
432
|
};
|
|
756
433
|
}
|
|
757
434
|
else if (hasNonNullishProperty(message, 'forward')) {
|
|
758
435
|
m = generateForwardMessageContent(message.forward, message.force);
|
|
759
436
|
}
|
|
760
437
|
else if (hasNonNullishProperty(message, 'disappearingMessagesInChat')) {
|
|
761
|
-
const
|
|
762
|
-
|
|
763
|
-
? disappearingMessagesInChat
|
|
438
|
+
const exp = typeof message.disappearingMessagesInChat === 'boolean'
|
|
439
|
+
? message.disappearingMessagesInChat
|
|
764
440
|
? WA_DEFAULT_EPHEMERAL
|
|
765
441
|
: 0
|
|
766
|
-
: disappearingMessagesInChat;
|
|
442
|
+
: message.disappearingMessagesInChat;
|
|
767
443
|
m = prepareDisappearingMessageSettingContent(exp);
|
|
768
444
|
}
|
|
769
445
|
else if (hasNonNullishProperty(message, 'groupInvite')) {
|
|
770
|
-
const { groupInvite } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (✷‿✷)
|
|
771
446
|
m.groupInviteMessage = {};
|
|
772
|
-
m.groupInviteMessage.inviteCode = groupInvite.inviteCode;
|
|
773
|
-
m.groupInviteMessage.inviteExpiration = groupInvite.inviteExpiration;
|
|
774
|
-
m.groupInviteMessage.caption = groupInvite.text;
|
|
775
|
-
m.groupInviteMessage.groupJid = groupInvite.jid;
|
|
776
|
-
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;
|
|
777
452
|
//TODO: use built-in interface and get disappearing mode info etc.
|
|
778
453
|
//TODO: cache / use store!?
|
|
779
454
|
if (options.getProfilePicUrl) {
|
|
780
|
-
const pfpUrl = await options.getProfilePicUrl(groupInvite.jid, 'preview');
|
|
455
|
+
const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid, 'preview');
|
|
781
456
|
if (pfpUrl) {
|
|
782
457
|
const resp = await fetch(pfpUrl, { method: 'GET', dispatcher: options?.options?.dispatcher });
|
|
783
458
|
if (resp.ok) {
|
|
@@ -787,137 +462,206 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
787
462
|
}
|
|
788
463
|
}
|
|
789
464
|
}
|
|
790
|
-
else if (hasNonNullishProperty(message, 'stickers')) {
|
|
791
|
-
m.stickerPackMessage = await prepareStickerPackMessage(message, options);
|
|
792
|
-
}
|
|
793
465
|
else if (hasNonNullishProperty(message, 'pin')) {
|
|
794
466
|
m.pinInChatMessage = {};
|
|
795
467
|
m.messageContextInfo = {};
|
|
796
|
-
m.pinInChatMessage.key = message.pin;
|
|
797
|
-
m.pinInChatMessage.type = message.type;
|
|
798
|
-
m.pinInChatMessage.senderTimestampMs = Date.now();
|
|
799
|
-
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;
|
|
800
473
|
}
|
|
801
474
|
else if (hasNonNullishProperty(message, 'keep')) {
|
|
802
475
|
m.keepInChatMessage = {};
|
|
803
|
-
m.keepInChatMessage.key = message.keep;
|
|
804
|
-
m.keepInChatMessage.keepType = message.type;
|
|
805
|
-
m.keepInChatMessage.timestampMs = Date.now();
|
|
806
|
-
}
|
|
807
|
-
else if (hasNonNullishProperty(message, '
|
|
808
|
-
|
|
809
|
-
m.
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
},
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
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 } : {})
|
|
819
498
|
};
|
|
820
499
|
}
|
|
821
500
|
else if (hasNonNullishProperty(message, 'buttonReply')) {
|
|
822
|
-
const { buttonReply } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (✷‿✷)
|
|
823
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;
|
|
824
512
|
case 'template':
|
|
825
513
|
m.templateButtonReplyMessage = {
|
|
826
|
-
selectedDisplayText: buttonReply.displayText,
|
|
827
|
-
selectedId: buttonReply.id,
|
|
828
|
-
selectedIndex: buttonReply.index
|
|
514
|
+
selectedDisplayText: message.buttonReply.displayText,
|
|
515
|
+
selectedId: message.buttonReply.id,
|
|
516
|
+
selectedIndex: message.buttonReply.index
|
|
829
517
|
};
|
|
830
518
|
break;
|
|
831
519
|
case 'plain':
|
|
832
520
|
m.buttonsResponseMessage = {
|
|
833
|
-
selectedButtonId: buttonReply.id,
|
|
834
|
-
selectedDisplayText: buttonReply.displayText,
|
|
521
|
+
selectedButtonId: message.buttonReply.id,
|
|
522
|
+
selectedDisplayText: message.buttonReply.displayText,
|
|
835
523
|
type: proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
|
|
836
524
|
};
|
|
837
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;
|
|
838
539
|
}
|
|
839
540
|
}
|
|
840
|
-
else if (hasNonNullishProperty(message, '
|
|
841
|
-
const
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
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
|
|
847
562
|
},
|
|
848
|
-
|
|
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
|
+
}
|
|
849
578
|
};
|
|
579
|
+
requestPaymentMessage.noteMessage.extendedTextMessage.contextInfo = {
|
|
580
|
+
...(message.contextInfo || {}),
|
|
581
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
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;
|
|
850
601
|
}
|
|
851
602
|
else if (hasOptionalProperty(message, 'ptv') && message.ptv) {
|
|
852
603
|
const { videoMessage } = await prepareWAMessageMedia({ video: message.video }, options);
|
|
853
604
|
m.ptvMessage = videoMessage;
|
|
854
605
|
}
|
|
855
606
|
else if (hasNonNullishProperty(message, 'product')) {
|
|
856
|
-
|
|
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 };
|
|
857
618
|
}
|
|
858
619
|
else if (hasNonNullishProperty(message, 'event')) {
|
|
859
|
-
const { event } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (✷‿✷)
|
|
860
620
|
m.eventMessage = {};
|
|
861
|
-
const startTime = Math.floor(event.startDate.getTime() / 1000);
|
|
862
|
-
if (event.call && options.getCallLink) {
|
|
863
|
-
const token = await options.getCallLink(event.call, { startTime });
|
|
864
|
-
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;
|
|
865
625
|
}
|
|
866
626
|
m.messageContextInfo = {
|
|
867
627
|
// encKey
|
|
868
|
-
messageSecret: event.messageSecret || randomBytes(32)
|
|
628
|
+
messageSecret: message.event.messageSecret || randomBytes(32)
|
|
869
629
|
};
|
|
870
|
-
m.eventMessage.name = event.name;
|
|
871
|
-
m.eventMessage.description = event.description;
|
|
630
|
+
m.eventMessage.name = message.event.name;
|
|
631
|
+
m.eventMessage.description = message.event.description;
|
|
872
632
|
m.eventMessage.startTime = startTime;
|
|
873
|
-
m.eventMessage.endTime = event.endDate ? event.endDate.getTime() / 1000 : undefined;
|
|
874
|
-
m.eventMessage.isCanceled = event.isCancelled ?? false;
|
|
875
|
-
m.eventMessage.extraGuestsAllowed = event.extraGuestsAllowed;
|
|
876
|
-
m.eventMessage.isScheduleCall = event.isScheduleCall ?? false;
|
|
877
|
-
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;
|
|
878
638
|
}
|
|
879
639
|
else if (hasNonNullishProperty(message, 'poll')) {
|
|
880
|
-
|
|
881
|
-
(
|
|
882
|
-
(
|
|
883
|
-
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)) {
|
|
884
643
|
throw new Boom('Invalid poll values', { statusCode: 400 });
|
|
885
644
|
}
|
|
886
|
-
if (poll.selectableCount < 0 || poll.selectableCount > poll.values.length) {
|
|
887
|
-
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}`, {
|
|
888
647
|
statusCode: 400
|
|
889
648
|
});
|
|
890
649
|
}
|
|
891
650
|
m.messageContextInfo = {
|
|
892
651
|
// encKey
|
|
893
|
-
messageSecret: poll.messageSecret || randomBytes(32)
|
|
652
|
+
messageSecret: message.poll.messageSecret || randomBytes(32)
|
|
894
653
|
};
|
|
895
654
|
const pollCreationMessage = {
|
|
896
|
-
name: poll.name,
|
|
897
|
-
selectableOptionsCount: poll.selectableCount,
|
|
898
|
-
options: poll.values.map(optionName => ({ optionName }))
|
|
655
|
+
name: message.poll.name,
|
|
656
|
+
selectableOptionsCount: message.poll.selectableCount,
|
|
657
|
+
options: message.poll.values.map(optionName => ({ optionName }))
|
|
899
658
|
};
|
|
900
|
-
if (poll.toAnnouncementGroup) {
|
|
659
|
+
if (message.poll.toAnnouncementGroup) {
|
|
901
660
|
// poll v2 is for community announcement groups (single select and multiple)
|
|
902
661
|
m.pollCreationMessageV2 = pollCreationMessage;
|
|
903
662
|
}
|
|
904
663
|
else {
|
|
905
|
-
|
|
906
|
-
if (poll.pollType === 1) {
|
|
907
|
-
if (!poll.correctAnswer) {
|
|
908
|
-
throw new Boom('No "correctAnswer" provided for quiz', { statusCode: 400 });
|
|
909
|
-
}
|
|
910
|
-
m.pollCreationMessageV5 = {
|
|
911
|
-
// Lia@Note 08-02-26 --- quiz for newsletter only
|
|
912
|
-
...pollCreationMessage,
|
|
913
|
-
correctAnswer: {
|
|
914
|
-
optionName: poll.correctAnswer.toString()
|
|
915
|
-
},
|
|
916
|
-
pollType: poll.pollType,
|
|
917
|
-
selectableOptionsCount: 1
|
|
918
|
-
};
|
|
919
|
-
}
|
|
920
|
-
else if (poll.selectableCount === 1) {
|
|
664
|
+
if (message.poll.selectableCount === 1) {
|
|
921
665
|
//poll v3 is for single select polls
|
|
922
666
|
m.pollCreationMessageV3 = pollCreationMessage;
|
|
923
667
|
}
|
|
@@ -927,44 +671,211 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
927
671
|
}
|
|
928
672
|
}
|
|
929
673
|
}
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
const {
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
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;
|
|
939
714
|
};
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
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}`);
|
|
947
847
|
}
|
|
848
|
+
|
|
849
|
+
m.stickerPackMessage.contextInfo = {
|
|
850
|
+
...(message.contextInfo || {}),
|
|
851
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
852
|
+
};
|
|
948
853
|
}
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
if (
|
|
956
|
-
|
|
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 (_) {}
|
|
957
870
|
}
|
|
958
|
-
m.
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
senderTimestampMs: Date.now(),
|
|
962
|
-
vote: pollUpdate.vote
|
|
871
|
+
m.newsletterAdminInviteMessage.contextInfo = {
|
|
872
|
+
...(message.contextInfo || {}),
|
|
873
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
963
874
|
};
|
|
964
875
|
}
|
|
965
876
|
else if (hasNonNullishProperty(message, 'sharePhoneNumber')) {
|
|
966
877
|
m.protocolMessage = {
|
|
967
|
-
type:
|
|
878
|
+
type: proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
|
|
968
879
|
};
|
|
969
880
|
}
|
|
970
881
|
else if (hasNonNullishProperty(message, 'requestPhoneNumber')) {
|
|
@@ -972,7 +883,7 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
972
883
|
}
|
|
973
884
|
else if (hasNonNullishProperty(message, 'limitSharing')) {
|
|
974
885
|
m.protocolMessage = {
|
|
975
|
-
type:
|
|
886
|
+
type: proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
|
|
976
887
|
limitSharing: {
|
|
977
888
|
sharingLimited: message.limitSharing === true,
|
|
978
889
|
trigger: 1,
|
|
@@ -981,442 +892,288 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
981
892
|
}
|
|
982
893
|
};
|
|
983
894
|
}
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
};
|
|
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 };
|
|
990
900
|
}
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
if (!Buffer.isBuffer(message.thumbnail)) {
|
|
994
|
-
throw new Boom('Must provide thumbnail buffer in order message', { statusCode: 400 });
|
|
995
|
-
}
|
|
996
|
-
m.orderMessage = {
|
|
997
|
-
itemCount: 1,
|
|
998
|
-
messageVersion: 1,
|
|
999
|
-
orderTitle: LIBRARY_NAME,
|
|
1000
|
-
status: proto.Message.OrderMessage.OrderStatus.INQUIRY,
|
|
1001
|
-
surface: proto.Message.OrderMessage.OrderSurface.CATALOG,
|
|
1002
|
-
token: generateMessageIDV2(),
|
|
1003
|
-
totalAmount1000: 1000,
|
|
1004
|
-
totalCurrencyCode: 'IDR',
|
|
1005
|
-
...message,
|
|
1006
|
-
message: message.orderText
|
|
1007
|
-
};
|
|
1008
|
-
delete m.orderMessage.orderText;
|
|
901
|
+
else {
|
|
902
|
+
m = await prepareWAMessageMedia(message, options);
|
|
1009
903
|
}
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
if (album[i].video) videoCount++;
|
|
1019
|
-
};
|
|
1020
|
-
let imageCount = 0;
|
|
1021
|
-
for (let i = 0; i < album.length; i++) {
|
|
1022
|
-
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
|
|
1023
912
|
};
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
m.albumMessage = {
|
|
1028
|
-
expectedImageCount: imageCount,
|
|
1029
|
-
expectedVideoCount: videoCount
|
|
913
|
+
listMessage.contextInfo = {
|
|
914
|
+
...(message.contextInfo || {}),
|
|
915
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
1030
916
|
};
|
|
917
|
+
m = { listMessage };
|
|
1031
918
|
}
|
|
1032
|
-
else {
|
|
1033
|
-
|
|
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 };
|
|
1034
941
|
}
|
|
1035
|
-
|
|
1036
|
-
if (hasNonNullishProperty(message, 'buttons')) {
|
|
942
|
+
else if ('buttons' in message && !!message.buttons) {
|
|
1037
943
|
const buttonsMessage = {
|
|
1038
|
-
buttons: message.buttons.map(
|
|
1039
|
-
// Lia@Changes 12-03-26 --- Add "single_select" shortcut!
|
|
1040
|
-
const buttonText = button.text || button.buttonText
|
|
1041
|
-
if (hasOptionalProperty(button, 'sections')) {
|
|
1042
|
-
return {
|
|
1043
|
-
nativeFlowInfo: {
|
|
1044
|
-
name: 'single_select',
|
|
1045
|
-
paramsJson: JSON.stringify({
|
|
1046
|
-
title: buttonText,
|
|
1047
|
-
sections: button.sections
|
|
1048
|
-
})
|
|
1049
|
-
},
|
|
1050
|
-
type: ButtonType.NATIVE_FLOW
|
|
1051
|
-
};
|
|
1052
|
-
}
|
|
1053
|
-
else if (hasOptionalProperty(button, 'name')) {
|
|
1054
|
-
return {
|
|
1055
|
-
nativeFlowInfo: {
|
|
1056
|
-
name: button.name,
|
|
1057
|
-
paramsJson: button.paramsJson
|
|
1058
|
-
},
|
|
1059
|
-
type: ButtonType.NATIVE_FLOW
|
|
1060
|
-
};
|
|
1061
|
-
}
|
|
1062
|
-
return {
|
|
1063
|
-
buttonId: button.id || button.buttonId,
|
|
1064
|
-
buttonText: typeof buttonText === 'string' ? { displayText: buttonText } : buttonText,
|
|
1065
|
-
type: button.type || ButtonType.RESPONSE
|
|
1066
|
-
};
|
|
1067
|
-
})
|
|
944
|
+
buttons: message.buttons.map(b => ({ ...b, type: proto.Message.ButtonsMessage.Button.Type.RESPONSE }))
|
|
1068
945
|
};
|
|
1069
|
-
if (
|
|
946
|
+
if ('text' in message) {
|
|
1070
947
|
buttonsMessage.contentText = message.text;
|
|
1071
|
-
buttonsMessage.headerType =
|
|
948
|
+
buttonsMessage.headerType = proto.Message.ButtonsMessage.HeaderType.EMPTY;
|
|
1072
949
|
}
|
|
1073
950
|
else {
|
|
1074
|
-
if (
|
|
951
|
+
if ('caption' in message) {
|
|
1075
952
|
buttonsMessage.contentText = message.caption;
|
|
1076
953
|
}
|
|
1077
954
|
const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
|
|
1078
|
-
buttonsMessage.headerType =
|
|
955
|
+
buttonsMessage.headerType = proto.Message.ButtonsMessage.HeaderType[type];
|
|
1079
956
|
Object.assign(buttonsMessage, m);
|
|
1080
957
|
}
|
|
1081
|
-
if (
|
|
958
|
+
if ('footer' in message && !!message.footer) {
|
|
1082
959
|
buttonsMessage.footerText = message.footer;
|
|
1083
960
|
}
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
footerText: message.footer,
|
|
1092
|
-
description: message.text,
|
|
1093
|
-
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 } : {})
|
|
1094
968
|
};
|
|
1095
|
-
m = {
|
|
969
|
+
m = { buttonsMessage };
|
|
1096
970
|
}
|
|
1097
|
-
|
|
1098
|
-
else if (hasNonNullishProperty(message, 'templateButtons')) {
|
|
971
|
+
else if ('templateButtons' in message && !!message.templateButtons) {
|
|
1099
972
|
const hydratedTemplate = {
|
|
1100
|
-
hydratedButtons: message.templateButtons
|
|
1101
|
-
if (hasOptionalProperty(button, 'id')) {
|
|
1102
|
-
return {
|
|
1103
|
-
index: i,
|
|
1104
|
-
quickReplyButton: {
|
|
1105
|
-
displayText: button.text || button.buttonText || '👉🏻 Click',
|
|
1106
|
-
id: button.id
|
|
1107
|
-
}
|
|
1108
|
-
};
|
|
1109
|
-
}
|
|
1110
|
-
else if (hasOptionalProperty(button, 'url')) {
|
|
1111
|
-
return {
|
|
1112
|
-
index: i,
|
|
1113
|
-
urlButton: {
|
|
1114
|
-
displayText: button.text || button.buttonText || '🌐 Visit',
|
|
1115
|
-
url: button.url
|
|
1116
|
-
}
|
|
1117
|
-
};
|
|
1118
|
-
}
|
|
1119
|
-
else if (hasOptionalProperty(button, 'call')) {
|
|
1120
|
-
return {
|
|
1121
|
-
index: i,
|
|
1122
|
-
callButton: {
|
|
1123
|
-
displayText: button.text || button.buttonText || '📞 Call',
|
|
1124
|
-
phoneNumber: button.call
|
|
1125
|
-
}
|
|
1126
|
-
};
|
|
1127
|
-
}
|
|
1128
|
-
button.index = button.index || i;
|
|
1129
|
-
return button;
|
|
1130
|
-
})
|
|
973
|
+
hydratedButtons: message.templateButtons
|
|
1131
974
|
};
|
|
1132
|
-
if (
|
|
975
|
+
if ('text' in message) {
|
|
1133
976
|
hydratedTemplate.hydratedContentText = message.text;
|
|
1134
977
|
}
|
|
1135
978
|
else {
|
|
1136
|
-
if (
|
|
1137
|
-
hydratedTemplate.hydratedTitleText = message.title;
|
|
979
|
+
if ('caption' in message) {
|
|
1138
980
|
hydratedTemplate.hydratedContentText = message.caption;
|
|
1139
|
-
}
|
|
981
|
+
}
|
|
1140
982
|
Object.assign(hydratedTemplate, m);
|
|
1141
983
|
}
|
|
1142
|
-
if (
|
|
984
|
+
if ('footer' in message && !!message.footer) {
|
|
1143
985
|
hydratedTemplate.hydratedFooterText = message.footer;
|
|
1144
986
|
}
|
|
1145
|
-
hydratedTemplate.
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
987
|
+
hydratedTemplate.contextInfo = {
|
|
988
|
+
...(message.contextInfo || {}),
|
|
989
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
990
|
+
};
|
|
991
|
+
m = { templateMessage: { fourRowTemplate: hydratedTemplate, hydratedTemplate } };
|
|
1152
992
|
}
|
|
1153
|
-
else if (
|
|
993
|
+
else if ('interactiveButtons' in message && !!message.interactiveButtons) {
|
|
1154
994
|
const interactiveMessage = {
|
|
1155
|
-
nativeFlowMessage:
|
|
995
|
+
nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.fromObject({
|
|
996
|
+
buttons: message.interactiveButtons,
|
|
997
|
+
})
|
|
1156
998
|
};
|
|
1157
|
-
if (
|
|
1158
|
-
interactiveMessage.
|
|
1159
|
-
bizJid: message.bizJid,
|
|
1160
|
-
id: message.id,
|
|
1161
|
-
messageVersion: 1
|
|
1162
|
-
};
|
|
999
|
+
if ('text' in message) {
|
|
1000
|
+
interactiveMessage.body = { text: message.text };
|
|
1163
1001
|
}
|
|
1164
|
-
else if (
|
|
1165
|
-
interactiveMessage.
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
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,
|
|
1169
1008
|
};
|
|
1009
|
+
Object.assign(interactiveMessage.header, m);
|
|
1170
1010
|
}
|
|
1171
|
-
if (
|
|
1172
|
-
interactiveMessage.
|
|
1011
|
+
if ('footer' in message && !!message.footer) {
|
|
1012
|
+
interactiveMessage.footer = { text: message.footer };
|
|
1173
1013
|
}
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
interactiveMessage.header = {
|
|
1181
|
-
title: message.title || '',
|
|
1182
|
-
subtitle: message.subtitle || '',
|
|
1183
|
-
hasMediaAttachment: isValidHeader
|
|
1184
|
-
};
|
|
1185
|
-
interactiveMessage.body = { text: message.caption };
|
|
1186
|
-
}
|
|
1187
|
-
if (hasOptionalProperty(message, 'thumbnail') && !!message.thumbnail) {
|
|
1188
|
-
interactiveMessage.jpegThumbnail = message.thumbnail;
|
|
1189
|
-
}
|
|
1014
|
+
if ('title' in message && !!message.title) {
|
|
1015
|
+
interactiveMessage.header = {
|
|
1016
|
+
title: message.title,
|
|
1017
|
+
subtitle: message.subtitle,
|
|
1018
|
+
hasMediaAttachment: message?.media ?? false,
|
|
1019
|
+
};
|
|
1190
1020
|
Object.assign(interactiveMessage.header, m);
|
|
1191
1021
|
}
|
|
1192
|
-
if (
|
|
1193
|
-
|
|
1194
|
-
audio: message.audioFooter
|
|
1195
|
-
}, options);
|
|
1196
|
-
interactiveMessage.footer = {
|
|
1197
|
-
audioMessage: parseFooter.audioMessage,
|
|
1198
|
-
hasMediaAttachment: true
|
|
1199
|
-
};
|
|
1022
|
+
if ('contextInfo' in message && !!message.contextInfo) {
|
|
1023
|
+
interactiveMessage.contextInfo = message.contextInfo;
|
|
1200
1024
|
}
|
|
1201
|
-
|
|
1202
|
-
interactiveMessage.
|
|
1025
|
+
if ('mentions' in message && !!message.mentions) {
|
|
1026
|
+
interactiveMessage.contextInfo = { mentionedJid: message.mentions };
|
|
1203
1027
|
}
|
|
1204
1028
|
m = { interactiveMessage };
|
|
1205
1029
|
}
|
|
1206
|
-
else if (
|
|
1030
|
+
else if ('shop' in message && !!message.shop) {
|
|
1207
1031
|
const interactiveMessage = {
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
carouselHeader.productMessage = await prepareProductMessage(card, options);
|
|
1213
|
-
}
|
|
1214
|
-
else {
|
|
1215
|
-
carouselHeader = await prepareWAMessageMedia(card, options).catch(() => ({ }));
|
|
1216
|
-
}
|
|
1217
|
-
const isValidHeader = hasValidCarouselHeader(carouselHeader)
|
|
1218
|
-
if (!isValidHeader) {
|
|
1219
|
-
throw new Boom('Invalid media type for carousel card', { statusCode: 400 });
|
|
1220
|
-
}
|
|
1221
|
-
const carouselCard = {
|
|
1222
|
-
nativeFlowMessage: prepareNativeFlowButtons(card.nativeFlow ? card : [])
|
|
1223
|
-
};
|
|
1224
|
-
if (hasOptionalProperty(card, 'text')) {
|
|
1225
|
-
carouselCard.body = { text: card.text };
|
|
1226
|
-
}
|
|
1227
|
-
else {
|
|
1228
|
-
if (hasOptionalProperty(card, 'caption')) {
|
|
1229
|
-
carouselCard.header = {
|
|
1230
|
-
title: card.title || '',
|
|
1231
|
-
subtitle: card.subtitle || '',
|
|
1232
|
-
hasMediaAttachment: isValidHeader
|
|
1233
|
-
};
|
|
1234
|
-
carouselCard.body = { text: card.caption };
|
|
1235
|
-
}
|
|
1236
|
-
if (hasOptionalProperty(card, 'thumbnail') && !!card.thumbnail) {
|
|
1237
|
-
carouselCard.jpegThumbnail = card.thumbnail;
|
|
1238
|
-
}
|
|
1239
|
-
Object.assign(carouselCard.header, carouselHeader);
|
|
1240
|
-
}
|
|
1241
|
-
if (hasOptionalProperty(card, 'audioFooter')) {
|
|
1242
|
-
const parseFooter = await prepareWAMessageMedia({
|
|
1243
|
-
audio: card.audioFooter
|
|
1244
|
-
}, options);
|
|
1245
|
-
carouselCard.footer = {
|
|
1246
|
-
audioMessage: parseFooter.audioMessage,
|
|
1247
|
-
hasMediaAttachment: true
|
|
1248
|
-
};
|
|
1249
|
-
}
|
|
1250
|
-
else if (hasOptionalProperty(card, 'footer')) {
|
|
1251
|
-
carouselCard.footer = { text: card.footer };
|
|
1252
|
-
}
|
|
1253
|
-
return carouselCard
|
|
1254
|
-
})),
|
|
1255
|
-
carouselCardType: CarouselCardType.UNKNOWN,
|
|
1256
|
-
messageVersion: 1
|
|
1257
|
-
}
|
|
1032
|
+
shopStorefrontMessage: proto.Message.InteractiveMessage.ShopMessage.fromObject({
|
|
1033
|
+
surface: message.shop,
|
|
1034
|
+
id: message.id
|
|
1035
|
+
})
|
|
1258
1036
|
};
|
|
1259
|
-
if (
|
|
1037
|
+
if ('text' in message) {
|
|
1260
1038
|
interactiveMessage.body = { text: message.text };
|
|
1261
1039
|
}
|
|
1262
|
-
if (
|
|
1263
|
-
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);
|
|
1264
1048
|
}
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
// Lia@Changes 01-02-26 --- Add request payment message
|
|
1268
|
-
else if (hasNonNullishProperty(message, 'requestPaymentFrom')) {
|
|
1269
|
-
const requestPaymentMessage = {
|
|
1270
|
-
amount: {
|
|
1271
|
-
currencyCode: 'IDR',
|
|
1272
|
-
offset: 1000,
|
|
1273
|
-
value: 1000
|
|
1274
|
-
},
|
|
1275
|
-
amount1000: 1000,
|
|
1276
|
-
currencyCodeIso4217: 'IDR',
|
|
1277
|
-
expiryTimestamp: Date.now(),
|
|
1278
|
-
noteMessage: m,
|
|
1279
|
-
requestFrom: message.requestPaymentFrom,
|
|
1280
|
-
...message
|
|
1281
|
-
};
|
|
1282
|
-
delete requestPaymentMessage.requestPaymentFrom;
|
|
1283
|
-
if (hasNonNullishProperty(m, 'extendedTextMessage') || hasNonNullishProperty(m, 'stickerMessage')) {
|
|
1284
|
-
Object.assign(requestPaymentMessage.noteMessage, m);
|
|
1049
|
+
if ('footer' in message && !!message.footer) {
|
|
1050
|
+
interactiveMessage.footer = { text: message.footer };
|
|
1285
1051
|
}
|
|
1286
|
-
|
|
1287
|
-
|
|
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);
|
|
1288
1059
|
}
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
// Lia@Changes 01-02-26 --- Add invoice message
|
|
1292
|
-
else if (hasNonNullishProperty(message, 'invoiceNote')) {
|
|
1293
|
-
const attachment = m.imageMessage || m.documentMessage;
|
|
1294
|
-
const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
|
|
1295
|
-
const invoiceMessage = {
|
|
1296
|
-
attachmentType: proto.Message.InvoiceMessage.AttachmentType[type === 'DOCUMENT' ? 'PDF' : 'IMAGE'],
|
|
1297
|
-
note: message.invoiceNote
|
|
1298
|
-
};
|
|
1299
|
-
if (attachment) {
|
|
1300
|
-
const { directPath, fileEncSha256, fileSha256, jpegThumbnail = undefined, mediaKey, mediaKeyTimestamp, mimetype } = attachment;
|
|
1301
|
-
Object.assign(invoiceMessage, {
|
|
1302
|
-
attachmentDirectPath: directPath,
|
|
1303
|
-
attachmentFileEncSha256: fileEncSha256,
|
|
1304
|
-
attachmentFileSha256: fileSha256,
|
|
1305
|
-
attachmentJpegThumbnail: jpegThumbnail,
|
|
1306
|
-
attachmentMediaKey: mediaKey,
|
|
1307
|
-
attachmentMediaKeyTimestamp: mediaKeyTimestamp,
|
|
1308
|
-
attachmentMimetype: mimetype,
|
|
1309
|
-
token: generateMessageIDV2()
|
|
1310
|
-
});
|
|
1060
|
+
if ('contextInfo' in message && !!message.contextInfo) {
|
|
1061
|
+
interactiveMessage.contextInfo = message.contextInfo;
|
|
1311
1062
|
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1063
|
+
if ('mentions' in message && !!message.mentions) {
|
|
1064
|
+
interactiveMessage.contextInfo = { mentionedJid: message.mentions };
|
|
1314
1065
|
}
|
|
1315
|
-
m = {
|
|
1066
|
+
m = { interactiveMessage };
|
|
1316
1067
|
}
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
}
|
|
1325
|
-
if (!content.url || typeof content.url !== 'string') {
|
|
1326
|
-
content.url = DONATE_URL; // Lia@Note 02-02-26 --- Apologies if this feels cheeky, just a fallback
|
|
1327
|
-
}
|
|
1328
|
-
const externalAdReply = {
|
|
1329
|
-
...content,
|
|
1330
|
-
body: content.body,
|
|
1331
|
-
mediaType: content.mediaType || 1,
|
|
1332
|
-
mediaUrl: content.url + '?update=' + Date.now(),
|
|
1333
|
-
renderLargerThumbnail: content.largeThumbnail,
|
|
1334
|
-
thumbnail: content.thumbnail,
|
|
1335
|
-
thumbnailUrl: content.url,
|
|
1336
|
-
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
|
+
}
|
|
1337
1075
|
};
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
else if (key) {
|
|
1345
|
-
key.contextInfo = { externalAdReply };
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
if ((hasOptionalProperty(message, 'mentions') && message.mentions?.length) ||
|
|
1349
|
-
(hasOptionalProperty(message, 'mentionAll') && message.mentionAll)) {
|
|
1350
|
-
const messageType = Object.keys(m)[0];
|
|
1351
|
-
const key = m[messageType];
|
|
1352
|
-
if ('contextInfo' in key && !!key.contextInfo) {
|
|
1353
|
-
key.contextInfo.mentionedJid = message.mentions || [];
|
|
1354
|
-
}
|
|
1355
|
-
else if (key) {
|
|
1356
|
-
key.contextInfo = {
|
|
1357
|
-
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
|
|
1358
1082
|
};
|
|
1359
1083
|
}
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
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
|
+
}
|
|
1370
1094
|
}
|
|
1371
|
-
|
|
1372
|
-
|
|
1095
|
+
if ('footer' in message && !!message.footer) {
|
|
1096
|
+
interactiveMessage.footer = { text: message.footer };
|
|
1373
1097
|
}
|
|
1098
|
+
interactiveMessage.contextInfo = {
|
|
1099
|
+
...(message.contextInfo || {}),
|
|
1100
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
1101
|
+
};
|
|
1102
|
+
m = { interactiveMessage };
|
|
1374
1103
|
}
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
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);
|
|
1385
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
|
+
};
|
|
1386
1147
|
}
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
}
|
|
1390
|
-
// Lia@Changes 02-02-26 --- Add "interactiveAsTemplate" boolean to wrap interactiveMessage into templateMessage
|
|
1391
|
-
else if (hasOptionalProperty(message, 'interactiveAsTemplate') && !!message.interactiveAsTemplate) {
|
|
1392
|
-
if (!m.interactiveMessage) {
|
|
1393
|
-
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 };
|
|
1394
1150
|
}
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
templateId: message.id || 'template-' + Date.now() // Lia@Note 04-02-26 --- Minimal templateId to satisfy WhatsApp ( ꈍᴗꈍ)
|
|
1399
|
-
}
|
|
1151
|
+
interactiveMessage.contextInfo = {
|
|
1152
|
+
...(message.contextInfo || {}),
|
|
1153
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
1400
1154
|
};
|
|
1401
|
-
|
|
1155
|
+
m = { interactiveMessage };
|
|
1402
1156
|
}
|
|
1403
|
-
// Lia@Changes 30-01-26 --- Add "ephemeral" boolean to wrap message into ephemeralMessage like "viewOnce"
|
|
1404
1157
|
if (hasOptionalProperty(message, 'ephemeral') && !!message.ephemeral) {
|
|
1405
1158
|
m = { ephemeralMessage: { message: m } };
|
|
1406
|
-
delete message.ephemeral;
|
|
1407
|
-
}
|
|
1408
|
-
else if (hasOptionalProperty(message, 'viewOnce') && !!message.viewOnce) {
|
|
1409
|
-
m = { viewOnceMessage: { message: m } };
|
|
1410
1159
|
}
|
|
1411
|
-
|
|
1412
|
-
else if (hasOptionalProperty(message, 'viewOnceV2') && !!message.viewOnceV2) {
|
|
1160
|
+
if (hasOptionalProperty(message, 'viewOnce') && !!message.viewOnce) {
|
|
1413
1161
|
m = { viewOnceMessageV2: { message: m } };
|
|
1414
|
-
delete message.viewOnceV2;
|
|
1415
1162
|
}
|
|
1416
|
-
|
|
1417
|
-
else if (hasOptionalProperty(message, 'viewOnceV2Extension') && !!message.viewOnceV2Extension) {
|
|
1163
|
+
if (hasOptionalProperty(message, 'viewOnceExt') && !!message.viewOnceExt) {
|
|
1418
1164
|
m = { viewOnceMessageV2Extension: { message: m } };
|
|
1419
|
-
|
|
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
|
+
}
|
|
1420
1177
|
}
|
|
1421
1178
|
if (hasOptionalProperty(message, 'edit')) {
|
|
1422
1179
|
m = {
|
|
@@ -1424,31 +1181,39 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1424
1181
|
key: message.edit,
|
|
1425
1182
|
editedMessage: m,
|
|
1426
1183
|
timestampMs: Date.now(),
|
|
1427
|
-
type:
|
|
1184
|
+
type: WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
|
|
1428
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;
|
|
1429
1196
|
}
|
|
1430
1197
|
}
|
|
1431
|
-
if (shouldIncludeReportingToken(m)) {
|
|
1198
|
+
if (shouldIncludeReportingToken(m) && !options.newsletter) {
|
|
1432
1199
|
m.messageContextInfo = m.messageContextInfo || {};
|
|
1433
1200
|
if (!m.messageContextInfo.messageSecret) {
|
|
1434
1201
|
m.messageContextInfo.messageSecret = randomBytes(32);
|
|
1435
1202
|
}
|
|
1436
1203
|
}
|
|
1437
|
-
return
|
|
1204
|
+
return WAProto.Message.create(m);
|
|
1438
1205
|
};
|
|
1439
1206
|
export const generateWAMessageFromContent = (jid, message, options) => {
|
|
1440
1207
|
// set timestamp to now
|
|
1441
1208
|
// if not specified
|
|
1442
1209
|
if (!options.timestamp) {
|
|
1443
|
-
options.timestamp = Date
|
|
1210
|
+
options.timestamp = new Date();
|
|
1444
1211
|
}
|
|
1445
|
-
const messageContextInfo = message.messageContextInfo
|
|
1446
1212
|
const innerMessage = normalizeMessageContent(message);
|
|
1447
1213
|
const key = getContentType(innerMessage);
|
|
1448
1214
|
const timestamp = unixTimestampSeconds(options.timestamp);
|
|
1449
|
-
const isNewsletter = isJidNewsletter(jid);
|
|
1450
1215
|
const { quoted, userJid } = options;
|
|
1451
|
-
if (quoted && !
|
|
1216
|
+
if (quoted && !isJidNewsletter(jid)) {
|
|
1452
1217
|
const participant = quoted.key.fromMe
|
|
1453
1218
|
? userJid // TODO: Add support for LIDs
|
|
1454
1219
|
: quoted.participant || quoted.key.participant || quoted.key.remoteJid;
|
|
@@ -1482,7 +1247,7 @@ export const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
1482
1247
|
// already not converted to disappearing message
|
|
1483
1248
|
key !== 'ephemeralMessage' &&
|
|
1484
1249
|
// newsletters don't support ephemeral messages
|
|
1485
|
-
!
|
|
1250
|
+
!isJidNewsletter(jid)) {
|
|
1486
1251
|
/* @ts-ignore */
|
|
1487
1252
|
innerMessage[key].contextInfo = {
|
|
1488
1253
|
...(innerMessage[key].contextInfo || {}),
|
|
@@ -1490,15 +1255,7 @@ export const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
1490
1255
|
//ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
|
|
1491
1256
|
};
|
|
1492
1257
|
}
|
|
1493
|
-
|
|
1494
|
-
if (messageContextInfo?.messageSecret && (isPnUser(jid) || isLidUser(jid))) {
|
|
1495
|
-
messageContextInfo.deviceListMetadata = {
|
|
1496
|
-
recipientKeyHash: randomBytes(10),
|
|
1497
|
-
recipientTimestamp: unixTimestampSeconds()
|
|
1498
|
-
};
|
|
1499
|
-
messageContextInfo.deviceListMetadataVersion = 2
|
|
1500
|
-
}
|
|
1501
|
-
message = proto.Message.create(message);
|
|
1258
|
+
message = WAProto.Message.create(message);
|
|
1502
1259
|
const messageJSON = {
|
|
1503
1260
|
key: {
|
|
1504
1261
|
remoteJid: jid,
|
|
@@ -1513,14 +1270,12 @@ export const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
1513
1270
|
};
|
|
1514
1271
|
return WAProto.WebMessageInfo.fromObject(messageJSON);
|
|
1515
1272
|
};
|
|
1516
|
-
export const generateWAMessage = async (jid, content, options
|
|
1273
|
+
export const generateWAMessage = async (jid, content, options) => {
|
|
1517
1274
|
// ensure msg ID is with every log
|
|
1518
1275
|
options.logger = options?.logger?.child({ msgId: options.messageId });
|
|
1519
|
-
// Pass jid
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
}
|
|
1523
|
-
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);
|
|
1524
1279
|
};
|
|
1525
1280
|
/** Get the key to access the true type of content */
|
|
1526
1281
|
export const getContentType = (content) => {
|
|
@@ -1530,6 +1285,21 @@ export const getContentType = (content) => {
|
|
|
1530
1285
|
return key;
|
|
1531
1286
|
}
|
|
1532
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
|
+
};
|
|
1533
1303
|
/**
|
|
1534
1304
|
* Normalizes ephemeral, view once messages to regular message content
|
|
1535
1305
|
* Eg. image messages in ephemeral messages, in view once messages etc.
|
|
@@ -1549,33 +1319,16 @@ export const normalizeMessageContent = (content) => {
|
|
|
1549
1319
|
content = inner.message;
|
|
1550
1320
|
}
|
|
1551
1321
|
return content;
|
|
1552
|
-
// Lia@Changes 03-02-26 --- Add all futureProofMessage into getFutureProofMessage()
|
|
1553
1322
|
function getFutureProofMessage(message) {
|
|
1554
|
-
return (
|
|
1555
|
-
message?.
|
|
1556
|
-
message?.botForwardedMessage ||
|
|
1557
|
-
message?.botInvokeMessage ||
|
|
1558
|
-
message?.botTaskMessage ||
|
|
1323
|
+
return (message?.ephemeralMessage ||
|
|
1324
|
+
message?.viewOnceMessage ||
|
|
1559
1325
|
message?.documentWithCaptionMessage ||
|
|
1326
|
+
message?.viewOnceMessageV2 ||
|
|
1327
|
+
message?.viewOnceMessageV2Extension ||
|
|
1560
1328
|
message?.editedMessage ||
|
|
1561
|
-
message?.
|
|
1562
|
-
message?.eventCoverImage ||
|
|
1563
|
-
message?.groupMentionedMessage ||
|
|
1564
|
-
message?.groupStatusMentionMessage ||
|
|
1329
|
+
message?.associatedChildMessage ||
|
|
1565
1330
|
message?.groupStatusMessage ||
|
|
1566
|
-
message?.groupStatusMessageV2
|
|
1567
|
-
message?.limitSharingMessage ||
|
|
1568
|
-
message?.lottieStickerMessage ||
|
|
1569
|
-
message?.pollCreationMessageV4 ||
|
|
1570
|
-
message?.pollCreationOptionImageMessage ||
|
|
1571
|
-
message?.questionMessage ||
|
|
1572
|
-
message?.questionReplyMessage ||
|
|
1573
|
-
message?.statusAddYours ||
|
|
1574
|
-
message?.statusMentionMessage ||
|
|
1575
|
-
message?.viewOnceMessage ||
|
|
1576
|
-
message?.viewOnceMessageV2 ||
|
|
1577
|
-
message?.viewOnceMessageV2Extension
|
|
1578
|
-
);
|
|
1331
|
+
message?.groupStatusMessageV2);
|
|
1579
1332
|
}
|
|
1580
1333
|
};
|
|
1581
1334
|
/**
|
|
@@ -1768,7 +1521,7 @@ export const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
|
1768
1521
|
throw new Boom('No message present', { statusCode: 400, data: message });
|
|
1769
1522
|
}
|
|
1770
1523
|
const contentType = getContentType(mContent);
|
|
1771
|
-
let mediaType = contentType
|
|
1524
|
+
let mediaType = getMediaTypeFromContentType(contentType, mContent);
|
|
1772
1525
|
const media = mContent[contentType];
|
|
1773
1526
|
if (!media || typeof media !== 'object' || (!('url' in media) && !('thumbnailDirectPath' in media))) {
|
|
1774
1527
|
throw new Boom(`"${contentType}" message is not a media message`);
|
|
@@ -1808,79 +1561,138 @@ export const assertMediaContent = (content) => {
|
|
|
1808
1561
|
}
|
|
1809
1562
|
return mediaContent;
|
|
1810
1563
|
};
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
if (
|
|
1818
|
-
buffer.length < 12 ||
|
|
1819
|
-
buffer[0] !== 0x52 ||
|
|
1820
|
-
buffer[1] !== 0x49 ||
|
|
1821
|
-
buffer[2] !== 0x46 ||
|
|
1822
|
-
buffer[3] !== 0x46 ||
|
|
1823
|
-
buffer[8] !== 0x57 ||
|
|
1824
|
-
buffer[9] !== 0x45 ||
|
|
1825
|
-
buffer[10] !== 0x42 ||
|
|
1826
|
-
buffer[11] !== 0x50
|
|
1564
|
+
|
|
1565
|
+
export const patchMessageForMdIfRequired = (message) => {
|
|
1566
|
+
if (message?.buttonsMessage ||
|
|
1567
|
+
message?.templateMessage ||
|
|
1568
|
+
message?.listMessage ||
|
|
1569
|
+
message?.interactiveMessage?.nativeFlowMessage
|
|
1827
1570
|
) {
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
if (flagsOffset < buffer.length) {
|
|
1839
|
-
const flags = buffer[flagsOffset];
|
|
1840
|
-
if (flags & 0x02) {
|
|
1841
|
-
return true;
|
|
1842
|
-
};
|
|
1843
|
-
};
|
|
1844
|
-
} else if (chunkFourCC === 'ANIM' || chunkFourCC === 'ANMF') {
|
|
1845
|
-
// ANIM or ANMF chunks indicate animation
|
|
1846
|
-
return true;
|
|
1571
|
+
message = {
|
|
1572
|
+
viewOnceMessageV2Extension: {
|
|
1573
|
+
message: {
|
|
1574
|
+
messageContextInfo: {
|
|
1575
|
+
deviceListMetadataVersion: 2,
|
|
1576
|
+
deviceListMetadata: {}
|
|
1577
|
+
},
|
|
1578
|
+
...message
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1847
1581
|
};
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
};
|
|
1851
|
-
return false;
|
|
1582
|
+
}
|
|
1583
|
+
return message;
|
|
1852
1584
|
};
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
const
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
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;
|
|
1868
1631
|
};
|
|
1869
1632
|
/**
|
|
1870
|
-
*
|
|
1871
|
-
*
|
|
1872
|
-
*
|
|
1873
|
-
*
|
|
1874
|
-
*
|
|
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
|
|
1875
1640
|
*/
|
|
1876
|
-
export const
|
|
1877
|
-
const
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
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
|