@mtcute/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +8 -0
- package/README.md +26 -0
- package/cjs/base-client.d.ts +297 -0
- package/cjs/base-client.js +324 -0
- package/cjs/base-client.js.map +1 -0
- package/cjs/index.d.ts +10 -0
- package/cjs/index.js +30 -0
- package/cjs/index.js.map +1 -0
- package/cjs/network/auth-key.d.ts +21 -0
- package/cjs/network/auth-key.js +101 -0
- package/cjs/network/auth-key.js.map +1 -0
- package/cjs/network/authorization.d.ts +9 -0
- package/cjs/network/authorization.js +332 -0
- package/cjs/network/authorization.js.map +1 -0
- package/cjs/network/config-manager.d.ts +33 -0
- package/cjs/network/config-manager.js +93 -0
- package/cjs/network/config-manager.js.map +1 -0
- package/cjs/network/index.d.ts +4 -0
- package/cjs/network/index.js +20 -0
- package/cjs/network/index.js.map +1 -0
- package/cjs/network/mtproto-session.d.ts +116 -0
- package/cjs/network/mtproto-session.js +209 -0
- package/cjs/network/mtproto-session.js.map +1 -0
- package/cjs/network/multi-session-connection.d.ts +33 -0
- package/cjs/network/multi-session-connection.js +243 -0
- package/cjs/network/multi-session-connection.js.map +1 -0
- package/cjs/network/network-manager.d.ts +190 -0
- package/cjs/network/network-manager.js +547 -0
- package/cjs/network/network-manager.js.map +1 -0
- package/cjs/network/persistent-connection.d.ts +51 -0
- package/cjs/network/persistent-connection.js +173 -0
- package/cjs/network/persistent-connection.js.map +1 -0
- package/cjs/network/reconnection.d.ts +11 -0
- package/cjs/network/reconnection.js +18 -0
- package/cjs/network/reconnection.js.map +1 -0
- package/cjs/network/session-connection.d.ts +72 -0
- package/cjs/network/session-connection.js +1363 -0
- package/cjs/network/session-connection.js.map +1 -0
- package/cjs/network/transports/abstract.d.ts +99 -0
- package/cjs/network/transports/abstract.js +38 -0
- package/cjs/network/transports/abstract.js.map +1 -0
- package/cjs/network/transports/index.d.ts +8 -0
- package/cjs/network/transports/index.js +26 -0
- package/cjs/network/transports/index.js.map +1 -0
- package/cjs/network/transports/intermediate.d.ts +20 -0
- package/cjs/network/transports/intermediate.js +67 -0
- package/cjs/network/transports/intermediate.js.map +1 -0
- package/cjs/network/transports/obfuscated.d.ts +18 -0
- package/cjs/network/transports/obfuscated.js +83 -0
- package/cjs/network/transports/obfuscated.js.map +1 -0
- package/cjs/network/transports/streamed.d.ts +24 -0
- package/cjs/network/transports/streamed.js +32 -0
- package/cjs/network/transports/streamed.js.map +1 -0
- package/cjs/network/transports/tcp.d.ts +33 -0
- package/cjs/network/transports/tcp.js +130 -0
- package/cjs/network/transports/tcp.js.map +1 -0
- package/cjs/network/transports/websocket.d.ts +47 -0
- package/cjs/network/transports/websocket.js +124 -0
- package/cjs/network/transports/websocket.js.map +1 -0
- package/cjs/network/transports/wrapped.d.ts +11 -0
- package/cjs/network/transports/wrapped.js +26 -0
- package/cjs/network/transports/wrapped.js.map +1 -0
- package/cjs/package.json +3 -0
- package/cjs/storage/abstract.d.ts +174 -0
- package/cjs/storage/abstract.js +3 -0
- package/cjs/storage/abstract.js.map +1 -0
- package/cjs/storage/index.d.ts +5 -0
- package/cjs/storage/index.js +22 -0
- package/cjs/storage/index.js.map +1 -0
- package/cjs/storage/json-file.d.ts +31 -0
- package/cjs/storage/json-file.js +90 -0
- package/cjs/storage/json-file.js.map +1 -0
- package/cjs/storage/json.d.ts +9 -0
- package/cjs/storage/json.js +65 -0
- package/cjs/storage/json.js.map +1 -0
- package/cjs/storage/localstorage.d.ts +7 -0
- package/cjs/storage/localstorage.js +25 -0
- package/cjs/storage/localstorage.js.map +1 -0
- package/cjs/storage/memory.d.ts +98 -0
- package/cjs/storage/memory.js +299 -0
- package/cjs/storage/memory.js.map +1 -0
- package/cjs/types/errors.d.ts +53 -0
- package/cjs/types/errors.js +58 -0
- package/cjs/types/errors.js.map +1 -0
- package/cjs/types/index.d.ts +3 -0
- package/cjs/types/index.js +20 -0
- package/cjs/types/index.js.map +1 -0
- package/cjs/types/peers.d.ts +4 -0
- package/cjs/types/peers.js +3 -0
- package/cjs/types/peers.js.map +1 -0
- package/cjs/types/utils.d.ts +6 -0
- package/cjs/types/utils.js +9 -0
- package/cjs/types/utils.js.map +1 -0
- package/cjs/utils/async-lock.d.ts +10 -0
- package/cjs/utils/async-lock.js +43 -0
- package/cjs/utils/async-lock.js.map +1 -0
- package/cjs/utils/bigint-utils.d.ts +40 -0
- package/cjs/utils/bigint-utils.js +104 -0
- package/cjs/utils/bigint-utils.js.map +1 -0
- package/cjs/utils/binary/asn1-parser.d.ts +17 -0
- package/cjs/utils/binary/asn1-parser.js +121 -0
- package/cjs/utils/binary/asn1-parser.js.map +1 -0
- package/cjs/utils/buffer-utils.d.ts +21 -0
- package/cjs/utils/buffer-utils.js +63 -0
- package/cjs/utils/buffer-utils.js.map +1 -0
- package/cjs/utils/condition-variable.d.ts +9 -0
- package/cjs/utils/condition-variable.js +28 -0
- package/cjs/utils/condition-variable.js.map +1 -0
- package/cjs/utils/controllable-promise.d.ts +16 -0
- package/cjs/utils/controllable-promise.js +25 -0
- package/cjs/utils/controllable-promise.js.map +1 -0
- package/cjs/utils/crypto/abstract.d.ts +23 -0
- package/cjs/utils/crypto/abstract.js +19 -0
- package/cjs/utils/crypto/abstract.js.map +1 -0
- package/cjs/utils/crypto/common.d.ts +12 -0
- package/cjs/utils/crypto/common.js +54 -0
- package/cjs/utils/crypto/common.js.map +1 -0
- package/cjs/utils/crypto/factorization.d.ts +5 -0
- package/cjs/utils/crypto/factorization.js +69 -0
- package/cjs/utils/crypto/factorization.js.map +1 -0
- package/cjs/utils/crypto/index.d.ts +5 -0
- package/cjs/utils/crypto/index.js +23 -0
- package/cjs/utils/crypto/index.js.map +1 -0
- package/cjs/utils/crypto/keys.d.ts +27 -0
- package/cjs/utils/crypto/keys.js +70 -0
- package/cjs/utils/crypto/keys.js.map +1 -0
- package/cjs/utils/crypto/miller-rabin.d.ts +2 -0
- package/cjs/utils/crypto/miller-rabin.js +44 -0
- package/cjs/utils/crypto/miller-rabin.js.map +1 -0
- package/cjs/utils/crypto/mtproto.d.ts +31 -0
- package/cjs/utils/crypto/mtproto.js +66 -0
- package/cjs/utils/crypto/mtproto.js.map +1 -0
- package/cjs/utils/crypto/node-crypto.d.ts +10 -0
- package/cjs/utils/crypto/node-crypto.js +46 -0
- package/cjs/utils/crypto/node-crypto.js.map +1 -0
- package/cjs/utils/crypto/password.d.ts +29 -0
- package/cjs/utils/crypto/password.js +106 -0
- package/cjs/utils/crypto/password.js.map +1 -0
- package/cjs/utils/crypto/subtle.d.ts +12 -0
- package/cjs/utils/crypto/subtle.js +86 -0
- package/cjs/utils/crypto/subtle.js.map +1 -0
- package/cjs/utils/crypto/utils.d.ts +14 -0
- package/cjs/utils/crypto/utils.js +30 -0
- package/cjs/utils/crypto/utils.js.map +1 -0
- package/cjs/utils/default-dcs.d.ts +7 -0
- package/cjs/utils/default-dcs.js +76 -0
- package/cjs/utils/default-dcs.js.map +1 -0
- package/cjs/utils/deque.d.ts +29 -0
- package/cjs/utils/deque.js +210 -0
- package/cjs/utils/deque.js.map +1 -0
- package/cjs/utils/early-timer.d.ts +42 -0
- package/cjs/utils/early-timer.js +79 -0
- package/cjs/utils/early-timer.js.map +1 -0
- package/cjs/utils/flood-control.d.ts +14 -0
- package/cjs/utils/flood-control.js +65 -0
- package/cjs/utils/flood-control.js.map +1 -0
- package/cjs/utils/function-utils.d.ts +13 -0
- package/cjs/utils/function-utils.js +30 -0
- package/cjs/utils/function-utils.js.map +1 -0
- package/cjs/utils/index.d.ts +24 -0
- package/cjs/utils/index.js +41 -0
- package/cjs/utils/index.js.map +1 -0
- package/cjs/utils/linked-list.d.ts +21 -0
- package/cjs/utils/linked-list.js +73 -0
- package/cjs/utils/linked-list.js.map +1 -0
- package/cjs/utils/links/bots.d.ts +71 -0
- package/cjs/utils/links/bots.js +259 -0
- package/cjs/utils/links/bots.js.map +1 -0
- package/cjs/utils/links/bundle.d.ts +7 -0
- package/cjs/utils/links/bundle.js +24 -0
- package/cjs/utils/links/bundle.js.map +1 -0
- package/cjs/utils/links/calls.d.ts +16 -0
- package/cjs/utils/links/calls.js +59 -0
- package/cjs/utils/links/calls.js.map +1 -0
- package/cjs/utils/links/chat.d.ts +46 -0
- package/cjs/utils/links/chat.js +160 -0
- package/cjs/utils/links/chat.js.map +1 -0
- package/cjs/utils/links/common.d.ts +31 -0
- package/cjs/utils/links/common.js +60 -0
- package/cjs/utils/links/common.js.map +1 -0
- package/cjs/utils/links/index.d.ts +2 -0
- package/cjs/utils/links/index.js +29 -0
- package/cjs/utils/links/index.js.map +1 -0
- package/cjs/utils/links/misc.d.ts +19 -0
- package/cjs/utils/links/misc.js +77 -0
- package/cjs/utils/links/misc.js.map +1 -0
- package/cjs/utils/links/proxy.d.ts +17 -0
- package/cjs/utils/links/proxy.js +61 -0
- package/cjs/utils/links/proxy.js.map +1 -0
- package/cjs/utils/links/stickers.d.ts +9 -0
- package/cjs/utils/links/stickers.js +28 -0
- package/cjs/utils/links/stickers.js.map +1 -0
- package/cjs/utils/links/user.d.ts +27 -0
- package/cjs/utils/links/user.js +81 -0
- package/cjs/utils/links/user.js.map +1 -0
- package/cjs/utils/logger.d.ts +54 -0
- package/cjs/utils/logger.js +158 -0
- package/cjs/utils/logger.js.map +1 -0
- package/cjs/utils/long-utils.d.ts +75 -0
- package/cjs/utils/long-utils.js +161 -0
- package/cjs/utils/long-utils.js.map +1 -0
- package/cjs/utils/lru-map.d.ts +21 -0
- package/cjs/utils/lru-map.js +107 -0
- package/cjs/utils/lru-map.js.map +1 -0
- package/cjs/utils/lru-set.d.ts +22 -0
- package/cjs/utils/lru-set.js +48 -0
- package/cjs/utils/lru-set.js.map +1 -0
- package/cjs/utils/misc-utils.d.ts +7 -0
- package/cjs/utils/misc-utils.js +15 -0
- package/cjs/utils/misc-utils.js.map +1 -0
- package/cjs/utils/peer-utils.d.ts +35 -0
- package/cjs/utils/peer-utils.js +188 -0
- package/cjs/utils/peer-utils.js.map +1 -0
- package/cjs/utils/platform/crypto.d.ts +1 -0
- package/cjs/utils/platform/crypto.js +8 -0
- package/cjs/utils/platform/crypto.js.map +1 -0
- package/cjs/utils/platform/crypto.web.d.ts +1 -0
- package/cjs/utils/platform/crypto.web.js +14 -0
- package/cjs/utils/platform/crypto.web.js.map +1 -0
- package/cjs/utils/platform/error-reporting.d.ts +3 -0
- package/cjs/utils/platform/error-reporting.js +22 -0
- package/cjs/utils/platform/error-reporting.js.map +1 -0
- package/cjs/utils/platform/logging.d.ts +1 -0
- package/cjs/utils/platform/logging.js +35 -0
- package/cjs/utils/platform/logging.js.map +1 -0
- package/cjs/utils/platform/logging.web.d.ts +1 -0
- package/cjs/utils/platform/logging.web.js +35 -0
- package/cjs/utils/platform/logging.web.js.map +1 -0
- package/cjs/utils/platform/random.d.ts +1 -0
- package/cjs/utils/platform/random.js +7 -0
- package/cjs/utils/platform/random.js.map +1 -0
- package/cjs/utils/platform/random.web.d.ts +1 -0
- package/cjs/utils/platform/random.web.js +10 -0
- package/cjs/utils/platform/random.web.js.map +1 -0
- package/cjs/utils/platform/transport.d.ts +1 -0
- package/cjs/utils/platform/transport.js +8 -0
- package/cjs/utils/platform/transport.js.map +1 -0
- package/cjs/utils/platform/transport.web.d.ts +1 -0
- package/cjs/utils/platform/transport.web.js +15 -0
- package/cjs/utils/platform/transport.web.js.map +1 -0
- package/cjs/utils/sorted-array.d.ts +17 -0
- package/cjs/utils/sorted-array.js +83 -0
- package/cjs/utils/sorted-array.js.map +1 -0
- package/cjs/utils/string-session.d.ts +11 -0
- package/cjs/utils/string-session.js +73 -0
- package/cjs/utils/string-session.js.map +1 -0
- package/cjs/utils/tl-json.d.ts +13 -0
- package/cjs/utils/tl-json.js +62 -0
- package/cjs/utils/tl-json.js.map +1 -0
- package/cjs/utils/type-assertions.d.ts +37 -0
- package/cjs/utils/type-assertions.js +70 -0
- package/cjs/utils/type-assertions.js.map +1 -0
- package/esm/base-client.d.ts +297 -0
- package/esm/base-client.js +317 -0
- package/esm/base-client.js.map +1 -0
- package/esm/index.d.ts +10 -0
- package/esm/index.js +11 -0
- package/esm/index.js.map +1 -0
- package/esm/network/auth-key.d.ts +21 -0
- package/esm/network/auth-key.js +97 -0
- package/esm/network/auth-key.js.map +1 -0
- package/esm/network/authorization.d.ts +9 -0
- package/esm/network/authorization.js +325 -0
- package/esm/network/authorization.js.map +1 -0
- package/esm/network/config-manager.d.ts +33 -0
- package/esm/network/config-manager.js +89 -0
- package/esm/network/config-manager.js.map +1 -0
- package/esm/network/index.d.ts +4 -0
- package/esm/network/index.js +4 -0
- package/esm/network/index.js.map +1 -0
- package/esm/network/mtproto-session.d.ts +116 -0
- package/esm/network/mtproto-session.js +202 -0
- package/esm/network/mtproto-session.js.map +1 -0
- package/esm/network/multi-session-connection.d.ts +33 -0
- package/esm/network/multi-session-connection.js +236 -0
- package/esm/network/multi-session-connection.js.map +1 -0
- package/esm/network/network-manager.d.ts +190 -0
- package/esm/network/network-manager.js +542 -0
- package/esm/network/network-manager.js.map +1 -0
- package/esm/network/persistent-connection.d.ts +51 -0
- package/esm/network/persistent-connection.js +166 -0
- package/esm/network/persistent-connection.js.map +1 -0
- package/esm/network/reconnection.d.ts +11 -0
- package/esm/network/reconnection.js +14 -0
- package/esm/network/reconnection.js.map +1 -0
- package/esm/network/session-connection.d.ts +72 -0
- package/esm/network/session-connection.js +1356 -0
- package/esm/network/session-connection.js.map +1 -0
- package/esm/network/transports/abstract.d.ts +99 -0
- package/esm/network/transports/abstract.js +34 -0
- package/esm/network/transports/abstract.js.map +1 -0
- package/esm/network/transports/index.d.ts +8 -0
- package/esm/network/transports/index.js +9 -0
- package/esm/network/transports/index.js.map +1 -0
- package/esm/network/transports/intermediate.d.ts +20 -0
- package/esm/network/transports/intermediate.js +62 -0
- package/esm/network/transports/intermediate.js.map +1 -0
- package/esm/network/transports/obfuscated.d.ts +18 -0
- package/esm/network/transports/obfuscated.js +79 -0
- package/esm/network/transports/obfuscated.js.map +1 -0
- package/esm/network/transports/streamed.d.ts +24 -0
- package/esm/network/transports/streamed.js +25 -0
- package/esm/network/transports/streamed.js.map +1 -0
- package/esm/network/transports/tcp.d.ts +33 -0
- package/esm/network/transports/tcp.js +122 -0
- package/esm/network/transports/tcp.js.map +1 -0
- package/esm/network/transports/websocket.d.ts +47 -0
- package/esm/network/transports/websocket.js +116 -0
- package/esm/network/transports/websocket.js.map +1 -0
- package/esm/network/transports/wrapped.d.ts +11 -0
- package/esm/network/transports/wrapped.js +19 -0
- package/esm/network/transports/wrapped.js.map +1 -0
- package/esm/storage/abstract.d.ts +174 -0
- package/esm/storage/abstract.js +2 -0
- package/esm/storage/abstract.js.map +1 -0
- package/esm/storage/index.d.ts +5 -0
- package/esm/storage/index.js +6 -0
- package/esm/storage/index.js.map +1 -0
- package/esm/storage/json-file.d.ts +31 -0
- package/esm/storage/json-file.js +63 -0
- package/esm/storage/json-file.js.map +1 -0
- package/esm/storage/json.d.ts +9 -0
- package/esm/storage/json.js +61 -0
- package/esm/storage/json.js.map +1 -0
- package/esm/storage/localstorage.d.ts +7 -0
- package/esm/storage/localstorage.js +21 -0
- package/esm/storage/localstorage.js.map +1 -0
- package/esm/storage/memory.d.ts +98 -0
- package/esm/storage/memory.js +295 -0
- package/esm/storage/memory.js.map +1 -0
- package/esm/types/errors.d.ts +53 -0
- package/esm/types/errors.js +49 -0
- package/esm/types/errors.js.map +1 -0
- package/esm/types/index.d.ts +3 -0
- package/esm/types/index.js +4 -0
- package/esm/types/index.js.map +1 -0
- package/esm/types/peers.d.ts +4 -0
- package/esm/types/peers.js +2 -0
- package/esm/types/peers.js.map +1 -0
- package/esm/types/utils.d.ts +6 -0
- package/esm/types/utils.js +5 -0
- package/esm/types/utils.js.map +1 -0
- package/esm/utils/async-lock.d.ts +10 -0
- package/esm/utils/async-lock.js +39 -0
- package/esm/utils/async-lock.js.map +1 -0
- package/esm/utils/bigint-utils.d.ts +40 -0
- package/esm/utils/bigint-utils.js +92 -0
- package/esm/utils/bigint-utils.js.map +1 -0
- package/esm/utils/binary/asn1-parser.d.ts +17 -0
- package/esm/utils/binary/asn1-parser.js +116 -0
- package/esm/utils/binary/asn1-parser.js.map +1 -0
- package/esm/utils/buffer-utils.d.ts +21 -0
- package/esm/utils/buffer-utils.js +55 -0
- package/esm/utils/buffer-utils.js.map +1 -0
- package/esm/utils/condition-variable.d.ts +9 -0
- package/esm/utils/condition-variable.js +24 -0
- package/esm/utils/condition-variable.js.map +1 -0
- package/esm/utils/controllable-promise.d.ts +16 -0
- package/esm/utils/controllable-promise.js +20 -0
- package/esm/utils/controllable-promise.js.map +1 -0
- package/esm/utils/crypto/abstract.d.ts +23 -0
- package/esm/utils/crypto/abstract.js +15 -0
- package/esm/utils/crypto/abstract.js.map +1 -0
- package/esm/utils/crypto/common.d.ts +12 -0
- package/esm/utils/crypto/common.js +50 -0
- package/esm/utils/crypto/common.js.map +1 -0
- package/esm/utils/crypto/factorization.d.ts +5 -0
- package/esm/utils/crypto/factorization.js +62 -0
- package/esm/utils/crypto/factorization.js.map +1 -0
- package/esm/utils/crypto/index.d.ts +5 -0
- package/esm/utils/crypto/index.js +6 -0
- package/esm/utils/crypto/index.js.map +1 -0
- package/esm/utils/crypto/keys.d.ts +27 -0
- package/esm/utils/crypto/keys.js +64 -0
- package/esm/utils/crypto/keys.js.map +1 -0
- package/esm/utils/crypto/miller-rabin.d.ts +2 -0
- package/esm/utils/crypto/miller-rabin.js +37 -0
- package/esm/utils/crypto/miller-rabin.js.map +1 -0
- package/esm/utils/crypto/mtproto.d.ts +31 -0
- package/esm/utils/crypto/mtproto.js +60 -0
- package/esm/utils/crypto/mtproto.js.map +1 -0
- package/esm/utils/crypto/node-crypto.d.ts +10 -0
- package/esm/utils/crypto/node-crypto.js +42 -0
- package/esm/utils/crypto/node-crypto.js.map +1 -0
- package/esm/utils/crypto/password.d.ts +29 -0
- package/esm/utils/crypto/password.js +97 -0
- package/esm/utils/crypto/password.js.map +1 -0
- package/esm/utils/crypto/subtle.d.ts +12 -0
- package/esm/utils/crypto/subtle.js +59 -0
- package/esm/utils/crypto/subtle.js.map +1 -0
- package/esm/utils/crypto/utils.d.ts +14 -0
- package/esm/utils/crypto/utils.js +25 -0
- package/esm/utils/crypto/utils.js.map +1 -0
- package/esm/utils/default-dcs.d.ts +7 -0
- package/esm/utils/default-dcs.js +73 -0
- package/esm/utils/default-dcs.js.map +1 -0
- package/esm/utils/deque.d.ts +29 -0
- package/esm/utils/deque.js +206 -0
- package/esm/utils/deque.js.map +1 -0
- package/esm/utils/early-timer.d.ts +42 -0
- package/esm/utils/early-timer.js +75 -0
- package/esm/utils/early-timer.js.map +1 -0
- package/esm/utils/flood-control.d.ts +14 -0
- package/esm/utils/flood-control.js +61 -0
- package/esm/utils/flood-control.js.map +1 -0
- package/esm/utils/function-utils.d.ts +13 -0
- package/esm/utils/function-utils.js +26 -0
- package/esm/utils/function-utils.js.map +1 -0
- package/esm/utils/index.d.ts +24 -0
- package/esm/utils/index.js +25 -0
- package/esm/utils/index.js.map +1 -0
- package/esm/utils/linked-list.d.ts +21 -0
- package/esm/utils/linked-list.js +69 -0
- package/esm/utils/linked-list.js.map +1 -0
- package/esm/utils/links/bots.d.ts +71 -0
- package/esm/utils/links/bots.js +256 -0
- package/esm/utils/links/bots.js.map +1 -0
- package/esm/utils/links/bundle.d.ts +7 -0
- package/esm/utils/links/bundle.js +8 -0
- package/esm/utils/links/bundle.js.map +1 -0
- package/esm/utils/links/calls.d.ts +16 -0
- package/esm/utils/links/calls.js +56 -0
- package/esm/utils/links/calls.js.map +1 -0
- package/esm/utils/links/chat.d.ts +46 -0
- package/esm/utils/links/chat.js +157 -0
- package/esm/utils/links/chat.js.map +1 -0
- package/esm/utils/links/common.d.ts +31 -0
- package/esm/utils/links/common.js +56 -0
- package/esm/utils/links/common.js.map +1 -0
- package/esm/utils/links/index.d.ts +2 -0
- package/esm/utils/links/index.js +3 -0
- package/esm/utils/links/index.js.map +1 -0
- package/esm/utils/links/misc.d.ts +19 -0
- package/esm/utils/links/misc.js +74 -0
- package/esm/utils/links/misc.js.map +1 -0
- package/esm/utils/links/proxy.d.ts +17 -0
- package/esm/utils/links/proxy.js +58 -0
- package/esm/utils/links/proxy.js.map +1 -0
- package/esm/utils/links/stickers.d.ts +9 -0
- package/esm/utils/links/stickers.js +25 -0
- package/esm/utils/links/stickers.js.map +1 -0
- package/esm/utils/links/user.d.ts +27 -0
- package/esm/utils/links/user.js +78 -0
- package/esm/utils/links/user.js.map +1 -0
- package/esm/utils/logger.d.ts +54 -0
- package/esm/utils/logger.js +154 -0
- package/esm/utils/logger.js.map +1 -0
- package/esm/utils/long-utils.d.ts +75 -0
- package/esm/utils/long-utils.js +148 -0
- package/esm/utils/long-utils.js.map +1 -0
- package/esm/utils/lru-map.d.ts +21 -0
- package/esm/utils/lru-map.js +103 -0
- package/esm/utils/lru-map.js.map +1 -0
- package/esm/utils/lru-set.d.ts +22 -0
- package/esm/utils/lru-set.js +44 -0
- package/esm/utils/lru-set.js.map +1 -0
- package/esm/utils/misc-utils.d.ts +7 -0
- package/esm/utils/misc-utils.js +10 -0
- package/esm/utils/misc-utils.js.map +1 -0
- package/esm/utils/peer-utils.d.ts +35 -0
- package/esm/utils/peer-utils.js +179 -0
- package/esm/utils/peer-utils.js.map +1 -0
- package/esm/utils/platform/crypto.d.ts +1 -0
- package/esm/utils/platform/crypto.js +4 -0
- package/esm/utils/platform/crypto.js.map +1 -0
- package/esm/utils/platform/crypto.web.d.ts +1 -0
- package/esm/utils/platform/crypto.web.js +10 -0
- package/esm/utils/platform/crypto.web.js.map +1 -0
- package/esm/utils/platform/error-reporting.d.ts +3 -0
- package/esm/utils/platform/error-reporting.js +18 -0
- package/esm/utils/platform/error-reporting.js.map +1 -0
- package/esm/utils/platform/logging.d.ts +1 -0
- package/esm/utils/platform/logging.js +32 -0
- package/esm/utils/platform/logging.js.map +1 -0
- package/esm/utils/platform/logging.web.d.ts +1 -0
- package/esm/utils/platform/logging.web.js +31 -0
- package/esm/utils/platform/logging.web.js.map +1 -0
- package/esm/utils/platform/random.d.ts +1 -0
- package/esm/utils/platform/random.js +4 -0
- package/esm/utils/platform/random.js.map +1 -0
- package/esm/utils/platform/random.web.d.ts +1 -0
- package/esm/utils/platform/random.web.js +6 -0
- package/esm/utils/platform/random.web.js.map +1 -0
- package/esm/utils/platform/transport.d.ts +1 -0
- package/esm/utils/platform/transport.js +4 -0
- package/esm/utils/platform/transport.js.map +1 -0
- package/esm/utils/platform/transport.web.d.ts +1 -0
- package/esm/utils/platform/transport.web.js +12 -0
- package/esm/utils/platform/transport.web.js.map +1 -0
- package/esm/utils/sorted-array.d.ts +17 -0
- package/esm/utils/sorted-array.js +79 -0
- package/esm/utils/sorted-array.js.map +1 -0
- package/esm/utils/string-session.d.ts +11 -0
- package/esm/utils/string-session.js +68 -0
- package/esm/utils/string-session.js.map +1 -0
- package/esm/utils/tl-json.d.ts +13 -0
- package/esm/utils/tl-json.js +57 -0
- package/esm/utils/tl-json.js.map +1 -0
- package/esm/utils/type-assertions.d.ts +37 -0
- package/esm/utils/type-assertions.js +61 -0
- package/esm/utils/type-assertions.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,1356 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
// will be reworked in MTQ-32
|
|
3
|
+
import Long from 'long';
|
|
4
|
+
import { tl } from '@mtcute/tl';
|
|
5
|
+
import { gzipDeflate, gzipInflate, TlBinaryReader, TlBinaryWriter, TlSerializationCounter, } from '@mtcute/tl-runtime';
|
|
6
|
+
import { MtArgumentError, MtcuteError, MtTimeoutError } from '../types/index.js';
|
|
7
|
+
import { createAesIgeForMessageOld } from '../utils/crypto/mtproto.js';
|
|
8
|
+
import { concatBuffers, createControllablePromise, EarlyTimer, longFromBuffer, randomBytes, randomLong, removeFromLongArray, } from '../utils/index.js';
|
|
9
|
+
import { reportUnknownError } from '../utils/platform/error-reporting.js';
|
|
10
|
+
import { doAuthorization } from './authorization.js';
|
|
11
|
+
import { PersistentConnection } from './persistent-connection.js';
|
|
12
|
+
import { TransportError } from './transports/abstract.js';
|
|
13
|
+
const TEMP_AUTH_KEY_EXPIRY = 86400;
|
|
14
|
+
// destroy_auth_key#d1435160 = DestroyAuthKeyRes;
|
|
15
|
+
// const DESTROY_AUTH_KEY = Buffer.from('605134d1', 'hex')
|
|
16
|
+
function makeNiceStack(error, stack, method) {
|
|
17
|
+
error.stack = `RpcError (${error.code} ${error.text}): ${error.message}\n at ${method}\n${stack
|
|
18
|
+
.split('\n')
|
|
19
|
+
.slice(2)
|
|
20
|
+
.join('\n')}`;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A connection to a single DC.
|
|
24
|
+
*/
|
|
25
|
+
export class SessionConnection extends PersistentConnection {
|
|
26
|
+
constructor(params, _session) {
|
|
27
|
+
super(params, _session.log.create('conn'));
|
|
28
|
+
this._session = _session;
|
|
29
|
+
this._flushTimer = new EarlyTimer();
|
|
30
|
+
this._queuedDestroySession = [];
|
|
31
|
+
// waitForMessage
|
|
32
|
+
this._pendingWaitForUnencrypted = [];
|
|
33
|
+
this._usePfs = this.params.usePfs ?? false;
|
|
34
|
+
this._isPfsBindingPending = false;
|
|
35
|
+
this._isPfsBindingPendingInBackground = false;
|
|
36
|
+
this._inactivityPendingFlush = false;
|
|
37
|
+
this._flushTimer.onTimeout(this._flush.bind(this));
|
|
38
|
+
this._readerMap = params.readerMap;
|
|
39
|
+
this._writerMap = params.writerMap;
|
|
40
|
+
this._handleRawMessage = this._handleRawMessage.bind(this);
|
|
41
|
+
}
|
|
42
|
+
getAuthKey(temp = false) {
|
|
43
|
+
const key = temp ? this._session._authKeyTemp : this._session._authKey;
|
|
44
|
+
if (!key.ready)
|
|
45
|
+
return null;
|
|
46
|
+
return key.key;
|
|
47
|
+
}
|
|
48
|
+
setUsePfs(usePfs) {
|
|
49
|
+
if (this._usePfs === usePfs)
|
|
50
|
+
return;
|
|
51
|
+
this.log.debug('use pfs changed to %s', usePfs);
|
|
52
|
+
this._usePfs = usePfs;
|
|
53
|
+
if (!usePfs) {
|
|
54
|
+
this._isPfsBindingPending = false;
|
|
55
|
+
this._isPfsBindingPendingInBackground = false;
|
|
56
|
+
this._session._authKeyTemp.reset();
|
|
57
|
+
clearTimeout(this._pfsUpdateTimeout);
|
|
58
|
+
}
|
|
59
|
+
this._resetSession();
|
|
60
|
+
}
|
|
61
|
+
onTransportClose() {
|
|
62
|
+
super.onTransportClose();
|
|
63
|
+
Object.values(this._pendingWaitForUnencrypted).forEach(([prom, timeout]) => {
|
|
64
|
+
prom.reject(new MtcuteError('Connection closed'));
|
|
65
|
+
clearTimeout(timeout);
|
|
66
|
+
});
|
|
67
|
+
this.emit('disconnect');
|
|
68
|
+
this.reset();
|
|
69
|
+
}
|
|
70
|
+
destroy() {
|
|
71
|
+
super.destroy();
|
|
72
|
+
this.reset(true);
|
|
73
|
+
}
|
|
74
|
+
reset(forever = false) {
|
|
75
|
+
this._session.initConnectionCalled = false;
|
|
76
|
+
this._flushTimer.reset();
|
|
77
|
+
if (forever) {
|
|
78
|
+
this.removeAllListeners();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
onConnected() {
|
|
82
|
+
// check if we have all the needed keys
|
|
83
|
+
if (!this._session._authKey.ready) {
|
|
84
|
+
if (!this.params.isMainConnection) {
|
|
85
|
+
this.log.info('no auth key, waiting for main connection');
|
|
86
|
+
// once it is done, we will be notified
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
this.log.info('no perm auth key, authorizing...');
|
|
90
|
+
this._authorize();
|
|
91
|
+
// if we use pfs, we *could* also start temp key exchange here
|
|
92
|
+
// but telegram restricts us to only have one auth session per connection,
|
|
93
|
+
// and having a separate connection for pfs is not worth it
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (this._usePfs && !this._session._authKeyTemp.ready) {
|
|
97
|
+
this.log.info('no temp auth key but using pfs, authorizing');
|
|
98
|
+
this._authorizePfs();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
this.log.info('auth keys are already available');
|
|
102
|
+
this.onConnectionUsable();
|
|
103
|
+
}
|
|
104
|
+
onError(error) {
|
|
105
|
+
// https://core.telegram.org/mtproto/mtproto-_transports#_transport-errors
|
|
106
|
+
if (error instanceof TransportError) {
|
|
107
|
+
if (error.code === 404) {
|
|
108
|
+
// if we are using pfs, this could be due to the server
|
|
109
|
+
// forgetting our temp key (which is kinda weird but expected)
|
|
110
|
+
if (this._usePfs) {
|
|
111
|
+
if (!this._isPfsBindingPending && this._session._authKeyTemp.ready) {
|
|
112
|
+
this.log.info('transport error 404, reauthorizing pfs');
|
|
113
|
+
// this is important! we must reset temp auth key before
|
|
114
|
+
// we proceed with new temp key derivation.
|
|
115
|
+
// otherwise, we can end up in an infinite loop in case it
|
|
116
|
+
// was actually perm_key that got 404-ed
|
|
117
|
+
//
|
|
118
|
+
// if temp key binding is already in process in background,
|
|
119
|
+
// _authorizePfs will mark it as foreground to prevent new
|
|
120
|
+
// queries from being sent (to avoid even more 404s)
|
|
121
|
+
this._session._authKeyTemp.reset();
|
|
122
|
+
this._authorizePfs();
|
|
123
|
+
this._onAllFailed('temp key expired, binding started');
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
else if (this._isPfsBindingPending) {
|
|
127
|
+
this.log.info('transport error 404, pfs binding in progress');
|
|
128
|
+
this._onAllFailed('temp key expired, binding pending');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
// otherwise, 404 must be referencing the perm_key
|
|
132
|
+
this.log.info('transport error 404, reauthorizing');
|
|
133
|
+
}
|
|
134
|
+
// there happened a little trolling
|
|
135
|
+
this._session.reset(true);
|
|
136
|
+
this.emit('key-change', null);
|
|
137
|
+
this._authorize();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
this.log.error('transport error %d', error.code);
|
|
141
|
+
// all pending queries must be resent
|
|
142
|
+
this._onAllFailed(`transport error ${error.code}`);
|
|
143
|
+
if (error.code === 429) {
|
|
144
|
+
this._session.onTransportFlood(this.emit.bind(this, 'flood-done'));
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
this.emit('error', error);
|
|
149
|
+
}
|
|
150
|
+
onConnectionUsable() {
|
|
151
|
+
super.onConnectionUsable();
|
|
152
|
+
if (this.params.withUpdates) {
|
|
153
|
+
// we must send some user-related rpc to the server to make sure that
|
|
154
|
+
// it will send us updates
|
|
155
|
+
this.sendRpc({ _: 'updates.getState' }).catch((err) => {
|
|
156
|
+
this.log.warn('failed to send updates.getState: %s', err.text || err.message);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
// just in case
|
|
160
|
+
this._flushTimer.emitBeforeNext(1000);
|
|
161
|
+
}
|
|
162
|
+
_authorize() {
|
|
163
|
+
if (this._session.authorizationPending) {
|
|
164
|
+
this.log.info('_authorize(): authorization already in progress');
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (!this.params.isMainConnection) {
|
|
168
|
+
// we don't authorize on non-main connections
|
|
169
|
+
this.log.debug('_authorize(): non-main connection, requesting...');
|
|
170
|
+
this.emit('request-auth');
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
this._session.authorizationPending = true;
|
|
174
|
+
this.emit('auth-begin');
|
|
175
|
+
doAuthorization(this, this.params.crypto)
|
|
176
|
+
.then(async ([authKey, serverSalt, timeOffset]) => {
|
|
177
|
+
await this._session._authKey.setup(authKey);
|
|
178
|
+
this._session.serverSalt = serverSalt;
|
|
179
|
+
this._session._timeOffset = timeOffset;
|
|
180
|
+
this._session.authorizationPending = false;
|
|
181
|
+
this.emit('key-change', authKey);
|
|
182
|
+
if (this._usePfs) {
|
|
183
|
+
return this._authorizePfs();
|
|
184
|
+
}
|
|
185
|
+
this.onConnectionUsable();
|
|
186
|
+
})
|
|
187
|
+
.catch((err) => {
|
|
188
|
+
this._session.authorizationPending = false;
|
|
189
|
+
this.log.error('Authorization error: %s', err.message);
|
|
190
|
+
this.onError(err);
|
|
191
|
+
this.reconnect();
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
_authorizePfs(background = false) {
|
|
195
|
+
if (this._isPfsBindingPending)
|
|
196
|
+
return;
|
|
197
|
+
if (this._pfsUpdateTimeout) {
|
|
198
|
+
clearTimeout(this._pfsUpdateTimeout);
|
|
199
|
+
this._pfsUpdateTimeout = undefined;
|
|
200
|
+
}
|
|
201
|
+
if (this._isPfsBindingPendingInBackground) {
|
|
202
|
+
// e.g. temp key has expired while we were binding a key in the background
|
|
203
|
+
// in this case, we shouldn't start pfs binding again, and instead wait for
|
|
204
|
+
// current operation to complete
|
|
205
|
+
this._isPfsBindingPendingInBackground = false;
|
|
206
|
+
this._isPfsBindingPending = true;
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (background) {
|
|
210
|
+
this._isPfsBindingPendingInBackground = true;
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
this._isPfsBindingPending = true;
|
|
214
|
+
}
|
|
215
|
+
doAuthorization(this, this.params.crypto, TEMP_AUTH_KEY_EXPIRY)
|
|
216
|
+
.then(async ([tempAuthKey, tempServerSalt]) => {
|
|
217
|
+
if (!this._usePfs) {
|
|
218
|
+
this.log.info('pfs has been disabled while generating temp key');
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const tempKey = this._session._authKeyTempSecondary;
|
|
222
|
+
await tempKey.setup(tempAuthKey);
|
|
223
|
+
const msgId = this._session.getMessageId();
|
|
224
|
+
this.log.debug('binding temp_auth_key (%h) to perm_auth_key (%h), msg_id = %l...', tempKey.id, this._session._authKey.id, msgId);
|
|
225
|
+
// we now need to bind the key
|
|
226
|
+
const inner = {
|
|
227
|
+
_: 'mt_bind_auth_key_inner',
|
|
228
|
+
nonce: randomLong(),
|
|
229
|
+
tempAuthKeyId: longFromBuffer(tempKey.id),
|
|
230
|
+
permAuthKeyId: longFromBuffer(this._session._authKey.id),
|
|
231
|
+
tempSessionId: this._session._sessionId,
|
|
232
|
+
expiresAt: Math.floor(Date.now() / 1000) + TEMP_AUTH_KEY_EXPIRY,
|
|
233
|
+
};
|
|
234
|
+
// encrypt using mtproto v1 (fucking kill me plz)
|
|
235
|
+
const writer = TlBinaryWriter.alloc(this.params.writerMap, 80);
|
|
236
|
+
// = 40 (inner length) + 32 (mtproto header) + 8 (pad 72 so mod 16 = 0)
|
|
237
|
+
writer.raw(randomBytes(16));
|
|
238
|
+
writer.long(msgId);
|
|
239
|
+
writer.int(0); // seq_no
|
|
240
|
+
writer.int(40); // msg_len
|
|
241
|
+
writer.object(inner);
|
|
242
|
+
const msgWithoutPadding = writer.result();
|
|
243
|
+
writer.raw(randomBytes(8));
|
|
244
|
+
const msgWithPadding = writer.result();
|
|
245
|
+
const hash = await this.params.crypto.sha1(msgWithoutPadding);
|
|
246
|
+
const msgKey = hash.subarray(4, 20);
|
|
247
|
+
const ige = await createAesIgeForMessageOld(this.params.crypto, this._session._authKey.key, msgKey, true);
|
|
248
|
+
const encryptedData = await ige.encrypt(msgWithPadding);
|
|
249
|
+
const encryptedMessage = concatBuffers([this._session._authKey.id, msgKey, encryptedData]);
|
|
250
|
+
const promise = createControllablePromise();
|
|
251
|
+
// encrypt the message using temp key and same msg id
|
|
252
|
+
// this is a bit of a hack, but it works
|
|
253
|
+
//
|
|
254
|
+
// hacking inside main send loop to allow sending
|
|
255
|
+
// with another key is just too much hassle.
|
|
256
|
+
// we could just always use temp key if one is available,
|
|
257
|
+
// but that way we won't be able to refresh the key
|
|
258
|
+
// that is about to expire in the background without
|
|
259
|
+
// interrupting actual message flow
|
|
260
|
+
// decrypting is trivial though, since key id
|
|
261
|
+
// is in the first bytes of the message, and is never used later on.
|
|
262
|
+
this._session.pendingMessages.set(msgId, {
|
|
263
|
+
_: 'bind',
|
|
264
|
+
promise,
|
|
265
|
+
});
|
|
266
|
+
const request = {
|
|
267
|
+
_: 'auth.bindTempAuthKey',
|
|
268
|
+
permAuthKeyId: inner.permAuthKeyId,
|
|
269
|
+
nonce: inner.nonce,
|
|
270
|
+
expiresAt: inner.expiresAt,
|
|
271
|
+
encryptedMessage,
|
|
272
|
+
};
|
|
273
|
+
const reqSize = TlSerializationCounter.countNeededBytes(this._writerMap, request);
|
|
274
|
+
const reqWriter = TlBinaryWriter.alloc(this._writerMap, reqSize + 16);
|
|
275
|
+
reqWriter.long(this._registerOutgoingMsgId(msgId));
|
|
276
|
+
reqWriter.uint(this._session.getSeqNo());
|
|
277
|
+
reqWriter.uint(reqSize);
|
|
278
|
+
reqWriter.object(request);
|
|
279
|
+
// we can now send it as is
|
|
280
|
+
const requestEncrypted = await tempKey.encryptMessage(reqWriter.result(), tempServerSalt, this._session._sessionId);
|
|
281
|
+
await this.send(requestEncrypted);
|
|
282
|
+
const res = await promise;
|
|
283
|
+
this._session.pendingMessages.delete(msgId);
|
|
284
|
+
if (!this._usePfs) {
|
|
285
|
+
this.log.info('pfs has been disabled while binding temp key');
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
if (typeof res === 'object') {
|
|
289
|
+
this.log.error('failed to bind temp key: %s:%s', res.errorCode, res.errorMessage);
|
|
290
|
+
throw new MtcuteError('Failed to bind temporary key');
|
|
291
|
+
}
|
|
292
|
+
// now we can swap the keys (secondary becomes primary,
|
|
293
|
+
// and primary is not immediately forgot because messages using it may still be in flight)
|
|
294
|
+
this._session._authKeyTempSecondary = this._session._authKeyTemp;
|
|
295
|
+
this._session._authKeyTemp = tempKey;
|
|
296
|
+
this._session.serverSalt = tempServerSalt;
|
|
297
|
+
this.log.debug('temp key has been bound, exp = %d', inner.expiresAt);
|
|
298
|
+
this._isPfsBindingPending = false;
|
|
299
|
+
this._isPfsBindingPendingInBackground = false;
|
|
300
|
+
// we must re-init connection after binding temp key
|
|
301
|
+
this._session.initConnectionCalled = false;
|
|
302
|
+
this.emit('tmp-key-change', tempAuthKey, inner.expiresAt);
|
|
303
|
+
this.onConnectionUsable();
|
|
304
|
+
// set a timeout to update temp auth key in advance to avoid interruption
|
|
305
|
+
this._pfsUpdateTimeout = setTimeout(() => {
|
|
306
|
+
this._pfsUpdateTimeout = undefined;
|
|
307
|
+
this.log.debug('temp key is expiring soon');
|
|
308
|
+
this._authorizePfs(true);
|
|
309
|
+
}, (TEMP_AUTH_KEY_EXPIRY - 60) * 1000);
|
|
310
|
+
})
|
|
311
|
+
.catch((err) => {
|
|
312
|
+
this.log.error('PFS Authorization error: %s', err.message);
|
|
313
|
+
if (this._isPfsBindingPendingInBackground) {
|
|
314
|
+
this._isPfsBindingPendingInBackground = false;
|
|
315
|
+
// if we are in background, we can just retry
|
|
316
|
+
return this._authorizePfs(true);
|
|
317
|
+
}
|
|
318
|
+
this._isPfsBindingPending = false;
|
|
319
|
+
this.onError(err);
|
|
320
|
+
this.reconnect();
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
waitForUnencryptedMessage(timeout = 5000) {
|
|
324
|
+
const promise = createControllablePromise();
|
|
325
|
+
const timeoutId = setTimeout(() => {
|
|
326
|
+
promise.reject(new MtTimeoutError(timeout));
|
|
327
|
+
this._pendingWaitForUnencrypted = this._pendingWaitForUnencrypted.filter((it) => it[0] !== promise);
|
|
328
|
+
}, timeout);
|
|
329
|
+
this._pendingWaitForUnencrypted.push([promise, timeoutId]);
|
|
330
|
+
return promise;
|
|
331
|
+
}
|
|
332
|
+
async onMessage(data) {
|
|
333
|
+
if (this._pendingWaitForUnencrypted.length) {
|
|
334
|
+
const int32 = new Int32Array(data.buffer, data.byteOffset, 2);
|
|
335
|
+
if (int32[0] === 0 && int32[1] === 0) {
|
|
336
|
+
// auth_key_id = 0, meaning it's an unencrypted message used for authorization
|
|
337
|
+
const [promise, timeout] = this._pendingWaitForUnencrypted.shift();
|
|
338
|
+
clearTimeout(timeout);
|
|
339
|
+
promise.resolve(data);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (!this._session._authKey.ready) {
|
|
344
|
+
// if a message is received before authorization,
|
|
345
|
+
// either the server is misbehaving,
|
|
346
|
+
// or there was a problem with authorization.
|
|
347
|
+
this.log.warn('received message before authorization: %h', data);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
try {
|
|
351
|
+
await this._session.decryptMessage(data, this._handleRawMessage);
|
|
352
|
+
}
|
|
353
|
+
catch (err) {
|
|
354
|
+
this.log.error('failed to decrypt message: %s\ndata: %h', err, data);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
_handleRawMessage(messageId, seqNo, message) {
|
|
358
|
+
if (message.peekUint() === 0x3072cfa1) {
|
|
359
|
+
// gzip_packed
|
|
360
|
+
// we can't use message.gzip() because it may contain msg_container,
|
|
361
|
+
// so we parse it manually.
|
|
362
|
+
message.uint();
|
|
363
|
+
return this._handleRawMessage(messageId, seqNo, new TlBinaryReader(this._readerMap, gzipInflate(message.bytes())));
|
|
364
|
+
}
|
|
365
|
+
if (message.peekUint() === 0x73f1f8dc) {
|
|
366
|
+
// msg_container
|
|
367
|
+
message.uint();
|
|
368
|
+
const count = message.uint();
|
|
369
|
+
for (let i = 0; i < count; i++) {
|
|
370
|
+
// msg_id:long seqno:int bytes:int
|
|
371
|
+
const msgId = message.long();
|
|
372
|
+
const seqNo = message.uint(); // seqno
|
|
373
|
+
const length = message.uint();
|
|
374
|
+
// container can't contain other containers, but can contain rpc_result
|
|
375
|
+
const obj = message.raw(length);
|
|
376
|
+
this._handleRawMessage(msgId, seqNo, new TlBinaryReader(this._readerMap, obj));
|
|
377
|
+
}
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (message.peekUint() === 0xf35c6d01) {
|
|
381
|
+
// rpc_result
|
|
382
|
+
message.uint();
|
|
383
|
+
return this._onRpcResult(messageId, message);
|
|
384
|
+
}
|
|
385
|
+
// we are safe.. i guess
|
|
386
|
+
this._handleMessage(messageId, message.object());
|
|
387
|
+
}
|
|
388
|
+
_handleMessage(messageId, message_) {
|
|
389
|
+
if (messageId.isEven()) {
|
|
390
|
+
this.log.warn('warn: ignoring message with invalid messageId = %s (is even)', messageId);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (this._session.recentIncomingMsgIds.has(messageId)) {
|
|
394
|
+
this.log.debug('ignoring duplicate message %s', messageId);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const message = message_;
|
|
398
|
+
this.log.debug('received %s (msg_id: %l)', message._, messageId);
|
|
399
|
+
this._session.recentIncomingMsgIds.add(messageId);
|
|
400
|
+
switch (message._) {
|
|
401
|
+
case 'mt_msgs_ack':
|
|
402
|
+
case 'mt_http_wait':
|
|
403
|
+
case 'mt_bad_msg_notification':
|
|
404
|
+
case 'mt_bad_server_salt':
|
|
405
|
+
case 'mt_msgs_all_info':
|
|
406
|
+
case 'mt_msgs_state_info':
|
|
407
|
+
case 'mt_msg_detailed_info':
|
|
408
|
+
case 'mt_msg_new_detailed_info':
|
|
409
|
+
break;
|
|
410
|
+
default:
|
|
411
|
+
this._sendAck(messageId);
|
|
412
|
+
}
|
|
413
|
+
switch (message._) {
|
|
414
|
+
case 'mt_pong':
|
|
415
|
+
this._onPong(message);
|
|
416
|
+
break;
|
|
417
|
+
case 'mt_bad_server_salt':
|
|
418
|
+
this._onBadServerSalt(message);
|
|
419
|
+
break;
|
|
420
|
+
case 'mt_bad_msg_notification':
|
|
421
|
+
this._onBadMsgNotification(messageId, message);
|
|
422
|
+
break;
|
|
423
|
+
case 'mt_msgs_ack':
|
|
424
|
+
message.msgIds.forEach((msgId) => this._onMessageAcked(msgId));
|
|
425
|
+
break;
|
|
426
|
+
case 'mt_new_session_created':
|
|
427
|
+
this._onNewSessionCreated(message);
|
|
428
|
+
break;
|
|
429
|
+
case 'mt_msgs_all_info':
|
|
430
|
+
this._onMessagesInfo(message.msgIds, message.info);
|
|
431
|
+
break;
|
|
432
|
+
case 'mt_msg_detailed_info':
|
|
433
|
+
this._onMessageInfo(message.msgId, message.status, message.answerMsgId);
|
|
434
|
+
break;
|
|
435
|
+
case 'mt_msg_new_detailed_info':
|
|
436
|
+
this._onMessageInfo(Long.ZERO, 0, message.answerMsgId);
|
|
437
|
+
break;
|
|
438
|
+
case 'mt_msgs_state_info':
|
|
439
|
+
this._onMsgsStateInfo(message);
|
|
440
|
+
break;
|
|
441
|
+
case 'mt_future_salts':
|
|
442
|
+
// todo
|
|
443
|
+
break;
|
|
444
|
+
case 'mt_msgs_state_req':
|
|
445
|
+
case 'mt_msg_resend_req':
|
|
446
|
+
// tdlib doesnt handle them, so why should we? :upside_down_face:
|
|
447
|
+
this.log.warn('received %s (msg_id = %l): %j', message._, messageId, message);
|
|
448
|
+
break;
|
|
449
|
+
case 'mt_destroy_session_ok':
|
|
450
|
+
case 'mt_destroy_session_none':
|
|
451
|
+
this._onDestroySessionResult(message);
|
|
452
|
+
break;
|
|
453
|
+
default:
|
|
454
|
+
if (tl.isAnyUpdates(message)) {
|
|
455
|
+
if (this._usable && this.params.inactivityTimeout) {
|
|
456
|
+
this._rescheduleInactivity();
|
|
457
|
+
}
|
|
458
|
+
this.log.verbose('<<< %j', message);
|
|
459
|
+
if (this.params.disableUpdates) {
|
|
460
|
+
this.log.warn('received updates, but updates are disabled');
|
|
461
|
+
// likely due to some request in the session missing invokeWithoutUpdates
|
|
462
|
+
break;
|
|
463
|
+
}
|
|
464
|
+
this.emit('update', message);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
this.log.warn('unknown message received: %j', message);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
_onRpcResult(messageId, message) {
|
|
471
|
+
if (this._usable && this.params.inactivityTimeout) {
|
|
472
|
+
this._rescheduleInactivity();
|
|
473
|
+
}
|
|
474
|
+
const reqMsgId = message.long();
|
|
475
|
+
if (reqMsgId.isZero()) {
|
|
476
|
+
let resultType;
|
|
477
|
+
try {
|
|
478
|
+
// eslint-disable-next-line
|
|
479
|
+
resultType = message.object()._;
|
|
480
|
+
}
|
|
481
|
+
catch (err) {
|
|
482
|
+
resultType = message.peekUint();
|
|
483
|
+
}
|
|
484
|
+
this.log.warn('received rpc_result with %j with req_msg_id = 0', resultType);
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
const msg = this._session.pendingMessages.get(reqMsgId);
|
|
488
|
+
if (!msg) {
|
|
489
|
+
let result;
|
|
490
|
+
try {
|
|
491
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
492
|
+
result = message.object();
|
|
493
|
+
}
|
|
494
|
+
catch (err) {
|
|
495
|
+
result = '[failed to parse]';
|
|
496
|
+
}
|
|
497
|
+
// check if the msg is one of the recent ones
|
|
498
|
+
if (this._session.recentOutgoingMsgIds.has(reqMsgId)) {
|
|
499
|
+
this.log.debug('received rpc_result again for %l (contains %j)', reqMsgId, result);
|
|
500
|
+
}
|
|
501
|
+
else {
|
|
502
|
+
this.log.warn('received rpc_result for unknown message %l: %j', reqMsgId, result);
|
|
503
|
+
}
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
this._sendAck(messageId);
|
|
507
|
+
// special case for auth key binding
|
|
508
|
+
if (msg._ !== 'rpc') {
|
|
509
|
+
if (msg._ === 'bind') {
|
|
510
|
+
this._sendAck(messageId);
|
|
511
|
+
msg.promise.resolve(message.object());
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
if (msg._ === 'cancel') {
|
|
515
|
+
let result;
|
|
516
|
+
try {
|
|
517
|
+
result = message.object();
|
|
518
|
+
}
|
|
519
|
+
catch (err) {
|
|
520
|
+
this.log.debug('failed to parse rpc_result for cancel request %l, ignoring', reqMsgId);
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
this.log.debug('received %s for cancelled request %l: %j', result._, reqMsgId);
|
|
524
|
+
this._onMessageAcked(reqMsgId);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
this.log.error('received rpc_result for %s request %l', msg._, reqMsgId);
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
const rpc = msg.rpc;
|
|
531
|
+
const customReader = this._readerMap._results[rpc.method];
|
|
532
|
+
const result = customReader ? customReader(message) : message.object();
|
|
533
|
+
// initConnection call was definitely received and
|
|
534
|
+
// processed by the server, so we no longer need to use it
|
|
535
|
+
if (rpc.initConn) {
|
|
536
|
+
this._session.initConnectionCalled = true;
|
|
537
|
+
}
|
|
538
|
+
rpc.done = true;
|
|
539
|
+
this.log.verbose('<<< (%s) %j', rpc.method, result);
|
|
540
|
+
if (result._ === 'mt_rpc_error') {
|
|
541
|
+
const res = result;
|
|
542
|
+
this.log.debug('received rpc_error [%d:%s] for %l (%s)', res.errorCode, res.errorMessage, reqMsgId, rpc.method);
|
|
543
|
+
if (res.errorMessage === 'AUTH_KEY_PERM_EMPTY') {
|
|
544
|
+
// happens when temp auth key is not yet bound
|
|
545
|
+
// this shouldn't happen as we block any outbound communications
|
|
546
|
+
// until the temp key is derived and bound.
|
|
547
|
+
//
|
|
548
|
+
// i think it is also possible for the error to be returned
|
|
549
|
+
// when the temp key has expired, but this still shouldn't happen
|
|
550
|
+
// but this is tg, so something may go wrong, and we will receive this as an error
|
|
551
|
+
// (for god's sake why is this not in mtproto and instead hacked into the app layer)
|
|
552
|
+
this._authorizePfs();
|
|
553
|
+
this._onMessageFailed(reqMsgId, 'AUTH_KEY_PERM_EMPTY', true);
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
if (res.errorMessage === 'CONNECTION_NOT_INITED') {
|
|
557
|
+
// this seems to sometimes happen when using pfs
|
|
558
|
+
// no idea why, but tdlib also seems to handle these, so whatever
|
|
559
|
+
this._session.initConnectionCalled = false;
|
|
560
|
+
this._onMessageFailed(reqMsgId, res.errorMessage, true);
|
|
561
|
+
// just setting this flag is not enough because the message
|
|
562
|
+
// is already serialized, so we do this awesome hack
|
|
563
|
+
this.sendRpc({ _: 'help.getNearestDc' })
|
|
564
|
+
.then(() => {
|
|
565
|
+
this.log.debug('additional help.getNearestDc for initConnection ok');
|
|
566
|
+
})
|
|
567
|
+
.catch((err) => {
|
|
568
|
+
this.log.debug('additional help.getNearestDc for initConnection error: %s', err);
|
|
569
|
+
});
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
if (rpc.cancelled)
|
|
573
|
+
return;
|
|
574
|
+
const error = tl.RpcError.fromTl(res);
|
|
575
|
+
if (this.params.niceStacks !== false) {
|
|
576
|
+
makeNiceStack(error, rpc.stack, rpc.method);
|
|
577
|
+
}
|
|
578
|
+
if (error.unknown && this.params.enableErrorReporting) {
|
|
579
|
+
reportUnknownError(this.log, error, rpc.method);
|
|
580
|
+
}
|
|
581
|
+
rpc.promise.reject(error);
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
this.log.debug('received rpc_result (%s) for request %l (%s)', result._, reqMsgId, rpc.method);
|
|
585
|
+
if (rpc.cancelled)
|
|
586
|
+
return;
|
|
587
|
+
rpc.promise.resolve(result);
|
|
588
|
+
}
|
|
589
|
+
this._onMessageAcked(reqMsgId);
|
|
590
|
+
this._session.pendingMessages.delete(reqMsgId);
|
|
591
|
+
}
|
|
592
|
+
_onMessageAcked(msgId, inContainer = false) {
|
|
593
|
+
const msg = this._session.pendingMessages.get(msgId);
|
|
594
|
+
if (!msg) {
|
|
595
|
+
if (!this._session.recentOutgoingMsgIds.has(msgId)) {
|
|
596
|
+
this.log.warn('received ack for unknown message %l', msgId);
|
|
597
|
+
}
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
switch (msg._) {
|
|
601
|
+
case 'container':
|
|
602
|
+
this.log.debug('received ack for container %l (size = %d)', msgId, msg.msgIds.length);
|
|
603
|
+
msg.msgIds.forEach((msgId) => this._onMessageAcked(msgId, true));
|
|
604
|
+
// we no longer need info about the container
|
|
605
|
+
this._session.pendingMessages.delete(msgId);
|
|
606
|
+
break;
|
|
607
|
+
case 'rpc': {
|
|
608
|
+
const rpc = msg.rpc;
|
|
609
|
+
this.log.debug('received ack for rpc query %l (%s, acked before = %s)', msgId, rpc.method, rpc.acked);
|
|
610
|
+
rpc.acked = true;
|
|
611
|
+
if (!inContainer && rpc.containerId && this._session.pendingMessages.has(rpc.containerId)) {
|
|
612
|
+
// ack all the messages in that container
|
|
613
|
+
this._onMessageAcked(rpc.containerId);
|
|
614
|
+
}
|
|
615
|
+
// this message could also already be in some queue,
|
|
616
|
+
removeFromLongArray(this._session.queuedStateReq, msgId);
|
|
617
|
+
removeFromLongArray(this._session.queuedResendReq, msgId);
|
|
618
|
+
// if resend/state was already requested, it will simply be ignored
|
|
619
|
+
this._session.getStateSchedule.remove(rpc);
|
|
620
|
+
break;
|
|
621
|
+
}
|
|
622
|
+
case 'bind':
|
|
623
|
+
break; // do nothing, wait for the result
|
|
624
|
+
default:
|
|
625
|
+
if (!inContainer) {
|
|
626
|
+
this.log.warn('received unexpected ack for %s query %l', msg._, msgId);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
_onAllFailed(reason) {
|
|
631
|
+
// called when all the pending messages are to be resent
|
|
632
|
+
// e.g. when server returns 429
|
|
633
|
+
// most service messages can be omitted as stale
|
|
634
|
+
for (const msgId of this._session.pendingMessages.keys()) {
|
|
635
|
+
const info = this._session.pendingMessages.get(msgId);
|
|
636
|
+
switch (info._) {
|
|
637
|
+
case 'container':
|
|
638
|
+
case 'state':
|
|
639
|
+
case 'resend':
|
|
640
|
+
case 'ping':
|
|
641
|
+
// no longer relevant
|
|
642
|
+
this._session.pendingMessages.delete(msgId);
|
|
643
|
+
break;
|
|
644
|
+
default:
|
|
645
|
+
this._onMessageFailed(msgId, reason, true);
|
|
646
|
+
break;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
_onMessageFailed(msgId, reason, inContainer = false) {
|
|
651
|
+
const msgInfo = this._session.pendingMessages.get(msgId);
|
|
652
|
+
if (!msgInfo) {
|
|
653
|
+
this.log.debug('unknown message %l failed because of %s', msgId, reason);
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
switch (msgInfo._) {
|
|
657
|
+
case 'container':
|
|
658
|
+
this.log.debug('container %l (size = %d) failed because of %s', msgId, msgInfo.msgIds.length, reason);
|
|
659
|
+
msgInfo.msgIds.forEach((msgId) => this._onMessageFailed(msgId, reason, true));
|
|
660
|
+
break;
|
|
661
|
+
case 'ping':
|
|
662
|
+
this.log.debug('ping (msg_id = %l) failed because of %s', msgId, reason);
|
|
663
|
+
// restart ping
|
|
664
|
+
this._session.resetLastPing(true);
|
|
665
|
+
break;
|
|
666
|
+
case 'rpc': {
|
|
667
|
+
const rpc = msgInfo.rpc;
|
|
668
|
+
this.log.debug('rpc query %l (%s) failed because of %s', msgId, rpc.method, reason);
|
|
669
|
+
// since the query was rejected, we can let it reassign msg_id to avoid containers
|
|
670
|
+
this._session.pendingMessages.delete(msgId);
|
|
671
|
+
rpc.msgId = undefined;
|
|
672
|
+
this._enqueueRpc(rpc, true);
|
|
673
|
+
if (!inContainer && rpc.containerId && this._session.pendingMessages.has(rpc.containerId)) {
|
|
674
|
+
// fail all the messages in that container
|
|
675
|
+
this._onMessageFailed(rpc.containerId, reason);
|
|
676
|
+
}
|
|
677
|
+
// this message could also already be in some queue,
|
|
678
|
+
removeFromLongArray(this._session.queuedStateReq, msgId);
|
|
679
|
+
removeFromLongArray(this._session.queuedResendReq, msgId);
|
|
680
|
+
// if resend/state was already requested, it will simply be ignored
|
|
681
|
+
this._session.getStateSchedule.remove(rpc);
|
|
682
|
+
break;
|
|
683
|
+
}
|
|
684
|
+
case 'resend':
|
|
685
|
+
this.log.debug('resend request %l (size = %d) failed because of %s', msgId, msgInfo.msgIds.length, reason);
|
|
686
|
+
this._session.queuedResendReq.splice(0, 0, ...msgInfo.msgIds);
|
|
687
|
+
this._flushTimer.emitWhenIdle();
|
|
688
|
+
break;
|
|
689
|
+
case 'state':
|
|
690
|
+
this.log.debug('state request %l (size = %d) failed because of %s', msgId, msgInfo.msgIds.length, reason);
|
|
691
|
+
this._session.queuedStateReq.splice(0, 0, ...msgInfo.msgIds);
|
|
692
|
+
this._flushTimer.emitWhenIdle();
|
|
693
|
+
break;
|
|
694
|
+
case 'bind':
|
|
695
|
+
this.log.debug('temp key binding request %l failed because of %s, retrying', msgId, reason);
|
|
696
|
+
msgInfo.promise.reject(Error(reason));
|
|
697
|
+
}
|
|
698
|
+
this._session.pendingMessages.delete(msgId);
|
|
699
|
+
}
|
|
700
|
+
_registerOutgoingMsgId(msgId) {
|
|
701
|
+
this._session.recentOutgoingMsgIds.add(msgId);
|
|
702
|
+
return msgId;
|
|
703
|
+
}
|
|
704
|
+
_onPong({ msgId, pingId }) {
|
|
705
|
+
const info = this._session.pendingMessages.get(msgId);
|
|
706
|
+
if (!info) {
|
|
707
|
+
this.log.warn('received pong to unknown ping (msg_id %l, ping_id %l)', msgId, pingId);
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
if (info._ !== 'ping') {
|
|
711
|
+
this.log.warn('received pong to %s query, not ping (msg_id %l, ping_id %l)', info._, msgId, pingId);
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
if (info.pingId.neq(pingId)) {
|
|
715
|
+
this.log.warn('received pong to %l, but expected ping_id = %l (got %l)', msgId, info.pingId, pingId);
|
|
716
|
+
}
|
|
717
|
+
const rtt = Date.now() - this._session.lastPingTime;
|
|
718
|
+
this._session.lastPingRtt = rtt;
|
|
719
|
+
if (info.containerId.neq(msgId)) {
|
|
720
|
+
this._onMessageAcked(info.containerId);
|
|
721
|
+
}
|
|
722
|
+
this.log.debug('received pong: msg_id %l, ping_id %l, rtt = %dms', msgId, pingId, rtt);
|
|
723
|
+
this._session.resetLastPing();
|
|
724
|
+
}
|
|
725
|
+
_onBadServerSalt(msg) {
|
|
726
|
+
this._session.serverSalt = msg.newServerSalt;
|
|
727
|
+
this._onMessageFailed(msg.badMsgId, 'bad_server_salt');
|
|
728
|
+
}
|
|
729
|
+
_onBadMsgNotification(msgId, msg) {
|
|
730
|
+
switch (msg.errorCode) {
|
|
731
|
+
case 16:
|
|
732
|
+
case 17:
|
|
733
|
+
case 20: {
|
|
734
|
+
if (msg.errorCode !== 20) {
|
|
735
|
+
// msg_id is either too high or too low
|
|
736
|
+
// code 20 means msg_id is too old,
|
|
737
|
+
// we just need to resend the message
|
|
738
|
+
const serverTime = msgId.low >>> 0;
|
|
739
|
+
const timeOffset = Math.floor(Date.now() / 1000) - serverTime;
|
|
740
|
+
this._session._timeOffset = timeOffset;
|
|
741
|
+
this.log.debug('server time: %d, corrected offset to %d', serverTime, timeOffset);
|
|
742
|
+
}
|
|
743
|
+
this._onMessageFailed(msg.badMsgId, `bad_msg_notification ${msg.errorCode}`);
|
|
744
|
+
break;
|
|
745
|
+
}
|
|
746
|
+
default:
|
|
747
|
+
// something went very wrong, we need to reset the session
|
|
748
|
+
this.log.error('received bad_msg_notification for msg_id = %l, code = %d. session will be reset', msg.badMsgId, msg.errorCode);
|
|
749
|
+
this._resetSession();
|
|
750
|
+
break;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
_onNewSessionCreated({ firstMsgId, serverSalt, uniqueId }) {
|
|
754
|
+
if (uniqueId.eq(this._session.lastSessionCreatedUid)) {
|
|
755
|
+
this.log.debug('received new_session_created with the same uid = %l, ignoring', uniqueId);
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
if (!this._session.lastSessionCreatedUid.isZero() && !this.params.disableUpdates) {
|
|
759
|
+
// force the client to fetch missed updates
|
|
760
|
+
// when _lastSessionCreatedUid == 0, the connection has
|
|
761
|
+
// just been established, and the client will fetch them anyways
|
|
762
|
+
this.emit('update', { _: 'updatesTooLong' });
|
|
763
|
+
}
|
|
764
|
+
this._session.serverSalt = serverSalt;
|
|
765
|
+
this.log.debug('received new_session_created, uid = %l, first msg_id = %l', uniqueId, firstMsgId);
|
|
766
|
+
for (const msgId of this._session.pendingMessages.keys()) {
|
|
767
|
+
const val = this._session.pendingMessages.get(msgId);
|
|
768
|
+
if (val._ === 'bind') {
|
|
769
|
+
// should NOT happen.
|
|
770
|
+
if (msgId.lt(firstMsgId)) {
|
|
771
|
+
this._onMessageFailed(msgId, 'received in wrong session');
|
|
772
|
+
}
|
|
773
|
+
continue;
|
|
774
|
+
}
|
|
775
|
+
if (val._ === 'container') {
|
|
776
|
+
if (msgId.lt(firstMsgId)) {
|
|
777
|
+
// all messages in this container will be resent
|
|
778
|
+
// info about this container is no longer needed
|
|
779
|
+
this._session.pendingMessages.delete(msgId);
|
|
780
|
+
}
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
const containerId = val._ === 'rpc' ? val.rpc.containerId || msgId : val.containerId;
|
|
784
|
+
if (containerId.lt(firstMsgId)) {
|
|
785
|
+
this._onMessageFailed(msgId, 'new_session_created', true);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
_onMessageInfo(msgId, status, answerMsgId) {
|
|
790
|
+
if (!msgId.isZero()) {
|
|
791
|
+
const info = this._session.pendingMessages.get(msgId);
|
|
792
|
+
if (!info) {
|
|
793
|
+
if (!this._session.recentOutgoingMsgIds.has(msgId)) {
|
|
794
|
+
this.log.warn('received message info about unknown message %l', msgId);
|
|
795
|
+
}
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
switch (status & 7) {
|
|
799
|
+
case 1:
|
|
800
|
+
case 2:
|
|
801
|
+
case 3:
|
|
802
|
+
// message wasn't received by the server
|
|
803
|
+
this._onMessageFailed(msgId, `message info state ${status}`);
|
|
804
|
+
break;
|
|
805
|
+
case 0:
|
|
806
|
+
if (!answerMsgId.isZero()) {
|
|
807
|
+
this.log.warn('received message info with status = 0: msg_id = %l, status = %d, ans_id = %l', msgId, status, answerMsgId);
|
|
808
|
+
return this._onMessageFailed(msgId, 'message info state = 0, ans_id = 0');
|
|
809
|
+
}
|
|
810
|
+
// fallthrough
|
|
811
|
+
case 4:
|
|
812
|
+
this._onMessageAcked(msgId);
|
|
813
|
+
break;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
if (!answerMsgId.isZero() && !this._session.recentIncomingMsgIds.has(answerMsgId)) {
|
|
817
|
+
this.log.debug('received message info for %l, but answer (%l) was not received yet', msgId, answerMsgId);
|
|
818
|
+
this._session.queuedResendReq.push(answerMsgId);
|
|
819
|
+
this._flushTimer.emitWhenIdle();
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
this.log.debug('received message info for %l, and answer (%l) was already received', msgId, answerMsgId);
|
|
823
|
+
}
|
|
824
|
+
_onMessagesInfo(msgIds, info) {
|
|
825
|
+
if (msgIds.length !== info.length) {
|
|
826
|
+
this.log.warn('messages state info was invalid: msg_ids.length !== info.length');
|
|
827
|
+
}
|
|
828
|
+
for (let i = 0; i < msgIds.length; i++) {
|
|
829
|
+
this._onMessageInfo(msgIds[i], info[i], Long.ZERO);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
_onMsgsStateInfo(msg) {
|
|
833
|
+
const info = this._session.pendingMessages.get(msg.reqMsgId);
|
|
834
|
+
if (!info) {
|
|
835
|
+
this.log.warn('received msgs_state_info to unknown request %l', msg.reqMsgId);
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
if (info._ !== 'state') {
|
|
839
|
+
this.log.warn('received msgs_state_info to %s query %l', info._, msg.reqMsgId);
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
this._onMessagesInfo(info.msgIds, msg.info);
|
|
843
|
+
}
|
|
844
|
+
_onDestroySessionResult(msg) {
|
|
845
|
+
const reqMsgId = this._session.destroySessionIdToMsgId.get(msg.sessionId);
|
|
846
|
+
if (!reqMsgId) {
|
|
847
|
+
this.log.warn('received %s for unknown session %h', msg._, msg.sessionId);
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
this._session.destroySessionIdToMsgId.delete(msg.sessionId);
|
|
851
|
+
this._session.pendingMessages.delete(reqMsgId);
|
|
852
|
+
this.log.debug('received %s for session %h', msg._, msg.sessionId);
|
|
853
|
+
}
|
|
854
|
+
_enqueueRpc(rpc, force) {
|
|
855
|
+
if (this._session.enqueueRpc(rpc, force)) {
|
|
856
|
+
this._flushTimer.emitWhenIdle();
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
_resetSession() {
|
|
860
|
+
this._queuedDestroySession.push(this._session._sessionId);
|
|
861
|
+
this._session.resetState(true);
|
|
862
|
+
this.reconnect();
|
|
863
|
+
// once we receive new_session_created, all pending messages will be resent.
|
|
864
|
+
this._flushTimer.reset();
|
|
865
|
+
}
|
|
866
|
+
_sendAck(msgId) {
|
|
867
|
+
if (this._session.queuedAcks.length === 0) {
|
|
868
|
+
this._flushTimer.emitBeforeNext(30000);
|
|
869
|
+
}
|
|
870
|
+
this._session.queuedAcks.push(msgId);
|
|
871
|
+
if (this._session.queuedAcks.length >= 100) {
|
|
872
|
+
this._flushTimer.emitWhenIdle();
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
sendRpc(request, stack, timeout, abortSignal) {
|
|
876
|
+
if (this._usable && this.params.inactivityTimeout) {
|
|
877
|
+
this._rescheduleInactivity();
|
|
878
|
+
}
|
|
879
|
+
if (!stack && this.params.niceStacks !== false) {
|
|
880
|
+
stack = new Error().stack;
|
|
881
|
+
}
|
|
882
|
+
const method = request._;
|
|
883
|
+
let obj = request;
|
|
884
|
+
let initConn = false;
|
|
885
|
+
if (this.params.disableUpdates) {
|
|
886
|
+
obj = {
|
|
887
|
+
_: 'invokeWithoutUpdates',
|
|
888
|
+
query: obj,
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
if (!this._session.initConnectionCalled) {
|
|
892
|
+
// we will wrap every rpc call with initConnection
|
|
893
|
+
// until some of the requests wrapped with it is
|
|
894
|
+
// either acked or returns rpc_result
|
|
895
|
+
this.log.debug('wrapping %s with initConnection, layer: %d', method, this.params.layer);
|
|
896
|
+
const proxy = this._transport.getMtproxyInfo?.();
|
|
897
|
+
obj = {
|
|
898
|
+
_: 'invokeWithLayer',
|
|
899
|
+
layer: this.params.layer,
|
|
900
|
+
query: {
|
|
901
|
+
...this.params.initConnection,
|
|
902
|
+
proxy,
|
|
903
|
+
query: obj,
|
|
904
|
+
},
|
|
905
|
+
};
|
|
906
|
+
initConn = true;
|
|
907
|
+
}
|
|
908
|
+
this.log.verbose('>>> %j', obj);
|
|
909
|
+
let content = TlBinaryWriter.serializeObject(this._writerMap, obj);
|
|
910
|
+
if (content.length > 1044404) {
|
|
911
|
+
// if you send larger payloads, telegram will just close connection,
|
|
912
|
+
// and since we resend them, it will get resent after reconnection and
|
|
913
|
+
// that will be an endless loop of reconnections. we don't want that,
|
|
914
|
+
// and payloads this large are usually a sign of an error in the code.
|
|
915
|
+
throw new MtArgumentError(`Payload is too big (${content.length} > 1044404)`);
|
|
916
|
+
}
|
|
917
|
+
// gzip
|
|
918
|
+
let shouldGzip = content.length > 128;
|
|
919
|
+
if (content.length > 16384) {
|
|
920
|
+
// test compression ratio for the middle part
|
|
921
|
+
// if it is less than 0.9, then try to compress the whole request
|
|
922
|
+
const middle = ~~((content.length - 1024) / 2);
|
|
923
|
+
const gzipped = gzipDeflate(content.subarray(middle, middle + 1024), 0.9);
|
|
924
|
+
if (!gzipped)
|
|
925
|
+
shouldGzip = false;
|
|
926
|
+
}
|
|
927
|
+
if (shouldGzip) {
|
|
928
|
+
const gzipped = gzipDeflate(content, 0.9);
|
|
929
|
+
if (gzipped) {
|
|
930
|
+
this.log.debug('gzipped %s (%db -> %db)', method, content.length, gzipped.length);
|
|
931
|
+
content = gzipped;
|
|
932
|
+
}
|
|
933
|
+
else {
|
|
934
|
+
shouldGzip = false;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
const pending = {
|
|
938
|
+
method,
|
|
939
|
+
promise: createControllablePromise(),
|
|
940
|
+
data: content,
|
|
941
|
+
stack,
|
|
942
|
+
// we will need to know size of gzip_packed overhead in _flush()
|
|
943
|
+
gzipOverhead: shouldGzip ? 4 + TlSerializationCounter.countBytesOverhead(content.length) : 0,
|
|
944
|
+
initConn,
|
|
945
|
+
// setting them as well so jit can optimize stuff
|
|
946
|
+
sent: undefined,
|
|
947
|
+
done: undefined,
|
|
948
|
+
getState: undefined,
|
|
949
|
+
msgId: undefined,
|
|
950
|
+
seqNo: undefined,
|
|
951
|
+
containerId: undefined,
|
|
952
|
+
acked: undefined,
|
|
953
|
+
cancelled: undefined,
|
|
954
|
+
timeout: undefined,
|
|
955
|
+
};
|
|
956
|
+
if (abortSignal?.aborted) {
|
|
957
|
+
pending.promise.reject(abortSignal.reason);
|
|
958
|
+
return pending.promise;
|
|
959
|
+
}
|
|
960
|
+
if (timeout) {
|
|
961
|
+
pending.timeout = setTimeout(this._cancelRpc, timeout, pending, true);
|
|
962
|
+
}
|
|
963
|
+
if (abortSignal) {
|
|
964
|
+
abortSignal.addEventListener('abort', () => this._cancelRpc(pending, false, abortSignal));
|
|
965
|
+
}
|
|
966
|
+
this._enqueueRpc(pending, true);
|
|
967
|
+
return pending.promise;
|
|
968
|
+
}
|
|
969
|
+
_cancelRpc(rpc, onTimeout = false, abortSignal) {
|
|
970
|
+
if (rpc.done)
|
|
971
|
+
return;
|
|
972
|
+
if (rpc.cancelled && !onTimeout) {
|
|
973
|
+
throw new MtcuteError('RPC was already cancelled');
|
|
974
|
+
}
|
|
975
|
+
if (!onTimeout && rpc.timeout) {
|
|
976
|
+
clearTimeout(rpc.timeout);
|
|
977
|
+
}
|
|
978
|
+
if (onTimeout) {
|
|
979
|
+
// todo: replace with MtTimeoutError
|
|
980
|
+
const error = new tl.RpcError(400, 'Client timeout');
|
|
981
|
+
if (this.params.niceStacks !== false) {
|
|
982
|
+
makeNiceStack(error, rpc.stack, rpc.method);
|
|
983
|
+
}
|
|
984
|
+
rpc.promise.reject(error);
|
|
985
|
+
}
|
|
986
|
+
else if (abortSignal) {
|
|
987
|
+
rpc.promise.reject(abortSignal.reason);
|
|
988
|
+
}
|
|
989
|
+
rpc.cancelled = true;
|
|
990
|
+
if (rpc.msgId) {
|
|
991
|
+
this._session.queuedCancelReq.push(rpc.msgId);
|
|
992
|
+
this._flushTimer.emitWhenIdle();
|
|
993
|
+
}
|
|
994
|
+
else {
|
|
995
|
+
// in case rpc wasn't sent yet (or had some error),
|
|
996
|
+
// we can simply remove it from queue
|
|
997
|
+
this._session.queuedRpc.remove(rpc);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
_onInactivityTimeout() {
|
|
1001
|
+
// we should send all pending acks and other service messages
|
|
1002
|
+
// before dropping the connection
|
|
1003
|
+
if (!this._session.hasPendingMessages) {
|
|
1004
|
+
this.log.debug('no pending service messages, closing connection');
|
|
1005
|
+
super._onInactivityTimeout();
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
this._inactivityPendingFlush = true;
|
|
1009
|
+
this._flush();
|
|
1010
|
+
}
|
|
1011
|
+
flushWhenIdle() {
|
|
1012
|
+
this._flushTimer.emitWhenIdle();
|
|
1013
|
+
}
|
|
1014
|
+
_flush() {
|
|
1015
|
+
if (!this._session._authKey.ready || this._isPfsBindingPending || this._session.current429Timeout) {
|
|
1016
|
+
this.log.debug('skipping flush, connection is not usable (auth key ready = %b, pfs binding pending = %b, 429 timeout = %b)', this._session._authKey.ready, this._isPfsBindingPending, Boolean(this._session.current429Timeout));
|
|
1017
|
+
// it will be flushed once connection is usable
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
try {
|
|
1021
|
+
this._doFlush();
|
|
1022
|
+
}
|
|
1023
|
+
catch (e) {
|
|
1024
|
+
this.log.error('flush error: %s', e.stack);
|
|
1025
|
+
// should not happen unless there's a bug in the code
|
|
1026
|
+
}
|
|
1027
|
+
// schedule next flush
|
|
1028
|
+
// if there are more queued requests, flush immediately
|
|
1029
|
+
// (they likely didn't fit into one message)
|
|
1030
|
+
if (this._session.hasPendingMessages) {
|
|
1031
|
+
// we schedule it on the next tick, so we can load-balance
|
|
1032
|
+
// between multiple connections using the same session
|
|
1033
|
+
this._flushTimer.emitWhenIdle();
|
|
1034
|
+
}
|
|
1035
|
+
else if (this._inactivityPendingFlush) {
|
|
1036
|
+
this.log.debug('pending messages sent, closing connection');
|
|
1037
|
+
this._flushTimer.reset();
|
|
1038
|
+
this._inactivityPendingFlush = false;
|
|
1039
|
+
super._onInactivityTimeout();
|
|
1040
|
+
}
|
|
1041
|
+
else {
|
|
1042
|
+
this._flushTimer.emitBefore(this._session.lastPingTime + 60000);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
_doFlush() {
|
|
1046
|
+
this.log.debug('flushing send queue. queued rpc: %d', this._session.queuedRpc.length);
|
|
1047
|
+
// oh bloody hell mate
|
|
1048
|
+
// total size & count
|
|
1049
|
+
let packetSize = 0;
|
|
1050
|
+
let messageCount = 0;
|
|
1051
|
+
// size & msg count that count towards container limit
|
|
1052
|
+
// these will be added to total later
|
|
1053
|
+
let containerMessageCount = 0;
|
|
1054
|
+
let containerSize = 0;
|
|
1055
|
+
let ackRequest = null;
|
|
1056
|
+
let ackMsgIds = null;
|
|
1057
|
+
let pingRequest = null;
|
|
1058
|
+
let pingId = null;
|
|
1059
|
+
let pingMsgId = null;
|
|
1060
|
+
let getStateRequest = null;
|
|
1061
|
+
let getStateMsgId = null;
|
|
1062
|
+
let getStateMsgIds = null;
|
|
1063
|
+
let resendRequest = null;
|
|
1064
|
+
let resendMsgId = null;
|
|
1065
|
+
let resendMsgIds = null;
|
|
1066
|
+
let cancelRpcs = null;
|
|
1067
|
+
let destroySessions = null;
|
|
1068
|
+
let rootMsgId = null;
|
|
1069
|
+
const now = Date.now();
|
|
1070
|
+
if (this._session.queuedAcks.length) {
|
|
1071
|
+
let acks = this._session.queuedAcks;
|
|
1072
|
+
if (acks.length > 8192) {
|
|
1073
|
+
this._session.queuedAcks = acks.slice(8192);
|
|
1074
|
+
acks = acks.slice(0, 8192);
|
|
1075
|
+
}
|
|
1076
|
+
else {
|
|
1077
|
+
this._session.queuedAcks = [];
|
|
1078
|
+
}
|
|
1079
|
+
const obj = {
|
|
1080
|
+
_: 'mt_msgs_ack',
|
|
1081
|
+
msgIds: acks,
|
|
1082
|
+
};
|
|
1083
|
+
ackMsgIds = obj.msgIds;
|
|
1084
|
+
ackRequest = TlBinaryWriter.serializeObject(this._writerMap, obj);
|
|
1085
|
+
packetSize += ackRequest.length + 16;
|
|
1086
|
+
messageCount += 1;
|
|
1087
|
+
}
|
|
1088
|
+
const getStateTime = now + 1500;
|
|
1089
|
+
if (now - this._session.lastPingTime > 60000) {
|
|
1090
|
+
if (!this._session.lastPingMsgId.isZero()) {
|
|
1091
|
+
this.log.warn("didn't receive pong for previous ping (msg_id = %l)", this._session.lastPingMsgId);
|
|
1092
|
+
this._session.pendingMessages.delete(this._session.lastPingMsgId);
|
|
1093
|
+
}
|
|
1094
|
+
pingId = randomLong();
|
|
1095
|
+
const obj = {
|
|
1096
|
+
_: 'mt_ping',
|
|
1097
|
+
pingId,
|
|
1098
|
+
};
|
|
1099
|
+
this._session.lastPingTime = Date.now();
|
|
1100
|
+
pingRequest = TlBinaryWriter.serializeObject(this._writerMap, obj);
|
|
1101
|
+
containerSize += pingRequest.length + 16;
|
|
1102
|
+
containerMessageCount += 1;
|
|
1103
|
+
}
|
|
1104
|
+
{
|
|
1105
|
+
if (this._session.queuedStateReq.length) {
|
|
1106
|
+
let ids = this._session.queuedStateReq;
|
|
1107
|
+
if (ids.length > 8192) {
|
|
1108
|
+
this._session.queuedStateReq = ids.slice(8192);
|
|
1109
|
+
ids = ids.slice(0, 8192);
|
|
1110
|
+
}
|
|
1111
|
+
else {
|
|
1112
|
+
this._session.queuedStateReq = [];
|
|
1113
|
+
}
|
|
1114
|
+
getStateMsgIds = ids;
|
|
1115
|
+
}
|
|
1116
|
+
const idx = this._session.getStateSchedule.index({ getState: now }, true);
|
|
1117
|
+
if (idx > 0) {
|
|
1118
|
+
const toGetState = this._session.getStateSchedule.raw.splice(0, idx);
|
|
1119
|
+
if (!getStateMsgIds)
|
|
1120
|
+
getStateMsgIds = [];
|
|
1121
|
+
toGetState.forEach((it) => getStateMsgIds.push(it.msgId));
|
|
1122
|
+
}
|
|
1123
|
+
if (getStateMsgIds) {
|
|
1124
|
+
const obj = {
|
|
1125
|
+
_: 'mt_msgs_state_req',
|
|
1126
|
+
msgIds: getStateMsgIds,
|
|
1127
|
+
};
|
|
1128
|
+
getStateRequest = TlBinaryWriter.serializeObject(this._writerMap, obj);
|
|
1129
|
+
packetSize += getStateRequest.length + 16;
|
|
1130
|
+
messageCount += 1;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
if (this._session.queuedResendReq.length) {
|
|
1134
|
+
resendMsgIds = this._session.queuedResendReq;
|
|
1135
|
+
if (resendMsgIds.length > 8192) {
|
|
1136
|
+
this._session.queuedResendReq = resendMsgIds.slice(8192);
|
|
1137
|
+
resendMsgIds = resendMsgIds.slice(0, 8192);
|
|
1138
|
+
}
|
|
1139
|
+
else {
|
|
1140
|
+
this._session.queuedResendReq = [];
|
|
1141
|
+
}
|
|
1142
|
+
const obj = {
|
|
1143
|
+
_: 'mt_msg_resend_req',
|
|
1144
|
+
msgIds: resendMsgIds,
|
|
1145
|
+
};
|
|
1146
|
+
resendRequest = TlBinaryWriter.serializeObject(this._writerMap, obj);
|
|
1147
|
+
packetSize += resendRequest.length + 16;
|
|
1148
|
+
messageCount += 1;
|
|
1149
|
+
}
|
|
1150
|
+
if (this._session.queuedCancelReq.length) {
|
|
1151
|
+
containerMessageCount += this._session.queuedCancelReq.length;
|
|
1152
|
+
containerSize += this._session.queuedCancelReq.length * 28;
|
|
1153
|
+
cancelRpcs = this._session.queuedCancelReq;
|
|
1154
|
+
this._session.queuedCancelReq = [];
|
|
1155
|
+
}
|
|
1156
|
+
if (this._queuedDestroySession.length) {
|
|
1157
|
+
containerMessageCount += this._queuedDestroySession.length;
|
|
1158
|
+
containerSize += this._queuedDestroySession.length * 28;
|
|
1159
|
+
destroySessions = this._queuedDestroySession;
|
|
1160
|
+
this._queuedDestroySession = [];
|
|
1161
|
+
}
|
|
1162
|
+
let forceContainer = false;
|
|
1163
|
+
const rpcToSend = [];
|
|
1164
|
+
while (this._session.queuedRpc.length &&
|
|
1165
|
+
containerSize < 32768 && // 2^15
|
|
1166
|
+
containerMessageCount < 1020) {
|
|
1167
|
+
const msg = this._session.queuedRpc.popFront();
|
|
1168
|
+
if (msg.cancelled)
|
|
1169
|
+
continue;
|
|
1170
|
+
// note: we don't check for <2^15 here
|
|
1171
|
+
// this is not documented, but large requests
|
|
1172
|
+
// (like upload.saveFilePart) *may* exceed that limit
|
|
1173
|
+
rpcToSend.push(msg);
|
|
1174
|
+
containerSize += msg.data.length + 16;
|
|
1175
|
+
if (msg.gzipOverhead)
|
|
1176
|
+
containerSize += msg.gzipOverhead;
|
|
1177
|
+
// if message was already assigned a msg_id,
|
|
1178
|
+
// we must wrap it in a container with a newer msg_id
|
|
1179
|
+
if (msg.msgId)
|
|
1180
|
+
forceContainer = true;
|
|
1181
|
+
// having >1 upload.getFile within a container seems to cause flood_wait errors
|
|
1182
|
+
// also a crutch for load-balancing
|
|
1183
|
+
if (msg.method === 'upload.getFile')
|
|
1184
|
+
break;
|
|
1185
|
+
}
|
|
1186
|
+
packetSize += containerSize;
|
|
1187
|
+
messageCount += containerMessageCount + rpcToSend.length;
|
|
1188
|
+
if (!messageCount) {
|
|
1189
|
+
this.log.debug('flush did not happen: nothing to flush');
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
const useContainer = forceContainer || messageCount > 1;
|
|
1193
|
+
if (useContainer)
|
|
1194
|
+
packetSize += 24; // 8 (msg_container) + 16 (mtproto header)
|
|
1195
|
+
const writer = TlBinaryWriter.alloc(this._writerMap, packetSize);
|
|
1196
|
+
if (useContainer) {
|
|
1197
|
+
// leave bytes for mtproto header (we'll write it later,
|
|
1198
|
+
// since we need seqno and msg_id to be larger than the content)
|
|
1199
|
+
writer.pos += 16;
|
|
1200
|
+
writer.uint(0x73f1f8dc); // msg_container
|
|
1201
|
+
writer.uint(messageCount);
|
|
1202
|
+
}
|
|
1203
|
+
const otherPendings = [];
|
|
1204
|
+
if (ackRequest) {
|
|
1205
|
+
this._registerOutgoingMsgId(this._session.writeMessage(writer, ackRequest));
|
|
1206
|
+
}
|
|
1207
|
+
if (pingRequest) {
|
|
1208
|
+
pingMsgId = this._registerOutgoingMsgId(this._session.writeMessage(writer, pingRequest));
|
|
1209
|
+
this._session.lastPingMsgId = pingMsgId;
|
|
1210
|
+
const pingPending = {
|
|
1211
|
+
_: 'ping',
|
|
1212
|
+
pingId: pingId,
|
|
1213
|
+
containerId: pingMsgId,
|
|
1214
|
+
};
|
|
1215
|
+
this._session.pendingMessages.set(pingMsgId, pingPending);
|
|
1216
|
+
otherPendings.push(pingPending);
|
|
1217
|
+
}
|
|
1218
|
+
if (getStateRequest) {
|
|
1219
|
+
getStateMsgId = this._registerOutgoingMsgId(this._session.writeMessage(writer, getStateRequest));
|
|
1220
|
+
const getStatePending = {
|
|
1221
|
+
_: 'state',
|
|
1222
|
+
msgIds: getStateMsgIds,
|
|
1223
|
+
containerId: getStateMsgId,
|
|
1224
|
+
};
|
|
1225
|
+
this._session.pendingMessages.set(getStateMsgId, getStatePending);
|
|
1226
|
+
otherPendings.push(getStatePending);
|
|
1227
|
+
}
|
|
1228
|
+
if (resendRequest) {
|
|
1229
|
+
resendMsgId = this._registerOutgoingMsgId(this._session.writeMessage(writer, resendRequest));
|
|
1230
|
+
const resendPending = {
|
|
1231
|
+
_: 'resend',
|
|
1232
|
+
msgIds: resendMsgIds,
|
|
1233
|
+
containerId: resendMsgId,
|
|
1234
|
+
};
|
|
1235
|
+
this._session.pendingMessages.set(resendMsgId, resendPending);
|
|
1236
|
+
otherPendings.push(resendPending);
|
|
1237
|
+
}
|
|
1238
|
+
if (cancelRpcs) {
|
|
1239
|
+
cancelRpcs.forEach((msgId) => {
|
|
1240
|
+
const cancelMsgId = this._registerOutgoingMsgId(this._session.writeMessage(writer, {
|
|
1241
|
+
_: 'mt_rpc_drop_answer',
|
|
1242
|
+
reqMsgId: msgId,
|
|
1243
|
+
}));
|
|
1244
|
+
const pending = {
|
|
1245
|
+
_: 'cancel',
|
|
1246
|
+
msgId,
|
|
1247
|
+
containerId: cancelMsgId,
|
|
1248
|
+
};
|
|
1249
|
+
this._session.pendingMessages.set(cancelMsgId, pending);
|
|
1250
|
+
otherPendings.push(pending);
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
if (destroySessions) {
|
|
1254
|
+
destroySessions.forEach((sessionId) => {
|
|
1255
|
+
const msgId = this._registerOutgoingMsgId(this._session.writeMessage(writer, {
|
|
1256
|
+
_: 'mt_destroy_session',
|
|
1257
|
+
sessionId,
|
|
1258
|
+
}));
|
|
1259
|
+
const pending = {
|
|
1260
|
+
_: 'destroy_session',
|
|
1261
|
+
sessionId,
|
|
1262
|
+
containerId: msgId,
|
|
1263
|
+
};
|
|
1264
|
+
this._session.pendingMessages.set(msgId, pending);
|
|
1265
|
+
this._session.destroySessionIdToMsgId.set(sessionId, msgId);
|
|
1266
|
+
otherPendings.push(pending);
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
for (let i = 0; i < rpcToSend.length; i++) {
|
|
1270
|
+
const msg = rpcToSend[i];
|
|
1271
|
+
// not using writeMessage here because we also need seqNo, and
|
|
1272
|
+
// i dont want to also return seqNo there because that would mean
|
|
1273
|
+
// extra object overhead
|
|
1274
|
+
if (!msg.msgId) {
|
|
1275
|
+
const msgId = this._session.getMessageId();
|
|
1276
|
+
const seqNo = this._session.getSeqNo();
|
|
1277
|
+
this.log.debug('%s: msg_id assigned %l, seqno: %d', msg.method, msgId, seqNo);
|
|
1278
|
+
msg.msgId = msgId;
|
|
1279
|
+
msg.seqNo = seqNo;
|
|
1280
|
+
this._session.pendingMessages.set(msgId, {
|
|
1281
|
+
_: 'rpc',
|
|
1282
|
+
rpc: msg,
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
else {
|
|
1286
|
+
this.log.debug('%s: msg_id already assigned, reusing %l, seqno: %d', msg.method, msg.msgId, msg.seqNo);
|
|
1287
|
+
}
|
|
1288
|
+
// (re-)schedule get_state if needed
|
|
1289
|
+
if (msg.getState) {
|
|
1290
|
+
this._session.getStateSchedule.remove(msg);
|
|
1291
|
+
}
|
|
1292
|
+
if (!msg.acked) {
|
|
1293
|
+
msg.getState = getStateTime;
|
|
1294
|
+
this._session.getStateSchedule.insert(msg);
|
|
1295
|
+
}
|
|
1296
|
+
if (rootMsgId === null)
|
|
1297
|
+
rootMsgId = msg.msgId;
|
|
1298
|
+
writer.long(this._registerOutgoingMsgId(msg.msgId));
|
|
1299
|
+
writer.uint(msg.seqNo);
|
|
1300
|
+
if (msg.gzipOverhead) {
|
|
1301
|
+
writer.uint(msg.data.length + msg.gzipOverhead);
|
|
1302
|
+
writer.uint(0x3072cfa1); // gzip_packed#3072cfa1
|
|
1303
|
+
writer.bytes(msg.data);
|
|
1304
|
+
}
|
|
1305
|
+
else {
|
|
1306
|
+
writer.uint(msg.data.length);
|
|
1307
|
+
writer.raw(msg.data);
|
|
1308
|
+
}
|
|
1309
|
+
msg.sent = true;
|
|
1310
|
+
}
|
|
1311
|
+
if (useContainer) {
|
|
1312
|
+
// we now need to assign the container msg_id and seqno
|
|
1313
|
+
// we couldn't have assigned them earlier because mtproto
|
|
1314
|
+
// requires them to be >= than the contained messages
|
|
1315
|
+
// writer.pos is expected to be packetSize
|
|
1316
|
+
const containerId = this._session.getMessageId();
|
|
1317
|
+
writer.pos = 0;
|
|
1318
|
+
rootMsgId = containerId;
|
|
1319
|
+
writer.long(this._registerOutgoingMsgId(containerId));
|
|
1320
|
+
writer.uint(this._session.getSeqNo(false));
|
|
1321
|
+
writer.uint(packetSize - 16);
|
|
1322
|
+
writer.pos = packetSize;
|
|
1323
|
+
const msgIds = [];
|
|
1324
|
+
for (let i = 0; i < rpcToSend.length; i++) {
|
|
1325
|
+
const msg = rpcToSend[i];
|
|
1326
|
+
msg.containerId = containerId;
|
|
1327
|
+
msgIds.push(msg.msgId);
|
|
1328
|
+
}
|
|
1329
|
+
if (otherPendings.length) {
|
|
1330
|
+
otherPendings.forEach((msg) => {
|
|
1331
|
+
msgIds.push(msg.containerId);
|
|
1332
|
+
msg.containerId = containerId;
|
|
1333
|
+
});
|
|
1334
|
+
}
|
|
1335
|
+
this._session.pendingMessages.set(containerId, {
|
|
1336
|
+
_: 'container',
|
|
1337
|
+
msgIds,
|
|
1338
|
+
});
|
|
1339
|
+
}
|
|
1340
|
+
const result = writer.result();
|
|
1341
|
+
// probably the easiest way lol
|
|
1342
|
+
this.log.debug('sending %d messages: size = %db, acks = %d (msg_id = %s), ping = %s (msg_id = %s), state_req = %s (msg_id = %s), resend = %s (msg_id = %s), cancels = %s (msg_id = %s), rpc = %s, container = %s, root msg_id = %l', messageCount, packetSize, ackMsgIds?.length || 'false', ackMsgIds?.map((it) => it.toString()), Boolean(pingRequest), pingMsgId, getStateMsgIds?.map((it) => it.toString()) || 'false', getStateMsgId, resendMsgIds?.map((it) => it.toString()) || 'false', cancelRpcs, cancelRpcs?.map((it) => it.toString()) || 'false', resendMsgId, rpcToSend.map((it) => it.method), useContainer, rootMsgId);
|
|
1343
|
+
this._session
|
|
1344
|
+
.encryptMessage(result)
|
|
1345
|
+
.then((enc) => this.send(enc))
|
|
1346
|
+
.catch((err) => {
|
|
1347
|
+
this.log.error('error while sending pending messages (root msg_id = %l): %s', rootMsgId, err.stack);
|
|
1348
|
+
// put acks in the front so they are the first to be sent
|
|
1349
|
+
if (ackMsgIds) {
|
|
1350
|
+
this._session.queuedAcks.splice(0, 0, ...ackMsgIds);
|
|
1351
|
+
}
|
|
1352
|
+
this._onMessageFailed(rootMsgId, 'unknown error');
|
|
1353
|
+
});
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
//# sourceMappingURL=session-connection.js.map
|