@ubedsockets/baileys 1.6.4 → 1.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/engine-requirements.js +10 -0
- package/lib/Defaults/index.d.ts +75 -0
- package/lib/Defaults/index.d.ts.map +1 -0
- package/lib/Defaults/index.js +76 -92
- package/lib/Defaults/index.js.map +1 -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 -5
- 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 +7 -41
- 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 +37 -51
- 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 +12 -57
- 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 +7 -44
- 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 +7 -15
- 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 +8 -11
- 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 +9 -12
- 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 -5
- 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 +9 -21
- 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 +27 -42
- 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 +4 -7
- 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 +347 -90
- package/lib/Signal/libsignal.js.map +1 -0
- package/lib/Signal/lid-mapping.d.ts +23 -0
- package/lib/Signal/lid-mapping.d.ts.map +1 -0
- package/lib/Signal/lid-mapping.js +277 -0
- 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 +3 -18
- 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 +4 -6
- 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 +21 -78
- package/lib/Socket/Client/websocket.js.map +1 -0
- package/lib/Socket/business.d.ts +217 -0
- package/lib/Socket/business.d.ts.map +1 -0
- package/lib/Socket/business.js +162 -43
- package/lib/Socket/business.js.map +1 -0
- package/lib/Socket/chats.d.ts +124 -0
- package/lib/Socket/chats.d.ts.map +1 -0
- package/lib/Socket/chats.js +571 -249
- package/lib/Socket/chats.js.map +1 -0
- package/lib/Socket/communities.d.ts +273 -0
- package/lib/Socket/communities.d.ts.map +1 -0
- package/lib/Socket/communities.js +431 -0
- package/lib/Socket/communities.js.map +1 -0
- package/lib/Socket/groups.d.ts +161 -0
- package/lib/Socket/groups.d.ts.map +1 -0
- package/lib/Socket/groups.js +114 -99
- package/lib/Socket/groups.js.map +1 -0
- package/lib/Socket/index.d.ts +260 -0
- package/lib/Socket/index.d.ts.map +1 -0
- package/lib/Socket/index.js +11 -9
- package/lib/Socket/index.js.map +1 -0
- package/lib/Socket/messages-recv.d.ts +203 -0
- package/lib/Socket/messages-recv.d.ts.map +1 -0
- package/lib/Socket/messages-recv.js +1277 -559
- package/lib/Socket/messages-recv.js.map +1 -0
- package/lib/Socket/messages-send.d.ts +199 -0
- package/lib/Socket/messages-send.d.ts.map +1 -0
- package/lib/Socket/messages-send.js +674 -669
- package/lib/Socket/messages-send.js.map +1 -0
- package/lib/Socket/mex.d.ts +3 -0
- package/lib/Socket/mex.d.ts.map +1 -0
- package/lib/Socket/mex.js +42 -0
- package/lib/Socket/mex.js.map +1 -0
- package/lib/Socket/newsletter.d.ts +170 -0
- package/lib/Socket/newsletter.d.ts.map +1 -0
- package/lib/Socket/newsletter.js +149 -286
- package/lib/Socket/newsletter.js.map +1 -0
- package/lib/Socket/socket.d.ts +59 -0
- package/lib/Socket/socket.d.ts.map +1 -0
- package/lib/Socket/socket.js +548 -235
- package/lib/Socket/socket.js.map +1 -0
- package/lib/Types/Auth.d.ts +117 -0
- package/lib/Types/Auth.d.ts.map +1 -0
- package/lib/Types/Auth.js +2 -2
- 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 -0
- 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 -2
- package/lib/Types/Call.js.map +1 -0
- package/lib/Types/Chat.d.ts +124 -0
- package/lib/Types/Chat.d.ts.map +1 -0
- package/lib/Types/Chat.js +8 -4
- package/lib/Types/Chat.js.map +1 -0
- package/lib/Types/Contact.d.ts +26 -0
- package/lib/Types/Contact.d.ts.map +1 -0
- package/lib/Types/Contact.js +2 -2
- package/lib/Types/Contact.js.map +1 -0
- package/lib/Types/Events.d.ts +256 -0
- package/lib/Types/Events.d.ts.map +1 -0
- package/lib/Types/Events.js +2 -2
- package/lib/Types/Events.js.map +1 -0
- package/lib/Types/GroupMetadata.d.ts +71 -0
- package/lib/Types/GroupMetadata.d.ts.map +1 -0
- package/lib/Types/GroupMetadata.js +2 -2
- 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 +3 -5
- 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 +3 -5
- package/lib/Types/LabelAssociation.js.map +1 -0
- package/lib/Types/Message.d.ts +320 -0
- package/lib/Types/Message.d.ts.map +1 -0
- package/lib/Types/Message.js +11 -7
- package/lib/Types/Message.js.map +1 -0
- package/lib/Types/Mex.d.ts +141 -0
- package/lib/Types/Mex.d.ts.map +1 -0
- package/lib/Types/Mex.js +37 -0
- package/lib/Types/Mex.js.map +1 -0
- package/lib/Types/Product.d.ts +79 -0
- package/lib/Types/Product.d.ts.map +1 -0
- package/lib/Types/Product.js +2 -2
- package/lib/Types/Product.js.map +1 -0
- package/lib/Types/Signal.d.ts +87 -0
- package/lib/Types/Signal.d.ts.map +1 -0
- package/lib/Types/Signal.js +2 -2
- package/lib/Types/Signal.js.map +1 -0
- package/lib/Types/Socket.d.ts +136 -0
- package/lib/Types/Socket.d.ts.map +1 -0
- package/lib/Types/Socket.js +3 -2
- package/lib/Types/Socket.js.map +1 -0
- package/lib/Types/State.d.ts +97 -0
- package/lib/Types/State.d.ts.map +1 -0
- package/lib/Types/State.js +56 -2
- 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 -2
- 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 +15 -31
- package/lib/Types/index.js.map +1 -0
- package/lib/Utils/auth-utils.d.ts +24 -0
- package/lib/Utils/auth-utils.d.ts.map +1 -0
- package/lib/Utils/auth-utils.js +239 -136
- package/lib/Utils/auth-utils.js.map +1 -0
- package/lib/Utils/browser-utils.d.ts +4 -0
- package/lib/Utils/browser-utils.d.ts.map +1 -0
- package/lib/Utils/browser-utils.js +28 -0
- package/lib/Utils/browser-utils.js.map +1 -0
- package/lib/Utils/business.d.ts +23 -0
- package/lib/Utils/business.d.ts.map +1 -0
- package/lib/Utils/business.js +66 -69
- package/lib/Utils/business.js.map +1 -0
- package/lib/Utils/chat-utils.d.ts +100 -0
- package/lib/Utils/chat-utils.d.ts.map +1 -0
- package/lib/Utils/chat-utils.js +380 -238
- package/lib/Utils/chat-utils.js.map +1 -0
- package/lib/Utils/companion-reg-client-utils.d.ts +17 -0
- package/lib/Utils/companion-reg-client-utils.d.ts.map +1 -0
- package/lib/Utils/companion-reg-client-utils.js +35 -0
- package/lib/Utils/companion-reg-client-utils.js.map +1 -0
- package/lib/Utils/crypto.d.ts +37 -0
- package/lib/Utils/crypto.d.ts.map +1 -0
- package/lib/Utils/crypto.js +56 -127
- package/lib/Utils/crypto.js.map +1 -0
- package/lib/Utils/decode-wa-message.d.ts +66 -0
- package/lib/Utils/decode-wa-message.d.ts.map +1 -0
- package/lib/Utils/decode-wa-message.js +173 -63
- package/lib/Utils/decode-wa-message.js.map +1 -0
- package/lib/Utils/event-buffer.d.ts +36 -0
- package/lib/Utils/event-buffer.d.ts.map +1 -0
- package/lib/Utils/event-buffer.js +174 -70
- 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 +159 -207
- package/lib/Utils/generics.js.map +1 -0
- package/lib/Utils/history.d.ts +24 -0
- package/lib/Utils/history.d.ts.map +1 -0
- package/lib/Utils/history.js +93 -53
- package/lib/Utils/history.js.map +1 -0
- package/lib/Utils/identity-change-handler.d.ts +44 -0
- package/lib/Utils/identity-change-handler.d.ts.map +1 -0
- package/lib/Utils/identity-change-handler.js +50 -0
- package/lib/Utils/identity-change-handler.js.map +1 -0
- package/lib/Utils/index.d.ts +22 -0
- package/lib/Utils/index.d.ts.map +1 -0
- package/lib/Utils/index.js +22 -33
- 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 +17 -58
- package/lib/Utils/link-preview.js.map +1 -0
- package/lib/Utils/logger.d.ts +12 -0
- package/lib/Utils/logger.d.ts.map +1 -0
- package/lib/Utils/logger.js +3 -7
- package/lib/Utils/logger.js.map +1 -0
- package/lib/Utils/lt-hash.d.ts +8 -0
- package/lib/Utils/lt-hash.d.ts.map +1 -0
- package/lib/Utils/lt-hash.js +3 -46
- 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 +24 -34
- package/lib/Utils/make-mutex.js.map +1 -0
- package/lib/Utils/message-retry-manager.d.ts +115 -0
- package/lib/Utils/message-retry-manager.d.ts.map +1 -0
- package/lib/Utils/message-retry-manager.js +265 -0
- package/lib/Utils/message-retry-manager.js.map +1 -0
- package/lib/Utils/messages-media.d.ts +133 -0
- package/lib/Utils/messages-media.d.ts.map +1 -0
- package/lib/Utils/messages-media.js +483 -744
- package/lib/Utils/messages-media.js.map +1 -0
- package/lib/Utils/messages.d.ts +91 -0
- package/lib/Utils/messages.d.ts.map +1 -0
- package/lib/Utils/messages.js +411 -621
- 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 +149 -99
- package/lib/Utils/noise-handler.js.map +1 -0
- package/lib/Utils/offline-node-processor.d.ts +17 -0
- package/lib/Utils/offline-node-processor.d.ts.map +1 -0
- package/lib/Utils/offline-node-processor.js +40 -0
- package/lib/Utils/offline-node-processor.js.map +1 -0
- package/lib/Utils/pre-key-manager.d.ts +28 -0
- package/lib/Utils/pre-key-manager.d.ts.map +1 -0
- package/lib/Utils/pre-key-manager.js +106 -0
- package/lib/Utils/pre-key-manager.js.map +1 -0
- package/lib/Utils/process-message.d.ts +60 -0
- package/lib/Utils/process-message.d.ts.map +1 -0
- package/lib/Utils/process-message.js +459 -150
- package/lib/Utils/process-message.js.map +1 -0
- package/lib/Utils/reporting-utils.d.ts +11 -0
- package/lib/Utils/reporting-utils.d.ts.map +1 -0
- package/lib/Utils/reporting-utils.js +258 -0
- package/lib/Utils/reporting-utils.js.map +1 -0
- package/lib/Utils/signal.d.ts +47 -0
- package/lib/Utils/signal.d.ts.map +1 -0
- package/lib/Utils/signal.js +112 -73
- package/lib/Utils/signal.js.map +1 -0
- package/lib/Utils/stanza-ack.d.ts +11 -0
- package/lib/Utils/stanza-ack.d.ts.map +1 -0
- package/lib/Utils/stanza-ack.js +38 -0
- package/lib/Utils/stanza-ack.js.map +1 -0
- package/lib/Utils/sync-action-utils.d.ts +19 -0
- package/lib/Utils/sync-action-utils.d.ts.map +1 -0
- package/lib/Utils/sync-action-utils.js +49 -0
- package/lib/Utils/sync-action-utils.js.map +1 -0
- package/lib/Utils/tc-token-utils.d.ts +37 -0
- package/lib/Utils/tc-token-utils.d.ts.map +1 -0
- package/lib/Utils/tc-token-utils.js +163 -0
- package/lib/Utils/tc-token-utils.js.map +1 -0
- package/lib/Utils/use-multi-file-auth-state.d.ts +13 -0
- package/lib/Utils/use-multi-file-auth-state.d.ts.map +1 -0
- package/lib/Utils/use-multi-file-auth-state.js +23 -27
- package/lib/Utils/use-multi-file-auth-state.js.map +1 -0
- package/lib/Utils/validate-connection.d.ts +11 -0
- package/lib/Utils/validate-connection.d.ts.map +1 -0
- package/lib/Utils/validate-connection.js +74 -104
- 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 +9 -11
- 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 +49 -52
- 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 +19 -49
- 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 +46 -43
- 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 +6 -21
- 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 +72 -45
- 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 -2
- 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 -5
- 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 +19071 -11568
- 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 +17 -22
- 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 +4 -19
- 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 +33 -13
- 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 +11 -14
- 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 +8 -11
- 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 +9 -13
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js.map +1 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +10 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts.map +1 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js.map +1 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +26 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts.map +1 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +20 -22
- 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 +13 -8
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js.map +1 -0
- package/lib/WAUSync/Protocols/index.d.ts +6 -0
- package/lib/WAUSync/Protocols/index.d.ts.map +1 -0
- package/lib/WAUSync/Protocols/index.js +6 -20
- package/lib/WAUSync/Protocols/index.js.map +1 -0
- package/lib/WAUSync/USyncQuery.d.ts +30 -0
- package/lib/WAUSync/USyncQuery.d.ts.map +1 -0
- package/lib/WAUSync/USyncQuery.js +44 -35
- package/lib/WAUSync/USyncQuery.js.map +1 -0
- package/lib/WAUSync/USyncUser.d.ts +17 -0
- package/lib/WAUSync/USyncUser.d.ts.map +1 -0
- package/lib/WAUSync/USyncUser.js +10 -5
- 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 +4 -19
- 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 +11 -70
- package/lib/index.js.map +1 -0
- package/package.json +100 -60
|
@@ -1,549 +1,244 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.getStatusCodeForMediaRetry =
|
|
40
|
-
exports.decryptMediaRetryData =
|
|
41
|
-
exports.decodeMediaRetryNode =
|
|
42
|
-
exports.encryptMediaRetryRequest =
|
|
43
|
-
exports.getWAUploadToServer =
|
|
44
|
-
exports.downloadEncryptedContent =
|
|
45
|
-
exports.downloadContentFromMessage =
|
|
46
|
-
exports.getUrlFromDirectPath =
|
|
47
|
-
exports.encryptedStream =
|
|
48
|
-
exports.prepareStream =
|
|
49
|
-
exports.getHttpStream =
|
|
50
|
-
exports.getStream =
|
|
51
|
-
exports.toBuffer =
|
|
52
|
-
exports.toReadable =
|
|
53
|
-
exports.mediaMessageSHA256B64 =
|
|
54
|
-
exports.generateProfilePicture =
|
|
55
|
-
exports.encodeBase64EncodedStringForUpload =
|
|
56
|
-
exports.extractImageThumb =
|
|
57
|
-
exports.extractVideoThumb =
|
|
58
|
-
exports.hkdfInfoKey = void 0;
|
|
59
|
-
exports.getMediaKeys = getMediaKeys;
|
|
60
|
-
exports.uploadFile = uploadFile;
|
|
61
|
-
exports.vid2jpg = vid2jpg;
|
|
62
|
-
exports.getAudioDuration = getAudioDuration;
|
|
63
|
-
exports.getAudioWaveform = getAudioWaveform;
|
|
64
|
-
exports.generateThumbnail = generateThumbnail;
|
|
65
|
-
exports.extensionForMediaMessage = extensionForMediaMessage;
|
|
66
|
-
// 🆅🆈🆉🅴🅽 🅱🅰🅸🅻
|
|
67
|
-
const boom_1 = require("@hapi/boom");
|
|
68
|
-
const axios_1 = __importDefault(require("axios"));
|
|
69
|
-
const form_data_1 = __importDefault(require("form-data"));
|
|
70
|
-
const cheerio = __importStar(require("cheerio"));
|
|
71
|
-
const Crypto = __importStar(require("crypto"));
|
|
72
|
-
const events_1 = require("events");
|
|
73
|
-
const fs_1 = require("fs");
|
|
74
|
-
const os_1 = require("os");
|
|
75
|
-
const path_1 = require("path");
|
|
76
|
-
const jimp_1 = __importDefault(require("jimp"));
|
|
77
|
-
const stream_1 = require("stream");
|
|
78
|
-
const child_process_1 = require("child_process");
|
|
79
|
-
const WAProto_1 = require("../../WAProto");
|
|
80
|
-
const Defaults_1 = require("../Defaults");
|
|
81
|
-
const WABinary_1 = require("../WABinary");
|
|
82
|
-
const crypto_1 = require("./crypto");
|
|
83
|
-
const generics_1 = require("./generics");
|
|
84
|
-
const getTmpFilesDirectory = () => (0, os_1.tmpdir)();
|
|
1
|
+
import { Boom } from '@hapi/boom';
|
|
2
|
+
import { exec } from 'child_process';
|
|
3
|
+
import * as Crypto from 'crypto';
|
|
4
|
+
import { once } from 'events';
|
|
5
|
+
import { createReadStream, createWriteStream, promises as fs, WriteStream } from 'fs';
|
|
6
|
+
import { tmpdir } from 'os';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { Readable, Transform } from 'stream';
|
|
9
|
+
import { URL } from 'url';
|
|
10
|
+
import { proto } from '../../WAProto/index.js';
|
|
11
|
+
import { DEFAULT_ORIGIN, MEDIA_HKDF_KEY_MAPPING, MEDIA_PATH_MAP } from '../Defaults/index.js';
|
|
12
|
+
import { getBinaryNodeChild, getBinaryNodeChildBuffer, jidNormalizedUser } from '../WABinary/index.js';
|
|
13
|
+
import { aesDecryptGCM, aesEncryptGCM, hkdf } from './crypto.js';
|
|
14
|
+
import { generateMessageIDV2 } from './generics.js';
|
|
15
|
+
const getTmpFilesDirectory = () => tmpdir();
|
|
85
16
|
const getImageProcessingLibrary = async () => {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const jimp = await (Promise.resolve().then(() => __importStar(require('jimp'))).catch(() => { }));
|
|
89
|
-
return jimp;
|
|
90
|
-
})(),
|
|
91
|
-
(async () => {
|
|
92
|
-
const sharp = await (Promise.resolve().then(() => __importStar(require('sharp'))).catch(() => { }));
|
|
93
|
-
return sharp;
|
|
94
|
-
})()
|
|
95
|
-
]);
|
|
17
|
+
//@ts-ignore
|
|
18
|
+
const [jimp, sharp] = await Promise.all([import('jimp').catch(() => { }), import('sharp').catch(() => { })]);
|
|
96
19
|
if (sharp) {
|
|
97
20
|
return { sharp };
|
|
98
21
|
}
|
|
99
|
-
const jimp = (_jimp === null || _jimp === void 0 ? void 0 : _jimp.default) || _jimp;
|
|
100
22
|
if (jimp) {
|
|
101
23
|
return { jimp };
|
|
102
24
|
}
|
|
103
|
-
throw new
|
|
25
|
+
throw new Boom('No image processing library available');
|
|
104
26
|
};
|
|
105
|
-
const hkdfInfoKey = (type) => {
|
|
106
|
-
const hkdfInfo =
|
|
27
|
+
export const hkdfInfoKey = (type) => {
|
|
28
|
+
const hkdfInfo = MEDIA_HKDF_KEY_MAPPING[type];
|
|
107
29
|
return `WhatsApp ${hkdfInfo} Keys`;
|
|
108
30
|
};
|
|
109
|
-
|
|
31
|
+
export const getRawMediaUploadData = async (media, mediaType, logger) => {
|
|
32
|
+
const { stream } = await getStream(media);
|
|
33
|
+
logger?.debug('got stream for raw upload');
|
|
34
|
+
const hasher = Crypto.createHash('sha256');
|
|
35
|
+
const filePath = join(tmpdir(), mediaType + generateMessageIDV2());
|
|
36
|
+
const fileWriteStream = createWriteStream(filePath);
|
|
37
|
+
let fileLength = 0;
|
|
38
|
+
try {
|
|
39
|
+
for await (const data of stream) {
|
|
40
|
+
fileLength += data.length;
|
|
41
|
+
hasher.update(data);
|
|
42
|
+
if (!fileWriteStream.write(data)) {
|
|
43
|
+
await once(fileWriteStream, 'drain');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
fileWriteStream.end();
|
|
47
|
+
await once(fileWriteStream, 'finish');
|
|
48
|
+
stream.destroy();
|
|
49
|
+
const fileSha256 = hasher.digest();
|
|
50
|
+
logger?.debug('hashed data for raw upload');
|
|
51
|
+
return {
|
|
52
|
+
filePath: filePath,
|
|
53
|
+
fileSha256,
|
|
54
|
+
fileLength
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
fileWriteStream.destroy();
|
|
59
|
+
stream.destroy();
|
|
60
|
+
try {
|
|
61
|
+
await fs.unlink(filePath);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
//
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
110
69
|
/** generates all the keys required to encrypt/decrypt & sign a media message */
|
|
111
|
-
async function getMediaKeys(buffer, mediaType) {
|
|
70
|
+
export async function getMediaKeys(buffer, mediaType) {
|
|
112
71
|
if (!buffer) {
|
|
113
|
-
throw new
|
|
72
|
+
throw new Boom('Cannot derive from empty media key');
|
|
114
73
|
}
|
|
115
74
|
if (typeof buffer === 'string') {
|
|
116
75
|
buffer = Buffer.from(buffer.replace('data:;base64,', ''), 'base64');
|
|
117
76
|
}
|
|
118
77
|
// expand using HKDF to 112 bytes, also pass in the relevant app info
|
|
119
|
-
const expandedMediaKey =
|
|
78
|
+
const expandedMediaKey = hkdf(buffer, 112, { info: hkdfInfoKey(mediaType) });
|
|
120
79
|
return {
|
|
121
80
|
iv: expandedMediaKey.slice(0, 16),
|
|
122
81
|
cipherKey: expandedMediaKey.slice(16, 48),
|
|
123
|
-
macKey: expandedMediaKey.slice(48, 80)
|
|
82
|
+
macKey: expandedMediaKey.slice(48, 80)
|
|
124
83
|
};
|
|
125
84
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const services = [
|
|
133
|
-
{
|
|
134
|
-
name: "catbox",
|
|
135
|
-
url: "https://catbox.moe/user/api.php",
|
|
136
|
-
buildForm: () => {
|
|
137
|
-
const form = new form_data_1.default();
|
|
138
|
-
form.append("fileToUpload", buffer, {
|
|
139
|
-
filename: `file.${ext}`,
|
|
140
|
-
contentType: mime || "application/octet-stream"
|
|
141
|
-
});
|
|
142
|
-
form.append("reqtype", "fileupload");
|
|
143
|
-
return form;
|
|
144
|
-
},
|
|
145
|
-
parseResponse: res => res.data
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
name: "pdi.moe",
|
|
149
|
-
url: "https://scdn.pdi.moe/upload",
|
|
150
|
-
buildForm: () => {
|
|
151
|
-
const form = new form_data_1.default();
|
|
152
|
-
form.append("file", buffer, {
|
|
153
|
-
filename: `file.${ext}`,
|
|
154
|
-
contentType: mime
|
|
155
|
-
});
|
|
156
|
-
return form;
|
|
157
|
-
},
|
|
158
|
-
parseResponse: res => res.data.result.url
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
name: "qu.ax",
|
|
162
|
-
url: "https://qu.ax/upload.php",
|
|
163
|
-
buildForm: () => {
|
|
164
|
-
const form = new form_data_1.default();
|
|
165
|
-
form.append("files[]", buffer, {
|
|
166
|
-
filename: `file.${ext}`,
|
|
167
|
-
contentType: mime || "application/octet-stream"
|
|
168
|
-
});
|
|
169
|
-
return form;
|
|
170
|
-
},
|
|
171
|
-
parseResponse: res => {
|
|
172
|
-
var _a, _b, _c;
|
|
173
|
-
if (!((_c = (_b = (_a = res.data) === null || _a === void 0 ? void 0 : _a.files) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.url))
|
|
174
|
-
throw new Error("Failed to get URL from qu.ax");
|
|
175
|
-
return res.data.files[0].url;
|
|
176
|
-
}
|
|
177
|
-
},
|
|
178
|
-
{
|
|
179
|
-
name: "uguu.se",
|
|
180
|
-
url: "https://uguu.se/upload.php",
|
|
181
|
-
buildForm: () => {
|
|
182
|
-
const form = new form_data_1.default();
|
|
183
|
-
form.append("files[]", buffer, {
|
|
184
|
-
filename: `file.${ext}`,
|
|
185
|
-
contentType: mime || "application/octet-stream"
|
|
186
|
-
});
|
|
187
|
-
return form;
|
|
188
|
-
},
|
|
189
|
-
parseResponse: res => {
|
|
190
|
-
var _a, _b, _c;
|
|
191
|
-
if (!((_c = (_b = (_a = res.data) === null || _a === void 0 ? void 0 : _a.files) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.url))
|
|
192
|
-
throw new Error("Failed to get URL from uguu.se");
|
|
193
|
-
return res.data.files[0].url;
|
|
194
|
-
}
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
name: "tmpfiles",
|
|
198
|
-
url: "https://tmpfiles.org/api/v1/upload",
|
|
199
|
-
buildForm: () => {
|
|
200
|
-
const form = new form_data_1.default();
|
|
201
|
-
form.append("file", buffer, {
|
|
202
|
-
filename: `file.${ext}`,
|
|
203
|
-
contentType: mime
|
|
204
|
-
});
|
|
205
|
-
return form;
|
|
206
|
-
},
|
|
207
|
-
parseResponse: res => {
|
|
208
|
-
const match = res.data.data.url.match(/https:\/\/tmpfiles\.org\/(.*)/);
|
|
209
|
-
if (!match)
|
|
210
|
-
throw new Error("Failed to parse tmpfiles URL.");
|
|
211
|
-
return `https://tmpfiles.org/dl/${match[1]}`;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
];
|
|
215
|
-
for (const service of services) {
|
|
216
|
-
try {
|
|
217
|
-
const form = service.buildForm();
|
|
218
|
-
const res = await axios_1.default.post(service.url, form, {
|
|
219
|
-
headers: form.getHeaders()
|
|
220
|
-
});
|
|
221
|
-
const url = service.parseResponse(res);
|
|
222
|
-
return url;
|
|
223
|
-
}
|
|
224
|
-
catch (error) {
|
|
225
|
-
logger === null || logger === void 0 ? void 0 : logger.debug(`[${service.name}] eror:`, (error === null || error === void 0 ? void 0 : error.message) || error);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
throw new Error("All upload services failed.");
|
|
229
|
-
}
|
|
230
|
-
async function vid2jpg(videoUrl) {
|
|
231
|
-
try {
|
|
232
|
-
const { data } = await axios_1.default.get(`https://ezgif.com/video-to-jpg?url=${encodeURIComponent(videoUrl)}`);
|
|
233
|
-
const $ = cheerio.load(data);
|
|
234
|
-
const fileToken = $('input[name="file"]').attr("value");
|
|
235
|
-
if (!fileToken) {
|
|
236
|
-
throw new Error("Failed to retrieve file token. The video URL may be invalid or inaccessible.");
|
|
85
|
+
/** Extracts video thumb using FFMPEG */
|
|
86
|
+
const extractVideoThumb = async (path, destPath, time, size) => new Promise((resolve, reject) => {
|
|
87
|
+
const cmd = `ffmpeg -ss ${time} -i ${path} -y -vf scale=${size.width}:-1 -vframes 1 -f image2 ${destPath}`;
|
|
88
|
+
exec(cmd, err => {
|
|
89
|
+
if (err) {
|
|
90
|
+
reject(err);
|
|
237
91
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
formData.append("end", "1");
|
|
241
|
-
formData.append("video-to-jpg", "Convert to JPG!");
|
|
242
|
-
const convert = await axios_1.default.post(`https://ezgif.com/video-to-jpg/${fileToken}`, formData);
|
|
243
|
-
const $2 = cheerio.load(convert.data);
|
|
244
|
-
let imageUrl = $2("#output img").first().attr("src");
|
|
245
|
-
if (!imageUrl) {
|
|
246
|
-
throw new Error("Could not locate the converted image output.");
|
|
247
|
-
}
|
|
248
|
-
if (imageUrl.startsWith("//")) {
|
|
249
|
-
imageUrl = "https:" + imageUrl;
|
|
250
|
-
}
|
|
251
|
-
else if (imageUrl.startsWith("/")) {
|
|
252
|
-
const cdnMatch = imageUrl.match(/\/(s\d+\..+?)\/.*/);
|
|
253
|
-
if (cdnMatch) {
|
|
254
|
-
imageUrl = "https://" + imageUrl.slice(2);
|
|
255
|
-
}
|
|
256
|
-
else {
|
|
257
|
-
imageUrl = "https://ezgif.com" + imageUrl;
|
|
258
|
-
}
|
|
92
|
+
else {
|
|
93
|
+
resolve();
|
|
259
94
|
}
|
|
260
|
-
return imageUrl;
|
|
261
|
-
}
|
|
262
|
-
catch (error) {
|
|
263
|
-
throw new Error("Failed to convert video to JPG: " + error.message);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* Extracts video thumbnail using FFmpeg
|
|
268
|
-
*/
|
|
269
|
-
const extractVideoThumb = async (videoPath, time = '00:00:00', size = { width: 256 }) => {
|
|
270
|
-
return new Promise((resolve, reject) => {
|
|
271
|
-
const args = [
|
|
272
|
-
'-ss', time,
|
|
273
|
-
'-i', videoPath,
|
|
274
|
-
'-y',
|
|
275
|
-
'-vf', `scale=${size.width}:-1`,
|
|
276
|
-
'-vframes', '1',
|
|
277
|
-
'-f', 'image2',
|
|
278
|
-
'-vcodec', 'mjpeg',
|
|
279
|
-
'pipe:1'
|
|
280
|
-
];
|
|
281
|
-
const ffmpeg = (0, child_process_1.spawn)('ffmpeg', args);
|
|
282
|
-
const chunks = [];
|
|
283
|
-
let errorOutput = '';
|
|
284
|
-
ffmpeg.stdout.on('data', chunk => chunks.push(chunk));
|
|
285
|
-
ffmpeg.stderr.on('data', data => {
|
|
286
|
-
errorOutput += data.toString();
|
|
287
|
-
});
|
|
288
|
-
ffmpeg.on('error', reject);
|
|
289
|
-
ffmpeg.on('close', code => {
|
|
290
|
-
if (code === 0) return resolve(Buffer.concat(chunks));
|
|
291
|
-
reject(new Error(`ffmpeg exited with code ${code}\n${errorOutput}`));
|
|
292
|
-
});
|
|
293
95
|
});
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
if (bufferOrFilePath instanceof
|
|
299
|
-
bufferOrFilePath = await
|
|
96
|
+
});
|
|
97
|
+
export const extractImageThumb = async (bufferOrFilePath, width = 32) => {
|
|
98
|
+
// TODO: Move entirely to sharp, removing jimp as it supports readable streams
|
|
99
|
+
// This will have positive speed and performance impacts as well as minimizing RAM usage.
|
|
100
|
+
if (bufferOrFilePath instanceof Readable) {
|
|
101
|
+
bufferOrFilePath = await toBuffer(bufferOrFilePath);
|
|
300
102
|
}
|
|
301
103
|
const lib = await getImageProcessingLibrary();
|
|
302
|
-
if ('sharp' in lib && typeof
|
|
104
|
+
if ('sharp' in lib && typeof lib.sharp?.default === 'function') {
|
|
303
105
|
const img = lib.sharp.default(bufferOrFilePath);
|
|
304
106
|
const dimensions = await img.metadata();
|
|
305
|
-
const buffer = await img
|
|
306
|
-
.resize(width)
|
|
307
|
-
.jpeg({ quality: 50 })
|
|
308
|
-
.toBuffer();
|
|
107
|
+
const buffer = await img.resize(width).jpeg({ quality: 50 }).toBuffer();
|
|
309
108
|
return {
|
|
310
109
|
buffer,
|
|
311
110
|
original: {
|
|
312
111
|
width: dimensions.width,
|
|
313
|
-
height: dimensions.height
|
|
314
|
-
}
|
|
112
|
+
height: dimensions.height
|
|
113
|
+
}
|
|
315
114
|
};
|
|
316
115
|
}
|
|
317
|
-
else if ('jimp' in lib && typeof
|
|
318
|
-
const
|
|
319
|
-
const jimp = await read(bufferOrFilePath);
|
|
116
|
+
else if ('jimp' in lib && typeof lib.jimp?.Jimp === 'object') {
|
|
117
|
+
const jimp = await lib.jimp.Jimp.read(bufferOrFilePath);
|
|
320
118
|
const dimensions = {
|
|
321
|
-
width: jimp.
|
|
322
|
-
height: jimp.
|
|
119
|
+
width: jimp.width,
|
|
120
|
+
height: jimp.height
|
|
323
121
|
};
|
|
324
122
|
const buffer = await jimp
|
|
325
|
-
.
|
|
326
|
-
.
|
|
327
|
-
.getBufferAsync(MIME_JPEG);
|
|
123
|
+
.resize({ w: width, mode: lib.jimp.ResizeStrategy.BILINEAR })
|
|
124
|
+
.getBuffer('image/jpeg', { quality: 50 });
|
|
328
125
|
return {
|
|
329
126
|
buffer,
|
|
330
127
|
original: dimensions
|
|
331
128
|
};
|
|
332
129
|
}
|
|
333
130
|
else {
|
|
334
|
-
throw new
|
|
131
|
+
throw new Boom('No image processing library available');
|
|
335
132
|
}
|
|
336
133
|
};
|
|
337
|
-
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
.replace(/\=+$/, '')));
|
|
342
|
-
exports.encodeBase64EncodedStringForUpload = encodeBase64EncodedStringForUpload;
|
|
343
|
-
const generateProfilePicture = async (mediaUpload) => {
|
|
344
|
-
let bufferOrFilePath;
|
|
345
|
-
let img;
|
|
134
|
+
export const encodeBase64EncodedStringForUpload = (b64) => encodeURIComponent(b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, ''));
|
|
135
|
+
export const generateProfilePicture = async (mediaUpload, dimensions) => {
|
|
136
|
+
let buffer;
|
|
137
|
+
const { width: w = 640, height: h = 640 } = dimensions || {};
|
|
346
138
|
if (Buffer.isBuffer(mediaUpload)) {
|
|
347
|
-
|
|
139
|
+
buffer = mediaUpload;
|
|
348
140
|
}
|
|
349
|
-
else
|
|
350
|
-
|
|
141
|
+
else {
|
|
142
|
+
// Use getStream to handle all WAMediaUpload types (Buffer, Stream, URL)
|
|
143
|
+
const { stream } = await getStream(mediaUpload);
|
|
144
|
+
// Convert the resulting stream to a buffer
|
|
145
|
+
buffer = await toBuffer(stream);
|
|
146
|
+
}
|
|
147
|
+
const lib = await getImageProcessingLibrary();
|
|
148
|
+
let img;
|
|
149
|
+
if ('sharp' in lib && typeof lib.sharp?.default === 'function') {
|
|
150
|
+
img = lib.sharp
|
|
151
|
+
.default(buffer)
|
|
152
|
+
.resize(w, h)
|
|
153
|
+
.jpeg({
|
|
154
|
+
quality: 50
|
|
155
|
+
})
|
|
156
|
+
.toBuffer();
|
|
157
|
+
}
|
|
158
|
+
else if ('jimp' in lib && typeof lib.jimp?.Jimp === 'function') {
|
|
159
|
+
const jimp = await lib.jimp.Jimp.read(buffer);
|
|
160
|
+
const min = Math.min(jimp.width, jimp.height);
|
|
161
|
+
const cropped = jimp.crop({ x: 0, y: 0, w: min, h: min });
|
|
162
|
+
img = cropped.resize({ w, h, mode: lib.jimp.ResizeStrategy.BILINEAR }).getBuffer('image/jpeg', { quality: 50 });
|
|
351
163
|
}
|
|
352
164
|
else {
|
|
353
|
-
|
|
165
|
+
throw new Boom('No image processing library available');
|
|
354
166
|
}
|
|
355
|
-
const jimp = await jimp_1.default.read(bufferOrFilePath);
|
|
356
|
-
const cropped = jimp.getWidth() > jimp.getHeight() ? jimp.resize(550, -1) : jimp.resize(-1, 650);
|
|
357
|
-
img = cropped
|
|
358
|
-
.quality(100)
|
|
359
|
-
.getBufferAsync(jimp_1.default.MIME_JPEG);
|
|
360
167
|
return {
|
|
361
|
-
img: await img
|
|
168
|
+
img: await img
|
|
362
169
|
};
|
|
363
170
|
};
|
|
364
|
-
exports.generateProfilePicture = generateProfilePicture;
|
|
365
171
|
/** gets the SHA256 of the given media message */
|
|
366
|
-
const mediaMessageSHA256B64 = (message) => {
|
|
172
|
+
export const mediaMessageSHA256B64 = (message) => {
|
|
367
173
|
const media = Object.values(message)[0];
|
|
368
|
-
return
|
|
174
|
+
return media?.fileSha256 && Buffer.from(media.fileSha256).toString('base64');
|
|
369
175
|
};
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
.ffprobe((err, data) => {
|
|
382
|
-
if (err) reject(err);
|
|
383
|
-
else resolve(data.format.duration);
|
|
384
|
-
});
|
|
385
|
-
});
|
|
386
|
-
} catch (error) {
|
|
387
|
-
const musicMetadata = await import('music-metadata');
|
|
388
|
-
let metadata;
|
|
389
|
-
if (Buffer.isBuffer(buffer)) {
|
|
390
|
-
metadata = await musicMetadata.parseBuffer(buffer, undefined, {
|
|
391
|
-
duration: true
|
|
392
|
-
});
|
|
393
|
-
} else if (typeof buffer === 'string') {
|
|
394
|
-
const rStream = (0, fs_1.createReadStream)(buffer);
|
|
395
|
-
try {
|
|
396
|
-
metadata = await musicMetadata.parseStream(rStream, undefined, {
|
|
397
|
-
duration: true
|
|
398
|
-
});
|
|
399
|
-
} finally {
|
|
400
|
-
rStream.destroy();
|
|
401
|
-
}
|
|
402
|
-
} else {
|
|
403
|
-
metadata = await musicMetadata.parseStream(buffer, undefined, {
|
|
404
|
-
duration: true
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
return metadata.format.duration;
|
|
176
|
+
export async function getAudioDuration(buffer) {
|
|
177
|
+
const musicMetadata = await import('music-metadata');
|
|
178
|
+
let metadata;
|
|
179
|
+
const options = {
|
|
180
|
+
duration: true
|
|
181
|
+
};
|
|
182
|
+
if (Buffer.isBuffer(buffer)) {
|
|
183
|
+
metadata = await musicMetadata.parseBuffer(buffer, undefined, options);
|
|
184
|
+
}
|
|
185
|
+
else if (typeof buffer === 'string') {
|
|
186
|
+
metadata = await musicMetadata.parseFile(buffer, options);
|
|
408
187
|
}
|
|
188
|
+
else {
|
|
189
|
+
metadata = await musicMetadata.parseStream(buffer, undefined, options);
|
|
190
|
+
}
|
|
191
|
+
return metadata.format.duration;
|
|
409
192
|
}
|
|
410
|
-
|
|
411
|
-
|
|
193
|
+
/**
|
|
194
|
+
referenced from and modifying https://github.com/wppconnect-team/wa-js/blob/main/src/chat/functions/prepareAudioWaveform.ts
|
|
195
|
+
*/
|
|
196
|
+
export async function getAudioWaveform(buffer, logger) {
|
|
412
197
|
try {
|
|
413
|
-
|
|
414
|
-
const
|
|
415
|
-
|
|
198
|
+
// @ts-ignore
|
|
199
|
+
const { default: decoder } = await import('audio-decode');
|
|
416
200
|
let audioData;
|
|
417
201
|
if (Buffer.isBuffer(buffer)) {
|
|
418
202
|
audioData = buffer;
|
|
419
|
-
} else if (typeof buffer === 'string') {
|
|
420
|
-
const rStream = require('fs').createReadStream(buffer);
|
|
421
|
-
audioData = await exports.toBuffer(rStream);
|
|
422
|
-
} else {
|
|
423
|
-
audioData = await exports.toBuffer(buffer);
|
|
424
203
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
const max = Math.max(...avg);
|
|
454
|
-
const normalized = avg.map(v => Math.floor((v / max) * 100));
|
|
455
|
-
resolve(new Uint8Array(normalized));
|
|
456
|
-
})
|
|
457
|
-
.pipe()
|
|
458
|
-
.on('data', chunk => chunks.push(chunk));
|
|
459
|
-
});
|
|
460
|
-
} catch (e) {
|
|
461
|
-
logger?.debug(e);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
exports.getAudioWaveform = getAudioWaveform;
|
|
465
|
-
async function convertToOpusBuffer(buffer, logger) {
|
|
466
|
-
try {
|
|
467
|
-
const { PassThrough } = require('stream');
|
|
468
|
-
const ff = require('fluent-ffmpeg');
|
|
469
|
-
|
|
470
|
-
return await new Promise((resolve, reject) => {
|
|
471
|
-
const inStream = new PassThrough();
|
|
472
|
-
const outStream = new PassThrough();
|
|
473
|
-
const chunks = [];
|
|
474
|
-
inStream.end(buffer);
|
|
475
|
-
|
|
476
|
-
ff(inStream)
|
|
477
|
-
.noVideo()
|
|
478
|
-
.audioCodec('libopus')
|
|
479
|
-
.format('ogg')
|
|
480
|
-
.audioBitrate('48k')
|
|
481
|
-
.audioChannels(1)
|
|
482
|
-
.audioFrequency(48000)
|
|
483
|
-
.outputOptions([
|
|
484
|
-
'-vn',
|
|
485
|
-
'-b:a 64k',
|
|
486
|
-
'-ac 2',
|
|
487
|
-
'-ar 48000',
|
|
488
|
-
'-map_metadata', '-1',
|
|
489
|
-
'-application', 'voip'
|
|
490
|
-
])
|
|
491
|
-
.on('error', reject)
|
|
492
|
-
.on('end', () => resolve(Buffer.concat(chunks)))
|
|
493
|
-
.pipe(outStream, {
|
|
494
|
-
end: true
|
|
495
|
-
});
|
|
496
|
-
outStream.on('data', c => chunks.push(c));
|
|
497
|
-
});
|
|
498
|
-
} catch (e) {
|
|
499
|
-
logger?.debug(e);
|
|
500
|
-
throw e;
|
|
204
|
+
else if (typeof buffer === 'string') {
|
|
205
|
+
const rStream = createReadStream(buffer);
|
|
206
|
+
audioData = await toBuffer(rStream);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
audioData = await toBuffer(buffer);
|
|
210
|
+
}
|
|
211
|
+
const audioBuffer = await decoder(audioData);
|
|
212
|
+
const rawData = audioBuffer.getChannelData(0); // We only need to work with one channel of data
|
|
213
|
+
const samples = 64; // Number of samples we want to have in our final data set
|
|
214
|
+
const blockSize = Math.floor(rawData.length / samples); // the number of samples in each subdivision
|
|
215
|
+
const filteredData = [];
|
|
216
|
+
for (let i = 0; i < samples; i++) {
|
|
217
|
+
const blockStart = blockSize * i; // the location of the first sample in the block
|
|
218
|
+
let sum = 0;
|
|
219
|
+
for (let j = 0; j < blockSize; j++) {
|
|
220
|
+
sum = sum + Math.abs(rawData[blockStart + j]); // find the sum of all the samples in the block
|
|
221
|
+
}
|
|
222
|
+
filteredData.push(sum / blockSize); // divide the sum by the block size to get the average
|
|
223
|
+
}
|
|
224
|
+
// This guarantees that the largest data point will be set to 1, and the rest of the data will scale proportionally.
|
|
225
|
+
const multiplier = Math.pow(Math.max(...filteredData), -1);
|
|
226
|
+
const normalizedData = filteredData.map(n => n * multiplier);
|
|
227
|
+
// Generate waveform like WhatsApp
|
|
228
|
+
const waveform = new Uint8Array(normalizedData.map(n => Math.floor(100 * n)));
|
|
229
|
+
return waveform;
|
|
501
230
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
async function convertToMp4Buffer(buffer, logger) {
|
|
505
|
-
try {
|
|
506
|
-
const { PassThrough } = require('stream');
|
|
507
|
-
const ff = require('fluent-ffmpeg');
|
|
508
|
-
|
|
509
|
-
return await new Promise((resolve, reject) => {
|
|
510
|
-
const inStream = new PassThrough();
|
|
511
|
-
const outStream = new PassThrough();
|
|
512
|
-
const chunks = [];
|
|
513
|
-
|
|
514
|
-
inStream.end(buffer);
|
|
515
|
-
|
|
516
|
-
ff(inStream)
|
|
517
|
-
.videoCodec('libx264')
|
|
518
|
-
.audioCodec('aac')
|
|
519
|
-
.format('mp4')
|
|
520
|
-
.outputOptions([
|
|
521
|
-
'-preset', 'veryfast',
|
|
522
|
-
'-crf', '23',
|
|
523
|
-
'-movflags', 'faststart',
|
|
524
|
-
'-map_metadata', '-1'
|
|
525
|
-
])
|
|
526
|
-
.on('error', reject)
|
|
527
|
-
.on('end', () => resolve(Buffer.concat(chunks)))
|
|
528
|
-
.pipe(outStream, { end: true });
|
|
529
|
-
|
|
530
|
-
outStream.on('data', c => chunks.push(c));
|
|
531
|
-
});
|
|
532
|
-
} catch (e) {
|
|
533
|
-
logger?.debug(e);
|
|
534
|
-
throw e;
|
|
231
|
+
catch (e) {
|
|
232
|
+
logger?.debug('Failed to generate waveform: ' + e);
|
|
535
233
|
}
|
|
536
234
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
const toReadable = (buffer) => {
|
|
540
|
-
const readable = new stream_1.Readable({ read: () => { } });
|
|
235
|
+
export const toReadable = (buffer) => {
|
|
236
|
+
const readable = new Readable({ read: () => { } });
|
|
541
237
|
readable.push(buffer);
|
|
542
238
|
readable.push(null);
|
|
543
239
|
return readable;
|
|
544
240
|
};
|
|
545
|
-
|
|
546
|
-
const toBuffer = async (stream) => {
|
|
241
|
+
export const toBuffer = async (stream) => {
|
|
547
242
|
const chunks = [];
|
|
548
243
|
for await (const chunk of stream) {
|
|
549
244
|
chunks.push(chunk);
|
|
@@ -551,61 +246,47 @@ const toBuffer = async (stream) => {
|
|
|
551
246
|
stream.destroy();
|
|
552
247
|
return Buffer.concat(chunks);
|
|
553
248
|
};
|
|
554
|
-
|
|
555
|
-
const getStream = async (item, opts) => {
|
|
249
|
+
export const getStream = async (item, opts) => {
|
|
556
250
|
if (Buffer.isBuffer(item)) {
|
|
557
|
-
return { stream:
|
|
251
|
+
return { stream: toReadable(item), type: 'buffer' };
|
|
558
252
|
}
|
|
559
253
|
if ('stream' in item) {
|
|
560
254
|
return { stream: item.stream, type: 'readable' };
|
|
561
255
|
}
|
|
562
|
-
|
|
563
|
-
|
|
256
|
+
const urlStr = item.url.toString();
|
|
257
|
+
if (urlStr.startsWith('data:')) {
|
|
258
|
+
const buffer = Buffer.from(urlStr.split(',')[1], 'base64');
|
|
259
|
+
return { stream: toReadable(buffer), type: 'buffer' };
|
|
564
260
|
}
|
|
565
|
-
|
|
261
|
+
if (urlStr.startsWith('http://') || urlStr.startsWith('https://')) {
|
|
262
|
+
return { stream: await getHttpStream(item.url, opts), type: 'remote' };
|
|
263
|
+
}
|
|
264
|
+
return { stream: createReadStream(item.url), type: 'file' };
|
|
566
265
|
};
|
|
567
|
-
exports.getStream = getStream;
|
|
568
266
|
/** generates a thumbnail for a given media, if required */
|
|
569
|
-
async function generateThumbnail(file, mediaType, options) {
|
|
570
|
-
var _a;
|
|
267
|
+
export async function generateThumbnail(file, mediaType, options) {
|
|
571
268
|
let thumbnail;
|
|
572
269
|
let originalImageDimensions;
|
|
573
270
|
if (mediaType === 'image') {
|
|
574
|
-
const { buffer, original } = await
|
|
271
|
+
const { buffer, original } = await extractImageThumb(file);
|
|
575
272
|
thumbnail = buffer.toString('base64');
|
|
576
273
|
if (original.width && original.height) {
|
|
577
274
|
originalImageDimensions = {
|
|
578
275
|
width: original.width,
|
|
579
|
-
height: original.height
|
|
276
|
+
height: original.height
|
|
580
277
|
};
|
|
581
278
|
}
|
|
582
279
|
}
|
|
583
280
|
else if (mediaType === 'video') {
|
|
281
|
+
const imgFilename = join(getTmpFilesDirectory(), generateMessageIDV2() + '.jpg');
|
|
584
282
|
try {
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
await fs_1.promises.writeFile(videoPath, buffer);
|
|
590
|
-
}
|
|
591
|
-
const thumbnailBuffer = await (0, exports.extractVideoThumb)(videoPath);
|
|
592
|
-
const imgFilename = (0, path_1.join)(getTmpFilesDirectory(), (0, generics_1.generateMessageIDV2)() + '.jpg');
|
|
593
|
-
await fs_1.promises.writeFile(imgFilename, thumbnailBuffer);
|
|
594
|
-
const { buffer: processedThumbnailBuffer, original } = await (0, exports.extractImageThumb)(imgFilename);
|
|
595
|
-
thumbnail = processedThumbnailBuffer.toString('base64');
|
|
596
|
-
if (original.width && original.height) {
|
|
597
|
-
originalImageDimensions = {
|
|
598
|
-
width: original.width,
|
|
599
|
-
height: original.height,
|
|
600
|
-
};
|
|
601
|
-
}
|
|
602
|
-
await fs_1.promises.unlink(imgFilename);
|
|
603
|
-
if (videoPath !== file) {
|
|
604
|
-
await fs_1.promises.unlink(videoPath);
|
|
605
|
-
}
|
|
283
|
+
await extractVideoThumb(file, imgFilename, '00:00:00', { width: 32, height: 32 });
|
|
284
|
+
const buff = await fs.readFile(imgFilename);
|
|
285
|
+
thumbnail = buff.toString('base64');
|
|
286
|
+
await fs.unlink(imgFilename);
|
|
606
287
|
}
|
|
607
288
|
catch (err) {
|
|
608
|
-
|
|
289
|
+
options.logger?.debug('could not generate video thumb: ' + err);
|
|
609
290
|
}
|
|
610
291
|
}
|
|
611
292
|
return {
|
|
@@ -613,196 +294,145 @@ async function generateThumbnail(file, mediaType, options) {
|
|
|
613
294
|
originalImageDimensions
|
|
614
295
|
};
|
|
615
296
|
}
|
|
616
|
-
const getHttpStream = async (url, options = {}) => {
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
let bodyPath;
|
|
625
|
-
let didSaveToTmpPath = false;
|
|
626
|
-
try {
|
|
627
|
-
const buffer = await (0, exports.toBuffer)(stream);
|
|
628
|
-
if (type === 'file') {
|
|
629
|
-
bodyPath = media.url;
|
|
630
|
-
}
|
|
631
|
-
else if (saveOriginalFileIfRequired) {
|
|
632
|
-
bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageIDV2)());
|
|
633
|
-
(0, fs_1.writeFileSync)(bodyPath, buffer);
|
|
634
|
-
didSaveToTmpPath = true;
|
|
635
|
-
}
|
|
636
|
-
const fileLength = buffer.length;
|
|
637
|
-
const fileSha256 = Crypto.createHash('sha256').update(buffer).digest();
|
|
638
|
-
stream === null || stream === void 0 ? void 0 : stream.destroy();
|
|
639
|
-
logger === null || logger === void 0 ? void 0 : logger.debug('prepare stream data successfully');
|
|
640
|
-
return {
|
|
641
|
-
mediaKey: undefined,
|
|
642
|
-
encWriteStream: buffer,
|
|
643
|
-
fileLength,
|
|
644
|
-
fileSha256,
|
|
645
|
-
fileEncSha256: undefined,
|
|
646
|
-
bodyPath,
|
|
647
|
-
didSaveToTmpPath
|
|
648
|
-
};
|
|
649
|
-
}
|
|
650
|
-
catch (error) {
|
|
651
|
-
stream.destroy();
|
|
652
|
-
if (didSaveToTmpPath) {
|
|
653
|
-
try {
|
|
654
|
-
await fs_1.promises.unlink(bodyPath);
|
|
655
|
-
}
|
|
656
|
-
catch (err) {
|
|
657
|
-
logger === null || logger === void 0 ? void 0 : logger.error({ err }, 'failed to save to tmp path');
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
throw error;
|
|
297
|
+
export const getHttpStream = async (url, options = {}) => {
|
|
298
|
+
const response = await fetch(url.toString(), {
|
|
299
|
+
dispatcher: options.dispatcher,
|
|
300
|
+
method: 'GET',
|
|
301
|
+
headers: options.headers
|
|
302
|
+
});
|
|
303
|
+
if (!response.ok) {
|
|
304
|
+
throw new Boom(`Failed to fetch stream from ${url}`, { statusCode: response.status, data: { url } });
|
|
661
305
|
}
|
|
306
|
+
// @ts-ignore Node18+ Readable.fromWeb exists
|
|
307
|
+
return response.body instanceof Readable ? response.body : Readable.fromWeb(response.body);
|
|
662
308
|
};
|
|
663
|
-
|
|
664
|
-
const
|
|
665
|
-
const { stream, type } = await (0, exports.getStream)(media, opts);
|
|
309
|
+
export const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
|
|
310
|
+
const { stream, type } = await getStream(media, opts);
|
|
666
311
|
logger?.debug('fetched media stream');
|
|
667
|
-
|
|
668
|
-
let finalStream = stream;
|
|
669
|
-
|
|
670
|
-
if (mediaType === 'audio' && (isPtt === true || forceOpus === true)) {
|
|
671
|
-
try {
|
|
672
|
-
const buffer = await (0, exports.toBuffer)(stream);
|
|
673
|
-
const opusBuffer = await exports.convertToOpusBuffer(buffer, logger);
|
|
674
|
-
finalStream = (0, exports.toReadable)(opusBuffer);
|
|
675
|
-
logger?.debug('converted audio to Opus');
|
|
676
|
-
} catch (error) {
|
|
677
|
-
logger?.error('failed to convert audio to Opus, fallback to original stream');
|
|
678
|
-
const { stream: newStream } = await (0, exports.getStream)(media, opts);
|
|
679
|
-
finalStream = newStream;
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
if (mediaType === 'video' && convertVideo === true) {
|
|
684
|
-
try {
|
|
685
|
-
const buffer = await (0, exports.toBuffer)(stream);
|
|
686
|
-
const mp4Buffer = await exports.convertToMp4Buffer(buffer, logger);
|
|
687
|
-
finalStream = (0, exports.toReadable)(mp4Buffer);
|
|
688
|
-
logger?.debug('converted video to mp4');
|
|
689
|
-
} catch (error) {
|
|
690
|
-
logger?.error('failed to convert video to mp4, fallback to original stream');
|
|
691
|
-
const { stream: newStream } = await (0, exports.getStream)(media, opts);
|
|
692
|
-
finalStream = newStream;
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
|
|
696
312
|
const mediaKey = Crypto.randomBytes(32);
|
|
697
313
|
const { cipherKey, iv, macKey } = await getMediaKeys(mediaKey, mediaType);
|
|
698
|
-
const
|
|
699
|
-
|
|
700
|
-
let
|
|
701
|
-
let
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
} else if (saveOriginalFileIfRequired) {
|
|
706
|
-
bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageIDV2)());
|
|
707
|
-
writeStream = (0, fs_1.createWriteStream)(bodyPath);
|
|
708
|
-
didSaveToTmpPath = true;
|
|
314
|
+
const encFilePath = join(getTmpFilesDirectory(), mediaType + generateMessageIDV2() + '-enc');
|
|
315
|
+
const encFileWriteStream = createWriteStream(encFilePath);
|
|
316
|
+
let originalFileStream;
|
|
317
|
+
let originalFilePath;
|
|
318
|
+
if (saveOriginalFileIfRequired) {
|
|
319
|
+
originalFilePath = join(getTmpFilesDirectory(), mediaType + generateMessageIDV2() + '-original');
|
|
320
|
+
originalFileStream = createWriteStream(originalFilePath);
|
|
709
321
|
}
|
|
710
|
-
|
|
711
322
|
let fileLength = 0;
|
|
712
323
|
const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv);
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
324
|
+
const hmac = Crypto.createHmac('sha256', macKey).update(iv);
|
|
325
|
+
const sha256Plain = Crypto.createHash('sha256');
|
|
326
|
+
const sha256Enc = Crypto.createHash('sha256');
|
|
327
|
+
const onChunk = async (buff) => {
|
|
328
|
+
sha256Enc.update(buff);
|
|
329
|
+
hmac.update(buff);
|
|
330
|
+
// Handle backpressure: if write returns false, wait for drain
|
|
331
|
+
if (!encFileWriteStream.write(buff)) {
|
|
332
|
+
await once(encFileWriteStream, 'drain');
|
|
333
|
+
}
|
|
334
|
+
};
|
|
717
335
|
try {
|
|
718
|
-
for await (const data of
|
|
336
|
+
for await (const data of stream) {
|
|
719
337
|
fileLength += data.length;
|
|
720
|
-
if (type === 'remote' &&
|
|
721
|
-
|
|
338
|
+
if (type === 'remote' &&
|
|
339
|
+
opts?.maxContentLength &&
|
|
340
|
+
fileLength + data.length > opts.maxContentLength) {
|
|
341
|
+
throw new Boom(`content length exceeded when encrypting "${type}"`, {
|
|
342
|
+
data: { media, type }
|
|
343
|
+
});
|
|
722
344
|
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
if (writeStream) {
|
|
727
|
-
if (!writeStream.write(data)) {
|
|
728
|
-
await (0, events_1.once)(writeStream, 'drain');
|
|
345
|
+
if (originalFileStream) {
|
|
346
|
+
if (!originalFileStream.write(data)) {
|
|
347
|
+
await once(originalFileStream, 'drain');
|
|
729
348
|
}
|
|
730
349
|
}
|
|
731
|
-
|
|
732
|
-
onChunk(aes.update(data));
|
|
350
|
+
sha256Plain.update(data);
|
|
351
|
+
await onChunk(aes.update(data));
|
|
733
352
|
}
|
|
734
|
-
|
|
735
|
-
onChunk(aes.final());
|
|
353
|
+
await onChunk(aes.final());
|
|
736
354
|
const mac = hmac.digest().slice(0, 10);
|
|
737
|
-
sha256Enc
|
|
355
|
+
sha256Enc.update(mac);
|
|
738
356
|
const fileSha256 = sha256Plain.digest();
|
|
739
357
|
const fileEncSha256 = sha256Enc.digest();
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
358
|
+
encFileWriteStream.write(mac);
|
|
359
|
+
const encFinishPromise = once(encFileWriteStream, 'finish');
|
|
360
|
+
const originalFinishPromise = originalFileStream ? once(originalFileStream, 'finish') : Promise.resolve();
|
|
361
|
+
encFileWriteStream.end();
|
|
362
|
+
originalFileStream?.end?.();
|
|
363
|
+
stream.destroy();
|
|
364
|
+
// Wait for write streams to fully flush to disk
|
|
365
|
+
// This helps reduce memory pressure by allowing OS to release buffers
|
|
366
|
+
await encFinishPromise;
|
|
367
|
+
await originalFinishPromise;
|
|
746
368
|
logger?.debug('encrypted data successfully');
|
|
747
|
-
|
|
748
369
|
return {
|
|
749
370
|
mediaKey,
|
|
750
|
-
|
|
751
|
-
|
|
371
|
+
originalFilePath,
|
|
372
|
+
encFilePath,
|
|
752
373
|
mac,
|
|
753
374
|
fileEncSha256,
|
|
754
375
|
fileSha256,
|
|
755
|
-
fileLength
|
|
756
|
-
didSaveToTmpPath
|
|
376
|
+
fileLength
|
|
757
377
|
};
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
// destroy all streams with error
|
|
381
|
+
encFileWriteStream.destroy();
|
|
382
|
+
originalFileStream?.destroy?.();
|
|
761
383
|
aes.destroy();
|
|
762
384
|
hmac.destroy();
|
|
763
385
|
sha256Plain.destroy();
|
|
764
386
|
sha256Enc.destroy();
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
await
|
|
770
|
-
} catch (err) {
|
|
771
|
-
logger?.error({ err }, 'failed to save to tmp path');
|
|
387
|
+
stream.destroy();
|
|
388
|
+
try {
|
|
389
|
+
await fs.unlink(encFilePath);
|
|
390
|
+
if (originalFilePath) {
|
|
391
|
+
await fs.unlink(originalFilePath);
|
|
772
392
|
}
|
|
773
393
|
}
|
|
394
|
+
catch (err) {
|
|
395
|
+
logger?.error({ err }, 'failed deleting tmp files');
|
|
396
|
+
}
|
|
774
397
|
throw error;
|
|
775
398
|
}
|
|
776
|
-
|
|
777
|
-
function onChunk(buff) {
|
|
778
|
-
sha256Enc = sha256Enc.update(buff);
|
|
779
|
-
hmac = hmac.update(buff);
|
|
780
|
-
encWriteStream.push(buff);
|
|
781
|
-
}
|
|
782
399
|
};
|
|
783
|
-
|
|
784
|
-
exports.encryptedStream = encryptedStream;
|
|
785
|
-
const DEF_HOST = 'mmg.whatsapp.net';
|
|
400
|
+
export const DEF_MEDIA_HOST = 'mmg.whatsapp.net';
|
|
786
401
|
const AES_CHUNK_SIZE = 16;
|
|
787
402
|
const toSmallestChunkSize = (num) => {
|
|
788
403
|
return Math.floor(num / AES_CHUNK_SIZE) * AES_CHUNK_SIZE;
|
|
789
404
|
};
|
|
790
|
-
const getUrlFromDirectPath = (directPath) => `https://${
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
405
|
+
export const getUrlFromDirectPath = (directPath, host = DEF_MEDIA_HOST) => `https://${host}${directPath}`;
|
|
406
|
+
const extractHost = (url) => {
|
|
407
|
+
if (!url)
|
|
408
|
+
return undefined;
|
|
409
|
+
try {
|
|
410
|
+
return new URL(url).host;
|
|
411
|
+
}
|
|
412
|
+
catch {
|
|
413
|
+
return undefined;
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
export const downloadContentFromMessage = async ({ mediaKey, directPath, url }, type, opts = {}) => {
|
|
417
|
+
// Fallback host: explicit opt > host parsed from `url` > DEF_MEDIA_HOST.
|
|
418
|
+
// Lets us honor a non-default host carried by the proto without forcing callers to thread it through.
|
|
419
|
+
const fallbackHost = opts.host ?? extractHost(url);
|
|
420
|
+
const downloadUrl = directPath ? getUrlFromDirectPath(directPath, fallbackHost) : url;
|
|
795
421
|
if (!downloadUrl) {
|
|
796
|
-
throw new
|
|
422
|
+
throw new Boom('No valid media URL or directPath present in message', { statusCode: 400 });
|
|
797
423
|
}
|
|
798
424
|
const keys = await getMediaKeys(mediaKey, type);
|
|
799
|
-
return
|
|
425
|
+
return downloadEncryptedContent(downloadUrl, keys, opts);
|
|
800
426
|
};
|
|
801
|
-
|
|
802
|
-
|
|
427
|
+
/**
|
|
428
|
+
* Decrypts and downloads an AES256-CBC encrypted file given the keys.
|
|
429
|
+
* Assumes the SHA256 of the plaintext is appended to the end of the ciphertext
|
|
430
|
+
* */
|
|
431
|
+
export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startByte, endByte, options } = {}) => {
|
|
803
432
|
let bytesFetched = 0;
|
|
804
433
|
let startChunk = 0;
|
|
805
434
|
let firstBlockIsIV = false;
|
|
435
|
+
// if a start byte is specified -- then we need to fetch the previous chunk as that will form the IV
|
|
806
436
|
if (startByte) {
|
|
807
437
|
const chunk = toSmallestChunkSize(startByte || 0);
|
|
808
438
|
if (chunk) {
|
|
@@ -812,9 +442,14 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
|
|
|
812
442
|
}
|
|
813
443
|
}
|
|
814
444
|
const endChunk = endByte ? toSmallestChunkSize(endByte || 0) + AES_CHUNK_SIZE : undefined;
|
|
445
|
+
const headersInit = options?.headers ? options.headers : undefined;
|
|
815
446
|
const headers = {
|
|
816
|
-
...(
|
|
817
|
-
|
|
447
|
+
...(headersInit
|
|
448
|
+
? Array.isArray(headersInit)
|
|
449
|
+
? Object.fromEntries(headersInit)
|
|
450
|
+
: headersInit
|
|
451
|
+
: {}),
|
|
452
|
+
Origin: DEFAULT_ORIGIN
|
|
818
453
|
};
|
|
819
454
|
if (startChunk || endChunk) {
|
|
820
455
|
headers.Range = `bytes=${startChunk}-`;
|
|
@@ -822,11 +457,10 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
|
|
|
822
457
|
headers.Range += endChunk;
|
|
823
458
|
}
|
|
824
459
|
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
maxContentLength: Infinity,
|
|
460
|
+
// download the message
|
|
461
|
+
const fetched = await getHttpStream(downloadUrl, {
|
|
462
|
+
...(options || {}),
|
|
463
|
+
headers
|
|
830
464
|
});
|
|
831
465
|
let remainingBytes = Buffer.from([]);
|
|
832
466
|
let aes;
|
|
@@ -841,9 +475,9 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
|
|
|
841
475
|
push(bytes);
|
|
842
476
|
}
|
|
843
477
|
};
|
|
844
|
-
const output = new
|
|
478
|
+
const output = new Transform({
|
|
845
479
|
transform(chunk, _, callback) {
|
|
846
|
-
let data = Buffer.concat([remainingBytes, chunk]);
|
|
480
|
+
let data = remainingBytes.length ? Buffer.concat([remainingBytes, chunk]) : chunk;
|
|
847
481
|
const decryptLength = toSmallestChunkSize(data.length);
|
|
848
482
|
remainingBytes = data.slice(decryptLength);
|
|
849
483
|
data = data.slice(0, decryptLength);
|
|
@@ -854,6 +488,8 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
|
|
|
854
488
|
data = data.slice(AES_CHUNK_SIZE);
|
|
855
489
|
}
|
|
856
490
|
aes = Crypto.createDecipheriv('aes-256-cbc', cipherKey, ivValue);
|
|
491
|
+
// if an end byte that is not EOF is specified
|
|
492
|
+
// stop auto padding (PKCS7) -- otherwise throws an error for decryption
|
|
857
493
|
if (endByte) {
|
|
858
494
|
aes.setAutoPadding(false);
|
|
859
495
|
}
|
|
@@ -874,18 +510,15 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
|
|
|
874
510
|
catch (error) {
|
|
875
511
|
callback(error);
|
|
876
512
|
}
|
|
877
|
-
}
|
|
513
|
+
}
|
|
878
514
|
});
|
|
879
515
|
return fetched.pipe(output, { end: true });
|
|
880
516
|
};
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
const getExtension = (mimetype) => mimetype.split(';')[0].split('/')[1];
|
|
517
|
+
export function extensionForMediaMessage(message) {
|
|
518
|
+
const getExtension = (mimetype) => mimetype.split(';')[0]?.split('/')[1];
|
|
884
519
|
const type = Object.keys(message)[0];
|
|
885
520
|
let extension;
|
|
886
|
-
if (type === 'locationMessage' ||
|
|
887
|
-
type === 'liveLocationMessage' ||
|
|
888
|
-
type === 'productMessage') {
|
|
521
|
+
if (type === 'locationMessage' || type === 'liveLocationMessage' || type === 'productMessage') {
|
|
889
522
|
extension = '.jpeg';
|
|
890
523
|
}
|
|
891
524
|
else {
|
|
@@ -894,52 +527,158 @@ function extensionForMediaMessage(message) {
|
|
|
894
527
|
}
|
|
895
528
|
return extension;
|
|
896
529
|
}
|
|
897
|
-
const
|
|
898
|
-
return
|
|
899
|
-
|
|
530
|
+
const isNodeRuntime = () => {
|
|
531
|
+
return (typeof process !== 'undefined' &&
|
|
532
|
+
process.versions?.node !== null &&
|
|
533
|
+
typeof process.versions.bun === 'undefined' &&
|
|
534
|
+
typeof globalThis.Deno === 'undefined');
|
|
535
|
+
};
|
|
536
|
+
export const uploadWithNodeHttp = async ({ url, filePath, headers, timeoutMs, agent }, redirectCount = 0) => {
|
|
537
|
+
if (redirectCount > 5) {
|
|
538
|
+
throw new Error('Too many redirects');
|
|
539
|
+
}
|
|
540
|
+
const parsedUrl = new URL(url);
|
|
541
|
+
const httpModule = parsedUrl.protocol === 'https:' ? await import('https') : await import('http');
|
|
542
|
+
// Get file size for Content-Length header (required for Node.js streaming)
|
|
543
|
+
const fileStats = await fs.stat(filePath);
|
|
544
|
+
const fileSize = fileStats.size;
|
|
545
|
+
return new Promise((resolve, reject) => {
|
|
546
|
+
const req = httpModule.request({
|
|
547
|
+
hostname: parsedUrl.hostname,
|
|
548
|
+
port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
|
|
549
|
+
path: parsedUrl.pathname + parsedUrl.search,
|
|
550
|
+
method: 'POST',
|
|
551
|
+
headers: {
|
|
552
|
+
...headers,
|
|
553
|
+
'Content-Length': fileSize
|
|
554
|
+
},
|
|
555
|
+
agent,
|
|
556
|
+
timeout: timeoutMs
|
|
557
|
+
}, res => {
|
|
558
|
+
// Handle redirects (3xx)
|
|
559
|
+
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
560
|
+
res.resume(); // Consume response to free resources
|
|
561
|
+
const newUrl = new URL(res.headers.location, url).toString();
|
|
562
|
+
resolve(uploadWithNodeHttp({
|
|
563
|
+
url: newUrl,
|
|
564
|
+
filePath,
|
|
565
|
+
headers,
|
|
566
|
+
timeoutMs,
|
|
567
|
+
agent
|
|
568
|
+
}, redirectCount + 1));
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
let body = '';
|
|
572
|
+
res.on('data', chunk => (body += chunk));
|
|
573
|
+
res.on('end', () => {
|
|
574
|
+
try {
|
|
575
|
+
resolve(JSON.parse(body));
|
|
576
|
+
}
|
|
577
|
+
catch {
|
|
578
|
+
resolve(undefined);
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
});
|
|
582
|
+
req.on('error', reject);
|
|
583
|
+
req.on('timeout', () => {
|
|
584
|
+
req.destroy();
|
|
585
|
+
reject(new Error('Upload timeout'));
|
|
586
|
+
});
|
|
587
|
+
const stream = createReadStream(filePath);
|
|
588
|
+
stream.pipe(req);
|
|
589
|
+
stream.on('error', err => {
|
|
590
|
+
req.destroy();
|
|
591
|
+
reject(err);
|
|
592
|
+
});
|
|
593
|
+
});
|
|
594
|
+
};
|
|
595
|
+
const uploadWithFetch = async ({ url, filePath, headers, timeoutMs, agent }) => {
|
|
596
|
+
// Convert Node.js Readable to Web ReadableStream
|
|
597
|
+
const nodeStream = createReadStream(filePath);
|
|
598
|
+
const webStream = Readable.toWeb(nodeStream);
|
|
599
|
+
// Native fetch only accepts Undici-style dispatchers, not generic https Agents.
|
|
600
|
+
const dispatcher = typeof agent?.dispatch === 'function' ? agent : undefined;
|
|
601
|
+
const response = await fetch(url, {
|
|
602
|
+
...(dispatcher ? { dispatcher } : {}),
|
|
603
|
+
method: 'POST',
|
|
604
|
+
body: webStream,
|
|
605
|
+
headers,
|
|
606
|
+
duplex: 'half',
|
|
607
|
+
signal: timeoutMs ? AbortSignal.timeout(timeoutMs) : undefined
|
|
608
|
+
});
|
|
609
|
+
try {
|
|
610
|
+
return (await response.json());
|
|
611
|
+
}
|
|
612
|
+
catch {
|
|
613
|
+
return undefined;
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
/**
|
|
617
|
+
* Uploads media to WhatsApp servers.
|
|
618
|
+
*
|
|
619
|
+
* ## Why we have two upload implementations:
|
|
620
|
+
*
|
|
621
|
+
* Node.js's native `fetch` (powered by undici) has a known bug where it buffers
|
|
622
|
+
* the entire request body in memory before sending, even when using streams.
|
|
623
|
+
* This causes memory issues with large files (e.g., 1GB file = 1GB+ memory usage).
|
|
624
|
+
* See: https://github.com/nodejs/undici/issues/4058
|
|
625
|
+
*
|
|
626
|
+
* Other runtimes (Bun, Deno, browsers) correctly stream the request body without
|
|
627
|
+
* buffering, so we can use the web-standard Fetch API there.
|
|
628
|
+
*
|
|
629
|
+
* ## Future considerations:
|
|
630
|
+
* Once the undici bug is fixed, we can simplify this to use only the Fetch API
|
|
631
|
+
* across all runtimes. Monitor the GitHub issue for updates.
|
|
632
|
+
*/
|
|
633
|
+
const uploadMedia = async (params, logger) => {
|
|
634
|
+
if (isNodeRuntime()) {
|
|
635
|
+
logger?.debug('Using Node.js https module for upload (avoids undici buffering bug)');
|
|
636
|
+
return uploadWithNodeHttp(params);
|
|
637
|
+
}
|
|
638
|
+
else {
|
|
639
|
+
logger?.debug('Using web-standard Fetch API for upload');
|
|
640
|
+
return uploadWithFetch(params);
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
export const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options }, refreshMediaConn) => {
|
|
644
|
+
return async (filePath, { mediaType, fileEncSha256B64, timeoutMs }) => {
|
|
645
|
+
// send a query JSON to obtain the url & auth token to upload our media
|
|
900
646
|
let uploadInfo = await refreshMediaConn(false);
|
|
901
647
|
let urls;
|
|
902
648
|
const hosts = [...customUploadHosts, ...uploadInfo.hosts];
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
649
|
+
fileEncSha256B64 = encodeBase64EncodedStringForUpload(fileEncSha256B64);
|
|
650
|
+
// Prepare common headers
|
|
651
|
+
const customHeaders = (() => {
|
|
652
|
+
const hdrs = options?.headers;
|
|
653
|
+
if (!hdrs)
|
|
654
|
+
return {};
|
|
655
|
+
return Array.isArray(hdrs) ? Object.fromEntries(hdrs) : hdrs;
|
|
656
|
+
})();
|
|
657
|
+
const headers = {
|
|
658
|
+
...customHeaders,
|
|
659
|
+
'Content-Type': 'application/octet-stream',
|
|
660
|
+
Origin: DEFAULT_ORIGIN
|
|
661
|
+
};
|
|
662
|
+
for (const { hostname } of hosts) {
|
|
916
663
|
logger.debug(`uploading to "${hostname}"`);
|
|
917
664
|
const auth = encodeURIComponent(uploadInfo.auth);
|
|
918
|
-
const url = `https://${hostname}${
|
|
665
|
+
const url = `https://${hostname}${MEDIA_PATH_MAP[mediaType]}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`;
|
|
919
666
|
let result;
|
|
920
667
|
try {
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
'Origin': Defaults_1.DEFAULT_ORIGIN
|
|
930
|
-
},
|
|
931
|
-
httpsAgent: fetchAgent,
|
|
932
|
-
timeout: timeoutMs,
|
|
933
|
-
responseType: 'json',
|
|
934
|
-
maxBodyLength: Infinity,
|
|
935
|
-
maxContentLength: Infinity,
|
|
936
|
-
});
|
|
937
|
-
result = body.data;
|
|
938
|
-
if ((result === null || result === void 0 ? void 0 : result.url) || (result === null || result === void 0 ? void 0 : result.directPath)) {
|
|
668
|
+
result = await uploadMedia({
|
|
669
|
+
url,
|
|
670
|
+
filePath,
|
|
671
|
+
headers,
|
|
672
|
+
timeoutMs,
|
|
673
|
+
agent: fetchAgent
|
|
674
|
+
}, logger);
|
|
675
|
+
if (result?.url || result?.direct_path) {
|
|
939
676
|
urls = {
|
|
940
677
|
mediaUrl: result.url,
|
|
941
678
|
directPath: result.direct_path,
|
|
942
|
-
|
|
679
|
+
meta_hmac: result.meta_hmac,
|
|
680
|
+
fbid: result.fbid,
|
|
681
|
+
ts: result.ts
|
|
943
682
|
};
|
|
944
683
|
break;
|
|
945
684
|
}
|
|
@@ -949,37 +688,39 @@ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options },
|
|
|
949
688
|
}
|
|
950
689
|
}
|
|
951
690
|
catch (error) {
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
}
|
|
955
|
-
const isLast = hostname === ((_b = hosts[uploadInfo.hosts.length - 1]) === null || _b === void 0 ? void 0 : _b.hostname);
|
|
956
|
-
logger.warn({ trace: error.stack, uploadResult: result }, `Error in uploading to ${hostname} ${isLast ? '' : ', retrying...'}`);
|
|
691
|
+
const isLast = hostname === hosts[uploadInfo.hosts.length - 1]?.hostname;
|
|
692
|
+
logger.warn({ trace: error?.stack, uploadResult: result }, `Error in uploading to ${hostname} ${isLast ? '' : ', retrying...'}`);
|
|
957
693
|
}
|
|
958
694
|
}
|
|
959
695
|
if (!urls) {
|
|
960
|
-
throw new
|
|
696
|
+
throw new Boom('Media upload failed on all hosts', { statusCode: 500 });
|
|
961
697
|
}
|
|
962
698
|
return urls;
|
|
963
699
|
};
|
|
964
700
|
};
|
|
965
|
-
exports.getWAUploadToServer = getWAUploadToServer;
|
|
966
701
|
const getMediaRetryKey = (mediaKey) => {
|
|
967
|
-
return
|
|
702
|
+
return hkdf(mediaKey, 32, { info: 'WhatsApp Media Retry Notification' });
|
|
968
703
|
};
|
|
969
|
-
|
|
704
|
+
/**
|
|
705
|
+
* Generate a binary node that will request the phone to re-upload the media & return the newly uploaded URL
|
|
706
|
+
*/
|
|
707
|
+
export const encryptMediaRetryRequest = (key, mediaKey, meId) => {
|
|
970
708
|
const recp = { stanzaId: key.id };
|
|
971
|
-
const recpBuffer =
|
|
709
|
+
const recpBuffer = proto.ServerErrorReceipt.encode(recp).finish();
|
|
972
710
|
const iv = Crypto.randomBytes(12);
|
|
973
|
-
const retryKey =
|
|
974
|
-
const ciphertext =
|
|
711
|
+
const retryKey = getMediaRetryKey(mediaKey);
|
|
712
|
+
const ciphertext = aesEncryptGCM(recpBuffer, retryKey, iv, Buffer.from(key.id));
|
|
975
713
|
const req = {
|
|
976
714
|
tag: 'receipt',
|
|
977
715
|
attrs: {
|
|
978
716
|
id: key.id,
|
|
979
|
-
to:
|
|
717
|
+
to: jidNormalizedUser(meId),
|
|
980
718
|
type: 'server-error'
|
|
981
719
|
},
|
|
982
720
|
content: [
|
|
721
|
+
// this encrypt node is actually pretty useless
|
|
722
|
+
// the media is returned even without this node
|
|
723
|
+
// keeping it here to maintain parity with WA Web
|
|
983
724
|
{
|
|
984
725
|
tag: 'encrypt',
|
|
985
726
|
attrs: {},
|
|
@@ -992,7 +733,8 @@ const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
|
|
|
992
733
|
tag: 'rmr',
|
|
993
734
|
attrs: {
|
|
994
735
|
jid: key.remoteJid,
|
|
995
|
-
|
|
736
|
+
from_me: (!!key.fromMe).toString(),
|
|
737
|
+
// @ts-ignore
|
|
996
738
|
participant: key.participant || undefined
|
|
997
739
|
}
|
|
998
740
|
}
|
|
@@ -1000,9 +742,8 @@ const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
|
|
|
1000
742
|
};
|
|
1001
743
|
return req;
|
|
1002
744
|
};
|
|
1003
|
-
|
|
1004
|
-
const
|
|
1005
|
-
const rmrNode = (0, WABinary_1.getBinaryNodeChild)(node, 'rmr');
|
|
745
|
+
export const decodeMediaRetryNode = (node) => {
|
|
746
|
+
const rmrNode = getBinaryNodeChild(node, 'rmr');
|
|
1006
747
|
const event = {
|
|
1007
748
|
key: {
|
|
1008
749
|
id: node.attrs.id,
|
|
@@ -1011,39 +752,37 @@ const decodeMediaRetryNode = (node) => {
|
|
|
1011
752
|
participant: rmrNode.attrs.participant
|
|
1012
753
|
}
|
|
1013
754
|
};
|
|
1014
|
-
const errorNode =
|
|
755
|
+
const errorNode = getBinaryNodeChild(node, 'error');
|
|
1015
756
|
if (errorNode) {
|
|
1016
757
|
const errorCode = +errorNode.attrs.code;
|
|
1017
|
-
event.error = new
|
|
758
|
+
event.error = new Boom(`Failed to re-upload media (${errorCode})`, {
|
|
759
|
+
data: errorNode.attrs,
|
|
760
|
+
statusCode: getStatusCodeForMediaRetry(errorCode)
|
|
761
|
+
});
|
|
1018
762
|
}
|
|
1019
763
|
else {
|
|
1020
|
-
const encryptedInfoNode =
|
|
1021
|
-
const ciphertext =
|
|
1022
|
-
const iv =
|
|
764
|
+
const encryptedInfoNode = getBinaryNodeChild(node, 'encrypt');
|
|
765
|
+
const ciphertext = getBinaryNodeChildBuffer(encryptedInfoNode, 'enc_p');
|
|
766
|
+
const iv = getBinaryNodeChildBuffer(encryptedInfoNode, 'enc_iv');
|
|
1023
767
|
if (ciphertext && iv) {
|
|
1024
768
|
event.media = { ciphertext, iv };
|
|
1025
769
|
}
|
|
1026
770
|
else {
|
|
1027
|
-
event.error = new
|
|
771
|
+
event.error = new Boom('Failed to re-upload media (missing ciphertext)', { statusCode: 404 });
|
|
1028
772
|
}
|
|
1029
773
|
}
|
|
1030
774
|
return event;
|
|
1031
775
|
};
|
|
1032
|
-
|
|
1033
|
-
const
|
|
1034
|
-
const
|
|
1035
|
-
|
|
1036
|
-
return WAProto_1.proto.MediaRetryNotification.decode(plaintext);
|
|
776
|
+
export const decryptMediaRetryData = ({ ciphertext, iv }, mediaKey, msgId) => {
|
|
777
|
+
const retryKey = getMediaRetryKey(mediaKey);
|
|
778
|
+
const plaintext = aesDecryptGCM(ciphertext, retryKey, iv, Buffer.from(msgId));
|
|
779
|
+
return proto.MediaRetryNotification.decode(plaintext);
|
|
1037
780
|
};
|
|
1038
|
-
|
|
1039
|
-
const getStatusCodeForMediaRetry = (code) => MEDIA_RETRY_STATUS_MAP[code];
|
|
1040
|
-
exports.getStatusCodeForMediaRetry = getStatusCodeForMediaRetry;
|
|
781
|
+
export const getStatusCodeForMediaRetry = (code) => MEDIA_RETRY_STATUS_MAP[code];
|
|
1041
782
|
const MEDIA_RETRY_STATUS_MAP = {
|
|
1042
|
-
[
|
|
1043
|
-
[
|
|
1044
|
-
[
|
|
1045
|
-
[
|
|
783
|
+
[proto.MediaRetryNotification.ResultType.SUCCESS]: 200,
|
|
784
|
+
[proto.MediaRetryNotification.ResultType.DECRYPTION_ERROR]: 412,
|
|
785
|
+
[proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
|
|
786
|
+
[proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418
|
|
1046
787
|
};
|
|
1047
|
-
|
|
1048
|
-
throw new Error('Function not implemented.');
|
|
1049
|
-
}
|
|
788
|
+
//# sourceMappingURL=messages-media.js.map
|