From c8e026dc033c0c24f6c00b0e462203dd1bac8ca1 Mon Sep 17 00:00:00 2001 From: Alina Sireneva Date: Wed, 31 Jan 2024 19:29:49 +0300 Subject: [PATCH] refactor!: merged client into core + fixed dispatcher for new storage --- .config/eslint.cjs | 2 +- package.json | 2 +- packages/client/README.md | 35 - packages/client/build.config.cjs | 3 - packages/client/package.json | 43 - packages/client/src/index.ts | 5 - packages/client/src/methods/_init.ts | 111 - packages/client/src/methods/auth/_state.ts | 52 - packages/client/src/methods/misc/chain-id.ts | 8 - .../client/src/methods/updates/manager.ts | 1835 ---------------- packages/client/src/methods/updates/utils.ts | 42 - packages/client/src/utils/platform/storage.ts | 5 - packages/client/src/utils/updates-utils.ts | 49 - packages/client/tsconfig.json | 14 - packages/client/typedoc.cjs | 9 - packages/client/utils.ts | 4 - packages/core/build.config.cjs | 1 + packages/core/package.json | 21 +- .../scripts/generate-client.cjs | 62 +- .../scripts/generate-updates.cjs | 3 +- .../{client => core}/scripts/update-types.txt | 0 packages/core/src/base-client.ts | 354 ---- packages/core/src/base-client.types.ts | 177 -- packages/core/src/highlevel/base.ts | 287 +++ .../src => core/src/highlevel}/client.ts | 1122 ++++------ packages/core/src/highlevel/client.types.ts | 53 + packages/core/src/highlevel/index.ts | 6 + .../src/highlevel}/methods/README.md | 4 +- .../src/highlevel}/methods/_imports.ts | 23 +- packages/core/src/highlevel/methods/_init.ts | 97 + .../highlevel}/methods/auth/check-password.ts | 11 +- .../methods/auth/get-password-hint.ts | 4 +- .../src/highlevel}/methods/auth/log-out.ts | 12 +- .../methods/auth/recover-password.ts | 7 +- .../highlevel}/methods/auth/resend-code.ts | 9 +- .../src/highlevel}/methods/auth/run.ts | 17 +- .../src/highlevel}/methods/auth/send-code.ts | 15 +- .../methods/auth/send-recovery-code.ts | 4 +- .../highlevel}/methods/auth/sign-in-bot.ts | 15 +- .../src/highlevel}/methods/auth/sign-in.ts | 9 +- .../src/highlevel}/methods/auth/start-test.ts | 8 +- .../src/highlevel}/methods/auth/start.ts | 32 +- .../core/src/highlevel/methods/auth/utils.ts | 39 + .../methods/bots/answer-callback-query.ts | 7 +- .../methods/bots/answer-inline-query.ts | 7 +- .../methods/bots/answer-pre-checkout-query.ts | 9 +- .../methods/bots/delete-my-commands.ts | 5 +- .../highlevel}/methods/bots/get-bot-info.ts | 5 +- .../methods/bots/get-bot-menu-button.ts | 5 +- .../methods/bots/get-callback-answer.ts | 9 +- .../methods/bots/get-game-high-scores.ts | 7 +- .../methods/bots/get-my-commands.ts | 5 +- .../methods/bots/normalize-command-scope.ts | 6 +- .../highlevel}/methods/bots/set-bot-info.ts | 7 +- .../methods/bots/set-bot-menu-button.ts | 7 +- .../highlevel}/methods/bots/set-game-score.ts | 9 +- .../methods/bots/set-my-commands.ts | 7 +- .../methods/bots/set-my-default-rights.ts | 8 +- .../methods/chats/add-chat-members.ts | 11 +- .../highlevel}/methods/chats/archive-chats.ts | 8 +- .../methods/chats/ban-chat-member.ts | 5 +- .../methods/chats/batched-queries.ts | 6 +- .../methods/chats/create-channel.ts | 9 +- .../highlevel}/methods/chats/create-group.ts | 10 +- .../methods/chats/create-supergroup.ts | 9 +- .../methods/chats/delete-channel.ts | 7 +- .../methods/chats/delete-chat-photo.ts | 7 +- .../highlevel}/methods/chats/delete-group.ts | 9 +- .../methods/chats/delete-history.ts | 11 +- .../methods/chats/delete-user-history.ts | 9 +- .../methods/chats/edit-admin-rights.ts | 7 +- .../methods/chats/get-chat-event-log.ts | 7 +- .../methods/chats/get-chat-member.ts | 7 +- .../methods/chats/get-chat-members.ts | 10 +- .../methods/chats/get-chat-preview.ts | 6 +- .../src/highlevel}/methods/chats/get-chat.ts | 6 +- .../highlevel}/methods/chats/get-full-chat.ts | 6 +- .../methods/chats/get-nearby-chats.ts | 12 +- .../methods/chats/get-similar-channels.ts | 5 +- .../methods/chats/iter-chat-event-log.ts | 7 +- .../methods/chats/iter-chat-members.ts | 5 +- .../src/highlevel}/methods/chats/join-chat.ts | 11 +- .../methods/chats/kick-chat-member.ts | 7 +- .../highlevel}/methods/chats/leave-chat.ts | 9 +- .../methods/chats/mark-chat-unread.ts | 7 +- .../src/highlevel}/methods/chats/open-chat.ts | 16 +- .../methods/chats/reorder-usernames.ts | 10 +- .../methods/chats/restrict-chat-member.ts | 7 +- .../highlevel}/methods/chats/save-draft.ts | 5 +- .../methods/chats/set-chat-color.ts | 13 +- .../chats/set-chat-default-permissions.ts | 9 +- .../methods/chats/set-chat-description.ts | 7 +- .../methods/chats/set-chat-photo.ts | 11 +- .../methods/chats/set-chat-title.ts | 7 +- .../highlevel}/methods/chats/set-chat-ttl.ts | 5 +- .../methods/chats/set-chat-username.ts | 7 +- .../highlevel}/methods/chats/set-slow-mode.ts | 7 +- .../chats/toggle-content-protection.ts | 7 +- .../methods/chats/toggle-fragment-username.ts | 10 +- .../methods/chats/toggle-join-requests.ts | 7 +- .../methods/chats/toggle-join-to-send.ts | 7 +- .../methods/chats/unarchive-chats.ts | 8 +- .../methods/chats/unban-chat-member.ts | 7 +- .../methods/contacts/add-contact.ts | 9 +- .../methods/contacts/delete-contacts.ts | 10 +- .../methods/contacts/get-contacts.ts | 7 +- .../methods/contacts/import-contacts.ts | 9 +- .../methods/dialogs/create-folder.ts | 8 +- .../methods/dialogs/delete-folder.ts | 8 +- .../highlevel}/methods/dialogs/edit-folder.ts | 8 +- .../highlevel}/methods/dialogs/find-folder.ts | 6 +- .../highlevel}/methods/dialogs/get-folders.ts | 8 +- .../methods/dialogs/get-peer-dialogs.ts | 6 +- .../methods/dialogs/iter-dialogs.ts | 8 +- .../methods/dialogs/set-folders-order.ts | 6 +- .../src/highlevel}/methods/files/_platform.ts | 0 .../highlevel}/methods/files/_platform.web.ts | 2 +- .../methods/files/download-buffer.ts | 7 +- .../highlevel}/methods/files/download-file.ts | 5 +- .../methods/files/download-file.web.ts | 2 +- .../methods/files/download-iterable.ts | 19 +- .../methods/files/download-stream.ts | 5 +- .../files/normalize-file-to-document.ts | 7 +- .../methods/files/normalize-input-file.ts | 6 +- .../methods/files/normalize-input-media.ts | 12 +- .../highlevel}/methods/files/upload-file.ts | 12 +- .../highlevel}/methods/files/upload-media.ts | 9 +- .../methods/forums/create-forum-topic.ts | 7 +- .../forums/delete-forum-topic-history.ts | 11 +- .../methods/forums/edit-forum-topic.ts | 7 +- .../methods/forums/get-forum-topics-by-id.ts | 6 +- .../methods/forums/get-forum-topics.ts | 5 +- .../methods/forums/iter-forum-topics.ts | 5 +- .../forums/reorder-pinned-forum-topics.ts | 5 +- .../forums/toggle-forum-topic-closed.ts | 5 +- .../forums/toggle-forum-topic-pinned.ts | 5 +- .../highlevel}/methods/forums/toggle-forum.ts | 7 +- .../forums/toggle-general-topic-hidden.ts | 5 +- .../invite-links/create-invite-link.ts | 5 +- .../methods/invite-links/edit-invite-link.ts | 5 +- .../invite-links/export-invite-link.ts | 5 +- .../invite-links/get-invite-link-members.ts | 5 +- .../methods/invite-links/get-invite-link.ts | 5 +- .../methods/invite-links/get-invite-links.ts | 5 +- .../invite-links/get-primary-invite-link.ts | 6 +- .../invite-links/hide-all-join-requests.ts | 5 +- .../methods/invite-links/hide-join-request.ts | 5 +- .../invite-links/iter-invite-link-members.ts | 5 +- .../methods/invite-links/iter-invite-links.ts | 5 +- .../invite-links/revoke-invite-link.ts | 5 +- .../highlevel}/methods/messages/close-poll.ts | 12 +- .../methods/messages/delete-messages.ts | 11 +- .../messages/delete-scheduled-messages.ts | 7 +- .../methods/messages/edit-inline-message.ts | 5 +- .../methods/messages/edit-message.ts | 5 +- .../methods/messages/find-in-update.ts | 14 +- .../methods/messages/forward-messages.ts | 14 +- .../messages/get-callback-query-message.ts | 7 +- .../messages/get-discussion-message.ts | 7 +- .../methods/messages/get-history.ts | 9 +- .../methods/messages/get-message-by-link.ts | 11 +- .../methods/messages/get-message-group.ts | 8 +- .../methods/messages/get-message-reactions.ts | 12 +- .../methods/messages/get-messages-unsafe.ts | 8 +- .../methods/messages/get-messages.ts | 8 +- .../methods/messages/get-reaction-users.ts | 5 +- .../methods/messages/get-reply-to.ts | 5 +- .../messages/get-scheduled-messages.ts | 8 +- .../methods/messages/iter-history.ts | 5 +- .../methods/messages/iter-reaction-users.ts | 5 +- .../methods/messages/iter-search-global.ts | 5 +- .../methods/messages/iter-search-messages.ts | 5 +- .../methods/messages/pin-message.ts | 5 +- .../methods/messages/read-history.ts | 15 +- .../methods/messages/read-reactions.ts | 9 +- .../methods/messages/search-global.ts | 7 +- .../methods/messages/search-messages.ts | 9 +- .../methods/messages/send-answer.ts | 9 +- .../methods/messages/send-comment.ts | 10 +- .../methods/messages/send-common.ts | 7 +- .../methods/messages/send-copy-group.ts | 8 +- .../highlevel}/methods/messages/send-copy.ts | 7 +- .../methods/messages/send-media-group.ts | 11 +- .../highlevel}/methods/messages/send-media.ts | 7 +- .../highlevel}/methods/messages/send-quote.ts | 13 +- .../methods/messages/send-reaction.ts | 8 +- .../highlevel}/methods/messages/send-reply.ts | 9 +- .../methods/messages/send-scheduled.ts | 10 +- .../methods/messages/send-text.test.ts | 17 +- .../highlevel}/methods/messages/send-text.ts | 13 +- .../methods/messages/send-typing.ts | 10 +- .../highlevel}/methods/messages/send-vote.ts | 14 +- .../methods/messages/translate-message.ts | 5 +- .../methods/messages/translate-text.ts | 6 +- .../methods/messages/unpin-all-messages.ts | 11 +- .../methods/messages/unpin-message.ts | 7 +- .../src/highlevel/methods/misc/chain-id.ts | 11 + .../methods/misc/init-takeout-session.ts | 5 +- .../methods/misc/normalize-privacy-rules.ts | 5 +- .../highlevel}/methods/misc/normalize-text.ts | 5 +- .../methods/password/change-cloud-password.ts | 11 +- .../methods/password/enable-cloud-password.ts | 9 +- .../methods/password/password-email.ts | 10 +- .../methods/password/remove-cloud-password.ts | 8 +- .../highlevel}/methods/premium/apply-boost.ts | 5 +- .../methods/premium/can-apply-boost.ts | 5 +- .../methods/premium/get-boost-stats.ts | 5 +- .../highlevel}/methods/premium/get-boosts.ts | 5 +- .../methods/premium/get-my-boost-slots.ts | 5 +- .../methods/premium/iter-boosters.ts | 5 +- .../methods/stickers/add-sticker-to-set.ts | 5 +- .../methods/stickers/create-sticker-set.ts | 5 +- .../stickers/delete-sticker-from-set.ts | 8 +- .../methods/stickers/get-custom-emojis.ts | 12 +- .../stickers/get-installed-stickers.ts | 7 +- .../methods/stickers/get-sticker-set.ts | 5 +- .../methods/stickers/move-sticker-in-set.ts | 8 +- .../methods/stickers/set-chat-sticker-set.ts | 8 +- .../methods/stickers/set-sticker-set-thumb.ts | 5 +- .../methods/stories/can-send-story.ts | 5 +- .../methods/stories/delete-stories.ts | 6 +- .../highlevel}/methods/stories/edit-story.ts | 5 +- .../methods/stories/find-in-update.ts | 12 +- .../methods/stories/get-all-stories.ts | 7 +- .../methods/stories/get-peer-stories.ts | 5 +- .../methods/stories/get-profile-stories.ts | 7 +- .../methods/stories/get-stories-by-id.ts | 8 +- .../stories/get-stories-interactions.ts | 6 +- .../methods/stories/get-story-link.ts | 5 +- .../methods/stories/get-story-viewers.ts | 5 +- .../methods/stories/hide-my-stories-views.ts | 11 +- .../stories/increment-stories-views.ts | 8 +- .../methods/stories/iter-all-stories.ts | 5 +- .../methods/stories/iter-profile-stories.ts | 5 +- .../methods/stories/iter-story-viewers.ts | 5 +- .../methods/stories/read-stories.ts | 5 +- .../methods/stories/report-story.ts | 8 +- .../methods/stories/send-story-reaction.ts | 7 +- .../highlevel}/methods/stories/send-story.ts | 7 +- .../stories/toggle-peer-stories-archived.ts | 7 +- .../methods/stories/toggle-stories-pinned.ts | 6 +- .../highlevel}/methods/users/block-user.ts | 7 +- .../methods/users/delete-profile-photos.ts | 9 +- .../methods/users/edit-close-friends.ts | 10 +- .../methods/users/get-common-chats.ts | 5 +- .../methods/users/get-global-ttl.ts | 4 +- .../src/highlevel}/methods/users/get-me.ts | 15 +- .../methods/users/get-my-username.ts | 4 +- .../methods/users/get-profile-photo.ts | 7 +- .../methods/users/get-profile-photos.ts | 9 +- .../methods/users/get-users.test.ts | 4 +- .../src/highlevel}/methods/users/get-users.ts | 6 +- .../methods/users/iter-profile-photos.ts | 5 +- .../methods/users/resolve-peer-many.ts | 11 +- .../methods/users/resolve-peer.test.ts | 2 +- .../highlevel}/methods/users/resolve-peer.ts | 17 +- .../methods/users/set-global-ttl.ts | 6 +- .../methods/users/set-my-emoji-status.ts | 8 +- .../methods/users/set-my-profile-photo.ts | 9 +- .../methods/users/set-my-username.ts | 8 +- .../highlevel}/methods/users/set-offline.ts | 6 +- .../highlevel}/methods/users/unblock-user.ts | 7 +- .../methods/users/update-profile.ts | 5 +- packages/core/src/highlevel/storage/index.ts | 4 + .../core/src/highlevel/storage/provider.ts | 8 + .../storage/repository/peers.test-utils.ts | 2 +- .../storage/repository/peers.ts | 4 +- .../repository/ref-messages.test-utils.ts | 2 +- .../storage/repository/ref-messages.ts | 2 +- .../highlevel/storage/service/current-user.ts | 167 ++ .../{ => highlevel}/storage/service/peers.ts | 28 +- .../storage/service/ref-messages.ts | 4 +- .../storage/service/updates.ts | 6 +- .../core/src/highlevel/storage/storage.ts | 46 + .../src/highlevel}/types/auth/index.ts | 0 .../src/highlevel}/types/auth/sent-code.ts | 2 +- .../highlevel}/types/bots/command-scope.ts | 2 +- .../highlevel}/types/bots/game-high-score.ts | 2 +- .../src/highlevel}/types/bots/index.ts | 0 .../src/highlevel}/types/bots/input/index.ts | 0 .../types/bots/input/input-inline-message.ts | 6 +- .../types/bots/input/input-inline-result.ts | 8 +- .../types/bots/keyboard-builder.test.ts | 0 .../highlevel}/types/bots/keyboard-builder.ts | 2 +- .../highlevel}/types/bots/keyboards.test.ts | 2 +- .../src/highlevel}/types/bots/keyboards.ts | 5 +- .../highlevel}/types/calls/discard-reason.ts | 4 +- .../src/highlevel}/types/calls/index.ts | 0 .../src/highlevel}/types/conversation.ts | 27 +- .../src/highlevel}/types/errors.ts | 3 +- .../highlevel}/types/files/file-location.ts | 2 +- .../src/highlevel}/types/files/index.ts | 0 .../highlevel}/types/files/uploaded-file.ts | 2 +- .../src/highlevel}/types/files/utils.ts | 2 +- .../highlevel}/types/files/web-document.ts | 3 +- .../src => core/src/highlevel}/types/index.ts | 0 .../src/highlevel}/types/media/audio.ts | 2 +- .../src/highlevel}/types/media/contact.ts | 2 +- .../src/highlevel}/types/media/dice.ts | 2 +- .../highlevel}/types/media/document-utils.ts | 2 +- .../src/highlevel}/types/media/document.ts | 2 +- .../src/highlevel}/types/media/game.ts | 2 +- .../src/highlevel}/types/media/index.ts | 0 .../src/highlevel}/types/media/input-media.ts | 3 +- .../src/highlevel}/types/media/invoice.ts | 3 +- .../src/highlevel}/types/media/location.ts | 2 +- .../src/highlevel}/types/media/photo.ts | 3 +- .../src/highlevel}/types/media/poll.ts | 4 +- .../src/highlevel}/types/media/sticker.ts | 3 +- .../src/highlevel}/types/media/story.ts | 2 +- .../src/highlevel}/types/media/thumbnail.ts | 9 +- .../src/highlevel}/types/media/venue.ts | 4 +- .../src/highlevel}/types/media/video.ts | 2 +- .../src/highlevel}/types/media/voice.ts | 2 +- .../src/highlevel}/types/media/web-page.ts | 3 +- .../src/highlevel}/types/messages/dialog.ts | 6 +- .../types/messages/draft-message.ts | 2 +- .../src/highlevel}/types/messages/index.ts | 0 .../types/messages/input-message-id.ts | 0 .../types/messages/message-action.ts | 2 +- .../types/messages/message-entity.ts | 2 +- .../types/messages/message-forward.ts | 3 +- .../types/messages/message-media.ts | 3 +- .../types/messages/message-reactions.ts | 2 +- .../types/messages/message-replies.ts | 3 +- .../src/highlevel}/types/messages/message.ts | 7 +- .../types/messages/replied-message.ts | 3 +- .../types/messages/search-filters.ts | 0 .../src/highlevel}/types/misc/entities.ts | 2 +- .../src/highlevel}/types/misc/index.ts | 0 .../types/misc/input-privacy-rule.ts | 3 +- .../src/highlevel}/types/misc/sticker-set.ts | 5 +- .../highlevel}/types/misc/takeout-session.ts | 12 +- .../src/highlevel}/types/peers/chat-colors.ts | 2 +- .../types/peers/chat-event/actions.ts | 5 +- .../types/peers/chat-event/filters.ts | 3 +- .../types/peers/chat-event/index.ts | 2 +- .../types/peers/chat-invite-link-member.ts | 2 +- .../types/peers/chat-invite-link.ts | 5 +- .../highlevel}/types/peers/chat-location.ts | 2 +- .../src/highlevel}/types/peers/chat-member.ts | 4 +- .../types/peers/chat-permissions.ts | 2 +- .../src/highlevel}/types/peers/chat-photo.ts | 8 +- .../highlevel}/types/peers/chat-preview.ts | 2 +- .../src/highlevel}/types/peers/chat.ts | 4 +- .../src/highlevel}/types/peers/forum-topic.ts | 5 +- .../src/highlevel}/types/peers/index.ts | 0 .../src/highlevel}/types/peers/peer.ts | 2 +- .../types/peers/peers-index.test.ts | 2 +- .../src/highlevel}/types/peers/peers-index.ts | 10 +- .../highlevel}/types/peers/typing-status.ts | 8 +- .../src/highlevel}/types/peers/user.test.ts | 2 +- .../src/highlevel}/types/peers/user.ts | 5 +- .../highlevel}/types/premium/boost-slot.ts | 4 +- .../highlevel}/types/premium/boost-stats.ts | 2 +- .../src/highlevel}/types/premium/boost.ts | 3 +- .../src/highlevel}/types/premium/index.ts | 0 .../types/reactions/emoji-status.ts | 2 +- .../src/highlevel}/types/reactions/index.ts | 0 .../types/reactions/peer-reaction.ts | 5 +- .../types/reactions/reaction-count.ts | 2 +- .../src/highlevel}/types/reactions/types.ts | 6 +- .../highlevel}/types/stories/all-stories.ts | 2 +- .../src/highlevel}/types/stories/index.ts | 0 .../types/stories/interactive/base.ts | 4 +- .../types/stories/interactive/channel-post.ts | 2 +- .../types/stories/interactive/index.ts | 3 +- .../types/stories/interactive/input.ts | 4 +- .../types/stories/interactive/location.ts | 4 +- .../types/stories/interactive/reaction.ts | 2 +- .../types/stories/interactive/venue.ts | 4 +- .../highlevel}/types/stories/peer-stories.ts | 5 +- .../highlevel}/types/stories/stealth-mode.ts | 2 +- .../types/stories/story-interactions.ts | 2 +- .../highlevel}/types/stories/story-viewer.ts | 3 +- .../src/highlevel}/types/stories/story.ts | 3 +- .../types/updates/bot-chat-join-request.ts | 3 +- .../highlevel}/types/updates/bot-reaction.ts | 2 +- .../highlevel}/types/updates/bot-stopped.ts | 2 +- .../types/updates/callback-query.ts | 6 +- .../types/updates/chat-join-request.ts | 3 +- .../types/updates/chat-member-update.ts | 3 +- .../types/updates/chosen-inline-result.ts | 2 +- .../types/updates/delete-message-update.ts | 3 +- .../types/updates/delete-story-update.ts | 2 +- .../types/updates/history-read-update.ts | 3 +- .../src/highlevel}/types/updates/index.ts | 0 .../highlevel}/types/updates/inline-query.ts | 2 +- .../highlevel}/types/updates/parse-update.ts | 2 +- .../highlevel}/types/updates/poll-update.ts | 2 +- .../src/highlevel}/types/updates/poll-vote.ts | 3 +- .../types/updates/pre-checkout-query.ts | 2 +- .../highlevel}/types/updates/story-update.ts | 5 +- .../types/updates/user-status-update.ts | 2 +- .../types/updates/user-typing-update.ts | 15 +- .../src => core/src/highlevel}/types/utils.ts | 2 +- .../src/highlevel}/updates/index.ts | 2 +- .../core/src/highlevel/updates/manager.ts | 1856 +++++++++++++++++ .../src/highlevel}/updates/parsed.ts | 6 +- .../src/highlevel}/updates/types.ts | 87 +- packages/core/src/highlevel/updates/utils.ts | 117 ++ .../src/highlevel/utils/convert-file-id.ts} | 10 +- .../src/highlevel}/utils/file-type.test.ts | 3 +- .../src/highlevel}/utils/file-type.ts | 0 .../src/highlevel}/utils/file-utils.test.ts | 2 +- .../src/highlevel}/utils/file-utils.ts | 6 +- .../src => core/src/highlevel}/utils/index.ts | 4 +- .../src/highlevel}/utils/inline-utils.test.ts | 3 +- .../src/highlevel}/utils/inline-utils.ts | 6 +- .../src/highlevel}/utils/inspectable.test.ts | 0 .../src/highlevel}/utils/inspectable.ts | 2 +- .../src/highlevel}/utils/memoize.test.ts | 0 .../src/highlevel}/utils/memoize.ts | 0 .../src/highlevel}/utils/misc-utils.ts | 3 +- .../src/highlevel}/utils/peer-utils.test.ts | 0 .../src/highlevel}/utils/peer-utils.ts | 10 +- .../src/highlevel/utils/platform/storage.ts | 7 + .../highlevel}/utils/platform/storage.web.ts | 5 +- .../highlevel}/utils/query-batcher.test.ts | 20 +- .../src/highlevel}/utils/query-batcher.ts | 22 +- .../src/highlevel}/utils/rps-meter.ts | 2 +- .../src/highlevel}/utils/stream-utils.test.ts | 0 .../src/highlevel}/utils/stream-utils.ts | 3 +- .../src/highlevel}/utils/voice-utils.test.ts | 2 +- .../src/highlevel}/utils/voice-utils.ts | 2 +- packages/core/src/highlevel/worker/errors.ts | 130 ++ packages/core/src/highlevel/worker/invoker.ts | 75 + .../src/highlevel/worker/platform/connect.ts | 20 + .../highlevel/worker/platform/connect.web.ts | 59 + .../src/highlevel/worker/platform/register.ts | 22 + .../highlevel/worker/platform/register.web.ts | 78 + packages/core/src/highlevel/worker/port.ts | 106 + .../core/src/highlevel/worker/protocol.ts | 55 + packages/core/src/highlevel/worker/storage.ts | 80 + packages/core/src/highlevel/worker/worker.ts | 123 ++ packages/core/src/index.ts | 3 +- packages/core/src/network/client.ts | 393 ++++ packages/core/src/network/index.ts | 1 + packages/core/src/network/network-manager.ts | 73 +- packages/core/src/storage/index.ts | 1 + packages/core/src/storage/provider.ts | 4 - .../src/storage/providers/idb/idb.test.ts | 4 +- .../core/src/storage/providers/idb/index.ts | 2 + .../storage/providers/idb/repository/peers.ts | 2 +- .../providers/idb/repository/ref-messages.ts | 2 +- .../src/storage/providers/memory/driver.ts | 4 +- .../src/storage/providers/memory/index.ts | 5 +- .../storage/providers/memory/memory.test.ts | 4 +- .../providers/memory/repository/auth-keys.ts | 4 +- .../storage/providers/memory/repository/kv.ts | 2 +- .../providers/memory/repository/peers.ts | 6 +- .../memory/repository/ref-messages.ts | 6 +- packages/core/src/storage/repository/index.ts | 4 +- packages/core/src/storage/service/base.ts | 8 +- .../core/src/storage/service/current-user.ts | 96 - .../core/src/storage/service/updates.test.ts | 2 +- packages/core/src/storage/storage.ts | 49 +- packages/core/src/types/utils.ts | 2 + packages/core/src/utils/crypto/password.ts | 5 +- packages/core/src/utils/function-utils.ts | 31 + packages/core/src/utils/index.ts | 1 + .../core/src/utils/string-session.test.ts | 14 +- packages/core/src/utils/string-session.ts | 8 +- packages/dispatcher/package.json | 14 +- packages/dispatcher/scripts/generate.cjs | 2 +- .../dispatcher/src/callback-data-builder.ts | 2 +- packages/dispatcher/src/context/base.ts | 2 +- .../dispatcher/src/context/callback-query.ts | 2 +- .../src/context/chat-join-request.ts | 2 +- .../src/context/chosen-inline-result.ts | 2 +- .../dispatcher/src/context/inline-query.ts | 2 +- packages/dispatcher/src/context/message.ts | 11 +- packages/dispatcher/src/context/parse.ts | 2 +- .../src/context/pre-checkout-query.ts | 2 +- packages/dispatcher/src/dispatcher.ts | 127 +- packages/dispatcher/src/filters/bots.test.ts | 2 +- packages/dispatcher/src/filters/bots.ts | 11 +- packages/dispatcher/src/filters/chat.ts | 5 +- packages/dispatcher/src/filters/group.ts | 2 +- packages/dispatcher/src/filters/logic.ts | 2 +- packages/dispatcher/src/filters/message.ts | 2 +- packages/dispatcher/src/filters/state.ts | 2 +- packages/dispatcher/src/filters/text.ts | 2 +- packages/dispatcher/src/filters/types.ts | 2 +- packages/dispatcher/src/filters/updates.ts | 2 +- packages/dispatcher/src/filters/user.ts | 12 +- packages/dispatcher/src/handler.ts | 2 +- packages/dispatcher/src/state/index.ts | 4 +- packages/dispatcher/src/state/key.ts | 2 +- packages/dispatcher/src/state/provider.ts | 7 + .../dispatcher/src/state/providers/index.ts | 2 + .../dispatcher/src/state/providers/memory.ts | 105 + .../dispatcher/src/state/providers/sqlite.ts | 123 ++ packages/dispatcher/src/state/repository.ts | 68 + packages/dispatcher/src/state/service.ts | 78 + packages/dispatcher/src/state/storage.ts | 122 -- packages/dispatcher/src/state/update-state.ts | 14 +- packages/dispatcher/src/wizard.ts | 2 +- packages/dispatcher/tsconfig.json | 3 - packages/file-id/package.json | 3 +- packages/file-id/src/index.ts | 1 - packages/file-id/src/parse.test.ts | 4 +- packages/file-id/src/parse.ts | 2 +- packages/file-id/src/serialize-unique.ts | 5 +- packages/file-id/src/serialize.ts | 12 +- packages/file-id/src/types.ts | 2 +- packages/file-id/src/utils.test.ts | 2 +- packages/file-id/src/utils.ts | 4 + packages/html-parser/package.json | 2 +- packages/i18n/package.json | 2 +- packages/markdown-parser/package.json | 2 +- packages/node/package.json | 2 +- packages/sqlite/src/driver.ts | 8 + packages/sqlite/src/index.ts | 10 +- packages/sqlite/src/repository/auth-keys.ts | 2 +- packages/sqlite/src/repository/kv.ts | 2 +- packages/sqlite/src/repository/peers.ts | 2 +- .../sqlite/src/repository/ref-messages.ts | 2 +- packages/test/src/client.ts | 6 +- packages/test/src/transport.ts | 2 +- packages/tl/scripts/fetch-api.ts | 2 +- pnpm-lock.yaml | 62 +- scripts/build-package.js | 11 + tsconfig.json | 6 + 524 files changed, 6262 insertions(+), 5215 deletions(-) delete mode 100644 packages/client/README.md delete mode 100644 packages/client/build.config.cjs delete mode 100644 packages/client/package.json delete mode 100644 packages/client/src/index.ts delete mode 100644 packages/client/src/methods/_init.ts delete mode 100644 packages/client/src/methods/auth/_state.ts delete mode 100644 packages/client/src/methods/misc/chain-id.ts delete mode 100644 packages/client/src/methods/updates/manager.ts delete mode 100644 packages/client/src/methods/updates/utils.ts delete mode 100644 packages/client/src/utils/platform/storage.ts delete mode 100644 packages/client/src/utils/updates-utils.ts delete mode 100644 packages/client/tsconfig.json delete mode 100644 packages/client/typedoc.cjs delete mode 100644 packages/client/utils.ts rename packages/{client => core}/scripts/generate-client.cjs (92%) rename packages/{client => core}/scripts/generate-updates.cjs (97%) rename packages/{client => core}/scripts/update-types.txt (100%) delete mode 100644 packages/core/src/base-client.ts delete mode 100644 packages/core/src/base-client.types.ts create mode 100644 packages/core/src/highlevel/base.ts rename packages/{client/src => core/src/highlevel}/client.ts (90%) create mode 100644 packages/core/src/highlevel/client.types.ts create mode 100644 packages/core/src/highlevel/index.ts rename packages/{client/src => core/src/highlevel}/methods/README.md (93%) rename packages/{client/src => core/src/highlevel}/methods/_imports.ts (82%) create mode 100644 packages/core/src/highlevel/methods/_init.ts rename packages/{client/src => core/src/highlevel}/methods/auth/check-password.ts (60%) rename packages/{client/src => core/src/highlevel}/methods/auth/get-password-hint.ts (63%) rename packages/{client/src => core/src/highlevel}/methods/auth/log-out.ts (52%) rename packages/{client/src => core/src/highlevel}/methods/auth/recover-password.ts (80%) rename packages/{client/src => core/src/highlevel}/methods/auth/resend-code.ts (79%) rename packages/{client/src => core/src/highlevel}/methods/auth/run.ts (60%) rename packages/{client/src => core/src/highlevel}/methods/auth/send-code.ts (67%) rename packages/{client/src => core/src/highlevel}/methods/auth/send-recovery-code.ts (68%) rename packages/{client/src => core/src/highlevel}/methods/auth/sign-in-bot.ts (54%) rename packages/{client/src => core/src/highlevel}/methods/auth/sign-in.ts (84%) rename packages/{client/src => core/src/highlevel}/methods/auth/start-test.ts (92%) rename packages/{client/src => core/src/highlevel}/methods/auth/start.ts (90%) create mode 100644 packages/core/src/highlevel/methods/auth/utils.ts rename packages/{client/src => core/src/highlevel}/methods/bots/answer-callback-query.ts (90%) rename packages/{client/src => core/src/highlevel}/methods/bots/answer-inline-query.ts (96%) rename packages/{client/src => core/src/highlevel}/methods/bots/answer-pre-checkout-query.ts (77%) rename packages/{client/src => core/src/highlevel}/methods/bots/delete-my-commands.ts (90%) rename packages/{client/src => core/src/highlevel}/methods/bots/get-bot-info.ts (88%) rename packages/{client/src => core/src/highlevel}/methods/bots/get-bot-menu-button.ts (64%) rename packages/{client/src => core/src/highlevel}/methods/bots/get-callback-answer.ts (86%) rename packages/{client/src => core/src/highlevel}/methods/bots/get-game-high-scores.ts (93%) rename packages/{client/src => core/src/highlevel}/methods/bots/get-my-commands.ts (89%) rename packages/{client/src => core/src/highlevel}/methods/bots/normalize-command-scope.ts (86%) rename packages/{client/src => core/src/highlevel}/methods/bots/set-bot-info.ts (89%) rename packages/{client/src => core/src/highlevel}/methods/bots/set-bot-menu-button.ts (75%) rename packages/{client/src => core/src/highlevel}/methods/bots/set-game-score.ts (93%) rename packages/{client/src => core/src/highlevel}/methods/bots/set-my-commands.ts (89%) rename packages/{client/src => core/src/highlevel}/methods/bots/set-my-default-rights.ts (79%) rename packages/{client/src => core/src/highlevel}/methods/chats/add-chat-members.ts (88%) rename packages/{client/src => core/src/highlevel}/methods/chats/archive-chats.ts (69%) rename packages/{client/src => core/src/highlevel}/methods/chats/ban-chat-member.ts (95%) rename packages/{client/src => core/src/highlevel}/methods/chats/batched-queries.ts (96%) rename packages/{client/src => core/src/highlevel}/methods/chats/create-channel.ts (77%) rename packages/{client/src => core/src/highlevel}/methods/chats/create-group.ts (84%) rename packages/{client/src => core/src/highlevel}/methods/chats/create-supergroup.ts (83%) rename packages/{client/src => core/src/highlevel}/methods/chats/delete-channel.ts (69%) rename packages/{client/src => core/src/highlevel}/methods/chats/delete-chat-photo.ts (82%) rename packages/{client/src => core/src/highlevel}/methods/chats/delete-group.ts (74%) rename packages/{client/src => core/src/highlevel}/methods/chats/delete-history.ts (80%) rename packages/{client/src => core/src/highlevel}/methods/chats/delete-user-history.ts (73%) rename packages/{client/src => core/src/highlevel}/methods/chats/edit-admin-rights.ts (86%) rename packages/{client/src => core/src/highlevel}/methods/chats/get-chat-event-log.ts (95%) rename packages/{client/src => core/src/highlevel}/methods/chats/get-chat-member.ts (92%) rename packages/{client/src => core/src/highlevel}/methods/chats/get-chat-members.ts (94%) rename packages/{client/src => core/src/highlevel}/methods/chats/get-chat-preview.ts (79%) rename packages/{client/src => core/src/highlevel}/methods/chats/get-chat.ts (85%) rename packages/{client/src => core/src/highlevel}/methods/chats/get-full-chat.ts (87%) rename packages/{client/src => core/src/highlevel}/methods/chats/get-nearby-chats.ts (68%) rename packages/{client/src => core/src/highlevel}/methods/chats/get-similar-channels.ts (93%) rename packages/{client/src => core/src/highlevel}/methods/chats/iter-chat-event-log.ts (94%) rename packages/{client/src => core/src/highlevel}/methods/chats/iter-chat-members.ts (95%) rename packages/{client/src => core/src/highlevel}/methods/chats/join-chat.ts (80%) rename packages/{client/src => core/src/highlevel}/methods/chats/kick-chat-member.ts (89%) rename packages/{client/src => core/src/highlevel}/methods/chats/leave-chat.ts (87%) rename packages/{client/src => core/src/highlevel}/methods/chats/mark-chat-unread.ts (66%) rename packages/{client/src => core/src/highlevel}/methods/chats/open-chat.ts (67%) rename packages/{client/src => core/src/highlevel}/methods/chats/reorder-usernames.ts (79%) rename packages/{client/src => core/src/highlevel}/methods/chats/restrict-chat-member.ts (92%) rename packages/{client/src => core/src/highlevel}/methods/chats/save-draft.ts (86%) rename packages/{client/src => core/src/highlevel}/methods/chats/set-chat-color.ts (83%) rename packages/{client/src => core/src/highlevel}/methods/chats/set-chat-default-permissions.ts (84%) rename packages/{client/src => core/src/highlevel}/methods/chats/set-chat-description.ts (81%) rename packages/{client/src => core/src/highlevel}/methods/chats/set-chat-photo.ts (90%) rename packages/{client/src => core/src/highlevel}/methods/chats/set-chat-title.ts (81%) rename packages/{client/src => core/src/highlevel}/methods/chats/set-chat-ttl.ts (71%) rename packages/{client/src => core/src/highlevel}/methods/chats/set-chat-username.ts (83%) rename packages/{client/src => core/src/highlevel}/methods/chats/set-slow-mode.ts (75%) rename packages/{client/src => core/src/highlevel}/methods/chats/toggle-content-protection.ts (82%) rename packages/{client/src => core/src/highlevel}/methods/chats/toggle-fragment-username.ts (84%) rename packages/{client/src => core/src/highlevel}/methods/chats/toggle-join-requests.ts (86%) rename packages/{client/src => core/src/highlevel}/methods/chats/toggle-join-to-send.ts (86%) rename packages/{client/src => core/src/highlevel}/methods/chats/unarchive-chats.ts (68%) rename packages/{client/src => core/src/highlevel}/methods/chats/unban-chat-member.ts (91%) rename packages/{client/src => core/src/highlevel}/methods/contacts/add-contact.ts (87%) rename packages/{client/src => core/src/highlevel}/methods/contacts/delete-contacts.ts (73%) rename packages/{client/src => core/src/highlevel}/methods/contacts/get-contacts.ts (59%) rename packages/{client/src => core/src/highlevel}/methods/contacts/import-contacts.ts (74%) rename packages/{client/src => core/src/highlevel}/methods/dialogs/create-folder.ts (82%) rename packages/{client/src => core/src/highlevel}/methods/dialogs/delete-folder.ts (50%) rename packages/{client/src => core/src/highlevel}/methods/dialogs/edit-folder.ts (86%) rename packages/{client/src => core/src/highlevel}/methods/dialogs/find-folder.ts (86%) rename packages/{client/src => core/src/highlevel}/methods/dialogs/get-folders.ts (74%) rename packages/{client/src => core/src/highlevel}/methods/dialogs/get-peer-dialogs.ts (73%) rename packages/{client/src => core/src/highlevel}/methods/dialogs/iter-dialogs.ts (97%) rename packages/{client/src => core/src/highlevel}/methods/dialogs/set-folders-order.ts (54%) rename packages/{client/src => core/src/highlevel}/methods/files/_platform.ts (100%) rename packages/{client/src => core/src/highlevel}/methods/files/_platform.web.ts (88%) rename packages/{client/src => core/src/highlevel}/methods/files/download-buffer.ts (84%) rename packages/{client/src => core/src/highlevel}/methods/files/download-file.ts (93%) rename packages/{client/src => core/src/highlevel}/methods/files/download-file.web.ts (66%) rename packages/{client/src => core/src/highlevel}/methods/files/download-iterable.ts (91%) rename packages/{client/src => core/src/highlevel}/methods/files/download-stream.ts (93%) rename packages/{client/src => core/src/highlevel}/methods/files/normalize-file-to-document.ts (81%) rename packages/{client/src => core/src/highlevel}/methods/files/normalize-input-file.ts (88%) rename packages/{client/src => core/src/highlevel}/methods/files/normalize-input-media.ts (97%) rename packages/{client/src => core/src/highlevel}/methods/files/upload-file.ts (96%) rename packages/{client/src => core/src/highlevel}/methods/files/upload-media.ts (90%) rename packages/{client/src => core/src/highlevel}/methods/forums/create-forum-topic.ts (90%) rename packages/{client/src => core/src/highlevel}/methods/forums/delete-forum-topic-history.ts (73%) rename packages/{client/src => core/src/highlevel}/methods/forums/edit-forum-topic.ts (92%) rename packages/{client/src => core/src/highlevel}/methods/forums/get-forum-topics-by-id.ts (82%) rename packages/{client/src => core/src/highlevel}/methods/forums/get-forum-topics.ts (95%) rename packages/{client/src => core/src/highlevel}/methods/forums/iter-forum-topics.ts (94%) rename packages/{client/src => core/src/highlevel}/methods/forums/reorder-pinned-forum-topics.ts (91%) rename packages/{client/src => core/src/highlevel}/methods/forums/toggle-forum-topic-closed.ts (93%) rename packages/{client/src => core/src/highlevel}/methods/forums/toggle-forum-topic-pinned.ts (91%) rename packages/{client/src => core/src/highlevel}/methods/forums/toggle-forum.ts (72%) rename packages/{client/src => core/src/highlevel}/methods/forums/toggle-general-topic-hidden.ts (93%) rename packages/{client/src => core/src/highlevel}/methods/invite-links/create-invite-link.ts (94%) rename packages/{client/src => core/src/highlevel}/methods/invite-links/edit-invite-link.ts (95%) rename packages/{client/src => core/src/highlevel}/methods/invite-links/export-invite-link.ts (76%) rename packages/{client/src => core/src/highlevel}/methods/invite-links/get-invite-link-members.ts (95%) rename packages/{client/src => core/src/highlevel}/methods/invite-links/get-invite-link.ts (87%) rename packages/{client/src => core/src/highlevel}/methods/invite-links/get-invite-links.ts (96%) rename packages/{client/src => core/src/highlevel}/methods/invite-links/get-primary-invite-link.ts (79%) rename packages/{client/src => core/src/highlevel}/methods/invite-links/hide-all-join-requests.ts (90%) rename packages/{client/src => core/src/highlevel}/methods/invite-links/hide-join-request.ts (90%) rename packages/{client/src => core/src/highlevel}/methods/invite-links/iter-invite-link-members.ts (95%) rename packages/{client/src => core/src/highlevel}/methods/invite-links/iter-invite-links.ts (95%) rename packages/{client/src => core/src/highlevel}/methods/invite-links/revoke-invite-link.ts (92%) rename packages/{client/src => core/src/highlevel}/methods/messages/close-poll.ts (79%) rename packages/{client/src => core/src/highlevel}/methods/messages/delete-messages.ts (90%) rename packages/{client/src => core/src/highlevel}/methods/messages/delete-scheduled-messages.ts (82%) rename packages/{client/src => core/src/highlevel}/methods/messages/edit-inline-message.ts (96%) rename packages/{client/src => core/src/highlevel}/methods/messages/edit-message.ts (96%) rename packages/{client/src => core/src/highlevel}/methods/messages/find-in-update.ts (80%) rename packages/{client/src => core/src/highlevel}/methods/messages/forward-messages.ts (92%) rename packages/{client/src => core/src/highlevel}/methods/messages/get-callback-query-message.ts (91%) rename packages/{client/src => core/src/highlevel}/methods/messages/get-discussion-message.ts (93%) rename packages/{client/src => core/src/highlevel}/methods/messages/get-history.ts (94%) rename packages/{client/src => core/src/highlevel}/methods/messages/get-message-by-link.ts (67%) rename packages/{client/src => core/src/highlevel}/methods/messages/get-message-group.ts (80%) rename packages/{client/src => core/src/highlevel}/methods/messages/get-message-reactions.ts (87%) rename packages/{client/src => core/src/highlevel}/methods/messages/get-messages-unsafe.ts (84%) rename packages/{client/src => core/src/highlevel}/methods/messages/get-messages.ts (92%) rename packages/{client/src => core/src/highlevel}/methods/messages/get-reaction-users.ts (94%) rename packages/{client/src => core/src/highlevel}/methods/messages/get-reply-to.ts (82%) rename packages/{client/src => core/src/highlevel}/methods/messages/get-scheduled-messages.ts (83%) rename packages/{client/src => core/src/highlevel}/methods/messages/iter-history.ts (94%) rename packages/{client/src => core/src/highlevel}/methods/messages/iter-reaction-users.ts (94%) rename packages/{client/src => core/src/highlevel}/methods/messages/iter-search-global.ts (95%) rename packages/{client/src => core/src/highlevel}/methods/messages/iter-search-messages.ts (96%) rename packages/{client/src => core/src/highlevel}/methods/messages/pin-message.ts (94%) rename packages/{client/src => core/src/highlevel}/methods/messages/read-history.ts (74%) rename packages/{client/src => core/src/highlevel}/methods/messages/read-reactions.ts (51%) rename packages/{client/src => core/src/highlevel}/methods/messages/search-global.ts (93%) rename packages/{client/src => core/src/highlevel}/methods/messages/search-messages.ts (95%) rename packages/{client/src => core/src/highlevel}/methods/messages/send-answer.ts (92%) rename packages/{client/src => core/src/highlevel}/methods/messages/send-comment.ts (93%) rename packages/{client/src => core/src/highlevel}/methods/messages/send-common.ts (95%) rename packages/{client/src => core/src/highlevel}/methods/messages/send-copy-group.ts (89%) rename packages/{client/src => core/src/highlevel}/methods/messages/send-copy.ts (92%) rename packages/{client/src => core/src/highlevel}/methods/messages/send-media-group.ts (93%) rename packages/{client/src => core/src/highlevel}/methods/messages/send-media.ts (96%) rename packages/{client/src => core/src/highlevel}/methods/messages/send-quote.ts (90%) rename packages/{client/src => core/src/highlevel}/methods/messages/send-reaction.ts (90%) rename packages/{client/src => core/src/highlevel}/methods/messages/send-reply.ts (89%) rename packages/{client/src => core/src/highlevel}/methods/messages/send-scheduled.ts (80%) rename packages/{client/src => core/src/highlevel}/methods/messages/send-text.test.ts (92%) rename packages/{client/src => core/src/highlevel}/methods/messages/send-text.ts (91%) rename packages/{client/src => core/src/highlevel}/methods/messages/send-typing.ts (89%) rename packages/{client/src => core/src/highlevel}/methods/messages/send-vote.ts (82%) rename packages/{client/src => core/src/highlevel}/methods/messages/translate-message.ts (90%) rename packages/{client/src => core/src/highlevel}/methods/messages/translate-text.ts (86%) rename packages/{client/src => core/src/highlevel}/methods/messages/unpin-all-messages.ts (70%) rename packages/{client/src => core/src/highlevel}/methods/messages/unpin-message.ts (77%) create mode 100644 packages/core/src/highlevel/methods/misc/chain-id.ts rename packages/{client/src => core/src/highlevel}/methods/misc/init-takeout-session.ts (79%) rename packages/{client/src => core/src/highlevel}/methods/misc/normalize-privacy-rules.ts (92%) rename packages/{client/src => core/src/highlevel}/methods/misc/normalize-text.ts (91%) rename packages/{client/src => core/src/highlevel}/methods/password/change-cloud-password.ts (64%) rename packages/{client/src => core/src/highlevel}/methods/password/enable-cloud-password.ts (81%) rename packages/{client/src => core/src/highlevel}/methods/password/password-email.ts (64%) rename packages/{client/src => core/src/highlevel}/methods/password/remove-cloud-password.ts (65%) rename packages/{client/src => core/src/highlevel}/methods/premium/apply-boost.ts (65%) rename packages/{client/src => core/src/highlevel}/methods/premium/can-apply-boost.ts (91%) rename packages/{client/src => core/src/highlevel}/methods/premium/get-boost-stats.ts (73%) rename packages/{client/src => core/src/highlevel}/methods/premium/get-boosts.ts (92%) rename packages/{client/src => core/src/highlevel}/methods/premium/get-my-boost-slots.ts (77%) rename packages/{client/src => core/src/highlevel}/methods/premium/iter-boosters.ts (94%) rename packages/{client/src => core/src/highlevel}/methods/stickers/add-sticker-to-set.ts (95%) rename packages/{client/src => core/src/highlevel}/methods/stickers/create-sticker-set.ts (97%) rename packages/{client/src => core/src/highlevel}/methods/stickers/delete-sticker-from-set.ts (75%) rename packages/{client/src => core/src/highlevel}/methods/stickers/get-custom-emojis.ts (74%) rename packages/{client/src => core/src/highlevel}/methods/stickers/get-installed-stickers.ts (71%) rename packages/{client/src => core/src/highlevel}/methods/stickers/get-sticker-set.ts (72%) rename packages/{client/src => core/src/highlevel}/methods/stickers/move-sticker-in-set.ts (78%) rename packages/{client/src => core/src/highlevel}/methods/stickers/set-chat-sticker-set.ts (78%) rename packages/{client/src => core/src/highlevel}/methods/stickers/set-sticker-set-thumb.ts (89%) rename packages/{client/src => core/src/highlevel}/methods/stories/can-send-story.ts (82%) rename packages/{client/src => core/src/highlevel}/methods/stories/delete-stories.ts (83%) rename packages/{client/src => core/src/highlevel}/methods/stories/edit-story.ts (95%) rename packages/{client/src => core/src/highlevel}/methods/stories/find-in-update.ts (53%) rename packages/{client/src => core/src/highlevel}/methods/stories/get-all-stories.ts (83%) rename packages/{client/src => core/src/highlevel}/methods/stories/get-peer-stories.ts (72%) rename packages/{client/src => core/src/highlevel}/methods/stories/get-profile-stories.ts (91%) rename packages/{client/src => core/src/highlevel}/methods/stories/get-stories-by-id.ts (79%) rename packages/{client/src => core/src/highlevel}/methods/stories/get-stories-interactions.ts (84%) rename packages/{client/src => core/src/highlevel}/methods/stories/get-story-link.ts (88%) rename packages/{client/src => core/src/highlevel}/methods/stories/get-story-viewers.ts (94%) rename packages/{client/src => core/src/highlevel}/methods/stories/hide-my-stories-views.ts (77%) rename packages/{client/src => core/src/highlevel}/methods/stories/increment-stories-views.ts (79%) rename packages/{client/src => core/src/highlevel}/methods/stories/iter-all-stories.ts (91%) rename packages/{client/src => core/src/highlevel}/methods/stories/iter-profile-stories.ts (94%) rename packages/{client/src => core/src/highlevel}/methods/stories/iter-story-viewers.ts (94%) rename packages/{client/src => core/src/highlevel}/methods/stories/read-stories.ts (73%) rename packages/{client/src => core/src/highlevel}/methods/stories/report-story.ts (79%) rename packages/{client/src => core/src/highlevel}/methods/stories/send-story-reaction.ts (85%) rename packages/{client/src => core/src/highlevel}/methods/stories/send-story.ts (94%) rename packages/{client/src => core/src/highlevel}/methods/stories/toggle-peer-stories-archived.ts (79%) rename packages/{client/src => core/src/highlevel}/methods/stories/toggle-stories-pinned.ts (86%) rename packages/{client/src => core/src/highlevel}/methods/users/block-user.ts (60%) rename packages/{client/src => core/src/highlevel}/methods/users/delete-profile-photos.ts (68%) rename packages/{client/src => core/src/highlevel}/methods/users/edit-close-friends.ts (67%) rename packages/{client/src => core/src/highlevel}/methods/users/get-common-chats.ts (78%) rename packages/{client/src => core/src/highlevel}/methods/users/get-global-ttl.ts (62%) rename packages/{client/src => core/src/highlevel}/methods/users/get-me.ts (53%) rename packages/{client/src => core/src/highlevel}/methods/users/get-my-username.ts (64%) rename packages/{client/src => core/src/highlevel}/methods/users/get-profile-photo.ts (81%) rename packages/{client/src => core/src/highlevel}/methods/users/get-profile-photos.ts (86%) rename packages/{client/src => core/src/highlevel}/methods/users/get-users.test.ts (92%) rename packages/{client/src => core/src/highlevel}/methods/users/get-users.ts (82%) rename packages/{client/src => core/src/highlevel}/methods/users/iter-profile-photos.ts (94%) rename packages/{client/src => core/src/highlevel}/methods/users/resolve-peer-many.ts (89%) rename packages/{client/src => core/src/highlevel}/methods/users/resolve-peer.test.ts (99%) rename packages/{client/src => core/src/highlevel}/methods/users/resolve-peer.ts (94%) rename packages/{client/src => core/src/highlevel}/methods/users/set-global-ttl.ts (60%) rename packages/{client/src => core/src/highlevel}/methods/users/set-my-emoji-status.ts (80%) rename packages/{client/src => core/src/highlevel}/methods/users/set-my-profile-photo.ts (85%) rename packages/{client/src => core/src/highlevel}/methods/users/set-my-username.ts (66%) rename packages/{client/src => core/src/highlevel}/methods/users/set-offline.ts (54%) rename packages/{client/src => core/src/highlevel}/methods/users/unblock-user.ts (61%) rename packages/{client/src => core/src/highlevel}/methods/users/update-profile.ts (89%) create mode 100644 packages/core/src/highlevel/storage/index.ts create mode 100644 packages/core/src/highlevel/storage/provider.ts rename packages/core/src/{ => highlevel}/storage/repository/peers.test-utils.ts (98%) rename packages/core/src/{ => highlevel}/storage/repository/peers.ts (90%) rename packages/core/src/{ => highlevel}/storage/repository/ref-messages.test-utils.ts (97%) rename packages/core/src/{ => highlevel}/storage/repository/ref-messages.ts (93%) create mode 100644 packages/core/src/highlevel/storage/service/current-user.ts rename packages/core/src/{ => highlevel}/storage/service/peers.ts (91%) rename packages/core/src/{ => highlevel}/storage/service/ref-messages.ts (91%) rename packages/core/src/{ => highlevel}/storage/service/updates.ts (91%) create mode 100644 packages/core/src/highlevel/storage/storage.ts rename packages/{client/src => core/src/highlevel}/types/auth/index.ts (100%) rename packages/{client/src => core/src/highlevel}/types/auth/sent-code.ts (98%) rename packages/{client/src => core/src/highlevel}/types/bots/command-scope.ts (98%) rename packages/{client/src => core/src/highlevel}/types/bots/game-high-score.ts (95%) rename packages/{client/src => core/src/highlevel}/types/bots/index.ts (100%) rename packages/{client/src => core/src/highlevel}/types/bots/input/index.ts (100%) rename packages/{client/src => core/src/highlevel}/types/bots/input/input-inline-message.ts (98%) rename packages/{client/src => core/src/highlevel}/types/bots/input/input-inline-result.ts (99%) rename packages/{client/src => core/src/highlevel}/types/bots/keyboard-builder.test.ts (100%) rename packages/{client/src => core/src/highlevel}/types/bots/keyboard-builder.ts (98%) rename packages/{client/src => core/src/highlevel}/types/bots/keyboards.test.ts (99%) rename packages/{client/src => core/src/highlevel}/types/bots/keyboards.ts (98%) rename packages/{client/src => core/src/highlevel}/types/calls/discard-reason.ts (93%) rename packages/{client/src => core/src/highlevel}/types/calls/index.ts (100%) rename packages/{client/src => core/src/highlevel}/types/conversation.ts (95%) rename packages/{client/src => core/src/highlevel}/types/errors.ts (95%) rename packages/{client/src => core/src/highlevel}/types/files/file-location.ts (97%) rename packages/{client/src => core/src/highlevel}/types/files/index.ts (100%) rename packages/{client/src => core/src/highlevel}/types/files/uploaded-file.ts (95%) rename packages/{client/src => core/src/highlevel}/types/files/utils.ts (99%) rename packages/{client/src => core/src/highlevel}/types/files/web-document.ts (94%) rename packages/{client/src => core/src/highlevel}/types/index.ts (100%) rename packages/{client/src => core/src/highlevel}/types/media/audio.ts (97%) rename packages/{client/src => core/src/highlevel}/types/media/contact.ts (96%) rename packages/{client/src => core/src/highlevel}/types/media/dice.ts (99%) rename packages/{client/src => core/src/highlevel}/types/media/document-utils.ts (97%) rename packages/{client/src => core/src/highlevel}/types/media/document.ts (99%) rename packages/{client/src => core/src/highlevel}/types/media/game.ts (98%) rename packages/{client/src => core/src/highlevel}/types/media/index.ts (100%) rename packages/{client/src => core/src/highlevel}/types/media/input-media.ts (99%) rename packages/{client/src => core/src/highlevel}/types/media/invoice.ts (98%) rename packages/{client/src => core/src/highlevel}/types/media/location.ts (99%) rename packages/{client/src => core/src/highlevel}/types/media/photo.ts (98%) rename packages/{client/src => core/src/highlevel}/types/media/poll.ts (98%) rename packages/{client/src => core/src/highlevel}/types/media/sticker.ts (98%) rename packages/{client/src => core/src/highlevel}/types/media/story.ts (97%) rename packages/{client/src => core/src/highlevel}/types/media/thumbnail.ts (97%) rename packages/{client/src => core/src/highlevel}/types/media/venue.ts (96%) rename packages/{client/src => core/src/highlevel}/types/media/video.ts (98%) rename packages/{client/src => core/src/highlevel}/types/media/voice.ts (97%) rename packages/{client/src => core/src/highlevel}/types/media/web-page.ts (98%) rename packages/{client/src => core/src/highlevel}/types/messages/dialog.ts (97%) rename packages/{client/src => core/src/highlevel}/types/messages/draft-message.ts (97%) rename packages/{client/src => core/src/highlevel}/types/messages/index.ts (100%) rename packages/{client/src => core/src/highlevel}/types/messages/input-message-id.ts (100%) rename packages/{client/src => core/src/highlevel}/types/messages/message-action.ts (99%) rename packages/{client/src => core/src/highlevel}/types/messages/message-entity.ts (99%) rename packages/{client/src => core/src/highlevel}/types/messages/message-forward.ts (95%) rename packages/{client/src => core/src/highlevel}/types/messages/message-media.ts (97%) rename packages/{client/src => core/src/highlevel}/types/messages/message-reactions.ts (97%) rename packages/{client/src => core/src/highlevel}/types/messages/message-replies.ts (95%) rename packages/{client/src => core/src/highlevel}/types/messages/message.ts (97%) rename packages/{client/src => core/src/highlevel}/types/messages/replied-message.ts (98%) rename packages/{client/src => core/src/highlevel}/types/messages/search-filters.ts (100%) rename packages/{client/src => core/src/highlevel}/types/misc/entities.ts (91%) rename packages/{client/src => core/src/highlevel}/types/misc/index.ts (100%) rename packages/{client/src => core/src/highlevel}/types/misc/input-privacy-rule.ts (97%) rename packages/{client/src => core/src/highlevel}/types/misc/sticker-set.ts (98%) rename packages/{client/src => core/src/highlevel}/types/misc/takeout-session.ts (86%) rename packages/{client/src => core/src/highlevel}/types/peers/chat-colors.ts (96%) rename packages/{client/src => core/src/highlevel}/types/peers/chat-event/actions.ts (99%) rename packages/{client/src => core/src/highlevel}/types/peers/chat-event/filters.ts (97%) rename packages/{client/src => core/src/highlevel}/types/peers/chat-event/index.ts (97%) rename packages/{client/src => core/src/highlevel}/types/peers/chat-invite-link-member.ts (97%) rename packages/{client/src => core/src/highlevel}/types/peers/chat-invite-link.ts (96%) rename packages/{client/src => core/src/highlevel}/types/peers/chat-location.ts (94%) rename packages/{client/src => core/src/highlevel}/types/peers/chat-member.ts (98%) rename packages/{client/src => core/src/highlevel}/types/peers/chat-permissions.ts (99%) rename packages/{client/src => core/src/highlevel}/types/peers/chat-photo.ts (95%) rename packages/{client/src => core/src/highlevel}/types/peers/chat-preview.ts (98%) rename packages/{client/src => core/src/highlevel}/types/peers/chat.ts (99%) rename packages/{client/src => core/src/highlevel}/types/peers/forum-topic.ts (96%) rename packages/{client/src => core/src/highlevel}/types/peers/index.ts (100%) rename packages/{client/src => core/src/highlevel}/types/peers/peer.ts (98%) rename packages/{client/src => core/src/highlevel}/types/peers/peers-index.test.ts (98%) rename packages/{client/src => core/src/highlevel}/types/peers/peers-index.ts (87%) rename packages/{client/src => core/src/highlevel}/types/peers/typing-status.ts (81%) rename packages/{client/src => core/src/highlevel}/types/peers/user.test.ts (99%) rename packages/{client/src => core/src/highlevel}/types/peers/user.ts (98%) rename packages/{client/src => core/src/highlevel}/types/premium/boost-slot.ts (94%) rename packages/{client/src => core/src/highlevel}/types/premium/boost-stats.ts (98%) rename packages/{client/src => core/src/highlevel}/types/premium/boost.ts (96%) rename packages/{client/src => core/src/highlevel}/types/premium/index.ts (100%) rename packages/{client/src => core/src/highlevel}/types/reactions/emoji-status.ts (94%) rename packages/{client/src => core/src/highlevel}/types/reactions/index.ts (100%) rename packages/{client/src => core/src/highlevel}/types/reactions/peer-reaction.ts (88%) rename packages/{client/src => core/src/highlevel}/types/reactions/reaction-count.ts (95%) rename packages/{client/src => core/src/highlevel}/types/reactions/types.ts (92%) rename packages/{client/src => core/src/highlevel}/types/stories/all-stories.ts (97%) rename packages/{client/src => core/src/highlevel}/types/stories/index.ts (100%) rename packages/{client/src => core/src/highlevel}/types/stories/interactive/base.ts (84%) rename packages/{client/src => core/src/highlevel}/types/stories/interactive/channel-post.ts (96%) rename packages/{client/src => core/src/highlevel}/types/stories/interactive/index.ts (92%) rename packages/{client/src => core/src/highlevel}/types/stories/interactive/input.ts (98%) rename packages/{client/src => core/src/highlevel}/types/stories/interactive/location.ts (88%) rename packages/{client/src => core/src/highlevel}/types/stories/interactive/reaction.ts (96%) rename packages/{client/src => core/src/highlevel}/types/stories/interactive/venue.ts (93%) rename packages/{client/src => core/src/highlevel}/types/stories/peer-stories.ts (85%) rename packages/{client/src => core/src/highlevel}/types/stories/stealth-mode.ts (94%) rename packages/{client/src => core/src/highlevel}/types/stories/story-interactions.ts (98%) rename packages/{client/src => core/src/highlevel}/types/stories/story-viewer.ts (97%) rename packages/{client/src => core/src/highlevel}/types/stories/story.ts (98%) rename packages/{client/src => core/src/highlevel}/types/updates/bot-chat-join-request.ts (93%) rename packages/{client/src => core/src/highlevel}/types/updates/bot-reaction.ts (98%) rename packages/{client/src => core/src/highlevel}/types/updates/bot-stopped.ts (96%) rename packages/{client/src => core/src/highlevel}/types/updates/callback-query.ts (95%) rename packages/{client/src => core/src/highlevel}/types/updates/chat-join-request.ts (93%) rename packages/{client/src => core/src/highlevel}/types/updates/chat-member-update.ts (98%) rename packages/{client/src => core/src/highlevel}/types/updates/chosen-inline-result.ts (98%) rename packages/{client/src => core/src/highlevel}/types/updates/delete-message-update.ts (86%) rename packages/{client/src => core/src/highlevel}/types/updates/delete-story-update.ts (95%) rename packages/{client/src => core/src/highlevel}/types/updates/history-read-update.ts (96%) rename packages/{client/src => core/src/highlevel}/types/updates/index.ts (100%) rename packages/{client/src => core/src/highlevel}/types/updates/inline-query.ts (98%) rename packages/{client/src => core/src/highlevel}/types/updates/parse-update.ts (99%) rename packages/{client/src => core/src/highlevel}/types/updates/poll-update.ts (98%) rename packages/{client/src => core/src/highlevel}/types/updates/poll-vote.ts (96%) rename packages/{client/src => core/src/highlevel}/types/updates/pre-checkout-query.ts (97%) rename packages/{client/src => core/src/highlevel}/types/updates/story-update.ts (86%) rename packages/{client/src => core/src/highlevel}/types/updates/user-status-update.ts (97%) rename packages/{client/src => core/src/highlevel}/types/updates/user-typing-update.ts (83%) rename packages/{client/src => core/src/highlevel}/types/utils.ts (91%) rename packages/{client/src/methods => core/src/highlevel}/updates/index.ts (60%) create mode 100644 packages/core/src/highlevel/updates/manager.ts rename packages/{client/src/methods => core/src/highlevel}/updates/parsed.ts (93%) rename packages/{client/src/methods => core/src/highlevel}/updates/types.ts (56%) create mode 100644 packages/core/src/highlevel/updates/utils.ts rename packages/{file-id/src/convert.ts => core/src/highlevel/utils/convert-file-id.ts} (97%) rename packages/{client/src => core/src/highlevel}/utils/file-type.test.ts (98%) rename packages/{client/src => core/src/highlevel}/utils/file-type.ts (100%) rename packages/{client/src => core/src/highlevel}/utils/file-utils.test.ts (99%) rename packages/{client/src => core/src/highlevel}/utils/file-utils.ts (95%) rename packages/{client/src => core/src/highlevel}/utils/index.ts (78%) rename packages/{client/src => core/src/highlevel}/utils/inline-utils.test.ts (96%) rename packages/{client/src => core/src/highlevel}/utils/inline-utils.ts (93%) rename packages/{client/src => core/src/highlevel}/utils/inspectable.test.ts (100%) rename packages/{client/src => core/src/highlevel}/utils/inspectable.ts (97%) rename packages/{client/src => core/src/highlevel}/utils/memoize.test.ts (100%) rename packages/{client/src => core/src/highlevel}/utils/memoize.ts (100%) rename packages/{client/src => core/src/highlevel}/utils/misc-utils.ts (96%) rename packages/{client/src => core/src/highlevel}/utils/peer-utils.test.ts (100%) rename packages/{client/src => core/src/highlevel}/utils/peer-utils.ts (93%) create mode 100644 packages/core/src/highlevel/utils/platform/storage.ts rename packages/{client/src => core/src/highlevel}/utils/platform/storage.web.ts (65%) rename packages/{client/src => core/src/highlevel}/utils/query-batcher.test.ts (96%) rename packages/{client/src => core/src/highlevel}/utils/query-batcher.ts (90%) rename packages/{client/src => core/src/highlevel}/utils/rps-meter.ts (96%) rename packages/{client/src => core/src/highlevel}/utils/stream-utils.test.ts (100%) rename packages/{client/src => core/src/highlevel}/utils/stream-utils.ts (97%) rename packages/{client/src => core/src/highlevel}/utils/voice-utils.test.ts (96%) rename packages/{client/src => core/src/highlevel}/utils/voice-utils.ts (97%) create mode 100644 packages/core/src/highlevel/worker/errors.ts create mode 100644 packages/core/src/highlevel/worker/invoker.ts create mode 100644 packages/core/src/highlevel/worker/platform/connect.ts create mode 100644 packages/core/src/highlevel/worker/platform/connect.web.ts create mode 100644 packages/core/src/highlevel/worker/platform/register.ts create mode 100644 packages/core/src/highlevel/worker/platform/register.web.ts create mode 100644 packages/core/src/highlevel/worker/port.ts create mode 100644 packages/core/src/highlevel/worker/protocol.ts create mode 100644 packages/core/src/highlevel/worker/storage.ts create mode 100644 packages/core/src/highlevel/worker/worker.ts create mode 100644 packages/core/src/network/client.ts delete mode 100644 packages/core/src/storage/service/current-user.ts create mode 100644 packages/dispatcher/src/state/provider.ts create mode 100644 packages/dispatcher/src/state/providers/index.ts create mode 100644 packages/dispatcher/src/state/providers/memory.ts create mode 100644 packages/dispatcher/src/state/providers/sqlite.ts create mode 100644 packages/dispatcher/src/state/repository.ts create mode 100644 packages/dispatcher/src/state/service.ts delete mode 100644 packages/dispatcher/src/state/storage.ts diff --git a/.config/eslint.cjs b/.config/eslint.cjs index f26e5229..8f16012a 100644 --- a/.config/eslint.cjs +++ b/.config/eslint.cjs @@ -160,7 +160,7 @@ module.exports = { 'simple-import-sort/imports': [ 'error', { - groups: [['^[a-z]'], ['^@mtcute'], ['^@/'], ['^~/'], ['^\\.']], + groups: [['^[a-z]'], ['^@?mtcute'], ['^@/'], ['^~/'], ['^\\.']], }, ], 'simple-import-sort/exports': 'error', diff --git a/package.json b/package.json index 7a6704ef..1c0704cb 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "mtcute", + "name": "mtcute-workspace", "private": true, "version": "0.6.0", "description": "Type-safe library for MTProto (Telegram API) for browser and NodeJS", diff --git a/packages/client/README.md b/packages/client/README.md deleted file mode 100644 index b11abacb..00000000 --- a/packages/client/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# @mtcute/client - -📖 [API Reference](https://ref.mtcute.dev/modules/_mtcute_client.html) - -High-level Telegram client implementation over the `@mtcute/core` base library. - -## Features -- **Updates handling**: Implements proper updates handling, including ordering and gap recovery ([learn more](https://core.telegram.org/api/updates)) -- **Wrapper classes**: Easy-to-use classes that wrap the complex TL objects and provide a clean interface -- **High-level methods**: Methods that wrap the low-level API calls and provide a clean interface -- **Tree-shaking**: Only import the methods you need, and the rest will not be included into the bundle -- **Web support**: Works in the browser with no additional configuration - -## Usage - -```ts -import { TelegramClient } from '@mtcute/client' - -const tg = new TelegramClient({ - apiId: 12345, - apiHash: '0123456789abcdef0123456789abcdef', - // ... + supports all options from @mtcute/core ... -}) - -tg.start({ - phone: '+1234567890', - password: () => prompt('Enter password'), - code: () => prompt('Enter code'), -}, (user) => { - console.log(`Logged in as ${user.displayName}`) -}) -``` - -> **Note**: for web, prefer BaseTelegramClient over TelegramClient, -> as it is tree-shakeable – [learn more](https://mtcute.dev/guide/topics/treeshaking.html) diff --git a/packages/client/build.config.cjs b/packages/client/build.config.cjs deleted file mode 100644 index da490312..00000000 --- a/packages/client/build.config.cjs +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - esmOnlyDirectives: true, -} diff --git a/packages/client/package.json b/packages/client/package.json deleted file mode 100644 index 5282f59e..00000000 --- a/packages/client/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "@mtcute/client", - "private": true, - "version": "0.6.0", - "description": "High-level API over @mtcute/core", - "author": "Alina Sireneva ", - "license": "MIT", - "main": "src/index.ts", - "type": "module", - "scripts": { - "build": "pnpm run -w build-package client", - "gen-client": "node ./scripts/generate-client.cjs", - "gen-updates": "node ./scripts/generate-updates.cjs" - }, - "distOnlyFields": { - "exports": { - ".": { - "import": "./esm/index.js", - "require": "./cjs/index.js" - }, - "./methods/*": { - "import": "./esm/methods/*", - "require": "./cjs/methods/*" - }, - "./utils.js": { - "import": "./esm/utils/index.js", - "require": "./cjs/utils/index.js" - } - } - }, - "browser": { - "./src/methods/files/_platform.js": "./src/methods/files/_platform.web.js", - "./src/methods/files/download-file.js": "./src/methods/files/download-file.web.js", - "./src/utils/platform/storage.js": "./src/utils/platform/storage.web.js" - }, - "dependencies": { - "@mtcute/core": "workspace:^", - "@mtcute/file-id": "workspace:^" - }, - "devDependencies": { - "@mtcute/test": "workspace:^" - } -} diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts deleted file mode 100644 index 4aaf6b14..00000000 --- a/packages/client/src/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './client.js' -export * from './types/index.js' -export * from './utils/peer-utils.js' -export { createDummyUpdate } from './utils/updates-utils.js' -export * from '@mtcute/core' diff --git a/packages/client/src/methods/_init.ts b/packages/client/src/methods/_init.ts deleted file mode 100644 index 5be401cf..00000000 --- a/packages/client/src/methods/_init.ts +++ /dev/null @@ -1,111 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ - -import { BaseTelegramClientOptions, IMtStorageProvider } from '@mtcute/core' -// @copy -import { MemoryStorage } from '@mtcute/core/src/storage/providers/memory/index.js' - -import { TelegramClient } from '../client.js' -// @copy -import { Conversation } from '../types/conversation.js' -// @copy -import { _defaultStorageFactory } from '../utils/platform/storage.js' -// @copy -import { - enableUpdatesProcessing, - makeParsedUpdateHandler, - ParsedUpdateHandlerParams, - UpdatesManagerParams, -} from './updates/index.js' - -// @extension -interface TelegramClientExt { - _disableUpdatesManager: boolean -} - -// @copy -interface TelegramClientOptions extends Omit { - /** - * Storage to use for this client. - * - * If a string is passed, it will be used as: - * - a path to a JSON file for Node.js - * - IndexedDB database name for browsers - * - * If omitted, {@link MemoryStorage} is used - */ - storage?: string | IMtStorageProvider - - /** - * Parameters for updates manager. - */ - updates?: Omit - - /** - * **ADVANCED** - * - * If set to `true`, updates manager will not be created, - * and only raw TL Updates will be emitted. - * - * Unlike {@link TelegramClientOptions.disableUpdates}, this - * does not prevent the updates from being sent by the server, - * but disables proper handling of them (see [Working with Updates](https://core.telegram.org/api/updates)) - * - * This may be useful in some cases when you require more control over - * the updates or to minimize additional overhead from properly handling them - * for some very particular use cases. - * - * The updates **will not** be dispatched the normal way, instead - * you should manually add a handler using `client.network.setUpdateHandler`. - */ - disableUpdatesManager?: boolean - - /** - * If `true`, the updates that were handled by some {@link Conversation} - * will not be dispatched any further. - * - * @default true - */ - skipConversationUpdates?: boolean -} - -// @initialize=super -/** @internal */ -function _initializeClientSuper(this: TelegramClient, opts: TelegramClientOptions) { - if (typeof opts.storage === 'string') { - opts.storage = _defaultStorageFactory(opts.storage) - } else if (!opts.storage) { - opts.storage = new MemoryStorage() - } - - /* eslint-disable @typescript-eslint/no-unsafe-call */ - // @ts-expect-error codegen - super(opts) - /* eslint-enable @typescript-eslint/no-unsafe-call */ -} - -// @initialize -/** @internal */ -function _initializeClient(this: TelegramClient, opts: TelegramClientOptions) { - this._disableUpdatesManager = opts.disableUpdatesManager ?? false - const skipConversationUpdates = opts.skipConversationUpdates ?? true - - if (!opts.disableUpdates && !opts.disableUpdatesManager) { - const { messageGroupingInterval, ...managerParams } = opts.updates ?? {} - - enableUpdatesProcessing(this, { - ...managerParams, - onUpdate: makeParsedUpdateHandler({ - messageGroupingInterval, - onUpdate: (update) => { - if (Conversation.handleUpdate(this, update) && skipConversationUpdates) return - - this.emit('update', update) - this.emit(update.name, update.data) - }, - onRawUpdate: (update, peers) => { - this.emit('raw_update', update, peers) - }, - }), - }) - } -} diff --git a/packages/client/src/methods/auth/_state.ts b/packages/client/src/methods/auth/_state.ts deleted file mode 100644 index 794e03f2..00000000 --- a/packages/client/src/methods/auth/_state.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* eslint-disable no-inner-declarations */ -import { BaseTelegramClient, MtUnsupportedError, tl } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' - -import { User } from '../../types/peers/user.js' - -/** @internal */ -export async function _onAuthorization( - client: BaseTelegramClient, - auth: tl.auth.TypeAuthorization, -): Promise { - if (auth._ === 'auth.authorizationSignUpRequired') { - throw new MtUnsupportedError( - 'Signup is no longer supported by Telegram for non-official clients. Please use your mobile device to sign up.', - ) - } - - assertTypeIs('_onAuthorization (@ auth.authorization -> user)', auth.user, 'user') - - // todo: selfUsername - await client.notifyLoggedIn(auth) - - // telegram ignores invokeWithoutUpdates for auth methods - if (client.network.params.disableUpdates) client.network.resetSessions() - - return new User(auth.user) -} - -/** - * Check if the given peer/input peer is referring to the current user - */ -export function isSelfPeer( - client: BaseTelegramClient, - peer: tl.TypeInputPeer | tl.TypePeer | tl.TypeInputUser, -): boolean { - const state = client.storage.self.getCached() - if (!state) return false - - switch (peer._) { - case 'inputPeerSelf': - case 'inputUserSelf': - return true - case 'inputPeerUser': - case 'inputPeerUserFromMessage': - case 'inputUser': - case 'inputUserFromMessage': - case 'peerUser': - return peer.userId === state.userId - default: - return false - } -} diff --git a/packages/client/src/methods/misc/chain-id.ts b/packages/client/src/methods/misc/chain-id.ts deleted file mode 100644 index a62c31af..00000000 --- a/packages/client/src/methods/misc/chain-id.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { BaseTelegramClient, getMarkedPeerId, tl } from '@mtcute/core' - -/** @internal */ -export function _getPeerChainId(client: BaseTelegramClient, peer: tl.TypeInputPeer, prefix = 'peer') { - const id = peer._ === 'inputPeerSelf' ? client.storage.self.getCached()!.userId : getMarkedPeerId(peer) - - return `${prefix}:${id}` -} diff --git a/packages/client/src/methods/updates/manager.ts b/packages/client/src/methods/updates/manager.ts deleted file mode 100644 index 55bf0095..00000000 --- a/packages/client/src/methods/updates/manager.ts +++ /dev/null @@ -1,1835 +0,0 @@ -/* eslint-disable max-depth,max-params */ -import { assertNever, BaseTelegramClient, MaybeAsync, MtArgumentError, parseMarkedPeerId, tl } from '@mtcute/core' -import { getBarePeerId, getMarkedPeerId, toggleChannelIdMark } from '@mtcute/core/utils.js' - -import { PeersIndex } from '../../types/index.js' -import { isInputPeerChannel, isInputPeerUser, toInputChannel, toInputUser } from '../../utils/peer-utils.js' -import { RpsMeter } from '../../utils/rps-meter.js' -import { createDummyUpdatesContainer } from '../../utils/updates-utils.js' -import { _getChannelsBatched, _getUsersBatched } from '../chats/batched-queries.js' -import { resolvePeer } from '../users/resolve-peer.js' -import { createUpdatesState, PendingUpdate, toPendingUpdate, UpdatesManagerParams, UpdatesState } from './types.js' -import { extractChannelIdFromUpdate, messageToUpdate } from './utils.js' - -// code in this file is very bad, thanks to Telegram's awesome updates mechanism - -/** - * Enable RPS meter. - * Only available in NodeJS v10.7.0 and newer - * - * > **Note**: This may have negative impact on performance - * - * @param size Sampling size - * @param time Window time - */ -export function enableRps(client: BaseTelegramClient, size?: number, time?: number): void { - const state = getState(client) - state.rpsIncoming = new RpsMeter(size, time) - state.rpsProcessing = new RpsMeter(size, time) -} - -/** - * Get current average incoming RPS - * - * Incoming RPS is calculated based on - * incoming update containers. Normally, - * they should be around the same, except - * rare situations when processing rps - * may peak. - */ -export function getCurrentRpsIncoming(client: BaseTelegramClient): number { - const state = getState(client) - - if (!state.rpsIncoming) { - throw new MtArgumentError('RPS meter is not enabled, use .enableRps() first') - } - - return state.rpsIncoming.getRps() -} - -/** - * Get current average processing RPS - * - * Processing RPS is calculated based on - * dispatched updates. Normally, - * they should be around the same, except - * rare situations when processing rps - * may peak. - */ -export function getCurrentRpsProcessing(client: BaseTelegramClient): number { - const state = getState(client) - - if (!state.rpsProcessing) { - throw new MtArgumentError('RPS meter is not enabled, use .enableRps() first') - } - - return state.rpsProcessing.getRps() -} - -/** - * Add updates handling capabilities to {@link BaseTelegramClient} - * - * {@link BaseTelegramClient} doesn't do any updates processing on its own, and instead - * dispatches raw TL updates to user of the class. - * - * This method enables updates processing according to Telegram's updates mechanism. - * - * > **Note**: you don't need to use this if you are using {@link TelegramClient} - * - * @param client Client instance - * @param params Updates manager parameters - * @noemit - */ -export function enableUpdatesProcessing(client: BaseTelegramClient, params: UpdatesManagerParams): void { - if (getState(client)) return - - if (client.network.params.disableUpdates) { - throw new MtArgumentError('Updates must be enabled to use updates manager') - } - - const authState = client.storage.self.getCached(true) - - const state = createUpdatesState(client, authState, params) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(client as any)[STATE_SYMBOL] = state - - function onLoggedIn(): void { - state.auth = client.storage.self.getCached() - fetchUpdatesState(client, state).catch((err) => client._emitError(err)) - } - - function onLoggedOut(): void { - stopUpdatesLoop(client) - state.cpts.clear() - state.cptsMod.clear() - state.pts = state.qts = state.date = state.seq = undefined - } - - function onBeforeConnect(): void { - loadUpdatesStorage(client, state).catch((err) => client._emitError(err)) - } - - function onKeepAlive() { - state.log.debug('no updates for >15 minutes, catching up') - handleUpdate(state, { _: 'updatesTooLong' }) - } - - state.postponedTimer.onTimeout(() => { - state.hasTimedoutPostponed = true - state.updatesLoopCv.notify() - }) - - client.on('logged_in', onLoggedIn) - client.on('logged_out', onLoggedOut) - client.on('before_connect', onBeforeConnect) - client.on('keep_alive', onKeepAlive) - client.network.setUpdateHandler((upd, fromClient) => handleUpdate(state, upd, fromClient)) - - function cleanup() { - client.off('logged_in', onLoggedIn) - client.off('logged_out', onLoggedOut) - client.off('before_connect', onBeforeConnect) - client.off('keep_alive', onKeepAlive) - client.off('before_stop', cleanup) - client.network.setUpdateHandler(() => {}) - stopUpdatesLoop(client) - } - - state.stop = cleanup - client.on('before_stop', cleanup) -} - -/** - * Disable updates processing. - * - * Basically reverts {@link enableUpdatesProcessing} - * - * @param client Client instance - * @noemit - */ -export function disableUpdatesProcessing(client: BaseTelegramClient): void { - const state = getState(client) - if (!state) return - - state.stop() - // eslint-disable-next-line @typescript-eslint/no-explicit-any - delete (client as any)[STATE_SYMBOL] -} - -/** - * Start updates loop. - * - * You must first call {@link enableUpdatesProcessing} to use this method. - * - * It is recommended to use this method in callback to {@link start}, - * or otherwise make sure the user is logged in. - * - * > **Note**: If you are using {@link UpdatesManagerParams.catchUp} option, - * > catching up will be done in background, you can't await it. - */ -export async function startUpdatesLoop(client: BaseTelegramClient): Promise { - const state = getState(client) - if (state.updatesLoopActive) return - - // otherwise we will catch up on the first update - if (!state.catchUpOnStart) { - await fetchUpdatesState(client, state) - } - - // start updates loop in background - state.updatesLoopActive = true - updatesLoop(client, state).catch((err) => client._emitError(err)) - - if (state.catchUpOnStart) { - catchUp(client) - } -} - -/** - * **ADVANCED** - * - * Manually stop updates loop. - * Usually done automatically when stopping the client with {@link close} - */ -export function stopUpdatesLoop(client: BaseTelegramClient): void { - const state = getState(client) - if (!state.updatesLoopActive) return - - for (const timer of state.channelDiffTimeouts.values()) { - clearTimeout(timer) - } - state.channelDiffTimeouts.clear() - - state.updatesLoopActive = false - state.pendingUpdateContainers.clear() - state.pendingUnorderedUpdates.clear() - state.pendingPtsUpdates.clear() - state.pendingQtsUpdates.clear() - state.pendingPtsUpdatesPostponed.clear() - state.pendingQtsUpdatesPostponed.clear() - state.postponedTimer.reset() - state.updatesLoopCv.notify() -} - -/** - * Catch up with the server by loading missed updates. - * - * > **Note**: In case the storage was not properly - * > closed the last time, "catching up" might - * > result in duplicate updates. - */ -export function catchUp(client: BaseTelegramClient): void { - const state = getState(client) - - state.log.debug('catch up requested') - - state.catchUpChannels = true - handleUpdate(state, { _: 'updatesTooLong' }) -} - -/** - * **ADVANCED** - * - * Notify the updates manager that some channel was "opened". - * Channel difference for "opened" channels will be fetched on a regular basis. - * This is a low-level method, prefer using {@link openChat} instead. - * - * Channel must be resolve-able with `resolvePeer` method (i.e. be in cache); - * base chat PTS must either be passed (e.g. from {@link Dialog}), or cached in storage. - * - * @param channelId Bare ID of the channel - * @param pts PTS of the channel, if known (e.g. from {@link Dialog}) - * @returns `true` if the channel was opened for the first time, `false` if it is already opened - */ -export function notifyChannelOpened(client: BaseTelegramClient, channelId: number, pts?: number): boolean { - // this method is intentionally very dumb to avoid making this file even more unreadable - - const state = getState(client) - - if (!state) { - throw new MtArgumentError('Updates processing is not enabled, use enableUpdatesProcessing() first') - } - - if (state.channelsOpened.has(channelId)) { - state.log.debug('channel %d opened again', channelId) - state.channelsOpened.set(channelId, state.channelsOpened.get(channelId)! + 1) - - return false - } - - state.channelsOpened.set(channelId, 1) - state.log.debug('channel %d opened (pts=%d)', channelId, pts) - - // force fetch channel difference - fetchChannelDifferenceViaUpdate(state, channelId, pts) - - return true -} - -/** - * **ADVANCED** - * - * Notify the updates manager that some channel was "closed". - * Basically the opposite of {@link notifyChannelOpened}. - * This is a low-level method, prefer using {@link closeChat} instead. - * - * @param channelId Bare channel ID - * @returns `true` if the chat was closed for the last time, `false` otherwise - */ -export function notifyChannelClosed(client: BaseTelegramClient, channelId: number): boolean { - const state = getState(client) - - if (!state) { - throw new MtArgumentError('Updates processing is not enabled, use enableUpdatesProcessing() first') - } - - const opened = state.channelsOpened.get(channelId)! - - if (opened === undefined) { - return false - } - - if (opened > 1) { - state.log.debug('channel %d closed, but is opened %d more times', channelId, opened - 1) - state.channelsOpened.set(channelId, opened - 1) - - return false - } - - state.channelsOpened.delete(channelId) - state.log.debug('channel %d closed', channelId) - - return true -} - -////////////////////////////////////////////// IMPLEMENTATION ////////////////////////////////////////////// - -const STATE_SYMBOL = Symbol('updatesState') - -function getState(client: BaseTelegramClient): UpdatesState { - // eslint-disable-next-line - return (client as any)[STATE_SYMBOL] -} - -async function fetchUpdatesState(client: BaseTelegramClient, state: UpdatesState): Promise { - await state.lock.acquire() - - state.log.debug('fetching initial state') - - try { - let fetchedState = await client.call({ _: 'updates.getState' }) - - state.log.debug( - 'updates.getState returned state: pts=%d, qts=%d, date=%d, seq=%d', - fetchedState.pts, - fetchedState.qts, - fetchedState.date, - fetchedState.seq, - ) - - // for some unknown fucking reason getState may return old qts - // call getDifference to get actual values :shrug: - const diff = await client.call({ - _: 'updates.getDifference', - pts: fetchedState.pts, - qts: fetchedState.qts, - date: fetchedState.date, - }) - - switch (diff._) { - case 'updates.differenceEmpty': - break - case 'updates.differenceTooLong': // shouldn't happen, but who knows? - (fetchedState as tl.Mutable).pts = diff.pts - break - case 'updates.differenceSlice': - fetchedState = diff.intermediateState - break - case 'updates.difference': - fetchedState = diff.state - break - default: - assertNever(diff) - } - - state.qts = fetchedState.qts - state.pts = fetchedState.pts - state.date = fetchedState.date - state.seq = fetchedState.seq - - state.log.debug( - 'loaded initial state: pts=%d, qts=%d, date=%d, seq=%d', - state.pts, - state.qts, - state.date, - state.seq, - ) - } catch (e) { - state.log.error('failed to fetch updates state: %s', e) - } - - state.lock.release() -} - -async function loadUpdatesStorage(client: BaseTelegramClient, state: UpdatesState): Promise { - const storedState = await client.storage.updates.getState() - - if (storedState) { - state.pts = state.oldPts = storedState[0] - state.qts = state.oldQts = storedState[1] - state.date = state.oldDate = storedState[2] - state.seq = state.oldSeq = storedState[3] - - state.log.debug( - 'loaded stored state: pts=%d, qts=%d, date=%d, seq=%d', - storedState[0], - storedState[1], - storedState[2], - storedState[3], - ) - } - // if no state, don't bother initializing properties - // since that means that there is no authorization, - // and thus fetchUpdatesState will be called -} - -async function saveUpdatesStorage(client: BaseTelegramClient, state: UpdatesState, save = false): Promise { - // before any authorization pts will be undefined - if (state.pts !== undefined) { - // if old* value is not available, assume it has changed. - if (state.oldPts === undefined || state.oldPts !== state.pts) { - await client.storage.updates.setPts(state.pts) - } - if (state.oldQts === undefined || state.oldQts !== state.qts) { - await client.storage.updates.setQts(state.qts!) - } - if (state.oldDate === undefined || state.oldDate !== state.date) { - await client.storage.updates.setDate(state.date!) - } - if (state.oldSeq === undefined || state.oldSeq !== state.seq) { - await client.storage.updates.setSeq(state.seq!) - } - - // update old* values - state.oldPts = state.pts - state.oldQts = state.qts - state.oldDate = state.date - state.oldSeq = state.seq - - await client.storage.updates.setManyChannelPts(state.cptsMod) - state.cptsMod.clear() - - if (save) { - await client.storage.save?.() - } - } -} - -function addToNoDispatchIndex(state: UpdatesState, updates?: tl.TypeUpdates): void { - if (!updates) return - - const addUpdate = (upd: tl.TypeUpdate) => { - const channelId = extractChannelIdFromUpdate(upd) ?? 0 - const pts = 'pts' in upd ? upd.pts : undefined - - if (pts) { - const set = state.noDispatchPts.get(channelId) - if (!set) state.noDispatchPts.set(channelId, new Set([pts])) - else set.add(pts) - } - - const qts = 'qts' in upd ? upd.qts : undefined - - if (qts) { - state.noDispatchQts.add(qts) - } - - switch (upd._) { - case 'updateNewMessage': - case 'updateNewChannelMessage': { - const channelId = upd.message.peerId?._ === 'peerChannel' ? upd.message.peerId.channelId : 0 - - const set = state.noDispatchMsg.get(channelId) - if (!set) state.noDispatchMsg.set(channelId, new Set([upd.message.id])) - else set.add(upd.message.id) - - break - } - } - } - - switch (updates._) { - case 'updates': - case 'updatesCombined': - updates.updates.forEach(addUpdate) - break - case 'updateShortMessage': - case 'updateShortChatMessage': - case 'updateShortSentMessage': { - // these updates are only used for non-channel messages, so we use 0 - let set = state.noDispatchMsg.get(0) - if (!set) state.noDispatchMsg.set(0, new Set([updates.id])) - else set.add(updates.id) - - set = state.noDispatchPts.get(0) - if (!set) state.noDispatchPts.set(0, new Set([updates.pts])) - else set.add(updates.pts) - break - } - case 'updateShort': - addUpdate(updates.update) - break - case 'updatesTooLong': - break - default: - assertNever(updates) - } -} - -async function fetchMissingPeers( - client: BaseTelegramClient, - upd: tl.TypeUpdate, - peers: PeersIndex, - allowMissing = false, -): Promise> { - const missing = new Set() - - async function fetchPeer(peer?: tl.TypePeer | number) { - if (!peer) return true - - const bare = typeof peer === 'number' ? parseMarkedPeerId(peer)[1] : getBarePeerId(peer) - - const marked = typeof peer === 'number' ? peer : getMarkedPeerId(peer) - const index = marked > 0 ? peers.users : peers.chats - - if (index.has(bare)) return true - if (missing.has(marked)) return false - - const cached = await client.storage.peers.getCompleteById(marked) - - if (!cached) { - missing.add(marked) - - return allowMissing - } - - // whatever, ts is not smart enough to understand - (index as Map).set(bare, cached) - - return true - } - - switch (upd._) { - case 'updateNewMessage': - case 'updateNewChannelMessage': - case 'updateEditMessage': - case 'updateEditChannelMessage': { - const msg = upd.message - if (msg._ === 'messageEmpty') return missing - - // ref: https://github.com/tdlib/td/blob/master/td/telegram/UpdatesManager.cpp - // (search by UpdatesManager::is_acceptable_update) - if (!(await fetchPeer(msg.peerId))) return missing - if (!(await fetchPeer(msg.fromId))) return missing - - if (msg.replyTo) { - if (msg.replyTo._ === 'messageReplyHeader' && !(await fetchPeer(msg.replyTo.replyToPeerId))) { - return missing - } - if (msg.replyTo._ === 'messageReplyStoryHeader' && !(await fetchPeer(msg.replyTo.userId))) { - return missing - } - } - - if (msg._ !== 'messageService') { - if ( - msg.fwdFrom && - (!(await fetchPeer(msg.fwdFrom.fromId)) || !(await fetchPeer(msg.fwdFrom.savedFromPeer))) - ) { - return missing - } - if (!(await fetchPeer(msg.viaBotId))) return missing - - if (msg.entities) { - for (const ent of msg.entities) { - if (ent._ === 'messageEntityMentionName') { - if (!(await fetchPeer(ent.userId))) return missing - } - } - } - - if (msg.media) { - switch (msg.media._) { - case 'messageMediaContact': - if (msg.media.userId && !(await fetchPeer(msg.media.userId))) { - return missing - } - } - } - } else { - switch (msg.action._) { - case 'messageActionChatCreate': - case 'messageActionChatAddUser': - case 'messageActionInviteToGroupCall': - for (const user of msg.action.users) { - if (!(await fetchPeer(user))) return missing - } - break - case 'messageActionChatJoinedByLink': - if (!(await fetchPeer(msg.action.inviterId))) { - return missing - } - break - case 'messageActionChatDeleteUser': - if (!(await fetchPeer(msg.action.userId))) return missing - break - case 'messageActionChatMigrateTo': - if (!(await fetchPeer(toggleChannelIdMark(msg.action.channelId)))) { - return missing - } - break - case 'messageActionChannelMigrateFrom': - if (!(await fetchPeer(-msg.action.chatId))) return missing - break - case 'messageActionGeoProximityReached': - if (!(await fetchPeer(msg.action.fromId))) return missing - if (!(await fetchPeer(msg.action.toId))) return missing - break - } - } - break - } - case 'updateDraftMessage': - if ('entities' in upd.draft && upd.draft.entities) { - for (const ent of upd.draft.entities) { - if (ent._ === 'messageEntityMentionName') { - if (!(await fetchPeer(ent.userId))) return missing - } - } - } - } - - return missing -} - -async function storeMessageReferences(client: BaseTelegramClient, msg: tl.TypeMessage): Promise { - if (msg._ === 'messageEmpty') return - - const peerId = msg.peerId - if (peerId._ !== 'peerChannel') return - - const channelId = toggleChannelIdMark(peerId.channelId) - - const promises: MaybeAsync[] = [] - - function store(peer?: tl.TypePeer | number | number[]): void { - if (!peer) return - - if (Array.isArray(peer)) { - peer.forEach(store) - - return - } - - const marked = typeof peer === 'number' ? peer : getMarkedPeerId(peer) - - promises.push(client.storage.refMsgs.store(marked, channelId, msg.id)) - } - - // reference: https://github.com/tdlib/td/blob/master/td/telegram/MessagesManager.cpp - // (search by get_message_user_ids, get_message_channel_ids) - store(msg.fromId) - - if (msg._ === 'message') { - store(msg.viaBotId) - store(msg.fwdFrom?.fromId) - - if (msg.media) { - switch (msg.media._) { - case 'messageMediaWebPage': - if (msg.media.webpage._ === 'webPage' && msg.media.webpage.attributes) { - for (const attr of msg.media.webpage.attributes) { - if (attr._ === 'webPageAttributeStory') { - store(attr.peer) - } - } - } - break - case 'messageMediaContact': - store(msg.media.userId) - break - case 'messageMediaStory': - store(msg.media.peer) - break - case 'messageMediaGiveaway': - store(msg.media.channels.map(toggleChannelIdMark)) - break - } - } - } else { - switch (msg.action._) { - case 'messageActionChatCreate': - case 'messageActionChatAddUser': - case 'messageActionInviteToGroupCall': - store(msg.action.users) - break - case 'messageActionChatDeleteUser': - store(msg.action.userId) - break - } - } - - if (msg.replyTo) { - switch (msg.replyTo._) { - case 'messageReplyHeader': - store(msg.replyTo.replyToPeerId) - store(msg.replyTo.replyFrom?.fromId) - break - case 'messageReplyStoryHeader': - store(msg.replyTo.userId) - break - } - // in fact, we can also use peers contained in the replied-to message, - // but we don't fetch it automatically, so we can't know which peers are there - } - - await Promise.all(promises) -} - -function isMessageEmpty(upd: tl.TypeUpdate): boolean { - return (upd as Extract).message?._ === 'messageEmpty' -} - -function handleUpdate(state: UpdatesState, update: tl.TypeUpdates, noDispatch = false): void { - if (noDispatch && state.noDispatchEnabled) { - addToNoDispatchIndex(state, update) - } - - state.log.debug( - 'received %s, queueing for processing. containers queue size: %d', - update._, - state.pendingUpdateContainers.length, - ) - state.rpsIncoming?.hit() - - switch (update._) { - case 'updatesTooLong': - case 'updateShortMessage': - case 'updateShortChatMessage': - case 'updateShort': - case 'updateShortSentMessage': - state.pendingUpdateContainers.add({ - upd: update, - seqStart: 0, - seqEnd: 0, - }) - break - case 'updates': - case 'updatesCombined': - state.pendingUpdateContainers.add({ - upd: update, - seqStart: update._ === 'updatesCombined' ? update.seqStart : update.seq, - seqEnd: update.seq, - }) - break - default: - assertNever(update) - } - - state.updatesLoopCv.notify() -} - -async function fetchChannelDifference( - client: BaseTelegramClient, - state: UpdatesState, - channelId: number, - fallbackPts?: number, -): Promise { - // clear timeout if any - if (state.channelDiffTimeouts.has(channelId)) { - clearTimeout(state.channelDiffTimeouts.get(channelId)) - state.channelDiffTimeouts.delete(channelId) - } - - let _pts: number | null | undefined = state.cpts.get(channelId) - - if (!_pts && state.catchUpChannels) { - _pts = await client.storage.updates.getChannelPts(channelId) - } - if (!_pts) _pts = fallbackPts - - if (!_pts) { - state.log.debug('fetchChannelDifference failed for channel %d: base pts not available', channelId) - - return false - } - - const channel = toInputChannel(await resolvePeer(client, toggleChannelIdMark(channelId))) - - if (channel._ === 'inputChannel' && channel.accessHash.isZero()) { - state.log.debug('fetchChannelDifference failed for channel %d: input peer not found', channelId) - - return false - } - - // to make TS happy - let pts = _pts - let limit = state.auth?.isBot ? 100000 : 100 - - if (pts <= 0) { - pts = 1 - limit = 1 - } - - let lastTimeout = 0 - - for (;;) { - const diff = await client.call({ - _: 'updates.getChannelDifference', - force: true, // Set to true to skip some possibly unneeded updates and reduce server-side load - channel, - pts, - limit, - filter: { _: 'channelMessagesFilterEmpty' }, - }) - - if (diff.timeout) lastTimeout = diff.timeout - - if (diff._ === 'updates.channelDifferenceEmpty') { - state.log.debug('getChannelDifference (cid = %d) returned channelDifferenceEmpty', channelId) - break - } - - const peers = PeersIndex.from(diff) - - if (diff._ === 'updates.channelDifferenceTooLong') { - if (diff.dialog._ === 'dialog') { - pts = diff.dialog.pts! - } - - state.log.warn( - 'getChannelDifference (cid = %d) returned channelDifferenceTooLong. new pts: %d, recent msgs: %d', - channelId, - pts, - diff.messages.length, - ) - - diff.messages.forEach((message) => { - state.log.debug( - 'processing message %d (%s) from TooLong diff for channel %d', - message.id, - message._, - channelId, - ) - - if (message._ === 'messageEmpty') return - - state.pendingUnorderedUpdates.pushBack(toPendingUpdate(messageToUpdate(message), peers, true)) - }) - break - } - - state.log.debug( - 'getChannelDifference (cid = %d) returned %d messages, %d updates. new pts: %d, final: %b', - channelId, - diff.newMessages.length, - diff.otherUpdates.length, - diff.pts, - diff.final, - ) - - diff.newMessages.forEach((message) => { - state.log.debug('processing message %d (%s) from diff for channel %d', message.id, message._, channelId) - - if (message._ === 'messageEmpty') return - - state.pendingUnorderedUpdates.pushBack(toPendingUpdate(messageToUpdate(message), peers, true)) - }) - - diff.otherUpdates.forEach((upd) => { - const parsed = toPendingUpdate(upd, peers, true) - - state.log.debug( - 'processing %s from diff for channel %d, pts_before: %d, pts: %d', - upd._, - channelId, - parsed.ptsBefore, - parsed.pts, - ) - - if (isMessageEmpty(upd)) return - - state.pendingUnorderedUpdates.pushBack(parsed) - }) - - pts = diff.pts - - if (diff.final) break - } - - state.cpts.set(channelId, pts) - state.cptsMod.set(channelId, pts) - - // schedule next fetch - if (lastTimeout !== 0 && state.channelsOpened.has(channelId)) { - state.log.debug('scheduling next fetch for channel %d in %d seconds', channelId, lastTimeout) - state.channelDiffTimeouts.set( - channelId, - setTimeout(() => fetchChannelDifferenceViaUpdate(state, channelId), lastTimeout * 1000), - ) - } - - return true -} - -function fetchChannelDifferenceLater( - client: BaseTelegramClient, - state: UpdatesState, - requestedDiff: Map>, - channelId: number, - fallbackPts?: number, -): void { - if (!requestedDiff.has(channelId)) { - requestedDiff.set( - channelId, - fetchChannelDifference(client, state, channelId, fallbackPts) - .catch((err) => { - state.log.warn('error fetching difference for %d: %s', channelId, err) - }) - .then((ok) => { - requestedDiff.delete(channelId) - - if (!ok) { - state.log.debug('channel difference for %d failed, falling back to common diff', channelId) - fetchDifferenceLater(client, state, requestedDiff) - } - }), - ) - } -} - -function fetchChannelDifferenceViaUpdate(state: UpdatesState, channelId: number, pts?: number): void { - handleUpdate( - state, - createDummyUpdatesContainer([ - { - _: 'updateChannelTooLong', - channelId, - pts, - }, - ]), - ) -} - -async function fetchDifference( - client: BaseTelegramClient, - state: UpdatesState, - requestedDiff: Map>, -): Promise { - for (;;) { - const diff = await client.call({ - _: 'updates.getDifference', - pts: state.pts!, - date: state.date!, - qts: state.qts!, - }) - - switch (diff._) { - case 'updates.differenceEmpty': - state.log.debug('updates.getDifference returned updates.differenceEmpty') - - return - case 'updates.differenceTooLong': - state.pts = diff.pts - state.log.debug('updates.getDifference returned updates.differenceTooLong') - - return - } - - const fetchedState = diff._ === 'updates.difference' ? diff.state : diff.intermediateState - - state.log.debug( - 'updates.getDifference returned %d messages, %d updates. new pts: %d, qts: %d, seq: %d, final: %b', - diff.newMessages.length, - diff.otherUpdates.length, - fetchedState.pts, - fetchedState.qts, - fetchedState.seq, - diff._ === 'updates.difference', - ) - - const peers = PeersIndex.from(diff) - - diff.newMessages.forEach((message) => { - state.log.debug('processing message %d in %j (%s) from common diff', message.id, message.peerId, message._) - - if (message._ === 'messageEmpty') return - - // pts does not need to be checked for them - state.pendingUnorderedUpdates.pushBack(toPendingUpdate(messageToUpdate(message), peers, true)) - }) - - diff.otherUpdates.forEach((upd) => { - if (upd._ === 'updateChannelTooLong') { - state.log.debug( - 'received updateChannelTooLong for channel %d in common diff (pts = %d), fetching diff', - upd.channelId, - upd.pts, - ) - - fetchChannelDifferenceLater(client, state, requestedDiff, upd.channelId, upd.pts) - - return - } - - if (isMessageEmpty(upd)) return - - const parsed = toPendingUpdate(upd, peers, true) - - if (parsed.channelId && parsed.ptsBefore) { - // we need to check pts for these updates, put into pts queue - state.pendingPtsUpdates.add(parsed) - } else { - // the updates are in order already, we can treat them as unordered - state.pendingUnorderedUpdates.pushBack(parsed) - } - - state.log.debug( - 'received %s from common diff, cid: %d, pts_before: %d, pts: %d, qts_before: %d', - upd._, - parsed.channelId, - parsed.ptsBefore, - parsed.pts, - parsed.qtsBefore, - ) - }) - - state.pts = fetchedState.pts - state.qts = fetchedState.qts - state.seq = fetchedState.seq - state.date = fetchedState.date - - if (diff._ === 'updates.difference') { - return - } - } -} - -function fetchDifferenceLater( - client: BaseTelegramClient, - state: UpdatesState, - requestedDiff: Map>, -): void { - if (!requestedDiff.has(0)) { - requestedDiff.set( - 0, - fetchDifference(client, state, requestedDiff) - .catch((err) => { - if (tl.RpcError.is(err, 'AUTH_KEY_UNREGISTERED')) { - // for some reason, when logging out telegram may send updatesTooLong - // in any case, we need to stop updates loop - stopUpdatesLoop(client) - - return - } - - state.log.warn('error fetching common difference: %s', err) - - if (tl.RpcError.is(err, 'PERSISTENT_TIMESTAMP_INVALID')) { - // this function never throws - return fetchUpdatesState(client, state) - } - }) - .then(() => { - requestedDiff.delete(0) - }), - ) - } -} - -async function onUpdate( - client: BaseTelegramClient, - state: UpdatesState, - pending: PendingUpdate, - requestedDiff: Map>, - postponed = false, - unordered = false, -): Promise { - const upd = pending.update - - let missing: Set | undefined = undefined - - // it is important to do this before updating pts - if (pending.peers.hasMin || pending.peers.empty) { - // even if we have min peers in difference, we can't do anything about them. - // we still want to collect them, so we can fetch them in the background. - // we won't wait for them, since that would block the updates loop - - state.log.debug('loading missing peers for %s (pts = %d, cid = %d)', upd._, pending.pts, pending.channelId) - missing = await fetchMissingPeers(client, upd, pending.peers, pending.fromDifference) - - if (!pending.fromDifference && missing.size) { - state.log.debug( - 'fetching difference because some peers were min (%J) and not cached for %s (pts = %d, cid = %d)', - missing, - upd._, - pending.pts, - pending.channelId, - ) - - if (pending.channelId && !(upd._ === 'updateNewChannelMessage' && upd.message._ === 'messageService')) { - // don't replace service messages, because they can be about bot's kicking - fetchChannelDifferenceLater(client, state, requestedDiff, pending.channelId, pending.ptsBefore) - } else { - fetchDifferenceLater(client, state, requestedDiff) - } - - return - } - - if (missing.size) { - state.log.debug( - 'peers still missing after fetching difference: %J for %s (pts = %d, cid = %d)', - missing, - upd._, - pending.pts, - pending.channelId, - ) - } - } - - // apply new pts/qts, if applicable - if (!unordered) { - // because unordered may contain pts/qts values when received from diff - - if (pending.pts) { - const localPts = pending.channelId ? state.cpts.get(pending.channelId) : state.pts - - if (localPts && pending.ptsBefore !== localPts) { - state.log.warn( - 'pts_before does not match local_pts for %s (cid = %d, pts_before = %d, pts = %d, local_pts = %d)', - upd._, - pending.channelId, - pending.ptsBefore, - pending.pts, - localPts, - ) - } - - state.log.debug( - 'applying new pts (cid = %d) because received %s: %d -> %d (before: %d, count: %d) (postponed = %s)', - pending.channelId, - upd._, - localPts, - pending.pts, - pending.ptsBefore, - pending.pts - pending.ptsBefore!, - postponed, - ) - - if (pending.channelId) { - state.cpts.set(pending.channelId, pending.pts) - state.cptsMod.set(pending.channelId, pending.pts) - } else { - state.pts = pending.pts - } - } - - if (pending.qtsBefore) { - state.log.debug( - 'applying new qts because received %s: %d -> %d (postponed = %s)', - upd._, - state.qts, - pending.qtsBefore + 1, - postponed, - ) - - state.qts = pending.qts - } - } - - if (isMessageEmpty(upd)) return - - state.rpsProcessing?.hit() - - // updates that are also used internally - switch (upd._) { - case 'mtcute.dummyUpdate': - // we just needed to apply new pts values - return - case 'updateDcOptions': { - const config = client.network.config.getNow() - - if (config) { - client.network.config.setConfig({ - ...config, - dcOptions: upd.dcOptions, - }) - } else { - client.network.config.update(true).catch((err) => client._emitError(err)) - } - break - } - case 'updateConfig': - client.network.config.update(true).catch((err) => client._emitError(err)) - break - case 'updateUserName': - // todo - // if (upd.userId === state.auth?.userId) { - // state.auth.selfUsername = upd.usernames.find((it) => it.active)?.username ?? null - // } - break - case 'updateDeleteChannelMessages': - if (!state.auth?.isBot) { - await client.storage.refMsgs.delete(toggleChannelIdMark(upd.channelId), upd.messages) - } - break - case 'updateNewMessage': - case 'updateEditMessage': - case 'updateNewChannelMessage': - case 'updateEditChannelMessage': - if (!state.auth?.isBot) { - await storeMessageReferences(client, upd.message) - } - break - } - - if (missing?.size) { - if (state.auth?.isBot) { - state.log.warn( - 'missing peers (%J) after getDifference for %s (pts = %d, cid = %d)', - missing, - upd._, - pending.pts, - pending.channelId, - ) - } else { - // force save storage so the min peers are stored - await client.storage.save?.() - - for (const id of missing) { - Promise.resolve(client.storage.peers.getById(id)) - .then((peer): unknown => { - if (!peer) { - state.log.warn('cannot fetch full peer %d - getPeerById returned null', id) - - return - } - - // the peer will be automatically cached by the `.call()`, we don't have to do anything - if (isInputPeerChannel(peer)) { - return _getChannelsBatched(client, toInputChannel(peer)) - } else if (isInputPeerUser(peer)) { - return _getUsersBatched(client, toInputUser(peer)) - } - - state.log.warn('cannot fetch full peer %d - unknown peer type %s', id, peer._) - }) - .catch((err) => { - state.log.warn('error fetching full peer %d: %s', id, err) - }) - } - } - } - - // dispatch the update - if (state.noDispatchEnabled) { - const channelId = pending.channelId ?? 0 - const msgId = upd._ === 'updateNewMessage' || upd._ === 'updateNewChannelMessage' ? upd.message.id : undefined - - // we first need to remove it from each index, and then check if it was there - const foundByMsgId = msgId && state.noDispatchMsg.get(channelId)?.delete(msgId) - const foundByPts = state.noDispatchPts.get(channelId)?.delete(pending.pts!) - const foundByQts = state.noDispatchQts.delete(pending.qts!) - - if (foundByMsgId || foundByPts || foundByQts) { - state.log.debug('not dispatching %s because it is in no_dispatch index', upd._) - - return - } - } - - state.log.debug('dispatching %s (postponed = %s)', upd._, postponed) - state.handler(upd, pending.peers) -} - -async function updatesLoop(client: BaseTelegramClient, state: UpdatesState): Promise { - const { log } = state - - log.debug('updates loop started, state available? %b', state.pts) - - try { - if (!state.pts) { - await fetchUpdatesState(client, state) - } - - while (state.updatesLoopActive) { - if ( - !( - state.pendingUpdateContainers.length || - state.pendingPtsUpdates.length || - state.pendingQtsUpdates.length || - state.pendingUnorderedUpdates.length || - state.hasTimedoutPostponed - ) - ) { - await state.updatesLoopCv.wait() - } - if (!state.updatesLoopActive) break - - log.debug( - 'updates loop tick. pending containers: %d, pts: %d, pts_postponed: %d, qts: %d, qts_postponed: %d, unordered: %d', - state.pendingUpdateContainers.length, - state.pendingPtsUpdates.length, - state.pendingPtsUpdatesPostponed.length, - state.pendingQtsUpdates.length, - state.pendingQtsUpdatesPostponed.length, - state.pendingUnorderedUpdates.length, - ) - - const requestedDiff = new Map>() - - // first process pending containers - while (state.pendingUpdateContainers.length) { - const { upd, seqStart, seqEnd } = state.pendingUpdateContainers.popFront()! - - switch (upd._) { - case 'updatesTooLong': - log.debug('received updatesTooLong, fetching difference') - fetchDifferenceLater(client, state, requestedDiff) - break - case 'updatesCombined': - case 'updates': { - if (seqStart !== 0) { - // https://t.me/tdlibchat/5843 - const nextLocalSeq = state.seq! + 1 - log.debug( - 'received seq-ordered %s (seq_start = %d, seq_end = %d, size = %d)', - upd._, - seqStart, - seqEnd, - upd.updates.length, - ) - - if (nextLocalSeq > seqStart) { - log.debug( - 'ignoring updates group because already applied (by seq: exp %d, got %d)', - nextLocalSeq, - seqStart, - ) - // "the updates were already applied, and must be ignored" - continue - } - - if (nextLocalSeq < seqStart) { - log.debug( - 'fetching difference because gap detected (by seq: exp %d, got %d)', - nextLocalSeq, - seqStart, - ) - // "there's an updates gap that must be filled" - fetchDifferenceLater(client, state, requestedDiff) - } - } else { - log.debug('received %s (size = %d)', upd._, upd.updates.length) - } - - await client.storage.peers.updatePeersFrom(upd) - - const peers = PeersIndex.from(upd) - - for (const update of upd.updates) { - switch (update._) { - case 'updateChannelTooLong': - log.debug( - 'received updateChannelTooLong for channel %d (pts = %d) in container, fetching diff', - update.channelId, - update.pts, - ) - fetchChannelDifferenceLater( - client, - state, - requestedDiff, - update.channelId, - update.pts, - ) - continue - case 'updatePtsChanged': - // see https://github.com/tdlib/td/blob/07c1d53a6d3cb1fad58d2822e55eef6d57363581/td/telegram/UpdatesManager.cpp#L4051 - if (client.network.getPoolSize('main') > 1) { - // highload bot - state.log.debug( - 'updatePtsChanged received, resetting pts to 1 and fetching difference', - ) - state.pts = 1 - fetchDifferenceLater(client, state, requestedDiff) - } else { - state.log.debug('updatePtsChanged received, fetching updates state') - await fetchUpdatesState(client, state) - } - continue - } - - const parsed = toPendingUpdate(update, peers) - - if (parsed.ptsBefore !== undefined) { - state.pendingPtsUpdates.add(parsed) - } else if (parsed.qtsBefore !== undefined) { - state.pendingQtsUpdates.add(parsed) - } else { - state.pendingUnorderedUpdates.pushBack(parsed) - } - } - - if (seqEnd !== 0 && seqEnd > state.seq!) { - state.seq = seqEnd - state.date = upd.date - } - - break - } - case 'updateShort': { - log.debug('received short %s', upd._) - - const parsed = toPendingUpdate(upd.update, new PeersIndex()) - - if (parsed.ptsBefore !== undefined) { - state.pendingPtsUpdates.add(parsed) - } else if (parsed.qtsBefore !== undefined) { - state.pendingQtsUpdates.add(parsed) - } else { - state.pendingUnorderedUpdates.pushBack(parsed) - } - - break - } - case 'updateShortMessage': { - log.debug('received updateShortMessage') - - const message: tl.RawMessage = { - _: 'message', - out: upd.out, - mentioned: upd.mentioned, - mediaUnread: upd.mediaUnread, - silent: upd.silent, - id: upd.id, - fromId: { - _: 'peerUser', - userId: upd.out ? state.auth!.userId : upd.userId, - }, - peerId: { - _: 'peerUser', - userId: upd.userId, - }, - fwdFrom: upd.fwdFrom, - viaBotId: upd.viaBotId, - replyTo: upd.replyTo, - date: upd.date, - message: upd.message, - entities: upd.entities, - ttlPeriod: upd.ttlPeriod, - } - - const update: tl.RawUpdateNewMessage = { - _: 'updateNewMessage', - message, - pts: upd.pts, - ptsCount: upd.ptsCount, - } - - state.pendingPtsUpdates.add({ - update, - ptsBefore: upd.pts - upd.ptsCount, - pts: upd.pts, - peers: new PeersIndex(), - fromDifference: false, - }) - - break - } - case 'updateShortChatMessage': { - log.debug('received updateShortChatMessage') - - const message: tl.RawMessage = { - _: 'message', - out: upd.out, - mentioned: upd.mentioned, - mediaUnread: upd.mediaUnread, - silent: upd.silent, - id: upd.id, - fromId: { - _: 'peerUser', - userId: upd.fromId, - }, - peerId: { - _: 'peerChat', - chatId: upd.chatId, - }, - fwdFrom: upd.fwdFrom, - viaBotId: upd.viaBotId, - replyTo: upd.replyTo, - date: upd.date, - message: upd.message, - entities: upd.entities, - ttlPeriod: upd.ttlPeriod, - } - - const update: tl.RawUpdateNewMessage = { - _: 'updateNewMessage', - message, - pts: upd.pts, - ptsCount: upd.ptsCount, - } - - state.pendingPtsUpdates.add({ - update, - ptsBefore: upd.pts - upd.ptsCount, - pts: upd.pts, - peers: new PeersIndex(), - fromDifference: false, - }) - - break - } - case 'updateShortSentMessage': { - // should not happen - log.warn('received updateShortSentMessage') - break - } - default: - assertNever(upd) - } - } - - // process pts-ordered updates - while (state.pendingPtsUpdates.length) { - const pending = state.pendingPtsUpdates.popFront()! - const upd = pending.update - - // check pts - - let localPts: number | null = null - - if (!pending.channelId) localPts = state.pts! - else if (state.cpts.has(pending.channelId)) { - localPts = state.cpts.get(pending.channelId)! - } else if (state.catchUpChannels) { - // only load stored channel pts in case - // the user has enabled catching up. - // not loading stored pts effectively disables - // catching up, but doesn't interfere with further - // update gaps (i.e. first update received is considered - // to be the base state) - - const saved = await client.storage.updates.getChannelPts(pending.channelId) - - if (saved) { - state.cpts.set(pending.channelId, saved) - localPts = saved - } - } - - if (localPts) { - const diff = localPts - pending.ptsBefore! - // PTS can only go up or drop cardinally - const isPtsDrop = diff > 1000009 - - if (diff > 0 && !isPtsDrop) { - // "the update was already applied, and must be ignored" - log.debug( - 'ignoring %s (cid = %d) because already applied (by pts: exp %d, got %d)', - upd._, - pending.channelId, - localPts, - pending.ptsBefore, - ) - continue - } - if (diff < 0) { - // "there's an update gap that must be filled" - // if the gap is less than 3, put the update into postponed queue - // otherwise, call getDifference - if (diff > -3) { - log.debug( - 'postponing %s for 0.5s (cid = %d) because small gap detected (by pts: exp %d, got %d, diff=%d)', - upd._, - pending.channelId, - localPts, - pending.ptsBefore, - diff, - ) - pending.timeout = Date.now() + 500 - state.pendingPtsUpdatesPostponed.add(pending) - state.postponedTimer.emitBefore(pending.timeout) - } else if (diff > -1000000) { - log.debug( - 'fetching difference after %s (cid = %d) because pts gap detected (by pts: exp %d, got %d, diff=%d)', - upd._, - pending.channelId, - localPts, - pending.ptsBefore, - diff, - ) - - if (pending.channelId) { - fetchChannelDifferenceLater(client, state, requestedDiff, pending.channelId) - } else { - fetchDifferenceLater(client, state, requestedDiff) - } - } else { - log.debug( - 'skipping all updates because pts gap is too big (by pts: exp %d, got %d, diff=%d)', - localPts, - pending.ptsBefore, - diff, - ) - - if (pending.channelId) { - state.cpts.set(pending.channelId, 0) - state.cptsMod.set(pending.channelId, 0) - } else { - await fetchUpdatesState(client, state) - } - } - continue - } - - if (isPtsDrop) { - log.debug('pts drop detected (%d -> %d)', localPts, pending.ptsBefore) - } - } - - await onUpdate(client, state, pending, requestedDiff) - } - - // process postponed pts-ordered updates - for (let item = state.pendingPtsUpdatesPostponed._first; item; item = item.n) { - // awesome fucking iteration because i'm so fucking tired and wanna kms - const pending = item.v - - const upd = pending.update - - let localPts - - if (!pending.channelId) localPts = state.pts! - else if (state.cpts.has(pending.channelId)) { - localPts = state.cpts.get(pending.channelId) - } - - // channel pts from storage will be available because we loaded it earlier - if (!localPts) { - log.warn('local pts not available for postponed %s (cid = %d), skipping', upd._, pending.channelId) - continue - } - - // check the pts to see if the gap was filled - if (localPts > pending.ptsBefore!) { - // "the update was already applied, and must be ignored" - log.debug( - 'ignoring postponed %s (cid = %d) because already applied (by pts: exp %d, got %d)', - upd._, - pending.channelId, - localPts, - pending.ptsBefore, - ) - state.pendingPtsUpdatesPostponed._remove(item) - continue - } - if (localPts < pending.ptsBefore!) { - // "there's an update gap that must be filled" - // if the timeout has not expired yet, keep the update in the queue - // otherwise, fetch diff - const now = Date.now() - - if (now < pending.timeout!) { - log.debug( - 'postponed %s (cid = %d) is still waiting (%dms left) (current pts %d, need %d)', - upd._, - pending.channelId, - pending.timeout! - now, - localPts, - pending.ptsBefore, - ) - } else { - log.debug( - "gap for postponed %s (cid = %d) wasn't filled, fetching diff (current pts %d, need %d)", - upd._, - pending.channelId, - localPts, - pending.ptsBefore, - ) - state.pendingPtsUpdatesPostponed._remove(item) - - if (pending.channelId) { - fetchChannelDifferenceLater(client, state, requestedDiff, pending.channelId) - } else { - fetchDifferenceLater(client, state, requestedDiff) - } - } - continue - } - - await onUpdate(client, state, pending, requestedDiff, true) - state.pendingPtsUpdatesPostponed._remove(item) - } - - // process qts-ordered updates - while (state.pendingQtsUpdates.length) { - const pending = state.pendingQtsUpdates.popFront()! - const upd = pending.update - - // check qts - const diff = state.qts! - pending.qtsBefore! - const isQtsDrop = diff > 1000009 - - if (diff > 0 && !isQtsDrop) { - // "the update was already applied, and must be ignored" - log.debug( - 'ignoring %s because already applied (by qts: exp %d, got %d)', - upd._, - state.qts!, - pending.qtsBefore, - ) - continue - } - if (state.qts! < pending.qtsBefore!) { - // "there's an update gap that must be filled" - // if the gap is less than 3, put the update into postponed queue - // otherwise, call getDifference - if (diff > -3) { - log.debug( - 'postponing %s for 0.5s because small gap detected (by qts: exp %d, got %d, diff=%d)', - upd._, - state.qts!, - pending.qtsBefore, - diff, - ) - pending.timeout = Date.now() + 500 - state.pendingQtsUpdatesPostponed.add(pending) - state.postponedTimer.emitBefore(pending.timeout) - } else { - log.debug( - 'fetching difference after %s because qts gap detected (by qts: exp %d, got %d, diff=%d)', - upd._, - state.qts!, - pending.qtsBefore, - diff, - ) - fetchDifferenceLater(client, state, requestedDiff) - } - continue - } - - if (isQtsDrop) { - log.debug('qts drop detected (%d -> %d)', state.qts, pending.qtsBefore) - } - - await onUpdate(client, state, pending, requestedDiff) - } - - // process postponed qts-ordered updates - for (let item = state.pendingQtsUpdatesPostponed._first; item; item = item.n) { - // awesome fucking iteration because i'm so fucking tired and wanna kms - const pending = item.v - const upd = pending.update - - // check the pts to see if the gap was filled - if (state.qts! > pending.qtsBefore!) { - // "the update was already applied, and must be ignored" - log.debug( - 'ignoring postponed %s because already applied (by qts: exp %d, got %d)', - upd._, - state.qts!, - pending.qtsBefore, - ) - continue - } - if (state.qts! < pending.qtsBefore!) { - // "there's an update gap that must be filled" - // if the timeout has not expired yet, keep the update in the queue - // otherwise, fetch diff - const now = Date.now() - - if (now < pending.timeout!) { - log.debug( - 'postponed %s is still waiting (%dms left) (current qts %d, need %d)', - upd._, - pending.timeout! - now, - state.qts!, - pending.qtsBefore, - ) - } else { - log.debug( - "gap for postponed %s wasn't filled, fetching diff (current qts %d, need %d)", - upd._, - state.qts!, - pending.qtsBefore, - ) - state.pendingQtsUpdatesPostponed._remove(item) - fetchDifferenceLater(client, state, requestedDiff) - } - continue - } - - // gap was filled, and the update can be applied - await onUpdate(client, state, pending, requestedDiff, true) - state.pendingQtsUpdatesPostponed._remove(item) - } - - state.hasTimedoutPostponed = false - - // wait for all pending diffs to load - while (requestedDiff.size) { - log.debug( - 'waiting for %d pending diffs before processing unordered: %J', - requestedDiff.size, - requestedDiff.keys(), - ) - - await Promise.all([...requestedDiff.values()]) - - // diff results may as well contain new diffs to be requested - log.debug( - 'pending diffs awaited, new diffs requested: %d (%J)', - requestedDiff.size, - requestedDiff.keys(), - ) - } - - // process unordered updates (or updates received from diff) - while (state.pendingUnorderedUpdates.length) { - const pending = state.pendingUnorderedUpdates.popFront()! - - await onUpdate(client, state, pending, requestedDiff, false, true) - } - - // onUpdate may also call getDiff in some cases, so we also need to check - // diff may also contain new updates, which will be processed in the next tick, - // but we don't want to postpone diff fetching - while (requestedDiff.size) { - log.debug( - 'waiting for %d pending diffs after processing unordered: %J', - requestedDiff.size, - requestedDiff.keys(), - ) - - await Promise.all([...requestedDiff.values()]) - - // diff results may as well contain new diffs to be requested - log.debug( - 'pending diffs awaited, new diffs requested: %d (%j)', - requestedDiff.size, - requestedDiff.keys(), - ) - } - - // save new update state - await saveUpdatesStorage(client, state, true) - } - - log.debug('updates loop stopped') - } catch (e) { - log.error('updates loop encountered error, restarting: %s', e) - updatesLoop(client, state).catch((err) => client._emitError(err)) - } -} diff --git a/packages/client/src/methods/updates/utils.ts b/packages/client/src/methods/updates/utils.ts deleted file mode 100644 index 6897d0f8..00000000 --- a/packages/client/src/methods/updates/utils.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { tl } from '@mtcute/core' - -export function messageToUpdate(message: tl.TypeMessage): tl.TypeUpdate { - switch (message.peerId!._) { - case 'peerUser': - case 'peerChat': - return { - _: 'updateNewMessage', - message, - pts: 0, - ptsCount: 0, - } - case 'peerChannel': - return { - _: 'updateNewChannelMessage', - message, - pts: 0, - ptsCount: 0, - } - } -} - -export function extractChannelIdFromUpdate(upd: tl.TypeUpdate): number | undefined { - // holy shit - let res = 0 - - if ('channelId' in upd) { - res = upd.channelId - } else if ( - 'message' in upd && - typeof upd.message !== 'string' && - 'peerId' in upd.message && - upd.message.peerId && - 'channelId' in upd.message.peerId - ) { - res = upd.message.peerId.channelId - } - - if (res === 0) return undefined - - return res -} diff --git a/packages/client/src/utils/platform/storage.ts b/packages/client/src/utils/platform/storage.ts deleted file mode 100644 index 900ca3fe..00000000 --- a/packages/client/src/utils/platform/storage.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** @internal */ -export const _defaultStorageFactory = (name: string) => { - // todo: move sqlite to core? - throw new Error('Not implemented') -} diff --git a/packages/client/src/utils/updates-utils.ts b/packages/client/src/utils/updates-utils.ts deleted file mode 100644 index 0351e19e..00000000 --- a/packages/client/src/utils/updates-utils.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { MtTypeAssertionError, tl } from '@mtcute/core' - -// dummy updates which are used for methods that return messages.affectedHistory. -// that is not an update, but it carries info about pts, and we need to handle it - -/** - * Create a dummy `updates` container with given updates. - */ -export function createDummyUpdatesContainer(updates: tl.TypeUpdate[], seq = 0): tl.TypeUpdates { - return { - _: 'updates', - seq, - date: 0, - chats: [], - users: [], - updates, - } -} - -/** - * Create a dummy update from PTS and PTS count. - * - * @param pts PTS - * @param ptsCount PTS count - * @param channelId Channel ID (bare), if applicable - */ -export function createDummyUpdate(pts: number, ptsCount: number, channelId = 0): tl.TypeUpdates { - return createDummyUpdatesContainer([ - { - _: 'mtcute.dummyUpdate', - channelId, - pts, - ptsCount, - }, - ]) -} - -/** @internal */ -export function assertIsUpdatesGroup( - ctx: string, - upd: tl.TypeUpdates, -): asserts upd is tl.RawUpdates | tl.RawUpdatesCombined { - switch (upd._) { - case 'updates': - case 'updatesCombined': - return - } - throw new MtTypeAssertionError(ctx, 'updates | updatesCombined', upd._) -} diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json deleted file mode 100644 index af184bdc..00000000 --- a/packages/client/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist/esm", - "rootDir": "./src" - }, - "include": [ - "./src", - ], - "references": [ - { "path": "../core" }, - { "path": "../test" } - ] -} diff --git a/packages/client/typedoc.cjs b/packages/client/typedoc.cjs deleted file mode 100644 index 8afba39c..00000000 --- a/packages/client/typedoc.cjs +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - extends: ['../../.config/typedoc/config.base.cjs'], - entryPoints: [ - './src/index.ts', - './src/utils/index.ts', - './src/methods/updates/index.ts', - ], - entryPointStrategy: 'expand', -} diff --git a/packages/client/utils.ts b/packages/client/utils.ts deleted file mode 100644 index 50894ad2..00000000 --- a/packages/client/utils.ts +++ /dev/null @@ -1,4 +0,0 @@ -// this file only exists as a hint to IDEs that we can use @mtcute/core/utils instead of @mtcute/core/src/utils. -// it is not present in the built package, just a DX improvement - -export * from './src/utils/index.js' diff --git a/packages/core/build.config.cjs b/packages/core/build.config.cjs index a112453f..1c80ab09 100644 --- a/packages/core/build.config.cjs +++ b/packages/core/build.config.cjs @@ -1,5 +1,6 @@ module.exports = ({ path, transformFile, packageDir, outDir }) => ({ esmOnlyDirectives: true, + esmImportDirectives: true, final() { const version = require(path.join(packageDir, 'package.json')).version const replaceVersion = (content) => content.replace('%VERSION%', version) diff --git a/packages/core/package.json b/packages/core/package.json index d5b3744c..eca113cb 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,13 +2,15 @@ "name": "@mtcute/core", "private": true, "version": "0.6.0", - "description": "Core functions and base MTProto client", + "description": "Type-safe library for MTProto (Telegram API)", "author": "Alina Sireneva ", "license": "MIT", "main": "src/index.ts", "type": "module", "scripts": { - "build": "pnpm run -w build-package core" + "build": "pnpm run -w build-package mtcute", + "gen-client": "node ./scripts/generate-client.cjs", + "gen-updates": "node ./scripts/generate-updates.cjs" }, "browser": { "./src/utils/platform/crypto.js": "./src/utils/platform/crypto.web.js", @@ -16,6 +18,11 @@ "./src/utils/platform/logging.js": "./src/utils/platform/logging.web.js", "./src/utils/platform/random.js": "./src/utils/platform/random.web.js", "./src/utils/platform/exit-hook.js": "./src/utils/platform/exit-hook.web.js", + "./src/highlevel/worker/platform/connect.js": "./src/highlevel/worker/platform/connect.web.js", + "./src/highlevel/worker/platform/register.js": "./src/highlevel/worker/platform/register.web.js", + "./src/highlevel/methods/files/_platform.js": "./src/highlevel/methods/files/_platform.web.js", + "./src/highlevel/methods/files/download-file.js": "./src/highlevel/methods/files/download-file.web.js", + "./src/highlevel/utils/platform/storage.js": "./src/highlevel/utils/platform/storage.web.js", "./src/storage/json-file.js": false }, "distOnlyFields": { @@ -39,6 +46,14 @@ "./storage/*": { "import": "./esm/storage/*", "require": "./cjs/storage/*" + }, + "./highlevel/*": { + "import": "./esm/highlevel/*", + "require": "./cjs/highlevel/*" + }, + "./methods/*": { + "import": "./esm/highlevel/methods/*", + "require": "./cjs/highlevel/methods/*" } } }, @@ -46,13 +61,13 @@ "@mtcute/tl": "workspace:^", "@mtcute/tl-runtime": "workspace:^", "@mtcute/wasm": "workspace:^", + "@mtcute/file-id": "workspace:^", "@types/events": "3.0.0", "events": "3.2.0", "long": "5.2.3" }, "devDependencies": { "@types/ws": "8.5.4", - "node-forge": "1.3.1", "@mtcute/test": "workspace:^", "ws": "8.13.0" } diff --git a/packages/client/scripts/generate-client.cjs b/packages/core/scripts/generate-client.cjs similarity index 92% rename from packages/client/scripts/generate-client.cjs rename to packages/core/scripts/generate-client.cjs index c78af227..e41aed79 100644 --- a/packages/client/scripts/generate-client.cjs +++ b/packages/core/scripts/generate-client.cjs @@ -13,7 +13,7 @@ function findMethodAvailability(method) { return entry.available ?? null } -const targetDir = path.join(__dirname, '../src') +const targetDir = path.join(__dirname, '../src/highlevel') async function* getFiles(dir) { const dirents = await fs.promises.readdir(dir, { withFileTypes: true }) @@ -315,7 +315,7 @@ async function addSingleMethod(state, fileName) { const firstArg = stmt.parameters[0] - if (isExported && (!firstArg || firstArg.type.getText() !== 'BaseTelegramClient')) { + if (isExported && (!firstArg || firstArg.type.getText() !== 'ITelegramClient')) { continue } @@ -421,7 +421,8 @@ async function addSingleMethod(state, fileName) { } async function main() { - const output = fs.createWriteStream(path.join(__dirname, '../src/client.ts')) + const targetFile = path.join(__dirname, '../src/highlevel/client.ts') + const output = fs.createWriteStream(targetFile) const state = { imports: {}, fields: [], @@ -435,7 +436,7 @@ async function main() { files: {}, } - for await (const file of getFiles(path.join(__dirname, '../src/methods'))) { + for await (const file of getFiles(path.join(__dirname, '../src/highlevel/methods'))) { if (!file.startsWith('.') && file.endsWith('.ts') && !file.endsWith('.web.ts') && !file.endsWith('.test.ts')) { await addSingleMethod(state, file) } @@ -444,7 +445,9 @@ async function main() { output.write( '/* eslint-disable @typescript-eslint/no-unsafe-declaration-merging, @typescript-eslint/unified-signatures */\n' + '/* eslint-disable @typescript-eslint/no-unsafe-argument */\n' + - '/* THIS FILE WAS AUTO-GENERATED */\n', + '/* THIS FILE WAS AUTO-GENERATED */\n' + + "import EventEmitter from 'events'\n" + + "import Long from 'long'\n", ) Object.entries(state.imports).forEach(([module, items]) => { items = [...items] @@ -458,7 +461,7 @@ async function main() { output.write(`// from ${from}\n${code}\n`) }) - output.write('\nexport interface TelegramClient extends BaseTelegramClient {\n') + output.write('\nexport interface TelegramClient extends ITelegramClient {\n') output.write(`/** * Register a raw update handler @@ -547,7 +550,7 @@ on(name: string, handler: (...args: any[]) => void): this\n`) `<${func.typeParameters.map((it) => it.getFullText()).join(', ')}>` : '' const rawParams = (func.parameters || []).filter( - (it) => !it.type || it.type.getText() !== 'BaseTelegramClient', + (it) => !it.type || it.type.getText() !== 'ITelegramClient', ) const parameters = rawParams .map((it) => { @@ -648,8 +651,8 @@ on(name: string, handler: (...args: any[]) => void): this\n`) if (hasOverloads) { classProtoDecls.push('// @ts-expect-error this kinda breaks typings for overloads, idc') } - classProtoDecls.push(` return ${origName}(this, ...args);`) - classProtoDecls.push('}\n') + classProtoDecls.push(` return ${origName}(this._client, ...args);`) + classProtoDecls.push('}') } } }, @@ -657,11 +660,13 @@ on(name: string, handler: (...args: any[]) => void): this\n`) output.write('}\n') output.write('\nexport type { TelegramClientOptions }\n') - output.write('\nexport class TelegramClient extends BaseTelegramClient {\n') + output.write('\nexport class TelegramClient extends EventEmitter implements ITelegramClient {\n') + output.write(' _client: ITelegramClient\n') state.fields.forEach(({ code }) => output.write(`protected ${code}\n`)) output.write('constructor(opts: TelegramClientOptions) {\n') + output.write(' super()\n') state.init.forEach((code) => { output.write(code + '\n') }) @@ -670,10 +675,45 @@ on(name: string, handler: (...args: any[]) => void): this\n`) classContents.forEach((line) => output.write(line + '\n')) output.write('}\n') classProtoDecls.forEach((line) => output.write(line + '\n')) + // proxied methods + ;[ + 'prepare', + 'connect', + 'close', + 'notifyLoggedIn', + 'notifyLoggedOut', + 'notifyChannelOpened', + 'notifyChannelClosed', + 'call', + 'importSession', + 'exportSession', + 'onError', + 'emitError', + 'handleClientUpdate', + 'getApiCrenetials', + 'getPoolSize', + 'getPrimaryDcId', + 'computeSrpParams', + 'computeNewPasswordHash', + ].forEach((name) => { + output.write( + `TelegramClient.prototype.${name} = function(...args) {\n` + + ` return this._client.${name}(...args)\n` + + '}\n', + ) + }) + // disabled methods - they are used internally and we don't want to expose them + // if the user *really* needs them, they can use `client._client` to access the underlying client + ;['onServerUpdate', 'onUpdate'].forEach((name) => { + output.write( + `TelegramClient.prototype.${name} = function() {\n` + + ` throw new Error('${name} is not available for TelegramClient, use .on() methods instead')\n` + + '}\n', + ) + }) state.impls.forEach(({ name, code }) => output.write(`TelegramClient.prototype.${name} = ${code}\n`)) // format the resulting file with prettier - const targetFile = path.join(__dirname, '../src/client.ts') const prettierConfig = await prettier.resolveConfig(targetFile) let fullSource = await fs.promises.readFile(targetFile, 'utf-8') fullSource = await prettier.format(fullSource, { diff --git a/packages/client/scripts/generate-updates.cjs b/packages/core/scripts/generate-updates.cjs similarity index 97% rename from packages/client/scripts/generate-updates.cjs rename to packages/core/scripts/generate-updates.cjs index 180fc727..c814655d 100644 --- a/packages/client/scripts/generate-updates.cjs +++ b/packages/core/scripts/generate-updates.cjs @@ -1,4 +1,3 @@ -/* eslint-disable no-restricted-globals */ const fs = require('fs') const path = require('path') const prettier = require('prettier') @@ -90,7 +89,7 @@ function toSentence(type, stype = 'inline') { } function generateParsedUpdate() { - replaceSections('types/updates/index.ts', { + replaceSections('highlevel/types/updates/index.ts', { codegen: 'export type ParsedUpdate =\n' + types.map((typ) => ` | { name: '${typ.typeName}'; data: ${typ.updateType} }\n`).join(''), diff --git a/packages/client/scripts/update-types.txt b/packages/core/scripts/update-types.txt similarity index 100% rename from packages/client/scripts/update-types.txt rename to packages/core/scripts/update-types.txt diff --git a/packages/core/src/base-client.ts b/packages/core/src/base-client.ts deleted file mode 100644 index 98ce1830..00000000 --- a/packages/core/src/base-client.ts +++ /dev/null @@ -1,354 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import EventEmitter from 'events' - -import { tl } from '@mtcute/tl' -import { __tlReaderMap as defaultReaderMap } from '@mtcute/tl/binary/reader.js' -import { __tlWriterMap as defaultWriterMap } from '@mtcute/tl/binary/writer.js' -import { TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime' - -import { BaseTelegramClientOptions } from './base-client.types.js' -import { ConfigManager } from './network/config-manager.js' -import { SessionConnection } from './network/index.js' -import { NetworkManager, RpcCallOptions } from './network/network-manager.js' -import { StorageManager } from './storage/storage.js' -import { MustEqual } from './types/index.js' -import { - ControllablePromise, - createControllablePromise, - DcOptions, - defaultCryptoProviderFactory, - defaultProductionDc, - defaultProductionIpv6Dc, - defaultTestDc, - defaultTestIpv6Dc, - ICryptoProvider, - LogManager, - readStringSession, - StringSessionData, - writeStringSession, -} from './utils/index.js' - -/** - * Basic Telegram client that only implements the bare minimum - * to make RPC calls and receive low-level updates. - */ -export class BaseTelegramClient extends EventEmitter { - /** - * Crypto provider taken from {@link BaseTelegramClientOptions.crypto} - */ - readonly crypto: ICryptoProvider - - /** Storage manager */ - readonly storage: StorageManager - - /** - * "Test mode" taken from {@link BaseTelegramClientOptions.testMode} - */ - protected readonly _testMode: boolean - - /** - * Primary DCs taken from {@link BaseTelegramClientOptions.defaultDcs}, - * loaded from session or changed by other means (like redirecting). - */ - protected _defaultDcs: DcOptions - - private _niceStacks: boolean - /** TL layer used by the client */ - readonly _layer: number - /** TL readers map used by the client */ - readonly _readerMap: TlReaderMap - /** TL writers map used by the client */ - readonly _writerMap: TlWriterMap - - readonly _config = new ConfigManager(() => this.call({ _: 'help.getConfig' })) - - // not really connected, but rather "connect() was called" - private _connected: ControllablePromise | boolean = false - - _emitError: (err: unknown, connection?: SessionConnection) => void = console.error.bind(console) - - private _importFrom?: StringSessionData - private _importForce?: boolean - - readonly log = new LogManager('client') - readonly network: NetworkManager - - constructor(readonly params: BaseTelegramClientOptions) { - super() - - if (params.logLevel !== undefined) { - this.log.level = params.logLevel - } - - this.crypto = (params.crypto ?? defaultCryptoProviderFactory)() - this._testMode = Boolean(params.testMode) - - let dc = params.defaultDcs - - if (!dc) { - if (params.testMode) { - dc = params.useIpv6 ? defaultTestIpv6Dc : defaultTestDc - } else { - dc = params.useIpv6 ? defaultProductionIpv6Dc : defaultProductionDc - } - } - - this._defaultDcs = dc - this._niceStacks = params.niceStacks ?? true - - this._layer = params.overrideLayer ?? tl.LAYER - this._readerMap = params.readerMap ?? defaultReaderMap - this._writerMap = params.writerMap ?? defaultWriterMap - - this.storage = new StorageManager({ - provider: params.storage, - log: this.log, - readerMap: this._readerMap, - writerMap: this._writerMap, - ...params.storageOptions, - }) - - this.network = new NetworkManager( - { - apiId: params.apiId, - crypto: this.crypto, - disableUpdates: params.disableUpdates ?? false, - initConnectionOptions: params.initConnectionOptions, - layer: this._layer, - log: this.log, - readerMap: this._readerMap, - writerMap: this._writerMap, - reconnectionStrategy: params.reconnectionStrategy, - storage: this.storage, - testMode: Boolean(params.testMode), - transport: params.transport, - _emitError: this._emitError.bind(this), - floodSleepThreshold: params.floodSleepThreshold ?? 10000, - maxRetryCount: params.maxRetryCount ?? 5, - isPremium: false, - useIpv6: Boolean(params.useIpv6), - enableErrorReporting: params.enableErrorReporting ?? false, - onUsable: () => this.emit('usable'), - ...params.network, - }, - this._config, - ) - } - - /** - * Initialize the connection to the primary DC. - * - * You shouldn't usually call this method directly as it is called - * implicitly the first time you call {@link call}. - */ - async connect(): Promise { - if (this._connected) { - // avoid double-connect - await this._connected - - return - } - - const promise = (this._connected = createControllablePromise()) - - await this.crypto.initialize?.() - await this.storage.load() - - const primaryDc = await this.storage.dcs.fetch() - if (primaryDc !== null) this._defaultDcs = primaryDc - - const self = await this.storage.self.fetch() - this.log.prefix = `[USER ${self?.userId ?? 'n/a'}] ` - - const defaultDcAuthKey = await this.storage.provider.authKeys.get(this._defaultDcs.main.id) - - if ((this._importForce || !defaultDcAuthKey) && this._importFrom) { - const data = this._importFrom - - if (data.testMode !== this._testMode) { - throw new Error( - 'This session string is not for the current backend. ' + - `Session is ${data.testMode ? 'test' : 'prod'}, but the client is ${ - this._testMode ? 'test' : 'prod' - }`, - ) - } - - this._defaultDcs = data.primaryDcs - await this.storage.dcs.store(data.primaryDcs) - - if (data.self) { - await this.storage.self.store(data.self) - } - - await this.storage.provider.authKeys.set(data.primaryDcs.main.id, data.authKey) - - await this.storage.save() - } - - this.emit('before_connect') - - this.network - .connect(this._defaultDcs) - .then(() => { - promise.resolve() - this._connected = true - }) - .catch((err: Error) => this._emitError(err)) - } - - /** - * Close all connections and finalize the client. - */ - async close(): Promise { - this.emit('before_close') - - this._config.destroy() - this.network.destroy() - - await this.storage.save() - await this.storage.destroy?.() - - this.emit('closed') - } - - /** - * Make an RPC call to the primary DC. - * This method handles DC migration, flood waits and retries automatically. - * - * If you want more low-level control, use - * `primaryConnection.sendForResult()` (which is what this method wraps) - * - * This method is still quite low-level and you shouldn't use this - * when using high-level API provided by `@mtcute/client`. - * - * @param message RPC method to call - * @param params Additional call parameters - */ - async call( - message: MustEqual, - params?: RpcCallOptions, - ): Promise { - if (this._connected !== true) { - await this.connect() - } - - const stack = this._niceStacks ? new Error().stack : undefined - - const res = await this.network.call(message, params, stack) - - await this.storage.peers.updatePeersFrom(res) - - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return res - } - - /** - * Create a Proxy that will call all methods with given call parameters - * (see {@link RpcCallOptions}}) - * - * This is useful when you don't call `call()` directly, but rather - * use high-level API provided by `@mtcute/client`, for example: - * - * ```ts - * const client = new TelegramClient(...) - * - * const someone = await client - * .withCallParams({ timeout: 500 }) - * .getUsers(...) - * ``` - */ - withCallParams(params: RpcCallOptions): this { - return new Proxy(this, { - get(target, prop, receiver) { - if (prop === 'call') { - return (message: tl.RpcMethod, paramsCustom?: RpcCallOptions) => - target.call(message, { - ...params, - ...paramsCustom, - }) - } - - return Reflect.get(target, prop, receiver) - }, - }) - } - - /** - * Shorthand for `withCallParams({ abortSignal })` - */ - withAbortSignal(signal: AbortSignal): this { - return this.withCallParams({ abortSignal: signal }) - } - - /** - * Register an error handler for the client - * - * @param handler - * Error handler. Called with one or two parameters. - * The first one is always the error, and the second is - * the connection in which the error has occurred, in case - * this was connection-related error. - */ - onError(handler: (err: unknown, connection?: SessionConnection) => void): void { - this._emitError = handler - } - - async notifyLoggedIn(auth: tl.auth.RawAuthorization): Promise { - this.network.notifyLoggedIn(auth) - this.log.prefix = `[USER ${auth.user.id}] ` - await this.storage.self.store({ - userId: auth.user.id, - isBot: auth.user._ === 'user' && auth.user.bot!, - }) - this.emit('logged_in', auth) - } - - /** - * Export current session to a single *LONG* string, containing - * all the needed information. - * - * > **Warning!** Anyone with this string will be able - * > to authorize as you and do anything. Treat this - * > as your password, and never give it away! - * > - * > In case you have accidentally leaked this string, - * > make sure to revoke this session in account settings: - * > "Privacy & Security" > "Active sessions" > - * > find the one containing `mtcute` > Revoke, - * > or, in case this is a bot, revoke bot token - * > with [@BotFather](//t.me/botfather) - */ - async exportSession(): Promise { - const primaryDcs = (await this.storage.dcs.fetch()) ?? this._defaultDcs - - const authKey = await this.storage.provider.authKeys.get(primaryDcs.main.id) - if (!authKey) throw new Error('Auth key is not ready yet') - - return writeStringSession(this._writerMap, { - version: 2, - self: await this.storage.self.fetch(), - testMode: this._testMode, - primaryDcs, - authKey, - }) - } - - /** - * Request the session to be imported from the given session string. - * - * Note that the session will not be imported right away, - * instead, it will be imported once `connect()` is called - * - * Also note that the session will only be imported in case - * the storage is missing authorization (i.e. does not contain - * auth key for the primary DC), otherwise it will be ignored (unless `force`). - * - * @param session Session string to import - * @param force Whether to overwrite existing session - */ - importSession(session: string | StringSessionData, force = false): void { - this._importFrom = typeof session === 'string' ? readStringSession(this._readerMap, session) : session - this._importForce = force - } -} diff --git a/packages/core/src/base-client.types.ts b/packages/core/src/base-client.types.ts deleted file mode 100644 index d8ea5705..00000000 --- a/packages/core/src/base-client.types.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { tl } from '@mtcute/tl' -import { TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime' - -import { NetworkManagerExtraParams, ReconnectionStrategy, TransportFactory } from './network/index.js' -import { PersistentConnectionParams } from './network/persistent-connection.js' -import { IMtStorageProvider } from './storage/provider.js' -import { StorageManagerExtraOptions } from './storage/storage.js' -import { CryptoProviderFactory, DcOptions } from './utils/index.js' - -/** Options for {@link BaseTelegramClient} */ -export interface BaseTelegramClientOptions { - /** - * API ID from my.telegram.org - */ - apiId: number - /** - * API hash from my.telegram.org - */ - apiHash: string - - /** - * Storage to use for this client. - */ - storage: IMtStorageProvider - - /** Additional options for the storage manager */ - storageOptions?: StorageManagerExtraOptions - - /** - * Cryptography provider factory to allow delegating - * crypto to native addon, worker, etc. - */ - crypto?: CryptoProviderFactory - - /** - * Whether to use IPv6 datacenters - * (IPv6 will be preferred when choosing a DC by id) - * (default: false) - */ - useIpv6?: boolean - - /** - * Primary DC to use for initial connection. - * This does not mean this will be the only DC used, - * nor that this DC will actually be primary, this only - * determines the first DC the library will try to connect to. - * Can be used to connect to other networks (like test DCs). - * - * When session already contains primary DC, this parameter is ignored. - * - * @default Production DC 2. - */ - defaultDcs?: DcOptions - - /** - * Whether to connect to test servers. - * - * If passed, {@link defaultDc} defaults to Test DC 2. - * - * **Must** be passed if using test servers, even if - * you passed custom {@link defaultDc} - */ - testMode?: boolean - - /** - * Additional options for initConnection call. - * `apiId` and `query` are not available and will be ignored. - * Omitted values will be filled with defaults - */ - initConnectionOptions?: Partial> - - /** - * Transport factory to use in the client. - * - * @default platform-specific transport: WebSocket on the web, TCP in node - */ - transport?: TransportFactory - - /** - * Reconnection strategy. - * - * @default simple reconnection strategy: first 0ms, then up to 5s (increasing by 1s) - */ - reconnectionStrategy?: ReconnectionStrategy - - /** - * Maximum duration of a flood_wait that will be waited automatically. - * Flood waits above this threshold will throw a FloodWaitError. - * Set to 0 to disable. Can be overridden with `throwFlood` parameter in call() params - * - * @default 10000 - */ - floodSleepThreshold?: number - - /** - * Maximum number of retries when calling RPC methods. - * Call is retried when InternalError or FloodWaitError is encountered. - * Can be set to Infinity. - * - * @default 5 - */ - maxRetryCount?: number - - /** - * If true, every single API call will be wrapped with `tl.invokeWithoutUpdates`, - * effectively disabling the server-sent events for the clients. - * May be useful in some cases. - * - * Note that this only wraps calls made with `.call()` within the primary - * connection. Additional connections and direct `.sendForResult()` calls - * must be wrapped manually. - * - * @default false - */ - disableUpdates?: boolean - - /** - * mtcute can send all unknown RPC errors to [danog](https://github.com/danog)'s - * [error reporting service](https://rpc.pwrtelegram.xyz/). - * - * This is fully anonymous (except maybe IP) and is only used to improve the library - * and developer experience for everyone working with MTProto. This is fully opt-in, - * and if you're too paranoid, you can disable it by manually passing `enableErrorReporting: false` to the client. - * - * @default false - */ - enableErrorReporting?: boolean - - /** - * If true, RPC errors will have a stack trace of the initial `.call()` - * or `.sendForResult()` call position, which drastically improves - * debugging experience.
- * If false, they will have a stack trace of mtcute internals. - * - * Internally this creates a stack capture before every RPC call - * and stores it until the result is received. This might - * use a lot more memory than normal, thus can be disabled here. - * - * @default true - */ - niceStacks?: boolean - - /** - * Extra parameters for {@link NetworkManager} - */ - network?: NetworkManagerExtraParams - - /** - * Set logging level for the client. - * - * See static members of {@link LogManager} for possible values. - */ - logLevel?: number - - /** - * **EXPERT USE ONLY!** - * - * Override TL layer used for the connection. - * - * **Does not** change the schema used. - */ - overrideLayer?: number - - /** - * **EXPERT USE ONLY** - * - * Override reader map used for the connection. - */ - readerMap?: TlReaderMap - - /** - * **EXPERT USE ONLY** - * - * Override writer map used for the connection. - */ - writerMap?: TlWriterMap -} diff --git a/packages/core/src/highlevel/base.ts b/packages/core/src/highlevel/base.ts new file mode 100644 index 00000000..21636414 --- /dev/null +++ b/packages/core/src/highlevel/base.ts @@ -0,0 +1,287 @@ +import { tl } from '@mtcute/tl' + +import { MtClient, MtClientOptions } from '../network/client.js' +import { ConnectionKind, RpcCallOptions } from '../network/network-manager.js' +import { StorageManagerExtraOptions } from '../storage/storage.js' +import { MtArgumentError } from '../types/errors.js' +import { MustEqual } from '../types/utils.js' +import { asyncResettable, computeNewPasswordHash, computeSrpParams, readStringSession, StringSessionData, writeStringSession } from '../utils/index.js' +import { LogManager } from '../utils/logger.js' +import { ITelegramClient } from './client.types.js' +import { ITelegramStorageProvider } from './storage/provider.js' +import { TelegramStorageManager, TelegramStorageManagerExtraOptions } from './storage/storage.js' +import { UpdatesManager } from './updates/manager.js' +import { RawUpdateHandler, UpdatesManagerParams } from './updates/types.js' + +export interface BaseTelegramClientOptions extends MtClientOptions { + storage: ITelegramStorageProvider + storageOptions?: StorageManagerExtraOptions & TelegramStorageManagerExtraOptions + updates?: UpdatesManagerParams | false +} + +export class BaseTelegramClient implements ITelegramClient { + readonly updates?: UpdatesManager + private _serverUpdatesHandler: (updates: tl.TypeUpdates) => void = () => {} + + constructor(readonly params: BaseTelegramClientOptions) { + if (!params.disableUpdates && params.updates !== false) { + this.updates = new UpdatesManager(this, params.updates) + this._serverUpdatesHandler = this.updates.handleUpdate.bind(this.updates) + } + + this.mt.on('update', (update) => { + this._serverUpdatesHandler(update) + }) + } + + readonly log = this.params.logger ?? new LogManager('client') + readonly mt = new MtClient({ + ...this.params, + logger: this.log.create('mtproto'), + }) + readonly crypto = this.mt.crypto + readonly storage = new TelegramStorageManager(this.mt.storage, { + provider: this.params.storage, + ...this.params.storageOptions, + }) + + private _prepare = asyncResettable(async () => { + await this.mt.prepare() + + const self = await this.storage.self.fetch() + this.log.prefix = `[USER ${self?.userId ?? 'n/a'}] ` + this.mt.network.setIsPremium(self?.isPremium ?? false) + + await this.updates?.prepare() + }) + + /** + * **ADVANCED** + * + * Do all the preparations, but don't connect just yet. + * Useful when you want to do some preparations before + * connecting, like setting up session. + * + * Call {@link connect} to actually connect. + */ + prepare() { + return this._prepare.run() + } + + // used in a hot path, avoid extra function calls + private _connected = false + private _connect = asyncResettable(async () => { + await this._prepare.run() + await this.mt.connect() + this._connected = true + }) + + /** + * Initialize the connection to the primary DC. + * + * You shouldn't usually call this method directly as it is called + * implicitly the first time you call {@link call}. + */ + async connect(): Promise { + return this._connect.run() + } + + get isConnected(): boolean { + return this._connected + } + + async close(): Promise { + await this.mt.close() + this.updates?.stopLoop() + this._prepare.reset() + this._connect.reset() + this._connected = false + } + + async notifyLoggedIn(auth: tl.auth.TypeAuthorization | tl.RawUser): Promise { + const user = this.mt.network.notifyLoggedIn(auth) + + this.log.prefix = `[USER ${user.id}] ` + const self = await this.storage.self.storeFrom(user) + + this.updates?.notifyLoggedIn(self) + + return user + } + + async notifyLoggedOut(): Promise { + this.mt.network.notifyLoggedOut() + + this.log.prefix = '[USER n/a] ' + await this.storage.self.store(null) + } + + async notifyChannelOpened(channelId: number, pts?: number): Promise { + return this.updates?.notifyChannelOpened(channelId, pts) ?? false + } + + async notifyChannelClosed(channelId: number): Promise { + return this.updates?.notifyChannelClosed(channelId) ?? false + } + + /** + * Make an RPC call + * + * This method is still quite low-level and you shouldn't use this + * when using high-level API provided by `@mtcute/client`. + * + * @param message RPC method to call + * @param params Additional call parameters + */ + async call( + message: MustEqual, + params?: RpcCallOptions, + ): Promise { + if (!this._connected) { + await this._connect.run() + } + + const res = await this.mt.call(message, params) + + await this.storage.peers.updatePeersFrom(res) + + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return res + } + + /** + * Import the session from the given session string. + * + * Note that the session will only be imported in case + * the storage is missing authorization (i.e. does not contain + * auth key for the primary DC), otherwise it will be ignored (unless `force`). + * + * @param session Session string to import + * @param force Whether to overwrite existing session + */ + async importSession(session: string | StringSessionData, force = false): Promise { + await this.prepare() + + const defaultDcAuthKey = await this.mt.storage.provider.authKeys.get(this.mt._defaultDcs.main.id) + + if (defaultDcAuthKey && !force) return + + const data = typeof session === 'string' ? readStringSession(this.mt._readerMap, session) : session + + if (data.testMode && !this.params.testMode) { + throw new Error( + 'This session string is not for the current backend. ' + + `Session is ${data.testMode ? 'test' : 'prod'}, ` + + `but the client is ${this.params.testMode ? 'test' : 'prod'}`, + ) + } + + this.mt._defaultDcs = data.primaryDcs + await this.mt.storage.dcs.store(data.primaryDcs) + + if (data.self) { + await this.storage.self.store(data.self) + } + + await this.mt.storage.provider.authKeys.set(data.primaryDcs.main.id, data.authKey) + + await this.mt.storage.save() + } + + /** + * Export current session to a single *LONG* string, containing + * all the needed information. + * + * > **Warning!** Anyone with this string will be able + * > to authorize as you and do anything. Treat this + * > as your password, and never give it away! + * > + * > In case you have accidentally leaked this string, + * > make sure to revoke this session in account settings: + * > "Privacy & Security" > "Active sessions" > + * > find the one containing `mtcute` > Revoke, + * > or, in case this is a bot, revoke bot token + * > with [@BotFather](//t.me/botfather) + */ + async exportSession(): Promise { + await this._prepare.run() + + const primaryDcs = (await this.mt.storage.dcs.fetch()) ?? this.mt._defaultDcs + + const authKey = await this.mt.storage.provider.authKeys.get(primaryDcs.main.id) + if (!authKey) throw new Error('Auth key is not ready yet') + + return writeStringSession(this.mt._writerMap, { + version: 2, + self: await this.storage.self.fetch(), + testMode: Boolean(this.params.testMode), + primaryDcs, + authKey, + }) + } + + /** + * Register an error handler for the client + * + * @param handler Error handler. + */ + onError(handler: (err: unknown) => void): void { + this.mt.onError(handler) + } + + emitError(err: unknown): void { + this.mt.emitError(err) + } + + handleClientUpdate(updates: tl.TypeUpdates, noDispatch?: boolean): void { + this.updates?.handleClientUpdate(updates, noDispatch) + } + + onServerUpdate(handler: (update: tl.TypeUpdates) => void): void { + this._serverUpdatesHandler = handler + } + + onUpdate(handler: RawUpdateHandler): void { + if (!this.updates) { + throw new MtArgumentError('Updates manager is disabled') + } + + this.updates.setHandler(handler) + } + + async getApiCrenetials() { + return { + id: this.params.apiId, + hash: this.params.apiHash, + } + } + + async getPoolSize(kind: ConnectionKind, dcId?: number): Promise { + if (!this._connected) { + await this._connect.run() + } + + return this.mt.network.getPoolSize(kind, dcId) + } + + async getPrimaryDcId(): Promise { + if (!this._connected) { + await this._connect.run() + } + + return this.mt.network.getPrimaryDcId() + } + + computeSrpParams( + request: tl.account.RawPassword, + password: string, + ): Promise { + return computeSrpParams(this.crypto, request, password) + } + computeNewPasswordHash( + algo: tl.TypePasswordKdfAlgo, + password: string, + ): Promise { + return computeNewPasswordHash(this.crypto, algo, password) + } +} diff --git a/packages/client/src/client.ts b/packages/core/src/highlevel/client.ts similarity index 90% rename from packages/client/src/client.ts rename to packages/core/src/highlevel/client.ts index d4bc1018..c0534745 100644 --- a/packages/client/src/client.ts +++ b/packages/core/src/highlevel/client.ts @@ -1,32 +1,30 @@ /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging, @typescript-eslint/unified-signatures */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* THIS FILE WAS AUTO-GENERATED */ -import { - BaseTelegramClient, - BaseTelegramClientOptions, - IMtStorageProvider, - Long, - MaybeArray, - MaybeAsync, - PartialExcept, - PartialOnly, - tl, -} from '@mtcute/core' -import { MemoryStorage } from '@mtcute/core/src/storage/providers/memory/index.js' -import { tdFileId } from '@mtcute/file-id' +import EventEmitter from 'events' +import Long from 'long' -import { isSelfPeer } from './methods/auth/_state.js' +import { tdFileId } from '@mtcute/file-id' +import { tl } from '@mtcute/tl' + +import { MemoryStorage } from '../storage/providers/memory/index.js' +import { MaybeArray, MaybeAsync, PartialExcept, PartialOnly } from '../types/index.js' +import { StringSessionData } from '../utils/string-session.js' +import { BaseTelegramClient, BaseTelegramClientOptions } from './base.js' +import { ITelegramClient } from './client.types.js' import { checkPassword } from './methods/auth/check-password.js' import { getPasswordHint } from './methods/auth/get-password-hint.js' import { logOut } from './methods/auth/log-out.js' import { recoverPassword } from './methods/auth/recover-password.js' import { resendCode } from './methods/auth/resend-code.js' +import { run } from './methods/auth/run.js' import { sendCode } from './methods/auth/send-code.js' import { sendRecoveryCode } from './methods/auth/send-recovery-code.js' import { signIn } from './methods/auth/sign-in.js' import { signInBot } from './methods/auth/sign-in-bot.js' import { start } from './methods/auth/start.js' import { startTest } from './methods/auth/start-test.js' +import { isSelfPeer } from './methods/auth/utils.js' import { answerCallbackQuery } from './methods/bots/answer-callback-query.js' import { answerInlineQuery } from './methods/bots/answer-inline-query.js' import { answerPreCheckoutQuery } from './methods/bots/answer-pre-checkout-query.js' @@ -214,22 +212,6 @@ import { sendStory } from './methods/stories/send-story.js' import { sendStoryReaction } from './methods/stories/send-story-reaction.js' import { togglePeerStoriesArchived } from './methods/stories/toggle-peer-stories-archived.js' import { toggleStoriesPinned } from './methods/stories/toggle-stories-pinned.js' -import { - enableUpdatesProcessing, - makeParsedUpdateHandler, - ParsedUpdateHandlerParams, - UpdatesManagerParams, -} from './methods/updates/index.js' -import { - catchUp, - enableRps, - getCurrentRpsIncoming, - getCurrentRpsProcessing, - notifyChannelClosed, - notifyChannelOpened, - startUpdatesLoop, - stopUpdatesLoop, -} from './methods/updates/manager.js' import { blockUser } from './methods/users/block-user.js' import { deleteProfilePhotos } from './methods/users/delete-profile-photos.js' import { editCloseFriends, editCloseFriendsRaw } from './methods/users/edit-close-friends.js' @@ -250,6 +232,7 @@ import { setMyUsername } from './methods/users/set-my-username.js' import { setOffline } from './methods/users/set-offline.js' import { unblockUser } from './methods/users/unblock-user.js' import { updateProfile } from './methods/users/update-profile.js' +import { ITelegramStorageProvider } from './storage/provider.js' import { Conversation } from './types/conversation.js' import { AllStories, @@ -331,45 +314,27 @@ import { UserStatusUpdate, UserTypingUpdate, } from './types/index.js' +import { makeParsedUpdateHandler, ParsedUpdateHandlerParams } from './updates/parsed.js' import { _defaultStorageFactory } from './utils/platform/storage.js' // from methods/_init.ts -interface TelegramClientOptions extends Omit { - /** - * Storage to use for this client. - * - * If a string is passed, it will be used as: - * - a path to a JSON file for Node.js - * - IndexedDB database name for browsers - * - * If omitted, {@link MemoryStorage} is used - */ - storage?: string | IMtStorageProvider - - /** - * Parameters for updates manager. - */ - updates?: Omit - - /** - * **ADVANCED** - * - * If set to `true`, updates manager will not be created, - * and only raw TL Updates will be emitted. - * - * Unlike {@link TelegramClientOptions.disableUpdates}, this - * does not prevent the updates from being sent by the server, - * but disables proper handling of them (see [Working with Updates](https://core.telegram.org/api/updates)) - * - * This may be useful in some cases when you require more control over - * the updates or to minimize additional overhead from properly handling them - * for some very particular use cases. - * - * The updates **will not** be dispatched the normal way, instead - * you should manually add a handler using `client.network.setUpdateHandler`. - */ - disableUpdatesManager?: boolean - +// @copy +type TelegramClientOptions = ( + | (Omit & { + /** + * Storage to use for this client. + * + * If a string is passed, it will be used as: + * - a path to a JSON file for Node.js + * - IndexedDB database name for browsers + * + * If omitted, {@link MemoryStorage} is used + */ + storage?: string | ITelegramStorageProvider + }) + | { client: ITelegramClient } +) & { + updates?: Omit /** * If `true`, the updates that were handled by some {@link Conversation} * will not be dispatched any further. @@ -379,7 +344,7 @@ interface TelegramClientOptions extends Omit void): this - /** - * Check if the given peer/input peer is referring to the current user - * **Available**: ✅ both users and bots - * - */ - isSelfPeer(peer: tl.TypeInputPeer | tl.TypePeer | tl.TypeInputUser): boolean /** * Check your Two-Step verification password and log in * @@ -625,7 +584,6 @@ export interface TelegramClient extends BaseTelegramClient { * * @param params Parameters to be passed to {@link start} * @param then Function to be called after {@link start} returns - * @manual=noemit */ run(params: Parameters[1], then?: (user: User) => void | Promise): void /** @@ -731,7 +689,7 @@ export interface TelegramClient extends BaseTelegramClient { * Note that passed session will be ignored in case storage already * contains authorization. */ - session?: string + session?: string | StringSessionData /** * Whether to overwrite existing session. @@ -785,6 +743,12 @@ export interface TelegramClient extends BaseTelegramClient { */ codeSentCallback?: (code: SentCode) => MaybeAsync }): Promise + /** + * Check if the given peer/input peer is referring to the current user + * **Available**: ✅ both users and bots + * + */ + isSelfPeer(peer: tl.TypeInputPeer | tl.TypePeer | tl.TypeInputUser): boolean /** * Send an answer to a callback query. * @@ -4003,7 +3967,7 @@ export interface TelegramClient extends BaseTelegramClient { */ sendTyping( chatId: InputPeerLike, - status?: TypingStatus | tl.TypeSendMessageAction, + status?: Exclude | tl.TypeSendMessageAction, params?: { /** * For `upload_*` and history import actions, progress of the upload @@ -4892,106 +4856,6 @@ export interface TelegramClient extends BaseTelegramClient { */ peer?: InputPeerLike }): Promise - // code in this file is very bad, thanks to Telegram's awesome updates mechanism - /** - * Enable RPS meter. - * Only available in NodeJS v10.7.0 and newer - * - * > **Note**: This may have negative impact on performance - * - * **Available**: ✅ both users and bots - * - * @param size Sampling size - * @param time Window time - */ - enableRps(size?: number, time?: number): void - /** - * Get current average incoming RPS - * - * Incoming RPS is calculated based on - * incoming update containers. Normally, - * they should be around the same, except - * rare situations when processing rps - * may peak. - * **Available**: ✅ both users and bots - * - */ - getCurrentRpsIncoming(): number - /** - * Get current average processing RPS - * - * Processing RPS is calculated based on - * dispatched updates. Normally, - * they should be around the same, except - * rare situations when processing rps - * may peak. - * **Available**: ✅ both users and bots - * - */ - getCurrentRpsProcessing(): number - /** - * Start updates loop. - * - * You must first call {@link enableUpdatesProcessing} to use this method. - * - * It is recommended to use this method in callback to {@link start}, - * or otherwise make sure the user is logged in. - * - * > **Note**: If you are using {@link UpdatesManagerParams.catchUp} option, - * > catching up will be done in background, you can't await it. - * **Available**: ✅ both users and bots - * - */ - startUpdatesLoop(): Promise - /** - * **ADVANCED** - * - * Manually stop updates loop. - * Usually done automatically when stopping the client with {@link close} - * **Available**: ✅ both users and bots - * - */ - stopUpdatesLoop(): void - /** - * Catch up with the server by loading missed updates. - * - * > **Note**: In case the storage was not properly - * > closed the last time, "catching up" might - * > result in duplicate updates. - * **Available**: ✅ both users and bots - * - */ - catchUp(): void - /** - * **ADVANCED** - * - * Notify the updates manager that some channel was "opened". - * Channel difference for "opened" channels will be fetched on a regular basis. - * This is a low-level method, prefer using {@link openChat} instead. - * - * Channel must be resolve-able with `resolvePeer` method (i.e. be in cache); - * base chat PTS must either be passed (e.g. from {@link Dialog}), or cached in storage. - * - * **Available**: ✅ both users and bots - * - * @param channelId Bare ID of the channel - * @param pts PTS of the channel, if known (e.g. from {@link Dialog}) - * @returns `true` if the channel was opened for the first time, `false` if it is already opened - */ - notifyChannelOpened(channelId: number, pts?: number): boolean - /** - * **ADVANCED** - * - * Notify the updates manager that some channel was "closed". - * Basically the opposite of {@link notifyChannelOpened}. - * This is a low-level method, prefer using {@link closeChat} instead. - * - * **Available**: ✅ both users and bots - * - * @param channelId Bare channel ID - * @returns `true` if the chat was closed for the last time, `false` otherwise - */ - notifyChannelClosed(channelId: number): boolean /** * Block a user * @@ -5256,1047 +5120,831 @@ export interface TelegramClient extends BaseTelegramClient { export type { TelegramClientOptions } -export class TelegramClient extends BaseTelegramClient { - protected _disableUpdatesManager: boolean +export class TelegramClient extends EventEmitter implements ITelegramClient { + _client: ITelegramClient constructor(opts: TelegramClientOptions) { - if (typeof opts.storage === 'string') { - opts.storage = _defaultStorageFactory(opts.storage) - } else if (!opts.storage) { - opts.storage = new MemoryStorage() - } + super() - /* eslint-disable @typescript-eslint/no-unsafe-call */ - // @ts-expect-error codegen - super(opts) - /* eslint-enable @typescript-eslint/no-unsafe-call */ - this._disableUpdatesManager = opts.disableUpdatesManager ?? false - const skipConversationUpdates = opts.skipConversationUpdates ?? true + if ('client' in opts) { + this._client = opts.client + } else { + let storage: ITelegramStorageProvider - if (!opts.disableUpdates && !opts.disableUpdatesManager) { - const { messageGroupingInterval, ...managerParams } = opts.updates ?? {} + if (typeof opts.storage === 'string') { + storage = _defaultStorageFactory(opts.storage) + } else if (!opts.storage) { + storage = new MemoryStorage() + } else { + storage = opts.storage + } - enableUpdatesProcessing(this, { - ...managerParams, - onUpdate: makeParsedUpdateHandler({ - messageGroupingInterval, - onUpdate: (update) => { - if (Conversation.handleUpdate(this, update) && skipConversationUpdates) return - - this.emit('update', update) - this.emit(update.name, update.data) - }, - onRawUpdate: (update, peers) => { - this.emit('raw_update', update, peers) - }, - }), + this._client = new BaseTelegramClient({ + ...opts, + storage, }) } - } -} -TelegramClient.prototype.isSelfPeer = function (...args) { - return isSelfPeer(this, ...args) + // @ts-expect-error codegen + this.log = this._client.log + // @ts-expect-error codegen + this.storage = this._client.storage + + const skipConversationUpdates = opts.skipConversationUpdates ?? true + const { messageGroupingInterval } = opts.updates ?? {} + + this._client.onUpdate( + makeParsedUpdateHandler({ + messageGroupingInterval, + onUpdate: (update) => { + if (Conversation.handleUpdate(this._client, update) && skipConversationUpdates) return + + this.emit('update', update) + this.emit(update.name, update.data) + }, + onRawUpdate: (update, peers) => { + this.emit('raw_update', update, peers) + }, + }), + ) + } } TelegramClient.prototype.checkPassword = function (...args) { - return checkPassword(this, ...args) + return checkPassword(this._client, ...args) } - TelegramClient.prototype.getPasswordHint = function (...args) { - return getPasswordHint(this, ...args) + return getPasswordHint(this._client, ...args) } - TelegramClient.prototype.logOut = function (...args) { - return logOut(this, ...args) + return logOut(this._client, ...args) } - TelegramClient.prototype.recoverPassword = function (...args) { - return recoverPassword(this, ...args) + return recoverPassword(this._client, ...args) } - TelegramClient.prototype.resendCode = function (...args) { - return resendCode(this, ...args) + return resendCode(this._client, ...args) +} +TelegramClient.prototype.run = function (...args) { + return run(this._client, ...args) } - TelegramClient.prototype.sendCode = function (...args) { - return sendCode(this, ...args) + return sendCode(this._client, ...args) } - TelegramClient.prototype.sendRecoveryCode = function (...args) { - return sendRecoveryCode(this, ...args) + return sendRecoveryCode(this._client, ...args) } - TelegramClient.prototype.signInBot = function (...args) { - return signInBot(this, ...args) + return signInBot(this._client, ...args) } - TelegramClient.prototype.signIn = function (...args) { - return signIn(this, ...args) + return signIn(this._client, ...args) } - TelegramClient.prototype.startTest = function (...args) { - return startTest(this, ...args) + return startTest(this._client, ...args) +} +TelegramClient.prototype.start = function (...args) { + return start(this._client, ...args) +} +TelegramClient.prototype.isSelfPeer = function (...args) { + return isSelfPeer(this._client, ...args) } - TelegramClient.prototype.answerCallbackQuery = function (...args) { - return answerCallbackQuery(this, ...args) + return answerCallbackQuery(this._client, ...args) } - TelegramClient.prototype.answerInlineQuery = function (...args) { - return answerInlineQuery(this, ...args) + return answerInlineQuery(this._client, ...args) } - TelegramClient.prototype.answerPreCheckoutQuery = function (...args) { - return answerPreCheckoutQuery(this, ...args) + return answerPreCheckoutQuery(this._client, ...args) } - TelegramClient.prototype.deleteMyCommands = function (...args) { - return deleteMyCommands(this, ...args) + return deleteMyCommands(this._client, ...args) } - TelegramClient.prototype.getBotInfo = function (...args) { - return getBotInfo(this, ...args) + return getBotInfo(this._client, ...args) } - TelegramClient.prototype.getBotMenuButton = function (...args) { - return getBotMenuButton(this, ...args) + return getBotMenuButton(this._client, ...args) } - TelegramClient.prototype.getCallbackAnswer = function (...args) { - return getCallbackAnswer(this, ...args) + return getCallbackAnswer(this._client, ...args) } - TelegramClient.prototype.getGameHighScores = function (...args) { - return getGameHighScores(this, ...args) + return getGameHighScores(this._client, ...args) } - TelegramClient.prototype.getInlineGameHighScores = function (...args) { - return getInlineGameHighScores(this, ...args) + return getInlineGameHighScores(this._client, ...args) } - TelegramClient.prototype.getMyCommands = function (...args) { - return getMyCommands(this, ...args) + return getMyCommands(this._client, ...args) } - TelegramClient.prototype.setBotInfo = function (...args) { - return setBotInfo(this, ...args) + return setBotInfo(this._client, ...args) } - TelegramClient.prototype.setBotMenuButton = function (...args) { - return setBotMenuButton(this, ...args) + return setBotMenuButton(this._client, ...args) } - TelegramClient.prototype.setGameScore = function (...args) { - return setGameScore(this, ...args) + return setGameScore(this._client, ...args) } - TelegramClient.prototype.setInlineGameScore = function (...args) { - return setInlineGameScore(this, ...args) + return setInlineGameScore(this._client, ...args) } - TelegramClient.prototype.setMyCommands = function (...args) { - return setMyCommands(this, ...args) + return setMyCommands(this._client, ...args) } - TelegramClient.prototype.setMyDefaultRights = function (...args) { - return setMyDefaultRights(this, ...args) + return setMyDefaultRights(this._client, ...args) } - TelegramClient.prototype.addChatMembers = function (...args) { - return addChatMembers(this, ...args) + return addChatMembers(this._client, ...args) } - TelegramClient.prototype.archiveChats = function (...args) { - return archiveChats(this, ...args) + return archiveChats(this._client, ...args) } - TelegramClient.prototype.banChatMember = function (...args) { - return banChatMember(this, ...args) + return banChatMember(this._client, ...args) } - TelegramClient.prototype.createChannel = function (...args) { - return createChannel(this, ...args) + return createChannel(this._client, ...args) } - TelegramClient.prototype.createGroup = function (...args) { - return createGroup(this, ...args) + return createGroup(this._client, ...args) } - TelegramClient.prototype.createSupergroup = function (...args) { - return createSupergroup(this, ...args) + return createSupergroup(this._client, ...args) } - TelegramClient.prototype.deleteChannel = function (...args) { - return deleteChannel(this, ...args) + return deleteChannel(this._client, ...args) } - TelegramClient.prototype.deleteSupergroup = function (...args) { - return deleteChannel(this, ...args) + return deleteChannel(this._client, ...args) } - TelegramClient.prototype.deleteChatPhoto = function (...args) { - return deleteChatPhoto(this, ...args) + return deleteChatPhoto(this._client, ...args) } - TelegramClient.prototype.deleteGroup = function (...args) { - return deleteGroup(this, ...args) + return deleteGroup(this._client, ...args) } - TelegramClient.prototype.deleteHistory = function (...args) { - return deleteHistory(this, ...args) + return deleteHistory(this._client, ...args) } - TelegramClient.prototype.deleteUserHistory = function (...args) { - return deleteUserHistory(this, ...args) + return deleteUserHistory(this._client, ...args) } - TelegramClient.prototype.editAdminRights = function (...args) { - return editAdminRights(this, ...args) + return editAdminRights(this._client, ...args) } - TelegramClient.prototype.getChatEventLog = function (...args) { - return getChatEventLog(this, ...args) + return getChatEventLog(this._client, ...args) } - TelegramClient.prototype.getChatMember = function (...args) { - return getChatMember(this, ...args) + return getChatMember(this._client, ...args) } - TelegramClient.prototype.getChatMembers = function (...args) { - return getChatMembers(this, ...args) + return getChatMembers(this._client, ...args) } - TelegramClient.prototype.getChatPreview = function (...args) { - return getChatPreview(this, ...args) + return getChatPreview(this._client, ...args) } - TelegramClient.prototype.getChat = function (...args) { - return getChat(this, ...args) + return getChat(this._client, ...args) } - TelegramClient.prototype.getFullChat = function (...args) { - return getFullChat(this, ...args) + return getFullChat(this._client, ...args) } - TelegramClient.prototype.getNearbyChats = function (...args) { - return getNearbyChats(this, ...args) + return getNearbyChats(this._client, ...args) } - TelegramClient.prototype.getSimilarChannels = function (...args) { - return getSimilarChannels(this, ...args) + return getSimilarChannels(this._client, ...args) } - TelegramClient.prototype.iterChatEventLog = function (...args) { - return iterChatEventLog(this, ...args) + return iterChatEventLog(this._client, ...args) } - TelegramClient.prototype.iterChatMembers = function (...args) { - return iterChatMembers(this, ...args) + return iterChatMembers(this._client, ...args) } - TelegramClient.prototype.joinChat = function (...args) { - return joinChat(this, ...args) + return joinChat(this._client, ...args) } - TelegramClient.prototype.kickChatMember = function (...args) { - return kickChatMember(this, ...args) + return kickChatMember(this._client, ...args) } - TelegramClient.prototype.leaveChat = function (...args) { - return leaveChat(this, ...args) + return leaveChat(this._client, ...args) } - TelegramClient.prototype.markChatUnread = function (...args) { - return markChatUnread(this, ...args) + return markChatUnread(this._client, ...args) } - TelegramClient.prototype.openChat = function (...args) { - return openChat(this, ...args) + return openChat(this._client, ...args) } - TelegramClient.prototype.closeChat = function (...args) { - return closeChat(this, ...args) + return closeChat(this._client, ...args) } - TelegramClient.prototype.reorderUsernames = function (...args) { - return reorderUsernames(this, ...args) + return reorderUsernames(this._client, ...args) } - TelegramClient.prototype.restrictChatMember = function (...args) { - return restrictChatMember(this, ...args) + return restrictChatMember(this._client, ...args) } - TelegramClient.prototype.saveDraft = function (...args) { - return saveDraft(this, ...args) + return saveDraft(this._client, ...args) } - TelegramClient.prototype.setChatColor = function (...args) { - return setChatColor(this, ...args) + return setChatColor(this._client, ...args) } - TelegramClient.prototype.setChatDefaultPermissions = function (...args) { - return setChatDefaultPermissions(this, ...args) + return setChatDefaultPermissions(this._client, ...args) } - TelegramClient.prototype.setChatDescription = function (...args) { - return setChatDescription(this, ...args) + return setChatDescription(this._client, ...args) } - TelegramClient.prototype.setChatPhoto = function (...args) { - return setChatPhoto(this, ...args) + return setChatPhoto(this._client, ...args) } - TelegramClient.prototype.setChatTitle = function (...args) { - return setChatTitle(this, ...args) + return setChatTitle(this._client, ...args) } - TelegramClient.prototype.setChatTtl = function (...args) { - return setChatTtl(this, ...args) + return setChatTtl(this._client, ...args) } - TelegramClient.prototype.setChatUsername = function (...args) { - return setChatUsername(this, ...args) + return setChatUsername(this._client, ...args) } - TelegramClient.prototype.setSlowMode = function (...args) { - return setSlowMode(this, ...args) + return setSlowMode(this._client, ...args) } - TelegramClient.prototype.toggleContentProtection = function (...args) { - return toggleContentProtection(this, ...args) + return toggleContentProtection(this._client, ...args) } - TelegramClient.prototype.toggleFragmentUsername = function (...args) { - return toggleFragmentUsername(this, ...args) + return toggleFragmentUsername(this._client, ...args) } - TelegramClient.prototype.toggleJoinRequests = function (...args) { - return toggleJoinRequests(this, ...args) + return toggleJoinRequests(this._client, ...args) } - TelegramClient.prototype.toggleJoinToSend = function (...args) { - return toggleJoinToSend(this, ...args) + return toggleJoinToSend(this._client, ...args) } - TelegramClient.prototype.unarchiveChats = function (...args) { - return unarchiveChats(this, ...args) + return unarchiveChats(this._client, ...args) } - TelegramClient.prototype.unbanChatMember = function (...args) { - return unbanChatMember(this, ...args) + return unbanChatMember(this._client, ...args) } - TelegramClient.prototype.unrestrictChatMember = function (...args) { - return unbanChatMember(this, ...args) + return unbanChatMember(this._client, ...args) } - TelegramClient.prototype.addContact = function (...args) { - return addContact(this, ...args) + return addContact(this._client, ...args) } - TelegramClient.prototype.deleteContacts = function (...args) { - return deleteContacts(this, ...args) + return deleteContacts(this._client, ...args) } - TelegramClient.prototype.getContacts = function (...args) { - return getContacts(this, ...args) + return getContacts(this._client, ...args) } - TelegramClient.prototype.importContacts = function (...args) { - return importContacts(this, ...args) + return importContacts(this._client, ...args) } - TelegramClient.prototype.createFolder = function (...args) { - return createFolder(this, ...args) + return createFolder(this._client, ...args) } - TelegramClient.prototype.deleteFolder = function (...args) { - return deleteFolder(this, ...args) + return deleteFolder(this._client, ...args) } - TelegramClient.prototype.editFolder = function (...args) { - return editFolder(this, ...args) + return editFolder(this._client, ...args) } - TelegramClient.prototype.findFolder = function (...args) { - return findFolder(this, ...args) + return findFolder(this._client, ...args) } - TelegramClient.prototype.getFolders = function (...args) { - return getFolders(this, ...args) + return getFolders(this._client, ...args) } - TelegramClient.prototype.getPeerDialogs = function (...args) { - return getPeerDialogs(this, ...args) + return getPeerDialogs(this._client, ...args) } - TelegramClient.prototype.iterDialogs = function (...args) { - return iterDialogs(this, ...args) + return iterDialogs(this._client, ...args) } - TelegramClient.prototype.setFoldersOrder = function (...args) { - return setFoldersOrder(this, ...args) + return setFoldersOrder(this._client, ...args) } - TelegramClient.prototype.downloadAsBuffer = function (...args) { - return downloadAsBuffer(this, ...args) + return downloadAsBuffer(this._client, ...args) } - TelegramClient.prototype.downloadToFile = function (...args) { - return downloadToFile(this, ...args) + return downloadToFile(this._client, ...args) } - TelegramClient.prototype.downloadAsIterable = function (...args) { - return downloadAsIterable(this, ...args) + return downloadAsIterable(this._client, ...args) } - TelegramClient.prototype.downloadAsStream = function (...args) { - return downloadAsStream(this, ...args) + return downloadAsStream(this._client, ...args) } - TelegramClient.prototype._normalizeInputFile = function (...args) { - return _normalizeInputFile(this, ...args) + return _normalizeInputFile(this._client, ...args) } - TelegramClient.prototype._normalizeInputMedia = function (...args) { - return _normalizeInputMedia(this, ...args) + return _normalizeInputMedia(this._client, ...args) } - TelegramClient.prototype.uploadFile = function (...args) { - return uploadFile(this, ...args) + return uploadFile(this._client, ...args) } - TelegramClient.prototype.uploadMedia = function (...args) { - return uploadMedia(this, ...args) + return uploadMedia(this._client, ...args) } - TelegramClient.prototype.createForumTopic = function (...args) { - return createForumTopic(this, ...args) + return createForumTopic(this._client, ...args) } - TelegramClient.prototype.deleteForumTopicHistory = function (...args) { - return deleteForumTopicHistory(this, ...args) + return deleteForumTopicHistory(this._client, ...args) } - TelegramClient.prototype.editForumTopic = function (...args) { - return editForumTopic(this, ...args) + return editForumTopic(this._client, ...args) } - TelegramClient.prototype.getForumTopicsById = function (...args) { - return getForumTopicsById(this, ...args) + return getForumTopicsById(this._client, ...args) } - TelegramClient.prototype.getForumTopics = function (...args) { - return getForumTopics(this, ...args) + return getForumTopics(this._client, ...args) } - TelegramClient.prototype.iterForumTopics = function (...args) { - return iterForumTopics(this, ...args) + return iterForumTopics(this._client, ...args) } - TelegramClient.prototype.reorderPinnedForumTopics = function (...args) { - return reorderPinnedForumTopics(this, ...args) + return reorderPinnedForumTopics(this._client, ...args) } - TelegramClient.prototype.toggleForumTopicClosed = function (...args) { - return toggleForumTopicClosed(this, ...args) + return toggleForumTopicClosed(this._client, ...args) } - TelegramClient.prototype.toggleForumTopicPinned = function (...args) { - return toggleForumTopicPinned(this, ...args) + return toggleForumTopicPinned(this._client, ...args) } - TelegramClient.prototype.toggleForum = function (...args) { - return toggleForum(this, ...args) + return toggleForum(this._client, ...args) } - TelegramClient.prototype.toggleGeneralTopicHidden = function (...args) { - return toggleGeneralTopicHidden(this, ...args) + return toggleGeneralTopicHidden(this._client, ...args) } - TelegramClient.prototype.createInviteLink = function (...args) { - return createInviteLink(this, ...args) + return createInviteLink(this._client, ...args) } - TelegramClient.prototype.editInviteLink = function (...args) { - return editInviteLink(this, ...args) + return editInviteLink(this._client, ...args) } - TelegramClient.prototype.exportInviteLink = function (...args) { - return exportInviteLink(this, ...args) + return exportInviteLink(this._client, ...args) } - TelegramClient.prototype.getInviteLinkMembers = function (...args) { - return getInviteLinkMembers(this, ...args) + return getInviteLinkMembers(this._client, ...args) } - TelegramClient.prototype.getInviteLink = function (...args) { - return getInviteLink(this, ...args) + return getInviteLink(this._client, ...args) } - TelegramClient.prototype.getInviteLinks = function (...args) { - return getInviteLinks(this, ...args) + return getInviteLinks(this._client, ...args) } - TelegramClient.prototype.getPrimaryInviteLink = function (...args) { - return getPrimaryInviteLink(this, ...args) + return getPrimaryInviteLink(this._client, ...args) } - TelegramClient.prototype.hideAllJoinRequests = function (...args) { - return hideAllJoinRequests(this, ...args) + return hideAllJoinRequests(this._client, ...args) } - TelegramClient.prototype.hideJoinRequest = function (...args) { - return hideJoinRequest(this, ...args) + return hideJoinRequest(this._client, ...args) } - TelegramClient.prototype.iterInviteLinkMembers = function (...args) { - return iterInviteLinkMembers(this, ...args) + return iterInviteLinkMembers(this._client, ...args) } - TelegramClient.prototype.iterInviteLinks = function (...args) { - return iterInviteLinks(this, ...args) + return iterInviteLinks(this._client, ...args) } - TelegramClient.prototype.revokeInviteLink = function (...args) { - return revokeInviteLink(this, ...args) + return revokeInviteLink(this._client, ...args) } - TelegramClient.prototype.closePoll = function (...args) { - return closePoll(this, ...args) + return closePoll(this._client, ...args) } - TelegramClient.prototype.deleteMessagesById = function (...args) { - return deleteMessagesById(this, ...args) + return deleteMessagesById(this._client, ...args) } - TelegramClient.prototype.deleteMessages = function (...args) { - return deleteMessages(this, ...args) + return deleteMessages(this._client, ...args) } - TelegramClient.prototype.deleteScheduledMessages = function (...args) { - return deleteScheduledMessages(this, ...args) + return deleteScheduledMessages(this._client, ...args) } - TelegramClient.prototype.editInlineMessage = function (...args) { - return editInlineMessage(this, ...args) + return editInlineMessage(this._client, ...args) } - TelegramClient.prototype.editMessage = function (...args) { - return editMessage(this, ...args) + return editMessage(this._client, ...args) } - TelegramClient.prototype.forwardMessagesById = function (...args) { - return forwardMessagesById(this, ...args) + return forwardMessagesById(this._client, ...args) } - TelegramClient.prototype.forwardMessages = function (...args) { - return forwardMessages(this, ...args) + return forwardMessages(this._client, ...args) } - TelegramClient.prototype.getCallbackQueryMessage = function (...args) { - return getCallbackQueryMessage(this, ...args) + return getCallbackQueryMessage(this._client, ...args) } - TelegramClient.prototype.getDiscussionMessage = function (...args) { - return getDiscussionMessage(this, ...args) + return getDiscussionMessage(this._client, ...args) } - TelegramClient.prototype.getHistory = function (...args) { - return getHistory(this, ...args) + return getHistory(this._client, ...args) } - TelegramClient.prototype.getMessageByLink = function (...args) { - return getMessageByLink(this, ...args) + return getMessageByLink(this._client, ...args) } - TelegramClient.prototype.getMessageGroup = function (...args) { - return getMessageGroup(this, ...args) + return getMessageGroup(this._client, ...args) } - TelegramClient.prototype.getMessageReactionsById = function (...args) { - return getMessageReactionsById(this, ...args) + return getMessageReactionsById(this._client, ...args) } - TelegramClient.prototype.getMessageReactions = function (...args) { - return getMessageReactions(this, ...args) + return getMessageReactions(this._client, ...args) } - TelegramClient.prototype.getMessagesUnsafe = function (...args) { - return getMessagesUnsafe(this, ...args) + return getMessagesUnsafe(this._client, ...args) } - TelegramClient.prototype.getMessages = function (...args) { - return getMessages(this, ...args) + return getMessages(this._client, ...args) } - TelegramClient.prototype.getReactionUsers = function (...args) { - return getReactionUsers(this, ...args) + return getReactionUsers(this._client, ...args) } - TelegramClient.prototype.getReplyTo = function (...args) { - return getReplyTo(this, ...args) + return getReplyTo(this._client, ...args) } - TelegramClient.prototype.getScheduledMessages = function (...args) { - return getScheduledMessages(this, ...args) + return getScheduledMessages(this._client, ...args) } - TelegramClient.prototype.iterHistory = function (...args) { - return iterHistory(this, ...args) + return iterHistory(this._client, ...args) } - TelegramClient.prototype.iterReactionUsers = function (...args) { - return iterReactionUsers(this, ...args) + return iterReactionUsers(this._client, ...args) } - TelegramClient.prototype.iterSearchGlobal = function (...args) { - return iterSearchGlobal(this, ...args) + return iterSearchGlobal(this._client, ...args) } - TelegramClient.prototype.iterSearchMessages = function (...args) { - return iterSearchMessages(this, ...args) + return iterSearchMessages(this._client, ...args) } - TelegramClient.prototype.pinMessage = function (...args) { - return pinMessage(this, ...args) + return pinMessage(this._client, ...args) } - TelegramClient.prototype.readHistory = function (...args) { - return readHistory(this, ...args) + return readHistory(this._client, ...args) } - TelegramClient.prototype.readReactions = function (...args) { - return readReactions(this, ...args) + return readReactions(this._client, ...args) } - TelegramClient.prototype.searchGlobal = function (...args) { - return searchGlobal(this, ...args) + return searchGlobal(this._client, ...args) } - TelegramClient.prototype.searchMessages = function (...args) { - return searchMessages(this, ...args) + return searchMessages(this._client, ...args) } - TelegramClient.prototype.answerText = function (...args) { - return answerText(this, ...args) + return answerText(this._client, ...args) } - TelegramClient.prototype.answerMedia = function (...args) { - return answerMedia(this, ...args) + return answerMedia(this._client, ...args) } - TelegramClient.prototype.answerMediaGroup = function (...args) { - return answerMediaGroup(this, ...args) + return answerMediaGroup(this._client, ...args) } - TelegramClient.prototype.commentText = function (...args) { - return commentText(this, ...args) + return commentText(this._client, ...args) } - TelegramClient.prototype.commentMedia = function (...args) { - return commentMedia(this, ...args) + return commentMedia(this._client, ...args) } - TelegramClient.prototype.commentMediaGroup = function (...args) { - return commentMediaGroup(this, ...args) + return commentMediaGroup(this._client, ...args) } - TelegramClient.prototype.sendCopyGroup = function (...args) { - return sendCopyGroup(this, ...args) + return sendCopyGroup(this._client, ...args) } - TelegramClient.prototype.sendCopy = function (...args) { - return sendCopy(this, ...args) + return sendCopy(this._client, ...args) } - TelegramClient.prototype.sendMediaGroup = function (...args) { - return sendMediaGroup(this, ...args) + return sendMediaGroup(this._client, ...args) } - TelegramClient.prototype.sendMedia = function (...args) { - return sendMedia(this, ...args) + return sendMedia(this._client, ...args) } - TelegramClient.prototype.quoteWithText = function (...args) { - return quoteWithText(this, ...args) + return quoteWithText(this._client, ...args) } - TelegramClient.prototype.quoteWithMedia = function (...args) { - return quoteWithMedia(this, ...args) + return quoteWithMedia(this._client, ...args) } - TelegramClient.prototype.quoteWithMediaGroup = function (...args) { - return quoteWithMediaGroup(this, ...args) + return quoteWithMediaGroup(this._client, ...args) } - TelegramClient.prototype.sendReaction = function (...args) { - return sendReaction(this, ...args) + return sendReaction(this._client, ...args) } - TelegramClient.prototype.replyText = function (...args) { - return replyText(this, ...args) + return replyText(this._client, ...args) } - TelegramClient.prototype.replyMedia = function (...args) { - return replyMedia(this, ...args) + return replyMedia(this._client, ...args) } - TelegramClient.prototype.replyMediaGroup = function (...args) { - return replyMediaGroup(this, ...args) + return replyMediaGroup(this._client, ...args) } - TelegramClient.prototype.sendScheduled = function (...args) { - return sendScheduled(this, ...args) + return sendScheduled(this._client, ...args) } - TelegramClient.prototype.sendText = function (...args) { - return sendText(this, ...args) + return sendText(this._client, ...args) } - TelegramClient.prototype.sendTyping = function (...args) { - return sendTyping(this, ...args) + return sendTyping(this._client, ...args) } - TelegramClient.prototype.sendVote = function (...args) { - return sendVote(this, ...args) + return sendVote(this._client, ...args) } - TelegramClient.prototype.translateMessage = function (...args) { - return translateMessage(this, ...args) + return translateMessage(this._client, ...args) } - TelegramClient.prototype.translateText = function (...args) { - return translateText(this, ...args) + return translateText(this._client, ...args) } - TelegramClient.prototype.unpinAllMessages = function (...args) { - return unpinAllMessages(this, ...args) + return unpinAllMessages(this._client, ...args) } - TelegramClient.prototype.unpinMessage = function (...args) { - return unpinMessage(this, ...args) + return unpinMessage(this._client, ...args) } - TelegramClient.prototype.initTakeoutSession = function (...args) { - return initTakeoutSession(this, ...args) + return initTakeoutSession(this._client, ...args) } - TelegramClient.prototype._normalizePrivacyRules = function (...args) { - return _normalizePrivacyRules(this, ...args) + return _normalizePrivacyRules(this._client, ...args) } - TelegramClient.prototype.changeCloudPassword = function (...args) { - return changeCloudPassword(this, ...args) + return changeCloudPassword(this._client, ...args) } - TelegramClient.prototype.enableCloudPassword = function (...args) { - return enableCloudPassword(this, ...args) + return enableCloudPassword(this._client, ...args) } - TelegramClient.prototype.verifyPasswordEmail = function (...args) { - return verifyPasswordEmail(this, ...args) + return verifyPasswordEmail(this._client, ...args) } - TelegramClient.prototype.resendPasswordEmail = function (...args) { - return resendPasswordEmail(this, ...args) + return resendPasswordEmail(this._client, ...args) } - TelegramClient.prototype.cancelPasswordEmail = function (...args) { - return cancelPasswordEmail(this, ...args) + return cancelPasswordEmail(this._client, ...args) } - TelegramClient.prototype.removeCloudPassword = function (...args) { - return removeCloudPassword(this, ...args) + return removeCloudPassword(this._client, ...args) } - TelegramClient.prototype.applyBoost = function (...args) { - return applyBoost(this, ...args) + return applyBoost(this._client, ...args) } - TelegramClient.prototype.canApplyBoost = function (...args) { - return canApplyBoost(this, ...args) + return canApplyBoost(this._client, ...args) } - TelegramClient.prototype.getBoostStats = function (...args) { - return getBoostStats(this, ...args) + return getBoostStats(this._client, ...args) } - TelegramClient.prototype.getBoosts = function (...args) { - return getBoosts(this, ...args) + return getBoosts(this._client, ...args) } - TelegramClient.prototype.getMyBoostSlots = function (...args) { - return getMyBoostSlots(this, ...args) + return getMyBoostSlots(this._client, ...args) } - TelegramClient.prototype.iterBoosters = function (...args) { - return iterBoosters(this, ...args) + return iterBoosters(this._client, ...args) } - TelegramClient.prototype.addStickerToSet = function (...args) { - return addStickerToSet(this, ...args) + return addStickerToSet(this._client, ...args) } - TelegramClient.prototype.createStickerSet = function (...args) { - return createStickerSet(this, ...args) + return createStickerSet(this._client, ...args) } - TelegramClient.prototype.deleteStickerFromSet = function (...args) { - return deleteStickerFromSet(this, ...args) + return deleteStickerFromSet(this._client, ...args) } - TelegramClient.prototype.getCustomEmojis = function (...args) { - return getCustomEmojis(this, ...args) + return getCustomEmojis(this._client, ...args) } - TelegramClient.prototype.getCustomEmojisFromMessages = function (...args) { - return getCustomEmojisFromMessages(this, ...args) + return getCustomEmojisFromMessages(this._client, ...args) } - TelegramClient.prototype.getInstalledStickers = function (...args) { - return getInstalledStickers(this, ...args) + return getInstalledStickers(this._client, ...args) } - TelegramClient.prototype.getStickerSet = function (...args) { - return getStickerSet(this, ...args) + return getStickerSet(this._client, ...args) } - TelegramClient.prototype.moveStickerInSet = function (...args) { - return moveStickerInSet(this, ...args) + return moveStickerInSet(this._client, ...args) } - TelegramClient.prototype.setChatStickerSet = function (...args) { - return setChatStickerSet(this, ...args) + return setChatStickerSet(this._client, ...args) } - TelegramClient.prototype.setStickerSetThumb = function (...args) { - return setStickerSetThumb(this, ...args) + return setStickerSetThumb(this._client, ...args) } - TelegramClient.prototype.canSendStory = function (...args) { - return canSendStory(this, ...args) + return canSendStory(this._client, ...args) } - TelegramClient.prototype.deleteStories = function (...args) { - return deleteStories(this, ...args) + return deleteStories(this._client, ...args) } - TelegramClient.prototype.editStory = function (...args) { - return editStory(this, ...args) + return editStory(this._client, ...args) } - TelegramClient.prototype.getAllStories = function (...args) { - return getAllStories(this, ...args) + return getAllStories(this._client, ...args) } - TelegramClient.prototype.getPeerStories = function (...args) { - return getPeerStories(this, ...args) + return getPeerStories(this._client, ...args) } - TelegramClient.prototype.getProfileStories = function (...args) { - return getProfileStories(this, ...args) + return getProfileStories(this._client, ...args) } - TelegramClient.prototype.getStoriesById = function (...args) { - return getStoriesById(this, ...args) + return getStoriesById(this._client, ...args) } - TelegramClient.prototype.getStoriesInteractions = function (...args) { - return getStoriesInteractions(this, ...args) + return getStoriesInteractions(this._client, ...args) } - TelegramClient.prototype.getStoryLink = function (...args) { - return getStoryLink(this, ...args) + return getStoryLink(this._client, ...args) } - TelegramClient.prototype.getStoryViewers = function (...args) { - return getStoryViewers(this, ...args) + return getStoryViewers(this._client, ...args) } - TelegramClient.prototype.hideMyStoriesViews = function (...args) { - return hideMyStoriesViews(this, ...args) + return hideMyStoriesViews(this._client, ...args) } - TelegramClient.prototype.incrementStoriesViews = function (...args) { - return incrementStoriesViews(this, ...args) + return incrementStoriesViews(this._client, ...args) } - TelegramClient.prototype.iterAllStories = function (...args) { - return iterAllStories(this, ...args) + return iterAllStories(this._client, ...args) } - TelegramClient.prototype.iterProfileStories = function (...args) { - return iterProfileStories(this, ...args) + return iterProfileStories(this._client, ...args) } - TelegramClient.prototype.iterStoryViewers = function (...args) { - return iterStoryViewers(this, ...args) + return iterStoryViewers(this._client, ...args) } - TelegramClient.prototype.readStories = function (...args) { - return readStories(this, ...args) + return readStories(this._client, ...args) } - TelegramClient.prototype.reportStory = function (...args) { - return reportStory(this, ...args) + return reportStory(this._client, ...args) } - TelegramClient.prototype.sendStoryReaction = function (...args) { - return sendStoryReaction(this, ...args) + return sendStoryReaction(this._client, ...args) } - TelegramClient.prototype.sendStory = function (...args) { - return sendStory(this, ...args) + return sendStory(this._client, ...args) } - TelegramClient.prototype.togglePeerStoriesArchived = function (...args) { - return togglePeerStoriesArchived(this, ...args) + return togglePeerStoriesArchived(this._client, ...args) } - TelegramClient.prototype.toggleStoriesPinned = function (...args) { - return toggleStoriesPinned(this, ...args) + return toggleStoriesPinned(this._client, ...args) } - -TelegramClient.prototype.enableRps = function (...args) { - return enableRps(this, ...args) -} - -TelegramClient.prototype.getCurrentRpsIncoming = function (...args) { - return getCurrentRpsIncoming(this, ...args) -} - -TelegramClient.prototype.getCurrentRpsProcessing = function (...args) { - return getCurrentRpsProcessing(this, ...args) -} - -TelegramClient.prototype.startUpdatesLoop = function (...args) { - return startUpdatesLoop(this, ...args) -} - -TelegramClient.prototype.stopUpdatesLoop = function (...args) { - return stopUpdatesLoop(this, ...args) -} - -TelegramClient.prototype.catchUp = function (...args) { - return catchUp(this, ...args) -} - -TelegramClient.prototype.notifyChannelOpened = function (...args) { - return notifyChannelOpened(this, ...args) -} - -TelegramClient.prototype.notifyChannelClosed = function (...args) { - return notifyChannelClosed(this, ...args) -} - TelegramClient.prototype.blockUser = function (...args) { - return blockUser(this, ...args) + return blockUser(this._client, ...args) } - TelegramClient.prototype.deleteProfilePhotos = function (...args) { - return deleteProfilePhotos(this, ...args) + return deleteProfilePhotos(this._client, ...args) } - TelegramClient.prototype.editCloseFriendsRaw = function (...args) { - return editCloseFriendsRaw(this, ...args) + return editCloseFriendsRaw(this._client, ...args) } - TelegramClient.prototype.editCloseFriends = function (...args) { - return editCloseFriends(this, ...args) + return editCloseFriends(this._client, ...args) } - TelegramClient.prototype.getCommonChats = function (...args) { - return getCommonChats(this, ...args) + return getCommonChats(this._client, ...args) } - TelegramClient.prototype.getGlobalTtl = function (...args) { - return getGlobalTtl(this, ...args) + return getGlobalTtl(this._client, ...args) } - TelegramClient.prototype.getMe = function (...args) { - return getMe(this, ...args) + return getMe(this._client, ...args) } - TelegramClient.prototype.getMyUsername = function (...args) { - return getMyUsername(this, ...args) + return getMyUsername(this._client, ...args) } - TelegramClient.prototype.getProfilePhoto = function (...args) { - return getProfilePhoto(this, ...args) + return getProfilePhoto(this._client, ...args) } - TelegramClient.prototype.getProfilePhotos = function (...args) { - return getProfilePhotos(this, ...args) + return getProfilePhotos(this._client, ...args) } - TelegramClient.prototype.getUsers = function (...args) { - return getUsers(this, ...args) + return getUsers(this._client, ...args) } - TelegramClient.prototype.iterProfilePhotos = function (...args) { - return iterProfilePhotos(this, ...args) + return iterProfilePhotos(this._client, ...args) } - // @ts-expect-error this kinda breaks typings for overloads, idc TelegramClient.prototype.resolvePeerMany = function (...args) { // @ts-expect-error this kinda breaks typings for overloads, idc - return resolvePeerMany(this, ...args) + return resolvePeerMany(this._client, ...args) } - TelegramClient.prototype.resolvePeer = function (...args) { - return resolvePeer(this, ...args) + return resolvePeer(this._client, ...args) } - TelegramClient.prototype.setGlobalTtl = function (...args) { - return setGlobalTtl(this, ...args) + return setGlobalTtl(this._client, ...args) } - TelegramClient.prototype.setMyEmojiStatus = function (...args) { - return setMyEmojiStatus(this, ...args) + return setMyEmojiStatus(this._client, ...args) } - TelegramClient.prototype.setMyProfilePhoto = function (...args) { - return setMyProfilePhoto(this, ...args) + return setMyProfilePhoto(this._client, ...args) } - TelegramClient.prototype.setMyUsername = function (...args) { - return setMyUsername(this, ...args) + return setMyUsername(this._client, ...args) } - TelegramClient.prototype.setOffline = function (...args) { - return setOffline(this, ...args) + return setOffline(this._client, ...args) } - TelegramClient.prototype.unblockUser = function (...args) { - return unblockUser(this, ...args) + return unblockUser(this._client, ...args) } - TelegramClient.prototype.updateProfile = function (...args) { - return updateProfile(this, ...args) + return updateProfile(this._client, ...args) +} +TelegramClient.prototype.prepare = function (...args) { + return this._client.prepare(...args) +} +TelegramClient.prototype.connect = function (...args) { + return this._client.connect(...args) +} +TelegramClient.prototype.close = function (...args) { + return this._client.close(...args) +} +TelegramClient.prototype.notifyLoggedIn = function (...args) { + return this._client.notifyLoggedIn(...args) +} +TelegramClient.prototype.notifyLoggedOut = function (...args) { + return this._client.notifyLoggedOut(...args) +} +TelegramClient.prototype.notifyChannelOpened = function (...args) { + return this._client.notifyChannelOpened(...args) +} +TelegramClient.prototype.notifyChannelClosed = function (...args) { + return this._client.notifyChannelClosed(...args) +} +TelegramClient.prototype.call = function (...args) { + return this._client.call(...args) +} +TelegramClient.prototype.importSession = function (...args) { + return this._client.importSession(...args) +} +TelegramClient.prototype.exportSession = function (...args) { + return this._client.exportSession(...args) +} +TelegramClient.prototype.onError = function (...args) { + return this._client.onError(...args) +} +TelegramClient.prototype.emitError = function (...args) { + return this._client.emitError(...args) +} +TelegramClient.prototype.handleClientUpdate = function (...args) { + return this._client.handleClientUpdate(...args) +} +TelegramClient.prototype.getApiCrenetials = function (...args) { + return this._client.getApiCrenetials(...args) +} +TelegramClient.prototype.getPoolSize = function (...args) { + return this._client.getPoolSize(...args) +} +TelegramClient.prototype.getPrimaryDcId = function (...args) { + return this._client.getPrimaryDcId(...args) +} +TelegramClient.prototype.computeSrpParams = function (...args) { + return this._client.computeSrpParams(...args) +} +TelegramClient.prototype.computeNewPasswordHash = function (...args) { + return this._client.computeNewPasswordHash(...args) +} +TelegramClient.prototype.onServerUpdate = function () { + throw new Error('onServerUpdate is not available for TelegramClient, use .on() methods instead') +} +TelegramClient.prototype.onUpdate = function () { + throw new Error('onUpdate is not available for TelegramClient, use .on() methods instead') } - -TelegramClient.prototype.run = - // @manual-impl=run - /** @internal */ - function _run( - this: TelegramClient, - params: Parameters[1], - then?: (user: User) => void | Promise, - ) { - this.start(params) - .then(then) - .catch((err) => this._emitError(err)) - } -TelegramClient.prototype.start = - // @manual-impl=start - /** @internal */ - async function _start(this: TelegramClient, params: Parameters[1]) { - const user = await start(this, params) - - if (!this.network.params.disableUpdates && !this._disableUpdatesManager) { - await this.startUpdatesLoop() - } - - return user - } diff --git a/packages/core/src/highlevel/client.types.ts b/packages/core/src/highlevel/client.types.ts new file mode 100644 index 00000000..804b6829 --- /dev/null +++ b/packages/core/src/highlevel/client.types.ts @@ -0,0 +1,53 @@ +import { tl } from '@mtcute/tl' + +import type { ConnectionKind, RpcCallOptions } from '../network/index.js' +import type { MustEqual, PublicPart } from '../types/utils.js' +import type { Logger } from '../utils/logger.js' +import type { StringSessionData } from '../utils/string-session.js' +import type { TelegramStorageManager } from './storage/storage.js' +import type { RawUpdateHandler } from './updates/types.js' + +// NB: when adding new methods, don't forget to add them to: +// - worker/port.ts +// - generate-client script + +export interface ITelegramClient { + readonly log: Logger + readonly storage: PublicPart + + prepare(): Promise + connect(): Promise + close(): Promise + notifyLoggedIn(auth: tl.auth.TypeAuthorization | tl.RawUser): Promise + notifyLoggedOut(): Promise + notifyChannelOpened(channelId: number, pts?: number): Promise + notifyChannelClosed(channelId: number): Promise + call( + message: MustEqual, + params?: RpcCallOptions, + ): Promise + importSession(session: string | StringSessionData, force?: boolean): Promise + exportSession(): Promise + onError(handler: (err: unknown) => void): void + emitError(err: unknown): void + handleClientUpdate(updates: tl.TypeUpdates, noDispatch?: boolean): void + + onServerUpdate(handler: (update: tl.TypeUpdates) => void): void + onUpdate(handler: RawUpdateHandler): void + + getApiCrenetials(): Promise<{ id: number; hash: string }> + // todo - this is only used for file dl/ul, which should probably be moved + // to the client to allow moving the thing to worker + // or at least load this once at startup (and then these methods can be made sync) + getPoolSize(kind: ConnectionKind, dcId?: number): Promise + getPrimaryDcId(): Promise + + computeSrpParams( + request: tl.account.RawPassword, + password: string, + ): Promise + computeNewPasswordHash( + algo: tl.TypePasswordKdfAlgo, + password: string, + ): Promise +} diff --git a/packages/core/src/highlevel/index.ts b/packages/core/src/highlevel/index.ts new file mode 100644 index 00000000..573e6f07 --- /dev/null +++ b/packages/core/src/highlevel/index.ts @@ -0,0 +1,6 @@ +export * from './base.js' +export * from './client.js' +export * from './client.types.js' +export * from './storage/index.js' +export * from './types/index.js' +export * from './updates/index.js' diff --git a/packages/client/src/methods/README.md b/packages/core/src/highlevel/methods/README.md similarity index 93% rename from packages/client/src/methods/README.md rename to packages/core/src/highlevel/methods/README.md index ca13f810..fcb30f28 100644 --- a/packages/client/src/methods/README.md +++ b/packages/core/src/highlevel/methods/README.md @@ -57,7 +57,7 @@ Example: ```typescript // @initialize -function _initializeAwesomeExtension(client: BaseTelegramClient) { +function _initializeAwesomeExtension(client: ITelegramClient) { this._field1 = 42 this._field2 = 'uwu' } @@ -74,7 +74,7 @@ Example: // @exported export type FooOrBar = Foo | Bar -export function getFooOrBar(client: BaseTelegramClient): FooOrBar { +export function getFooOrBar(client: ITelegramClient): FooOrBar { return new Foo() } ``` diff --git a/packages/client/src/methods/_imports.ts b/packages/core/src/highlevel/methods/_imports.ts similarity index 82% rename from packages/client/src/methods/_imports.ts rename to packages/core/src/highlevel/methods/_imports.ts index 3edf033b..bd0296c7 100644 --- a/packages/client/src/methods/_imports.ts +++ b/packages/core/src/highlevel/methods/_imports.ts @@ -1,19 +1,18 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -// @copy -import { - BaseTelegramClient, - BaseTelegramClientOptions, - IMtStorageProvider, - Long, - MaybeArray, - MaybeAsync, - PartialExcept, - PartialOnly, - tl, -} from '@mtcute/core' + // @copy import { tdFileId } from '@mtcute/file-id' +// @copy +import { tl } from '@mtcute/tl' +// @copy +import { MaybeArray, MaybeAsync, PartialExcept, PartialOnly } from '../../types/index.js' +// @copy +import { StringSessionData } from '../../utils/string-session.js' +// @copy +import { BaseTelegramClient, BaseTelegramClientOptions } from '../base.js' +// @copy +import { ITelegramClient } from '../client.types.js' // @copy import { AllStories, diff --git a/packages/core/src/highlevel/methods/_init.ts b/packages/core/src/highlevel/methods/_init.ts new file mode 100644 index 00000000..f5e7588d --- /dev/null +++ b/packages/core/src/highlevel/methods/_init.ts @@ -0,0 +1,97 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ + +// @copy +import { MemoryStorage } from '../../storage/providers/memory/index.js' +import { BaseTelegramClient, BaseTelegramClientOptions } from '../base.js' +import { TelegramClient } from '../client.js' +import { ITelegramClient } from '../client.types.js' +// @copy +import { ITelegramStorageProvider } from '../storage/provider.js' +// @copy +import { Conversation } from '../types/conversation.js' +// @copy +import { makeParsedUpdateHandler, ParsedUpdateHandlerParams } from '../updates/parsed.js' +// @copy +import { _defaultStorageFactory } from '../utils/platform/storage.js' + +// @copy +type TelegramClientOptions = ((Omit & { + /** + * Storage to use for this client. + * + * If a string is passed, it will be used as: + * - a path to a JSON file for Node.js + * - IndexedDB database name for browsers + * + * If omitted, {@link MemoryStorage} is used + */ + storage?: string | ITelegramStorageProvider +}) | ({ client: ITelegramClient })) & { + updates?: Omit + /** + * If `true`, the updates that were handled by some {@link Conversation} + * will not be dispatched any further. + * + * @default true + */ + skipConversationUpdates?: boolean +} + +// // @initialize=super +// /** @internal */ +// function _initializeClientSuper(this: TelegramClient, opts: TelegramClientOptions) { +// if (typeof opts.storage === 'string') { +// opts.storage = _defaultStorageFactory(opts.storage) +// } else if (!opts.storage) { +// opts.storage = new MemoryStorage() +// } + +// /* eslint-disable @typescript-eslint/no-unsafe-call */ +// // @ts-expect-error codegen +// super(opts) +// /* eslint-enable @typescript-eslint/no-unsafe-call */ +// } + +// @initialize +/** @internal */ +function _initializeClient(this: TelegramClient, opts: TelegramClientOptions) { + if ('client' in opts) { + this._client = opts.client + } else { + let storage: ITelegramStorageProvider + + if (typeof opts.storage === 'string') { + storage = _defaultStorageFactory(opts.storage) + } else if (!opts.storage) { + storage = new MemoryStorage() + } else { + storage = opts.storage + } + + this._client = new BaseTelegramClient({ + ...opts, + storage, + }) + } + + // @ts-expect-error codegen + this.log = this._client.log + // @ts-expect-error codegen + this.storage = this._client.storage + + const skipConversationUpdates = opts.skipConversationUpdates ?? true + const { messageGroupingInterval } = opts.updates ?? {} + + this._client.onUpdate(makeParsedUpdateHandler({ + messageGroupingInterval, + onUpdate: (update) => { + if (Conversation.handleUpdate(this._client, update) && skipConversationUpdates) return + + this.emit('update', update) + this.emit(update.name, update.data) + }, + onRawUpdate: (update, peers) => { + this.emit('raw_update', update, peers) + }, + })) +} diff --git a/packages/client/src/methods/auth/check-password.ts b/packages/core/src/highlevel/methods/auth/check-password.ts similarity index 60% rename from packages/client/src/methods/auth/check-password.ts rename to packages/core/src/highlevel/methods/auth/check-password.ts index 88c594e9..12b1620a 100644 --- a/packages/client/src/methods/auth/check-password.ts +++ b/packages/core/src/highlevel/methods/auth/check-password.ts @@ -1,8 +1,6 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { computeSrpParams } from '@mtcute/core/utils.js' - +import { ITelegramClient } from '../../client.types.js' import { User } from '../../types/index.js' -import { _onAuthorization } from './_state.js' +import { _onAuthorization } from './utils.js' /** * Check your Two-Step verification password and log in @@ -11,11 +9,10 @@ import { _onAuthorization } from './_state.js' * @returns The authorized user * @throws BadRequestError In case the password is invalid */ -export async function checkPassword(client: BaseTelegramClient, password: string): Promise { +export async function checkPassword(client: ITelegramClient, password: string): Promise { const res = await client.call({ _: 'auth.checkPassword', - password: await computeSrpParams( - client.crypto, + password: await client.computeSrpParams( await client.call({ _: 'account.getPassword', }), diff --git a/packages/client/src/methods/auth/get-password-hint.ts b/packages/core/src/highlevel/methods/auth/get-password-hint.ts similarity index 63% rename from packages/client/src/methods/auth/get-password-hint.ts rename to packages/core/src/highlevel/methods/auth/get-password-hint.ts index 9bc05030..7cee79da 100644 --- a/packages/client/src/methods/auth/get-password-hint.ts +++ b/packages/core/src/highlevel/methods/auth/get-password-hint.ts @@ -1,11 +1,11 @@ -import { BaseTelegramClient } from '@mtcute/core' +import { ITelegramClient } from '../../client.types.js' /** * Get your Two-Step Verification password hint. * * @returns The password hint as a string, if any */ -export function getPasswordHint(client: BaseTelegramClient): Promise { +export function getPasswordHint(client: ITelegramClient): Promise { return client .call({ _: 'account.getPassword', diff --git a/packages/client/src/methods/auth/log-out.ts b/packages/core/src/highlevel/methods/auth/log-out.ts similarity index 52% rename from packages/client/src/methods/auth/log-out.ts rename to packages/core/src/highlevel/methods/auth/log-out.ts index 909d93bd..fe284668 100644 --- a/packages/client/src/methods/auth/log-out.ts +++ b/packages/core/src/highlevel/methods/auth/log-out.ts @@ -1,4 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' +import { ITelegramClient } from '../../client.types.js' /** * Log out from Telegram account and optionally reset the session storage. @@ -8,15 +8,9 @@ import { BaseTelegramClient } from '@mtcute/core' * * @returns On success, `true` is returned */ -export async function logOut(client: BaseTelegramClient): Promise { +export async function logOut(client: ITelegramClient): Promise { await client.call({ _: 'auth.logOut' }) - - await client.storage.self.store(null) - // authState.selfUsername = null todo - - client.emit('logged_out') - - await client.storage.clear() + await client.notifyLoggedOut() return true } diff --git a/packages/client/src/methods/auth/recover-password.ts b/packages/core/src/highlevel/methods/auth/recover-password.ts similarity index 80% rename from packages/client/src/methods/auth/recover-password.ts rename to packages/core/src/highlevel/methods/auth/recover-password.ts index dfeaa5e2..45a30ed0 100644 --- a/packages/client/src/methods/auth/recover-password.ts +++ b/packages/core/src/highlevel/methods/auth/recover-password.ts @@ -1,7 +1,6 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { User } from '../../types/index.js' -import { _onAuthorization } from './_state.js' +import { _onAuthorization } from './utils.js' /** * Recover your password with a recovery code and log in. @@ -10,7 +9,7 @@ import { _onAuthorization } from './_state.js' * @throws BadRequestError In case the code is invalid */ export async function recoverPassword( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** The recovery code sent via email */ recoveryCode: string diff --git a/packages/client/src/methods/auth/resend-code.ts b/packages/core/src/highlevel/methods/auth/resend-code.ts similarity index 79% rename from packages/client/src/methods/auth/resend-code.ts rename to packages/core/src/highlevel/methods/auth/resend-code.ts index ec49825a..9f93ce68 100644 --- a/packages/client/src/methods/auth/resend-code.ts +++ b/packages/core/src/highlevel/methods/auth/resend-code.ts @@ -1,7 +1,6 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' - -import { SentCode } from '../../types/index.js' +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' +import { SentCode } from '../../types/auth/sent-code.js' import { normalizePhoneNumber } from '../../utils/misc-utils.js' /** @@ -11,7 +10,7 @@ import { normalizePhoneNumber } from '../../utils/misc-utils.js' * {@link SentCode} object returned by {@link sendCode} */ export async function resendCode( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Phone number in international format */ phone: string diff --git a/packages/client/src/methods/auth/run.ts b/packages/core/src/highlevel/methods/auth/run.ts similarity index 60% rename from packages/client/src/methods/auth/run.ts rename to packages/core/src/highlevel/methods/auth/run.ts index 0a074435..a0d8b23f 100644 --- a/packages/client/src/methods/auth/run.ts +++ b/packages/core/src/highlevel/methods/auth/run.ts @@ -1,6 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - -import { TelegramClient } from '../../index.js' +import { ITelegramClient } from '../../client.types.js' import { User } from '../../types/index.js' import { start } from './start.js' @@ -14,22 +12,13 @@ import { start } from './start.js' * * @param params Parameters to be passed to {@link start} * @param then Function to be called after {@link start} returns - * @manual=noemit */ export function run( - client: BaseTelegramClient, + client: ITelegramClient, params: Parameters[1], then?: (user: User) => void | Promise, ): void { start(client, params) .then(then) - .catch((err) => client._emitError(err)) -} - -// @manual-impl=run -/** @internal */ -function _run(this: TelegramClient, params: Parameters[1], then?: (user: User) => void | Promise) { - this.start(params) - .then(then) - .catch((err) => this._emitError(err)) + .catch((err) => client.emitError(err)) } diff --git a/packages/client/src/methods/auth/send-code.ts b/packages/core/src/highlevel/methods/auth/send-code.ts similarity index 67% rename from packages/client/src/methods/auth/send-code.ts rename to packages/core/src/highlevel/methods/auth/send-code.ts index 754d5d71..aa74c715 100644 --- a/packages/client/src/methods/auth/send-code.ts +++ b/packages/core/src/highlevel/methods/auth/send-code.ts @@ -1,7 +1,6 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' - -import { SentCode } from '../../types/index.js' +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' +import { SentCode } from '../../types/auth/sent-code.js' import { normalizePhoneNumber } from '../../utils/misc-utils.js' /** @@ -10,7 +9,7 @@ import { normalizePhoneNumber } from '../../utils/misc-utils.js' * @returns An object containing information about the sent confirmation code */ export async function sendCode( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Phone number in international format */ phone: string @@ -18,11 +17,13 @@ export async function sendCode( ): Promise { const phone = normalizePhoneNumber(params.phone) + const { id, hash } = await client.getApiCrenetials() + const res = await client.call({ _: 'auth.sendCode', phoneNumber: phone, - apiId: client.params.apiId, - apiHash: client.params.apiHash, + apiId: id, + apiHash: hash, settings: { _: 'codeSettings' }, }) diff --git a/packages/client/src/methods/auth/send-recovery-code.ts b/packages/core/src/highlevel/methods/auth/send-recovery-code.ts similarity index 68% rename from packages/client/src/methods/auth/send-recovery-code.ts rename to packages/core/src/highlevel/methods/auth/send-recovery-code.ts index d6a4e6ce..c6cab6f3 100644 --- a/packages/client/src/methods/auth/send-recovery-code.ts +++ b/packages/core/src/highlevel/methods/auth/send-recovery-code.ts @@ -1,11 +1,11 @@ -import { BaseTelegramClient } from '@mtcute/core' +import { ITelegramClient } from '../../client.types.js' /** * Send a code to email needed to recover your password * * @returns String containing email pattern to which the recovery code was sent */ -export function sendRecoveryCode(client: BaseTelegramClient): Promise { +export function sendRecoveryCode(client: ITelegramClient): Promise { return client .call({ _: 'auth.requestPasswordRecovery', diff --git a/packages/client/src/methods/auth/sign-in-bot.ts b/packages/core/src/highlevel/methods/auth/sign-in-bot.ts similarity index 54% rename from packages/client/src/methods/auth/sign-in-bot.ts rename to packages/core/src/highlevel/methods/auth/sign-in-bot.ts index 39251fb2..4af5231f 100644 --- a/packages/client/src/methods/auth/sign-in-bot.ts +++ b/packages/core/src/highlevel/methods/auth/sign-in-bot.ts @@ -1,7 +1,6 @@ -import { BaseTelegramClient } from '@mtcute/core' - -import { User } from '../../types/index.js' -import { _onAuthorization } from './_state.js' +import { ITelegramClient } from '../../client.types.js' +import { User } from '../../types/peers/user.js' +import { _onAuthorization } from './utils.js' /** * Authorize a bot using its token issued by [@BotFather](//t.me/BotFather) @@ -10,12 +9,14 @@ import { _onAuthorization } from './_state.js' * @returns Bot's {@link User} object * @throws BadRequestError In case the bot token is invalid */ -export async function signInBot(client: BaseTelegramClient, token: string): Promise { +export async function signInBot(client: ITelegramClient, token: string): Promise { + const { id, hash } = await client.getApiCrenetials() + const res = await client.call({ _: 'auth.importBotAuthorization', flags: 0, - apiId: client.params.apiId, - apiHash: client.params.apiHash, + apiId: id, + apiHash: hash, botAuthToken: token, }) diff --git a/packages/client/src/methods/auth/sign-in.ts b/packages/core/src/highlevel/methods/auth/sign-in.ts similarity index 84% rename from packages/client/src/methods/auth/sign-in.ts rename to packages/core/src/highlevel/methods/auth/sign-in.ts index 43e2d3ab..739a2c7b 100644 --- a/packages/client/src/methods/auth/sign-in.ts +++ b/packages/core/src/highlevel/methods/auth/sign-in.ts @@ -1,8 +1,7 @@ -import { BaseTelegramClient } from '@mtcute/core' - -import { User } from '../../types/index.js' +import { ITelegramClient } from '../../client.types.js' +import { User } from '../../types/peers/user.js' import { normalizePhoneNumber } from '../../utils/misc-utils.js' -import { _onAuthorization } from './_state.js' +import { _onAuthorization } from './utils.js' /** * Authorize a user in Telegram with a valid confirmation code. @@ -12,7 +11,7 @@ import { _onAuthorization } from './_state.js' * @throws SessionPasswordNeededError In case a password is needed to sign in */ export async function signIn( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Phone number in international format */ phone: string diff --git a/packages/client/src/methods/auth/start-test.ts b/packages/core/src/highlevel/methods/auth/start-test.ts similarity index 92% rename from packages/client/src/methods/auth/start-test.ts rename to packages/core/src/highlevel/methods/auth/start-test.ts index 14038b4a..ca3537e2 100644 --- a/packages/client/src/methods/auth/start-test.ts +++ b/packages/core/src/highlevel/methods/auth/start-test.ts @@ -1,5 +1,5 @@ -import { BaseTelegramClient, MtArgumentError } from '@mtcute/core' - +import { MtArgumentError } from '../../../types/errors.js' +import { ITelegramClient } from '../../client.types.js' import { User } from '../../types/index.js' import { logOut } from './log-out.js' import { start } from './start.js' @@ -15,7 +15,7 @@ import { start } from './start.js' * @param params Additional parameters */ export async function startTest( - client: BaseTelegramClient, + client: ITelegramClient, params?: { /** * Whether to log out if current session is logged in. @@ -63,7 +63,7 @@ export async function startTest( throw new MtArgumentError(`${phone} has invalid DC ID (${id})`) } } else { - let dcId = client.network.getPrimaryDcId() + let dcId = await client.getPrimaryDcId() if (params.dcId) { if (!availableDcs.find((dc) => dc.id === params!.dcId)) { diff --git a/packages/client/src/methods/auth/start.ts b/packages/core/src/highlevel/methods/auth/start.ts similarity index 90% rename from packages/client/src/methods/auth/start.ts rename to packages/core/src/highlevel/methods/auth/start.ts index 42a633b5..e47eca03 100644 --- a/packages/client/src/methods/auth/start.ts +++ b/packages/core/src/highlevel/methods/auth/start.ts @@ -1,8 +1,13 @@ /* eslint-disable no-console */ -import { BaseTelegramClient, MaybeAsync, MtArgumentError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' -import type { TelegramClient } from '../../client.js' -import { MaybeDynamic, SentCode, User } from '../../types/index.js' +import { MtArgumentError } from '../../../types/errors.js' +import { MaybeAsync } from '../../../types/utils.js' +import { StringSessionData } from '../../../utils/string-session.js' +import { ITelegramClient } from '../../client.types.js' +import { SentCode } from '../../types/auth/sent-code.js' +import { User } from '../../types/peers/user.js' +import { MaybeDynamic } from '../../types/utils.js' import { normalizePhoneNumber, resolveMaybeDynamic } from '../../utils/misc-utils.js' import { getMe } from '../users/get-me.js' import { checkPassword } from './check-password.js' @@ -11,7 +16,6 @@ import { sendCode } from './send-code.js' import { signIn } from './sign-in.js' import { signInBot } from './sign-in-bot.js' -// @manual // @available=both /** * Start the client in an interactive and declarative manner, @@ -27,7 +31,7 @@ import { signInBot } from './sign-in-bot.js' * you'll probably need to use other auth methods. */ export async function start( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** * String session exported using {@link TelegramClient.exportSession}. @@ -37,7 +41,7 @@ export async function start( * Note that passed session will be ignored in case storage already * contains authorization. */ - session?: string + session?: string | StringSessionData /** * Whether to overwrite existing session. @@ -93,7 +97,7 @@ export async function start( }, ): Promise { if (params.session) { - client.importSession(params.session, params.sessionForce) + await client.importSession(params.session, params.sessionForce) } try { @@ -103,7 +107,7 @@ export async function start( client.log.info('Logged in as %s (ID: %s, username: %s, bot: %s)', me.displayName, me.id, me.username, me.isBot) - client.network.setIsPremium(me.isPremium) + await client.notifyLoggedIn(me.raw) return me } catch (e) { @@ -211,15 +215,3 @@ export async function start( throw new MtArgumentError('Failed to log in with provided credentials') } - -// @manual-impl=start -/** @internal */ -async function _start(this: TelegramClient, params: Parameters[1]) { - const user = await start(this, params) - - if (!this.network.params.disableUpdates && !this._disableUpdatesManager) { - await this.startUpdatesLoop() - } - - return user -} diff --git a/packages/core/src/highlevel/methods/auth/utils.ts b/packages/core/src/highlevel/methods/auth/utils.ts new file mode 100644 index 00000000..8e02ec65 --- /dev/null +++ b/packages/core/src/highlevel/methods/auth/utils.ts @@ -0,0 +1,39 @@ +import { tl } from '@mtcute/tl' + +import { ITelegramClient } from '../../client.types.js' +import { User } from '../../types/peers/user.js' + +/** @internal */ +export async function _onAuthorization( + client: ITelegramClient, + auth: tl.auth.TypeAuthorization, +): Promise { + const user = await client.notifyLoggedIn(auth) + + return new User(user) +} + +/** + * Check if the given peer/input peer is referring to the current user + */ +export function isSelfPeer( + client: ITelegramClient, + peer: tl.TypeInputPeer | tl.TypePeer | tl.TypeInputUser, +): boolean { + const state = client.storage.self.getCached() + if (!state) return false + + switch (peer._) { + case 'inputPeerSelf': + case 'inputUserSelf': + return true + case 'inputPeerUser': + case 'inputPeerUserFromMessage': + case 'inputUser': + case 'inputUserFromMessage': + case 'peerUser': + return peer.userId === state.userId + default: + return false + } +} diff --git a/packages/client/src/methods/bots/answer-callback-query.ts b/packages/core/src/highlevel/methods/bots/answer-callback-query.ts similarity index 90% rename from packages/client/src/methods/bots/answer-callback-query.ts rename to packages/core/src/highlevel/methods/bots/answer-callback-query.ts index eb48918f..19a5ea03 100644 --- a/packages/client/src/methods/bots/answer-callback-query.ts +++ b/packages/core/src/highlevel/methods/bots/answer-callback-query.ts @@ -1,6 +1,7 @@ -import { BaseTelegramClient, Long } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' +import Long from 'long' +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { CallbackQuery } from '../../types/updates/callback-query.js' /** @@ -10,7 +11,7 @@ import { CallbackQuery } from '../../types/updates/callback-query.js' * @param params Parameters of the answer */ export async function answerCallbackQuery( - client: BaseTelegramClient, + client: ITelegramClient, queryId: Long | CallbackQuery, params?: { /** diff --git a/packages/client/src/methods/bots/answer-inline-query.ts b/packages/core/src/highlevel/methods/bots/answer-inline-query.ts similarity index 96% rename from packages/client/src/methods/bots/answer-inline-query.ts rename to packages/core/src/highlevel/methods/bots/answer-inline-query.ts index 79021e38..631be8fc 100644 --- a/packages/client/src/methods/bots/answer-inline-query.ts +++ b/packages/core/src/highlevel/methods/bots/answer-inline-query.ts @@ -1,5 +1,8 @@ -import { BaseTelegramClient, Long, tl } from '@mtcute/core' +import Long from 'long' +import { tl } from '@mtcute/tl' + +import { ITelegramClient } from '../../client.types.js' import { BotInline, InputInlineResult } from '../../types/bots/index.js' import { InlineQuery } from '../../types/updates/inline-query.js' @@ -11,7 +14,7 @@ import { InlineQuery } from '../../types/updates/inline-query.js' * @param params Additional parameters */ export async function answerInlineQuery( - client: BaseTelegramClient, + client: ITelegramClient, queryId: tl.Long | InlineQuery, results: InputInlineResult[], params?: { diff --git a/packages/client/src/methods/bots/answer-pre-checkout-query.ts b/packages/core/src/highlevel/methods/bots/answer-pre-checkout-query.ts similarity index 77% rename from packages/client/src/methods/bots/answer-pre-checkout-query.ts rename to packages/core/src/highlevel/methods/bots/answer-pre-checkout-query.ts index 18bde294..2b4740fe 100644 --- a/packages/client/src/methods/bots/answer-pre-checkout-query.ts +++ b/packages/core/src/highlevel/methods/bots/answer-pre-checkout-query.ts @@ -1,6 +1,9 @@ -import { BaseTelegramClient, Long, tl } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' +import Long from 'long' +import { tl } from '@mtcute/tl' + +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import type { PreCheckoutQuery } from '../../types/updates/pre-checkout-query.js' /** @@ -9,7 +12,7 @@ import type { PreCheckoutQuery } from '../../types/updates/pre-checkout-query.js * @param queryId Pre-checkout query ID */ export async function answerPreCheckoutQuery( - client: BaseTelegramClient, + client: ITelegramClient, queryId: tl.Long | PreCheckoutQuery, params?: { /** If pre-checkout is rejected, error message to show to the user */ diff --git a/packages/client/src/methods/bots/delete-my-commands.ts b/packages/core/src/highlevel/methods/bots/delete-my-commands.ts similarity index 90% rename from packages/client/src/methods/bots/delete-my-commands.ts rename to packages/core/src/highlevel/methods/bots/delete-my-commands.ts index 61ff23e5..4926ba85 100644 --- a/packages/client/src/methods/bots/delete-my-commands.ts +++ b/packages/core/src/highlevel/methods/bots/delete-my-commands.ts @@ -1,5 +1,6 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { BotCommands } from '../../types/index.js' import { _normalizeCommandScope } from './normalize-command-scope.js' @@ -11,7 +12,7 @@ import { _normalizeCommandScope } from './normalize-command-scope.js' * Learn more about scopes in the [Bot API docs](https://core.telegram.org/bots/api#botcommandscope) */ export async function deleteMyCommands( - client: BaseTelegramClient, + client: ITelegramClient, params?: { /** * Scope of the commands. diff --git a/packages/client/src/methods/bots/get-bot-info.ts b/packages/core/src/highlevel/methods/bots/get-bot-info.ts similarity index 88% rename from packages/client/src/methods/bots/get-bot-info.ts rename to packages/core/src/highlevel/methods/bots/get-bot-info.ts index 5c383660..8b16ac10 100644 --- a/packages/client/src/methods/bots/get-bot-info.ts +++ b/packages/core/src/highlevel/methods/bots/get-bot-info.ts @@ -1,5 +1,6 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { toInputUser } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -8,7 +9,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * Gets information about a bot the current uzer owns (or the current bot) */ export async function getBotInfo( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** * When called by a user, a bot the user owns must be specified. diff --git a/packages/client/src/methods/bots/get-bot-menu-button.ts b/packages/core/src/highlevel/methods/bots/get-bot-menu-button.ts similarity index 64% rename from packages/client/src/methods/bots/get-bot-menu-button.ts rename to packages/core/src/highlevel/methods/bots/get-bot-menu-button.ts index 73fde523..4164dd22 100644 --- a/packages/client/src/methods/bots/get-bot-menu-button.ts +++ b/packages/core/src/highlevel/methods/bots/get-bot-menu-button.ts @@ -1,5 +1,6 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { toInputUser } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -7,7 +8,7 @@ import { resolvePeer } from '../users/resolve-peer.js' /** * Fetches the menu button set for the given user. */ -export async function getBotMenuButton(client: BaseTelegramClient, user: InputPeerLike): Promise { +export async function getBotMenuButton(client: ITelegramClient, user: InputPeerLike): Promise { return await client.call({ _: 'bots.getBotMenuButton', userId: toInputUser(await resolvePeer(client, user), user), diff --git a/packages/client/src/methods/bots/get-callback-answer.ts b/packages/core/src/highlevel/methods/bots/get-callback-answer.ts similarity index 86% rename from packages/client/src/methods/bots/get-callback-answer.ts rename to packages/core/src/highlevel/methods/bots/get-callback-answer.ts index 025d80de..81022b47 100644 --- a/packages/client/src/methods/bots/get-callback-answer.ts +++ b/packages/core/src/highlevel/methods/bots/get-callback-answer.ts @@ -1,6 +1,7 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' -import { computeSrpParams, utf8EncodeToBuffer } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { utf8EncodeToBuffer } from '@mtcute/tl-runtime' +import { ITelegramClient } from '../../client.types.js' import { InputMessageId, normalizeInputMessageId } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -11,7 +12,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param params */ export async function getCallbackAnswer( - client: BaseTelegramClient, + client: ITelegramClient, params: InputMessageId & { /** Data contained in the button */ data: string | Uint8Array @@ -44,7 +45,7 @@ export async function getCallbackAnswer( if (params?.password) { const pwd = await client.call({ _: 'account.getPassword' }) - password = await computeSrpParams(client.crypto, pwd, params.password) + password = await client.computeSrpParams(pwd, params.password) } return await client.call( diff --git a/packages/client/src/methods/bots/get-game-high-scores.ts b/packages/core/src/highlevel/methods/bots/get-game-high-scores.ts similarity index 93% rename from packages/client/src/methods/bots/get-game-high-scores.ts rename to packages/core/src/highlevel/methods/bots/get-game-high-scores.ts index f1225dda..c3fa6bc2 100644 --- a/packages/client/src/methods/bots/get-game-high-scores.ts +++ b/packages/core/src/highlevel/methods/bots/get-game-high-scores.ts @@ -1,5 +1,6 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { GameHighScore, InputMessageId, InputPeerLike, normalizeInputMessageId, PeersIndex } from '../../types/index.js' import { normalizeInlineId } from '../../utils/inline-utils.js' import { toInputUser } from '../../utils/peer-utils.js' @@ -9,7 +10,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * Get high scores of a game */ export async function getGameHighScores( - client: BaseTelegramClient, + client: ITelegramClient, params: InputMessageId & { /** ID of the user to find high scores for */ userId?: InputPeerLike @@ -47,7 +48,7 @@ export async function getGameHighScores( * @param userId ID of the user to find high scores for */ export async function getInlineGameHighScores( - client: BaseTelegramClient, + client: ITelegramClient, messageId: string | tl.TypeInputBotInlineMessageID, userId?: InputPeerLike, ): Promise { diff --git a/packages/client/src/methods/bots/get-my-commands.ts b/packages/core/src/highlevel/methods/bots/get-my-commands.ts similarity index 89% rename from packages/client/src/methods/bots/get-my-commands.ts rename to packages/core/src/highlevel/methods/bots/get-my-commands.ts index 56a8695e..af933e56 100644 --- a/packages/client/src/methods/bots/get-my-commands.ts +++ b/packages/core/src/highlevel/methods/bots/get-my-commands.ts @@ -1,5 +1,6 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { BotCommands } from '../../types/index.js' import { _normalizeCommandScope } from './normalize-command-scope.js' @@ -10,7 +11,7 @@ import { _normalizeCommandScope } from './normalize-command-scope.js' * Learn more about scopes in the [Bot API docs](https://core.telegram.org/bots/api#botcommandscope) */ export async function getMyCommands( - client: BaseTelegramClient, + client: ITelegramClient, params?: { /** * Scope of the commands. diff --git a/packages/client/src/methods/bots/normalize-command-scope.ts b/packages/core/src/highlevel/methods/bots/normalize-command-scope.ts similarity index 86% rename from packages/client/src/methods/bots/normalize-command-scope.ts rename to packages/core/src/highlevel/methods/bots/normalize-command-scope.ts index 587cd05c..65eb11fe 100644 --- a/packages/client/src/methods/bots/normalize-command-scope.ts +++ b/packages/core/src/highlevel/methods/bots/normalize-command-scope.ts @@ -1,12 +1,14 @@ -import { assertNever, BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { assertNever } from '../../../types/utils.js' +import { ITelegramClient } from '../../client.types.js' import { BotCommands } from '../../types/index.js' import { toInputUser } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' /** @internal */ export async function _normalizeCommandScope( - client: BaseTelegramClient, + client: ITelegramClient, scope: tl.TypeBotCommandScope | BotCommands.IntermediateScope, ): Promise { if (tl.isAnyBotCommandScope(scope)) return scope diff --git a/packages/client/src/methods/bots/set-bot-info.ts b/packages/core/src/highlevel/methods/bots/set-bot-info.ts similarity index 89% rename from packages/client/src/methods/bots/set-bot-info.ts rename to packages/core/src/highlevel/methods/bots/set-bot-info.ts index c22f8e9e..ddbd2303 100644 --- a/packages/client/src/methods/bots/set-bot-info.ts +++ b/packages/core/src/highlevel/methods/bots/set-bot-info.ts @@ -1,6 +1,5 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' - +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { toInputUser } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -9,7 +8,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * Sets information about a bot the current uzer owns (or the current bot) */ export async function setBotInfo( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** * When called by a user, a bot the user owns must be specified. diff --git a/packages/client/src/methods/bots/set-bot-menu-button.ts b/packages/core/src/highlevel/methods/bots/set-bot-menu-button.ts similarity index 75% rename from packages/client/src/methods/bots/set-bot-menu-button.ts rename to packages/core/src/highlevel/methods/bots/set-bot-menu-button.ts index 06090f58..8378e66e 100644 --- a/packages/client/src/methods/bots/set-bot-menu-button.ts +++ b/packages/core/src/highlevel/methods/bots/set-bot-menu-button.ts @@ -1,6 +1,7 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { toInputUser } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -9,7 +10,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * Sets a menu button for the given user. */ export async function setBotMenuButton( - client: BaseTelegramClient, + client: ITelegramClient, user: InputPeerLike, button: tl.TypeBotMenuButton, ): Promise { diff --git a/packages/client/src/methods/bots/set-game-score.ts b/packages/core/src/highlevel/methods/bots/set-game-score.ts similarity index 93% rename from packages/client/src/methods/bots/set-game-score.ts rename to packages/core/src/highlevel/methods/bots/set-game-score.ts index e470c19f..2403b624 100644 --- a/packages/client/src/methods/bots/set-game-score.ts +++ b/packages/core/src/highlevel/methods/bots/set-game-score.ts @@ -1,6 +1,7 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputMessageId, InputPeerLike, Message, normalizeInputMessageId } from '../../types/index.js' import { normalizeInlineId } from '../../utils/inline-utils.js' import { toInputUser } from '../../utils/peer-utils.js' @@ -14,7 +15,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @returns The modified message */ export async function setGameScore( - client: BaseTelegramClient, + client: ITelegramClient, params: InputMessageId & { /** ID of the user who has scored */ userId: InputPeerLike @@ -67,7 +68,7 @@ export async function setGameScore( * @param params */ export async function setInlineGameScore( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** ID of the inline message */ messageId: string | tl.TypeInputBotInlineMessageID diff --git a/packages/client/src/methods/bots/set-my-commands.ts b/packages/core/src/highlevel/methods/bots/set-my-commands.ts similarity index 89% rename from packages/client/src/methods/bots/set-my-commands.ts rename to packages/core/src/highlevel/methods/bots/set-my-commands.ts index ae7e66a8..a19124b5 100644 --- a/packages/client/src/methods/bots/set-my-commands.ts +++ b/packages/core/src/highlevel/methods/bots/set-my-commands.ts @@ -1,6 +1,7 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { BotCommands } from '../../types/index.js' import { _normalizeCommandScope } from './normalize-command-scope.js' @@ -10,7 +11,7 @@ import { _normalizeCommandScope } from './normalize-command-scope.js' * Learn more about scopes in the [Bot API docs](https://core.telegram.org/bots/api#botcommandscope) */ export async function setMyCommands( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** * New list of bot commands for the given scope. diff --git a/packages/client/src/methods/bots/set-my-default-rights.ts b/packages/core/src/highlevel/methods/bots/set-my-default-rights.ts similarity index 79% rename from packages/client/src/methods/bots/set-my-default-rights.ts rename to packages/core/src/highlevel/methods/bots/set-my-default-rights.ts index da1c30c0..96cac184 100644 --- a/packages/client/src/methods/bots/set-my-default-rights.ts +++ b/packages/core/src/highlevel/methods/bots/set-my-default-rights.ts @@ -1,11 +1,13 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' + +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' /** * Sets the default chat permissions for the bot in the supergroup or channel. */ export async function setMyDefaultRights( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Whether to target groups or channels. */ target: 'channel' | 'group' diff --git a/packages/client/src/methods/chats/add-chat-members.ts b/packages/core/src/highlevel/methods/chats/add-chat-members.ts similarity index 88% rename from packages/client/src/methods/chats/add-chat-members.ts rename to packages/core/src/highlevel/methods/chats/add-chat-members.ts index 813ef40d..9afec6a3 100644 --- a/packages/client/src/methods/chats/add-chat-members.ts +++ b/packages/core/src/highlevel/methods/chats/add-chat-members.ts @@ -1,5 +1,5 @@ -import { BaseTelegramClient, MaybeArray } from '@mtcute/core' - +import { MaybeArray } from '../../../types/utils.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, MtInvalidPeerTypeError } from '../../types/index.js' import { isInputPeerChannel, isInputPeerChat, toInputChannel, toInputUser } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -12,7 +12,7 @@ import { resolvePeerMany } from '../users/resolve-peer-many.js' * @param users ID(s) of the user(s) to add */ export async function addChatMembers( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, users: MaybeArray, params: { @@ -41,7 +41,7 @@ export async function addChatMembers( userId: p, fwdLimit: forwardCount, }) - client.network.handleUpdate(updates) + client.handleClientUpdate(updates) } } else if (isInputPeerChannel(chat)) { const updates = await client.call({ @@ -49,7 +49,6 @@ export async function addChatMembers( channel: toInputChannel(chat), users: await resolvePeerMany(client, users, toInputUser), }) - - client.network.handleUpdate(updates) + client.handleClientUpdate(updates) } else throw new MtInvalidPeerTypeError(chatId, 'chat or channel') } diff --git a/packages/client/src/methods/chats/archive-chats.ts b/packages/core/src/highlevel/methods/chats/archive-chats.ts similarity index 69% rename from packages/client/src/methods/chats/archive-chats.ts rename to packages/core/src/highlevel/methods/chats/archive-chats.ts index a0c78e55..1e0e53b0 100644 --- a/packages/client/src/methods/chats/archive-chats.ts +++ b/packages/core/src/highlevel/methods/chats/archive-chats.ts @@ -1,5 +1,5 @@ -import { BaseTelegramClient, MaybeArray } from '@mtcute/core' - +import { MaybeArray } from '../../../types/utils.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { resolvePeerMany } from '../users/resolve-peer-many.js' @@ -8,7 +8,7 @@ import { resolvePeerMany } from '../users/resolve-peer-many.js' * * @param chats Chat ID(s), username(s), phone number(s), `"me"` or `"self"` */ -export async function archiveChats(client: BaseTelegramClient, chats: MaybeArray): Promise { +export async function archiveChats(client: ITelegramClient, chats: MaybeArray): Promise { if (!Array.isArray(chats)) chats = [chats] const resolvedPeers = await resolvePeerMany(client, chats) @@ -21,5 +21,5 @@ export async function archiveChats(client: BaseTelegramClient, chats: MaybeArray folderId: 1, })), }) - client.network.handleUpdate(updates) + client.handleClientUpdate(updates) } diff --git a/packages/client/src/methods/chats/ban-chat-member.ts b/packages/core/src/highlevel/methods/chats/ban-chat-member.ts similarity index 95% rename from packages/client/src/methods/chats/ban-chat-member.ts rename to packages/core/src/highlevel/methods/chats/ban-chat-member.ts index 526be26e..41760939 100644 --- a/packages/client/src/methods/chats/ban-chat-member.ts +++ b/packages/core/src/highlevel/methods/chats/ban-chat-member.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, Message, MtInvalidPeerTypeError } from '../../types/index.js' import { isInputPeerChannel, isInputPeerChat, toInputChannel, toInputUser } from '../../utils/peer-utils.js' import { _findMessageInUpdate } from '../messages/find-in-update.js' @@ -16,7 +15,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @returns Service message about removed user, if one was generated. */ export async function banChatMember( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Chat ID */ chatId: InputPeerLike diff --git a/packages/client/src/methods/chats/batched-queries.ts b/packages/core/src/highlevel/methods/chats/batched-queries.ts similarity index 96% rename from packages/client/src/methods/chats/batched-queries.ts rename to packages/core/src/highlevel/methods/chats/batched-queries.ts index 5204dd5c..2e8eeed0 100644 --- a/packages/client/src/methods/chats/batched-queries.ts +++ b/packages/core/src/highlevel/methods/chats/batched-queries.ts @@ -1,5 +1,7 @@ -import { BaseTelegramClient, MtArgumentError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MtArgumentError } from '../../../types/errors.js' +import { ITelegramClient } from '../../client.types.js' import { isInputPeerChannel, isInputPeerChat, @@ -129,7 +131,7 @@ export const _getChannelsBatched = batchedQuery { if (isInputPeerUser(peer)) { diff --git a/packages/client/src/methods/chats/create-channel.ts b/packages/core/src/highlevel/methods/chats/create-channel.ts similarity index 77% rename from packages/client/src/methods/chats/create-channel.ts rename to packages/core/src/highlevel/methods/chats/create-channel.ts index 15c8acde..dbbea136 100644 --- a/packages/client/src/methods/chats/create-channel.ts +++ b/packages/core/src/highlevel/methods/chats/create-channel.ts @@ -1,7 +1,6 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { Chat } from '../../types/index.js' -import { assertIsUpdatesGroup } from '../../utils/updates-utils.js' +import { assertIsUpdatesGroup } from '../../updates/utils.js' /** * Create a new broadcast channel @@ -9,7 +8,7 @@ import { assertIsUpdatesGroup } from '../../utils/updates-utils.js' * @returns Newly created channel */ export async function createChannel( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** * Channel title @@ -33,7 +32,7 @@ export async function createChannel( assertIsUpdatesGroup('channels.createChannel', res) - client.network.handleUpdate(res) + client.handleClientUpdate(res) return new Chat(res.chats[0]) } diff --git a/packages/client/src/methods/chats/create-group.ts b/packages/core/src/highlevel/methods/chats/create-group.ts similarity index 84% rename from packages/client/src/methods/chats/create-group.ts rename to packages/core/src/highlevel/methods/chats/create-group.ts index a0e01ec5..2ca837a0 100644 --- a/packages/client/src/methods/chats/create-group.ts +++ b/packages/core/src/highlevel/methods/chats/create-group.ts @@ -1,8 +1,8 @@ -import { BaseTelegramClient, MaybeArray } from '@mtcute/core' - +import { MaybeArray } from '../../../types/utils.js' +import { ITelegramClient } from '../../client.types.js' import { Chat, InputPeerLike } from '../../types/index.js' +import { assertIsUpdatesGroup } from '../../updates/utils.js' import { toInputUser } from '../../utils/peer-utils.js' -import { assertIsUpdatesGroup } from '../../utils/updates-utils.js' import { resolvePeerMany } from '../users/resolve-peer-many.js' /** @@ -12,7 +12,7 @@ import { resolvePeerMany } from '../users/resolve-peer-many.js' * instead. */ export async function createGroup( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** * Group title @@ -48,7 +48,7 @@ export async function createGroup( assertIsUpdatesGroup('messages.createChat', res) - client.network.handleUpdate(res) + client.handleClientUpdate(res) return new Chat(res.chats[0]) } diff --git a/packages/client/src/methods/chats/create-supergroup.ts b/packages/core/src/highlevel/methods/chats/create-supergroup.ts similarity index 83% rename from packages/client/src/methods/chats/create-supergroup.ts rename to packages/core/src/highlevel/methods/chats/create-supergroup.ts index 70db4510..a11419d0 100644 --- a/packages/client/src/methods/chats/create-supergroup.ts +++ b/packages/core/src/highlevel/methods/chats/create-supergroup.ts @@ -1,7 +1,6 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { Chat } from '../../types/index.js' -import { assertIsUpdatesGroup } from '../../utils/updates-utils.js' +import { assertIsUpdatesGroup } from '../../updates/utils.js' /** * Create a new supergroup @@ -9,7 +8,7 @@ import { assertIsUpdatesGroup } from '../../utils/updates-utils.js' * @returns Newly created supergroup */ export async function createSupergroup( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** * Supergroup title @@ -47,7 +46,7 @@ export async function createSupergroup( assertIsUpdatesGroup('channels.createChannel', res) - client.network.handleUpdate(res) + client.handleClientUpdate(res) return new Chat(res.chats[0]) } diff --git a/packages/client/src/methods/chats/delete-channel.ts b/packages/core/src/highlevel/methods/chats/delete-channel.ts similarity index 69% rename from packages/client/src/methods/chats/delete-channel.ts rename to packages/core/src/highlevel/methods/chats/delete-channel.ts index 49d6dab7..31b5fd0e 100644 --- a/packages/client/src/methods/chats/delete-channel.ts +++ b/packages/core/src/highlevel/methods/chats/delete-channel.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { toInputChannel } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -10,10 +9,10 @@ import { resolvePeer } from '../users/resolve-peer.js' * * @param chatId Chat ID or username */ -export async function deleteChannel(client: BaseTelegramClient, chatId: InputPeerLike): Promise { +export async function deleteChannel(client: ITelegramClient, chatId: InputPeerLike): Promise { const res = await client.call({ _: 'channels.deleteChannel', channel: toInputChannel(await resolvePeer(client, chatId), chatId), }) - client.network.handleUpdate(res) + client.handleClientUpdate(res) } diff --git a/packages/client/src/methods/chats/delete-chat-photo.ts b/packages/core/src/highlevel/methods/chats/delete-chat-photo.ts similarity index 82% rename from packages/client/src/methods/chats/delete-chat-photo.ts rename to packages/core/src/highlevel/methods/chats/delete-chat-photo.ts index 49a73a70..3098803e 100644 --- a/packages/client/src/methods/chats/delete-chat-photo.ts +++ b/packages/core/src/highlevel/methods/chats/delete-chat-photo.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, MtInvalidPeerTypeError } from '../../types/index.js' import { isInputPeerChannel, isInputPeerChat, toInputChannel } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -11,7 +10,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * * @param chatId Chat ID or username */ -export async function deleteChatPhoto(client: BaseTelegramClient, chatId: InputPeerLike): Promise { +export async function deleteChatPhoto(client: ITelegramClient, chatId: InputPeerLike): Promise { const chat = await resolvePeer(client, chatId) let res @@ -29,5 +28,5 @@ export async function deleteChatPhoto(client: BaseTelegramClient, chatId: InputP }) } else throw new MtInvalidPeerTypeError(chatId, 'chat or channel') - client.network.handleUpdate(res) + client.handleClientUpdate(res) } diff --git a/packages/client/src/methods/chats/delete-group.ts b/packages/core/src/highlevel/methods/chats/delete-group.ts similarity index 74% rename from packages/client/src/methods/chats/delete-group.ts rename to packages/core/src/highlevel/methods/chats/delete-group.ts index 7b14d703..5764c887 100644 --- a/packages/client/src/methods/chats/delete-group.ts +++ b/packages/core/src/highlevel/methods/chats/delete-group.ts @@ -1,6 +1,5 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' - +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, MtInvalidPeerTypeError } from '../../types/index.js' import { isInputPeerChat } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -10,7 +9,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * * @param chatId Chat ID */ -export async function deleteGroup(client: BaseTelegramClient, chatId: InputPeerLike): Promise { +export async function deleteGroup(client: ITelegramClient, chatId: InputPeerLike): Promise { const chat = await resolvePeer(client, chatId) if (!isInputPeerChat(chat)) throw new MtInvalidPeerTypeError(chatId, 'chat') @@ -20,7 +19,7 @@ export async function deleteGroup(client: BaseTelegramClient, chatId: InputPeerL chatId: chat.chatId, userId: { _: 'inputUserSelf' }, }) - client.network.handleUpdate(res) + client.handleClientUpdate(res) const r = await client.call({ _: 'messages.deleteChat', diff --git a/packages/client/src/methods/chats/delete-history.ts b/packages/core/src/highlevel/methods/chats/delete-history.ts similarity index 80% rename from packages/client/src/methods/chats/delete-history.ts rename to packages/core/src/highlevel/methods/chats/delete-history.ts index 4433ead5..118c920e 100644 --- a/packages/client/src/methods/chats/delete-history.ts +++ b/packages/core/src/highlevel/methods/chats/delete-history.ts @@ -1,15 +1,14 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' +import { createDummyUpdate } from '../../updates/utils.js' import { isInputPeerChannel } from '../../utils/peer-utils.js' -import { createDummyUpdate } from '../../utils/updates-utils.js' import { resolvePeer } from '../users/resolve-peer.js' /** * Delete communication history (for private chats and legacy groups) */ export async function deleteHistory( - client: BaseTelegramClient, + client: ITelegramClient, chat: InputPeerLike, params?: { /** @@ -45,8 +44,8 @@ export async function deleteHistory( }) if (isInputPeerChannel(peer)) { - client.network.handleUpdate(createDummyUpdate(res.pts, res.ptsCount, peer.channelId)) + client.handleClientUpdate(createDummyUpdate(res.pts, res.ptsCount, peer.channelId)) } else { - client.network.handleUpdate(createDummyUpdate(res.pts, res.ptsCount)) + client.handleClientUpdate(createDummyUpdate(res.pts, res.ptsCount)) } } diff --git a/packages/client/src/methods/chats/delete-user-history.ts b/packages/core/src/highlevel/methods/chats/delete-user-history.ts similarity index 73% rename from packages/client/src/methods/chats/delete-user-history.ts rename to packages/core/src/highlevel/methods/chats/delete-user-history.ts index 5bdc2536..a9ae8944 100644 --- a/packages/client/src/methods/chats/delete-user-history.ts +++ b/packages/core/src/highlevel/methods/chats/delete-user-history.ts @@ -1,15 +1,16 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' +import { createDummyUpdate } from '../../updates/utils.js' import { toInputChannel } from '../../utils/peer-utils.js' -import { createDummyUpdate } from '../../utils/updates-utils.js' import { resolvePeer } from '../users/resolve-peer.js' /** * Delete all messages of a user (or channel) in a supergroup */ export async function deleteUserHistory( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Chat ID */ chatId: InputPeerLike @@ -29,5 +30,5 @@ export async function deleteUserHistory( participant: peer, }) - client.network.handleUpdate(createDummyUpdate(res.pts, res.ptsCount, (channel as tl.RawInputChannel).channelId)) + client.handleClientUpdate(createDummyUpdate(res.pts, res.ptsCount, (channel as tl.RawInputChannel).channelId)) } diff --git a/packages/client/src/methods/chats/edit-admin-rights.ts b/packages/core/src/highlevel/methods/chats/edit-admin-rights.ts similarity index 86% rename from packages/client/src/methods/chats/edit-admin-rights.ts rename to packages/core/src/highlevel/methods/chats/edit-admin-rights.ts index 830e58b7..b5dac765 100644 --- a/packages/client/src/methods/chats/edit-admin-rights.ts +++ b/packages/core/src/highlevel/methods/chats/edit-admin-rights.ts @@ -1,5 +1,6 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { toInputChannel, toInputUser } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -8,7 +9,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * Edit supergroup/channel admin rights of a user. */ export async function editAdminRights( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Chat ID */ chatId: InputPeerLike @@ -36,5 +37,5 @@ export async function editAdminRights( rank, }) - client.network.handleUpdate(res) + client.handleClientUpdate(res) } diff --git a/packages/client/src/methods/chats/get-chat-event-log.ts b/packages/core/src/highlevel/methods/chats/get-chat-event-log.ts similarity index 95% rename from packages/client/src/methods/chats/get-chat-event-log.ts rename to packages/core/src/highlevel/methods/chats/get-chat-event-log.ts index 87c9b276..27120083 100644 --- a/packages/client/src/methods/chats/get-chat-event-log.ts +++ b/packages/core/src/highlevel/methods/chats/get-chat-event-log.ts @@ -1,5 +1,8 @@ -import { BaseTelegramClient, Long, tl } from '@mtcute/core' +import Long from 'long' +import { tl } from '@mtcute/tl' + +import { ITelegramClient } from '../../client.types.js' import { ChatEvent, InputPeerLike, PeersIndex } from '../../types/index.js' import { InputChatEventFilters, normalizeChatEventFilters } from '../../types/peers/chat-event/filters.js' import { toInputChannel, toInputUser } from '../../utils/peer-utils.js' @@ -20,7 +23,7 @@ import { resolvePeerMany } from '../users/resolve-peer-many.js' * @param params */ export async function getChatEventLog( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, params?: { /** diff --git a/packages/client/src/methods/chats/get-chat-member.ts b/packages/core/src/highlevel/methods/chats/get-chat-member.ts similarity index 92% rename from packages/client/src/methods/chats/get-chat-member.ts rename to packages/core/src/highlevel/methods/chats/get-chat-member.ts index 4234afc1..4c337184 100644 --- a/packages/client/src/methods/chats/get-chat-member.ts +++ b/packages/core/src/highlevel/methods/chats/get-chat-member.ts @@ -1,6 +1,7 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { ChatMember, InputPeerLike, MtInvalidPeerTypeError, PeersIndex } from '../../types/index.js' import { isInputPeerChannel, isInputPeerChat, isInputPeerUser, toInputChannel } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -13,7 +14,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @returns Chat member, or `null` if user is not a member of the chat */ export async function getChatMember( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Chat ID or username */ chatId: InputPeerLike diff --git a/packages/client/src/methods/chats/get-chat-members.ts b/packages/core/src/highlevel/methods/chats/get-chat-members.ts similarity index 94% rename from packages/client/src/methods/chats/get-chat-members.ts rename to packages/core/src/highlevel/methods/chats/get-chat-members.ts index b1911394..998672f1 100644 --- a/packages/client/src/methods/chats/get-chat-members.ts +++ b/packages/core/src/highlevel/methods/chats/get-chat-members.ts @@ -1,6 +1,10 @@ -import { assertNever, BaseTelegramClient, Long, tl } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' +import Long from 'long' +import { tl } from '@mtcute/tl' + +import { assertNever } from '../../../types/utils.js' +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { ArrayWithTotal, ChatMember, InputPeerLike, MtInvalidPeerTypeError, PeersIndex } from '../../types/index.js' import { makeArrayWithTotal } from '../../utils/index.js' import { isInputPeerChannel, isInputPeerChat, toInputChannel } from '../../utils/peer-utils.js' @@ -15,7 +19,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param params Additional parameters */ export async function getChatMembers( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, params?: { /** diff --git a/packages/client/src/methods/chats/get-chat-preview.ts b/packages/core/src/highlevel/methods/chats/get-chat-preview.ts similarity index 79% rename from packages/client/src/methods/chats/get-chat-preview.ts rename to packages/core/src/highlevel/methods/chats/get-chat-preview.ts index 5cbec222..07c4f0d7 100644 --- a/packages/client/src/methods/chats/get-chat-preview.ts +++ b/packages/core/src/highlevel/methods/chats/get-chat-preview.ts @@ -1,5 +1,5 @@ -import { BaseTelegramClient, MtArgumentError } from '@mtcute/core' - +import { MtArgumentError } from '../../../types/errors.js' +import { ITelegramClient } from '../../client.types.js' import { ChatPreview, MtPeerNotFoundError } from '../../types/index.js' import { INVITE_LINK_REGEX } from '../../utils/peer-utils.js' @@ -12,7 +12,7 @@ import { INVITE_LINK_REGEX } from '../../utils/peer-utils.js' * In case you are trying to get info about private chat that you have already joined. * Use {@link getChat} or {@link getFullChat} instead. */ -export async function getChatPreview(client: BaseTelegramClient, inviteLink: string): Promise { +export async function getChatPreview(client: ITelegramClient, inviteLink: string): Promise { const m = inviteLink.match(INVITE_LINK_REGEX) if (!m) throw new MtArgumentError('Invalid invite link') diff --git a/packages/client/src/methods/chats/get-chat.ts b/packages/core/src/highlevel/methods/chats/get-chat.ts similarity index 85% rename from packages/client/src/methods/chats/get-chat.ts rename to packages/core/src/highlevel/methods/chats/get-chat.ts index 51cac4d8..aaa51ccb 100644 --- a/packages/client/src/methods/chats/get-chat.ts +++ b/packages/core/src/highlevel/methods/chats/get-chat.ts @@ -1,5 +1,5 @@ -import { BaseTelegramClient, MtArgumentError } from '@mtcute/core' - +import { MtArgumentError } from '../../../types/errors.js' +import { ITelegramClient } from '../../client.types.js' import { Chat, InputPeerLike, MtPeerNotFoundError } from '../../types/index.js' import { INVITE_LINK_REGEX } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -14,7 +14,7 @@ import { _getRawPeerBatched } from './batched-queries.js' * In case you are trying to get info about private chat that you haven't joined. * Use {@link getChatPreview} instead. */ -export async function getChat(client: BaseTelegramClient, chatId: InputPeerLike): Promise { +export async function getChat(client: ITelegramClient, chatId: InputPeerLike): Promise { if (typeof chatId === 'string') { const m = chatId.match(INVITE_LINK_REGEX) diff --git a/packages/client/src/methods/chats/get-full-chat.ts b/packages/core/src/highlevel/methods/chats/get-full-chat.ts similarity index 87% rename from packages/client/src/methods/chats/get-full-chat.ts rename to packages/core/src/highlevel/methods/chats/get-full-chat.ts index c2596ee5..57401d17 100644 --- a/packages/client/src/methods/chats/get-full-chat.ts +++ b/packages/core/src/highlevel/methods/chats/get-full-chat.ts @@ -1,5 +1,7 @@ -import { BaseTelegramClient, MtArgumentError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MtArgumentError } from '../../../types/errors.js' +import { ITelegramClient } from '../../client.types.js' import { Chat, InputPeerLike } from '../../types/index.js' import { INVITE_LINK_REGEX, @@ -20,7 +22,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * In case you are trying to get info about private chat that you haven't joined. * Use {@link getChatPreview} instead. */ -export async function getFullChat(client: BaseTelegramClient, chatId: InputPeerLike): Promise { +export async function getFullChat(client: ITelegramClient, chatId: InputPeerLike): Promise { if (typeof chatId === 'string') { const m = chatId.match(INVITE_LINK_REGEX) diff --git a/packages/client/src/methods/chats/get-nearby-chats.ts b/packages/core/src/highlevel/methods/chats/get-nearby-chats.ts similarity index 68% rename from packages/client/src/methods/chats/get-nearby-chats.ts rename to packages/core/src/highlevel/methods/chats/get-nearby-chats.ts index 376811ca..6dab21b2 100644 --- a/packages/client/src/methods/chats/get-nearby-chats.ts +++ b/packages/core/src/highlevel/methods/chats/get-nearby-chats.ts @@ -1,8 +1,10 @@ -import { BaseTelegramClient, getMarkedPeerId, tl } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { getMarkedPeerId } from '../../../utils/peer-utils.js' +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { Chat } from '../../types/index.js' -import { assertIsUpdatesGroup } from '../../utils/updates-utils.js' +import { assertIsUpdatesGroup } from '../../updates/utils.js' /** * Get nearby chats @@ -10,7 +12,7 @@ import { assertIsUpdatesGroup } from '../../utils/updates-utils.js' * @param latitude Latitude of the location * @param longitude Longitude of the location */ -export async function getNearbyChats(client: BaseTelegramClient, latitude: number, longitude: number): Promise { +export async function getNearbyChats(client: ITelegramClient, latitude: number, longitude: number): Promise { const res = await client.call({ _: 'contacts.getLocated', geoPoint: { @@ -21,7 +23,7 @@ export async function getNearbyChats(client: BaseTelegramClient, latitude: numbe }) assertIsUpdatesGroup('contacts.getLocated', res) - client.network.handleUpdate(res, true) + // client.handleClientUpdate(res, true) if (!res.updates.length) return [] diff --git a/packages/client/src/methods/chats/get-similar-channels.ts b/packages/core/src/highlevel/methods/chats/get-similar-channels.ts similarity index 93% rename from packages/client/src/methods/chats/get-similar-channels.ts rename to packages/core/src/highlevel/methods/chats/get-similar-channels.ts index 4dc37d9c..0ddd5588 100644 --- a/packages/client/src/methods/chats/get-similar-channels.ts +++ b/packages/core/src/highlevel/methods/chats/get-similar-channels.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { ArrayWithTotal, Chat, InputPeerLike } from '../../types/index.js' import { makeArrayWithTotal } from '../../utils/misc-utils.js' import { toInputChannel } from '../../utils/peer-utils.js' @@ -16,7 +15,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * > Returns empty array in case there are no similar channels available. */ export async function getSimilarChannels( - client: BaseTelegramClient, + client: ITelegramClient, channel: InputPeerLike, ): Promise> { const res = await client.call({ diff --git a/packages/client/src/methods/chats/iter-chat-event-log.ts b/packages/core/src/highlevel/methods/chats/iter-chat-event-log.ts similarity index 94% rename from packages/client/src/methods/chats/iter-chat-event-log.ts rename to packages/core/src/highlevel/methods/chats/iter-chat-event-log.ts index 706efc94..2220687b 100644 --- a/packages/client/src/methods/chats/iter-chat-event-log.ts +++ b/packages/core/src/highlevel/methods/chats/iter-chat-event-log.ts @@ -1,5 +1,8 @@ -import { BaseTelegramClient, Long, tl } from '@mtcute/core' +import Long from 'long' +import { tl } from '@mtcute/tl' + +import { ITelegramClient } from '../../client.types.js' import { ChatEvent, InputPeerLike } from '../../types/index.js' import { normalizeChatEventFilters } from '../../types/peers/chat-event/filters.js' import { toInputChannel, toInputUser } from '../../utils/peer-utils.js' @@ -16,7 +19,7 @@ import { getChatEventLog } from './get-chat-event-log.js' * @param params */ export async function* iterChatEventLog( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, params?: Parameters[2] & { /** diff --git a/packages/client/src/methods/chats/iter-chat-members.ts b/packages/core/src/highlevel/methods/chats/iter-chat-members.ts similarity index 95% rename from packages/client/src/methods/chats/iter-chat-members.ts rename to packages/core/src/highlevel/methods/chats/iter-chat-members.ts index 18917f40..7ea0ba00 100644 --- a/packages/client/src/methods/chats/iter-chat-members.ts +++ b/packages/core/src/highlevel/methods/chats/iter-chat-members.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { ChatMember, InputPeerLike } from '../../types/index.js' import { isInputPeerChat } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -16,7 +15,7 @@ import { getChatMembers } from './get-chat-members.js' * @param params Additional parameters */ export async function* iterChatMembers( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, params?: Parameters[2] & { /** diff --git a/packages/client/src/methods/chats/join-chat.ts b/packages/core/src/highlevel/methods/chats/join-chat.ts similarity index 80% rename from packages/client/src/methods/chats/join-chat.ts rename to packages/core/src/highlevel/methods/chats/join-chat.ts index 7fb3d5f6..d4124b17 100644 --- a/packages/client/src/methods/chats/join-chat.ts +++ b/packages/core/src/highlevel/methods/chats/join-chat.ts @@ -1,8 +1,7 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { Chat, InputPeerLike } from '../../types/index.js' +import { assertIsUpdatesGroup } from '../../updates/utils.js' import { INVITE_LINK_REGEX, toInputChannel } from '../../utils/peer-utils.js' -import { assertIsUpdatesGroup } from '../../utils/updates-utils.js' import { resolvePeer } from '../users/resolve-peer.js' /** @@ -16,7 +15,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * Chat identifier. Either an invite link (`t.me/joinchat/*`), a username (`@username`) * or ID of the linked supergroup or channel. */ -export async function joinChat(client: BaseTelegramClient, chatId: InputPeerLike): Promise { +export async function joinChat(client: ITelegramClient, chatId: InputPeerLike): Promise { if (typeof chatId === 'string') { const m = chatId.match(INVITE_LINK_REGEX) @@ -27,7 +26,7 @@ export async function joinChat(client: BaseTelegramClient, chatId: InputPeerLike }) assertIsUpdatesGroup('messages.importChatInvite', res) - client.network.handleUpdate(res) + client.handleClientUpdate(res) return new Chat(res.chats[0]) } @@ -40,7 +39,7 @@ export async function joinChat(client: BaseTelegramClient, chatId: InputPeerLike assertIsUpdatesGroup('channels.joinChannel', res) - client.network.handleUpdate(res) + client.handleClientUpdate(res) return new Chat(res.chats[0]) } diff --git a/packages/client/src/methods/chats/kick-chat-member.ts b/packages/core/src/highlevel/methods/chats/kick-chat-member.ts similarity index 89% rename from packages/client/src/methods/chats/kick-chat-member.ts rename to packages/core/src/highlevel/methods/chats/kick-chat-member.ts index fea2124f..15a0f495 100644 --- a/packages/client/src/methods/chats/kick-chat-member.ts +++ b/packages/core/src/highlevel/methods/chats/kick-chat-member.ts @@ -1,6 +1,5 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { sleep } from '@mtcute/core/utils.js' - +import { sleep } from '../../../utils/misc-utils.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, Message } from '../../types/index.js' import { isInputPeerChannel } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -15,7 +14,7 @@ import { unbanChatMember } from './unban-chat-member.js' * @returns Service message about removed user, if one was generated. */ export async function kickChatMember( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Chat ID */ chatId: InputPeerLike diff --git a/packages/client/src/methods/chats/leave-chat.ts b/packages/core/src/highlevel/methods/chats/leave-chat.ts similarity index 87% rename from packages/client/src/methods/chats/leave-chat.ts rename to packages/core/src/highlevel/methods/chats/leave-chat.ts index 65f16f28..ddb89538 100644 --- a/packages/client/src/methods/chats/leave-chat.ts +++ b/packages/core/src/highlevel/methods/chats/leave-chat.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, MtInvalidPeerTypeError } from '../../types/index.js' import { isInputPeerChannel, isInputPeerChat, toInputChannel } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -11,7 +10,7 @@ import { deleteHistory } from './delete-history.js' * @param chatId Chat ID or username */ export async function leaveChat( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, params?: { /** @@ -27,14 +26,14 @@ export async function leaveChat( _: 'channels.leaveChannel', channel: toInputChannel(chat), }) - client.network.handleUpdate(res) + client.handleClientUpdate(res) } else if (isInputPeerChat(chat)) { const res = await client.call({ _: 'messages.deleteChatUser', chatId: chat.chatId, userId: { _: 'inputUserSelf' }, }) - client.network.handleUpdate(res) + client.handleClientUpdate(res) if (params?.clear) { await deleteHistory(client, chat) diff --git a/packages/client/src/methods/chats/mark-chat-unread.ts b/packages/core/src/highlevel/methods/chats/mark-chat-unread.ts similarity index 66% rename from packages/client/src/methods/chats/mark-chat-unread.ts rename to packages/core/src/highlevel/methods/chats/mark-chat-unread.ts index 300c397b..eb491dd4 100644 --- a/packages/client/src/methods/chats/mark-chat-unread.ts +++ b/packages/core/src/highlevel/methods/chats/mark-chat-unread.ts @@ -1,6 +1,5 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' - +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -9,7 +8,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * * @param chatId Chat ID */ -export async function markChatUnread(client: BaseTelegramClient, chatId: InputPeerLike): Promise { +export async function markChatUnread(client: ITelegramClient, chatId: InputPeerLike): Promise { const r = await client.call({ _: 'messages.markDialogUnread', peer: { diff --git a/packages/client/src/methods/chats/open-chat.ts b/packages/core/src/highlevel/methods/chats/open-chat.ts similarity index 67% rename from packages/client/src/methods/chats/open-chat.ts rename to packages/core/src/highlevel/methods/chats/open-chat.ts index 7317ebca..addd8a08 100644 --- a/packages/client/src/methods/chats/open-chat.ts +++ b/packages/core/src/highlevel/methods/chats/open-chat.ts @@ -1,9 +1,7 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/peers/index.js' import { isInputPeerChannel } from '../../utils/peer-utils.js' import { getPeerDialogs } from '../dialogs/get-peer-dialogs.js' -import { notifyChannelClosed, notifyChannelOpened } from '../updates/manager.js' import { resolvePeer } from '../users/resolve-peer.js' /** @@ -14,15 +12,13 @@ import { resolvePeer } from '../users/resolve-peer.js' * * @param chat Chat to open */ -export async function openChat(client: BaseTelegramClient, chat: InputPeerLike): Promise { +export async function openChat(client: ITelegramClient, chat: InputPeerLike): Promise { const peer = await resolvePeer(client, chat) if (isInputPeerChannel(peer)) { const [dialog] = await getPeerDialogs(client, peer) - if (!client.network.params.disableUpdates) { - notifyChannelOpened(client, peer.channelId, dialog.raw.pts) - } + await client.notifyChannelOpened(peer.channelId, dialog.raw.pts) } // todo: once we have proper dialogs/peers db, we should also @@ -38,11 +34,11 @@ export async function openChat(client: BaseTelegramClient, chat: InputPeerLike): * * @param chat Chat to open */ -export async function closeChat(client: BaseTelegramClient, chat: InputPeerLike): Promise { +export async function closeChat(client: ITelegramClient, chat: InputPeerLike): Promise { const peer = await resolvePeer(client, chat) - if (isInputPeerChannel(peer) && !client.network.params.disableUpdates) { - notifyChannelClosed(client, peer.channelId) + if (isInputPeerChannel(peer)) { + await client.notifyChannelClosed(peer.channelId) } // todo: once we have proper dialogs/peers db, we should also diff --git a/packages/client/src/methods/chats/reorder-usernames.ts b/packages/core/src/highlevel/methods/chats/reorder-usernames.ts similarity index 79% rename from packages/client/src/methods/chats/reorder-usernames.ts rename to packages/core/src/highlevel/methods/chats/reorder-usernames.ts index 2c2ff167..9a217aae 100644 --- a/packages/client/src/methods/chats/reorder-usernames.ts +++ b/packages/core/src/highlevel/methods/chats/reorder-usernames.ts @@ -1,8 +1,8 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' -import { assertTrue, isInputPeerChannel, isInputPeerUser, toInputChannel, toInputUser } from '../../utils/index.js' -import { isSelfPeer } from '../auth/_state.js' +import { isInputPeerChannel, isInputPeerUser, toInputChannel, toInputUser } from '../../utils/index.js' +import { isSelfPeer } from '../auth/utils.js' import { resolvePeer } from '../users/resolve-peer.js' /** @@ -11,7 +11,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param peerId Bot, channel or "me"/"self" */ export async function reorderUsernames( - client: BaseTelegramClient, + client: ITelegramClient, peerId: InputPeerLike, order: string[], ): Promise { diff --git a/packages/client/src/methods/chats/restrict-chat-member.ts b/packages/core/src/highlevel/methods/chats/restrict-chat-member.ts similarity index 92% rename from packages/client/src/methods/chats/restrict-chat-member.ts rename to packages/core/src/highlevel/methods/chats/restrict-chat-member.ts index 290cfb0e..a9544506 100644 --- a/packages/client/src/methods/chats/restrict-chat-member.ts +++ b/packages/core/src/highlevel/methods/chats/restrict-chat-member.ts @@ -1,5 +1,6 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, MtInvalidPeerTypeError } from '../../types/index.js' import { normalizeDate } from '../../utils/misc-utils.js' import { isInputPeerChannel, toInputChannel } from '../../utils/peer-utils.js' @@ -9,7 +10,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * Restrict a user in a supergroup. */ export async function restrictChatMember( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Chat ID */ chatId: InputPeerLike @@ -56,5 +57,5 @@ export async function restrictChatMember( ...restrictions, }, }) - client.network.handleUpdate(res) + client.handleClientUpdate(res) } diff --git a/packages/client/src/methods/chats/save-draft.ts b/packages/core/src/highlevel/methods/chats/save-draft.ts similarity index 86% rename from packages/client/src/methods/chats/save-draft.ts rename to packages/core/src/highlevel/methods/chats/save-draft.ts index d2bb4dd1..73dc7d18 100644 --- a/packages/client/src/methods/chats/save-draft.ts +++ b/packages/core/src/highlevel/methods/chats/save-draft.ts @@ -1,5 +1,6 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -10,7 +11,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param draft Draft message, or `null` to delete. */ export async function saveDraft( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, draft: null | Omit, ): Promise { diff --git a/packages/client/src/methods/chats/set-chat-color.ts b/packages/core/src/highlevel/methods/chats/set-chat-color.ts similarity index 83% rename from packages/client/src/methods/chats/set-chat-color.ts rename to packages/core/src/highlevel/methods/chats/set-chat-color.ts index e16cc050..247db161 100644 --- a/packages/client/src/methods/chats/set-chat-color.ts +++ b/packages/core/src/highlevel/methods/chats/set-chat-color.ts @@ -1,8 +1,11 @@ -import { BaseTelegramClient, MtTypeAssertionError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MtTypeAssertionError } from '../../../types/errors.js' +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, MtInvalidPeerTypeError } from '../../types/index.js' -import { assertTrue, isInputPeerChannel, isInputPeerUser, toInputChannel } from '../../utils/index.js' -import { isSelfPeer } from '../auth/_state.js' +import { isInputPeerChannel, isInputPeerUser, toInputChannel } from '../../utils/index.js' +import { isSelfPeer } from '../auth/utils.js' import { resolvePeer } from '../users/resolve-peer.js' // @available=user @@ -10,7 +13,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * Set peer color and optionally background pattern */ export async function setChatColor( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** * Peer where to update the color. @@ -58,7 +61,7 @@ export async function setChatColor( backgroundEmojiId, }) - client.network.handleUpdate(res) + client.handleClientUpdate(res) return } diff --git a/packages/client/src/methods/chats/set-chat-default-permissions.ts b/packages/core/src/highlevel/methods/chats/set-chat-default-permissions.ts similarity index 84% rename from packages/client/src/methods/chats/set-chat-default-permissions.ts rename to packages/core/src/highlevel/methods/chats/set-chat-default-permissions.ts index bd99fcd4..576f8b7f 100644 --- a/packages/client/src/methods/chats/set-chat-default-permissions.ts +++ b/packages/core/src/highlevel/methods/chats/set-chat-default-permissions.ts @@ -1,7 +1,8 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { Chat, InputPeerLike } from '../../types/index.js' -import { assertIsUpdatesGroup } from '../../utils/updates-utils.js' +import { assertIsUpdatesGroup } from '../../updates/utils.js' import { resolvePeer } from '../users/resolve-peer.js' /** @@ -17,7 +18,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * and passing `{}` (empty object) will lift any restrictions */ export async function setChatDefaultPermissions( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, restrictions: Omit, ): Promise { @@ -35,7 +36,7 @@ export async function setChatDefaultPermissions( assertIsUpdatesGroup('messages.editChatDefaultBannedRights', res) - client.network.handleUpdate(res) + client.handleClientUpdate(res) return new Chat(res.chats[0]) } diff --git a/packages/client/src/methods/chats/set-chat-description.ts b/packages/core/src/highlevel/methods/chats/set-chat-description.ts similarity index 81% rename from packages/client/src/methods/chats/set-chat-description.ts rename to packages/core/src/highlevel/methods/chats/set-chat-description.ts index 794d3893..df01fbb7 100644 --- a/packages/client/src/methods/chats/set-chat-description.ts +++ b/packages/core/src/highlevel/methods/chats/set-chat-description.ts @@ -1,6 +1,5 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' - +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -13,7 +12,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param description New chat description, 0-255 characters */ export async function setChatDescription( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, description: string, ): Promise { diff --git a/packages/client/src/methods/chats/set-chat-photo.ts b/packages/core/src/highlevel/methods/chats/set-chat-photo.ts similarity index 90% rename from packages/client/src/methods/chats/set-chat-photo.ts rename to packages/core/src/highlevel/methods/chats/set-chat-photo.ts index 931da031..ef90a111 100644 --- a/packages/client/src/methods/chats/set-chat-photo.ts +++ b/packages/core/src/highlevel/methods/chats/set-chat-photo.ts @@ -1,7 +1,10 @@ -import { BaseTelegramClient, MtArgumentError, tl } from '@mtcute/core' -import { fileIdToInputPhoto, tdFileId } from '@mtcute/file-id' +import { tdFileId } from '@mtcute/file-id' +import { tl } from '@mtcute/tl' +import { MtArgumentError } from '../../../types/errors.js' +import { ITelegramClient } from '../../client.types.js' import { InputFileLike, InputPeerLike, isUploadedFile, MtInvalidPeerTypeError } from '../../types/index.js' +import { fileIdToInputPhoto } from '../../utils/convert-file-id.js' import { isInputPeerChannel, isInputPeerChat, toInputChannel } from '../../utils/peer-utils.js' import { uploadFile } from '../files/upload-file.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -12,7 +15,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * You must be an administrator and have the appropriate permissions. */ export async function setChatPhoto( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Chat ID or username */ chatId: InputPeerLike @@ -99,5 +102,5 @@ export async function setChatPhoto( photo, }) } - client.network.handleUpdate(res) + client.handleClientUpdate(res) } diff --git a/packages/client/src/methods/chats/set-chat-title.ts b/packages/core/src/highlevel/methods/chats/set-chat-title.ts similarity index 81% rename from packages/client/src/methods/chats/set-chat-title.ts rename to packages/core/src/highlevel/methods/chats/set-chat-title.ts index 1765b10e..d44ffc6d 100644 --- a/packages/client/src/methods/chats/set-chat-title.ts +++ b/packages/core/src/highlevel/methods/chats/set-chat-title.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, MtInvalidPeerTypeError } from '../../types/index.js' import { isInputPeerChannel, isInputPeerChat, toInputChannel } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -12,7 +11,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param chatId Chat ID or username * @param title New chat title, 1-255 characters */ -export async function setChatTitle(client: BaseTelegramClient, chatId: InputPeerLike, title: string): Promise { +export async function setChatTitle(client: ITelegramClient, chatId: InputPeerLike, title: string): Promise { const chat = await resolvePeer(client, chatId) let res @@ -30,5 +29,5 @@ export async function setChatTitle(client: BaseTelegramClient, chatId: InputPeer }) } else throw new MtInvalidPeerTypeError(chatId, 'chat or channel') - client.network.handleUpdate(res) + client.handleClientUpdate(res) } diff --git a/packages/client/src/methods/chats/set-chat-ttl.ts b/packages/core/src/highlevel/methods/chats/set-chat-ttl.ts similarity index 71% rename from packages/client/src/methods/chats/set-chat-ttl.ts rename to packages/core/src/highlevel/methods/chats/set-chat-ttl.ts index 317bc08e..1c87130a 100644 --- a/packages/client/src/methods/chats/set-chat-ttl.ts +++ b/packages/core/src/highlevel/methods/chats/set-chat-ttl.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -9,7 +8,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param chatId Chat ID * @param period New TTL period, in seconds (or 0 to disable) */ -export async function setChatTtl(client: BaseTelegramClient, chatId: InputPeerLike, period: number): Promise { +export async function setChatTtl(client: ITelegramClient, chatId: InputPeerLike, period: number): Promise { await client.call({ _: 'messages.setHistoryTTL', peer: await resolvePeer(client, chatId), diff --git a/packages/client/src/methods/chats/set-chat-username.ts b/packages/core/src/highlevel/methods/chats/set-chat-username.ts similarity index 83% rename from packages/client/src/methods/chats/set-chat-username.ts rename to packages/core/src/highlevel/methods/chats/set-chat-username.ts index f9a57b79..a1e218ba 100644 --- a/packages/client/src/methods/chats/set-chat-username.ts +++ b/packages/core/src/highlevel/methods/chats/set-chat-username.ts @@ -1,6 +1,5 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' - +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { toInputChannel } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -14,7 +13,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param username New username, or `null` to remove */ export async function setChatUsername( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, username: string | null, ): Promise { diff --git a/packages/client/src/methods/chats/set-slow-mode.ts b/packages/core/src/highlevel/methods/chats/set-slow-mode.ts similarity index 75% rename from packages/client/src/methods/chats/set-slow-mode.ts rename to packages/core/src/highlevel/methods/chats/set-slow-mode.ts index c684597f..6b05a0d1 100644 --- a/packages/client/src/methods/chats/set-slow-mode.ts +++ b/packages/core/src/highlevel/methods/chats/set-slow-mode.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { toInputChannel } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -13,11 +12,11 @@ import { resolvePeer } from '../users/resolve-peer.js' * Users will be able to send a message only once per this interval. * Valid values are: `0 (off), 10, 30, 60 (1m), 300 (5m), 900 (15m) or 3600 (1h)` */ -export async function setSlowMode(client: BaseTelegramClient, chatId: InputPeerLike, seconds = 0): Promise { +export async function setSlowMode(client: ITelegramClient, chatId: InputPeerLike, seconds = 0): Promise { const res = await client.call({ _: 'channels.toggleSlowMode', channel: toInputChannel(await resolvePeer(client, chatId), chatId), seconds, }) - client.network.handleUpdate(res) + client.handleClientUpdate(res) } diff --git a/packages/client/src/methods/chats/toggle-content-protection.ts b/packages/core/src/highlevel/methods/chats/toggle-content-protection.ts similarity index 82% rename from packages/client/src/methods/chats/toggle-content-protection.ts rename to packages/core/src/highlevel/methods/chats/toggle-content-protection.ts index 5c79dbf2..70a6b6c5 100644 --- a/packages/client/src/methods/chats/toggle-content-protection.ts +++ b/packages/core/src/highlevel/methods/chats/toggle-content-protection.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -10,7 +9,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param enabled Whether content protection should be enabled */ export async function toggleContentProtection( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, enabled = false, ): Promise { @@ -19,5 +18,5 @@ export async function toggleContentProtection( peer: await resolvePeer(client, chatId), enabled, }) - client.network.handleUpdate(res) + client.handleClientUpdate(res) } diff --git a/packages/client/src/methods/chats/toggle-fragment-username.ts b/packages/core/src/highlevel/methods/chats/toggle-fragment-username.ts similarity index 84% rename from packages/client/src/methods/chats/toggle-fragment-username.ts rename to packages/core/src/highlevel/methods/chats/toggle-fragment-username.ts index 934b2f1f..e55fede5 100644 --- a/packages/client/src/methods/chats/toggle-fragment-username.ts +++ b/packages/core/src/highlevel/methods/chats/toggle-fragment-username.ts @@ -1,8 +1,8 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' -import { assertTrue, isInputPeerChannel, isInputPeerUser, toInputChannel, toInputUser } from '../../utils/index.js' -import { isSelfPeer } from '../auth/_state.js' +import { isInputPeerChannel, isInputPeerUser, toInputChannel, toInputUser } from '../../utils/index.js' +import { isSelfPeer } from '../auth/utils.js' import { resolvePeer } from '../users/resolve-peer.js' /** @@ -12,7 +12,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * > using {@link setUsername}/{@link setChatUsername} */ export async function toggleFragmentUsername( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Peer ID whose username to toggle */ peerId: InputPeerLike diff --git a/packages/client/src/methods/chats/toggle-join-requests.ts b/packages/core/src/highlevel/methods/chats/toggle-join-requests.ts similarity index 86% rename from packages/client/src/methods/chats/toggle-join-requests.ts rename to packages/core/src/highlevel/methods/chats/toggle-join-requests.ts index ebae5309..ed6bb5c1 100644 --- a/packages/client/src/methods/chats/toggle-join-requests.ts +++ b/packages/core/src/highlevel/methods/chats/toggle-join-requests.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { toInputChannel } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -14,7 +13,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param enabled Whether join requests should be enabled */ export async function toggleJoinRequests( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, enabled = false, ): Promise { @@ -23,5 +22,5 @@ export async function toggleJoinRequests( channel: toInputChannel(await resolvePeer(client, chatId), chatId), enabled, }) - client.network.handleUpdate(res) + client.handleClientUpdate(res) } diff --git a/packages/client/src/methods/chats/toggle-join-to-send.ts b/packages/core/src/highlevel/methods/chats/toggle-join-to-send.ts similarity index 86% rename from packages/client/src/methods/chats/toggle-join-to-send.ts rename to packages/core/src/highlevel/methods/chats/toggle-join-to-send.ts index a1f9a35e..2374f018 100644 --- a/packages/client/src/methods/chats/toggle-join-to-send.ts +++ b/packages/core/src/highlevel/methods/chats/toggle-join-to-send.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { toInputChannel } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -14,7 +13,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param enabled Whether join-to-send setting should be enabled */ export async function toggleJoinToSend( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, enabled = false, ): Promise { @@ -23,5 +22,5 @@ export async function toggleJoinToSend( channel: toInputChannel(await resolvePeer(client, chatId), chatId), enabled, }) - client.network.handleUpdate(res) + client.handleClientUpdate(res) } diff --git a/packages/client/src/methods/chats/unarchive-chats.ts b/packages/core/src/highlevel/methods/chats/unarchive-chats.ts similarity index 68% rename from packages/client/src/methods/chats/unarchive-chats.ts rename to packages/core/src/highlevel/methods/chats/unarchive-chats.ts index b00c5626..63ec7500 100644 --- a/packages/client/src/methods/chats/unarchive-chats.ts +++ b/packages/core/src/highlevel/methods/chats/unarchive-chats.ts @@ -1,5 +1,7 @@ -import { BaseTelegramClient, MaybeArray, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MaybeArray } from '../../../types/utils.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -8,7 +10,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * * @param chats Chat ID(s), username(s), phone number(s), `"me"` or `"self"` */ -export async function unarchiveChats(client: BaseTelegramClient, chats: MaybeArray): Promise { +export async function unarchiveChats(client: ITelegramClient, chats: MaybeArray): Promise { if (!Array.isArray(chats)) chats = [chats] const folderPeers: tl.TypeInputFolderPeer[] = [] @@ -25,5 +27,5 @@ export async function unarchiveChats(client: BaseTelegramClient, chats: MaybeArr _: 'folders.editPeerFolders', folderPeers, }) - client.network.handleUpdate(res) + client.handleClientUpdate(res) } diff --git a/packages/client/src/methods/chats/unban-chat-member.ts b/packages/core/src/highlevel/methods/chats/unban-chat-member.ts similarity index 91% rename from packages/client/src/methods/chats/unban-chat-member.ts rename to packages/core/src/highlevel/methods/chats/unban-chat-member.ts index 9407dad1..66c8ef09 100644 --- a/packages/client/src/methods/chats/unban-chat-member.ts +++ b/packages/core/src/highlevel/methods/chats/unban-chat-member.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, MtInvalidPeerTypeError } from '../../types/index.js' import { isInputPeerChannel, isInputPeerChat, toInputChannel } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -14,7 +13,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * This method acts as a no-op in case a legacy group is passed. */ export async function unbanChatMember( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Chat ID */ chatId: InputPeerLike @@ -38,7 +37,7 @@ export async function unbanChatMember( }, }) - client.network.handleUpdate(res) + client.handleClientUpdate(res) } else if (isInputPeerChat(chat)) { // no-op // } else throw new MtInvalidPeerTypeError(chatId, 'chat or channel') diff --git a/packages/client/src/methods/contacts/add-contact.ts b/packages/core/src/highlevel/methods/contacts/add-contact.ts similarity index 87% rename from packages/client/src/methods/contacts/add-contact.ts rename to packages/core/src/highlevel/methods/contacts/add-contact.ts index 79e06379..aee93f8a 100644 --- a/packages/client/src/methods/contacts/add-contact.ts +++ b/packages/core/src/highlevel/methods/contacts/add-contact.ts @@ -1,15 +1,14 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, User } from '../../types/index.js' +import { assertIsUpdatesGroup } from '../../updates/utils.js' import { toInputUser } from '../../utils/peer-utils.js' -import { assertIsUpdatesGroup } from '../../utils/updates-utils.js' import { resolvePeer } from '../users/resolve-peer.js' /** * Add an existing Telegram user as a contact */ export async function addContact( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** User ID, username or phone number */ userId: InputPeerLike @@ -52,7 +51,7 @@ export async function addContact( assertIsUpdatesGroup('contacts.addContact', res) - client.network.handleUpdate(res) + client.handleClientUpdate(res) return new User(res.users[0]) } diff --git a/packages/client/src/methods/contacts/delete-contacts.ts b/packages/core/src/highlevel/methods/contacts/delete-contacts.ts similarity index 73% rename from packages/client/src/methods/contacts/delete-contacts.ts rename to packages/core/src/highlevel/methods/contacts/delete-contacts.ts index b28313d7..8d033ae4 100644 --- a/packages/client/src/methods/contacts/delete-contacts.ts +++ b/packages/core/src/highlevel/methods/contacts/delete-contacts.ts @@ -1,8 +1,8 @@ -import { BaseTelegramClient, MaybeArray } from '@mtcute/core' - +import { MaybeArray } from '../../../types/utils.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, MtInvalidPeerTypeError, User } from '../../types/index.js' +import { assertIsUpdatesGroup } from '../../updates/utils.js' import { toInputUser } from '../../utils/peer-utils.js' -import { assertIsUpdatesGroup } from '../../utils/updates-utils.js' import { resolvePeerMany } from '../users/resolve-peer-many.js' /** @@ -13,7 +13,7 @@ import { resolvePeerMany } from '../users/resolve-peer-many.js' * * @param userIds User IDs, usernames or phone numbers */ -export async function deleteContacts(client: BaseTelegramClient, userIds: MaybeArray): Promise { +export async function deleteContacts(client: ITelegramClient, userIds: MaybeArray): Promise { if (!Array.isArray(userIds)) userIds = [userIds] const inputPeers = await resolvePeerMany(client, userIds, toInputUser) @@ -29,7 +29,7 @@ export async function deleteContacts(client: BaseTelegramClient, userIds: MaybeA assertIsUpdatesGroup('contacts.deleteContacts', res) - client.network.handleUpdate(res) + client.handleClientUpdate(res) return res.users.map((user) => new User(user)) } diff --git a/packages/client/src/methods/contacts/get-contacts.ts b/packages/core/src/highlevel/methods/contacts/get-contacts.ts similarity index 59% rename from packages/client/src/methods/contacts/get-contacts.ts rename to packages/core/src/highlevel/methods/contacts/get-contacts.ts index d6b8b4a5..90c18e5a 100644 --- a/packages/client/src/methods/contacts/get-contacts.ts +++ b/packages/core/src/highlevel/methods/contacts/get-contacts.ts @@ -1,12 +1,13 @@ -import { BaseTelegramClient, Long } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' +import Long from 'long' +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { User } from '../../types/index.js' /** * Get list of contacts from your Telegram contacts list. */ -export async function getContacts(client: BaseTelegramClient): Promise { +export async function getContacts(client: ITelegramClient): Promise { const res = await client.call({ _: 'contacts.getContacts', hash: Long.ZERO, diff --git a/packages/client/src/methods/contacts/import-contacts.ts b/packages/core/src/highlevel/methods/contacts/import-contacts.ts similarity index 74% rename from packages/client/src/methods/contacts/import-contacts.ts rename to packages/core/src/highlevel/methods/contacts/import-contacts.ts index f79c40cb..16d1a397 100644 --- a/packages/client/src/methods/contacts/import-contacts.ts +++ b/packages/core/src/highlevel/methods/contacts/import-contacts.ts @@ -1,4 +1,9 @@ -import { BaseTelegramClient, Long, PartialOnly, tl } from '@mtcute/core' +import Long from 'long' + +import { tl } from '@mtcute/tl' + +import { PartialOnly } from '../../../types/utils.js' +import { ITelegramClient } from '../../client.types.js' /** * Import contacts to your Telegram contacts list. @@ -6,7 +11,7 @@ import { BaseTelegramClient, Long, PartialOnly, tl } from '@mtcute/core' * @param contacts List of contacts */ export async function importContacts( - client: BaseTelegramClient, + client: ITelegramClient, contacts: PartialOnly, 'clientId'>[], ): Promise { let seq = Long.ZERO diff --git a/packages/client/src/methods/dialogs/create-folder.ts b/packages/core/src/highlevel/methods/dialogs/create-folder.ts similarity index 82% rename from packages/client/src/methods/dialogs/create-folder.ts rename to packages/core/src/highlevel/methods/dialogs/create-folder.ts index b3e40c69..7700dbd5 100644 --- a/packages/client/src/methods/dialogs/create-folder.ts +++ b/packages/core/src/highlevel/methods/dialogs/create-folder.ts @@ -1,6 +1,8 @@ -import { BaseTelegramClient, PartialExcept, tl } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { PartialExcept } from '../../../types/utils.js' +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { getFolders } from './get-folders.js' /** @@ -13,7 +15,7 @@ import { getFolders } from './get-folders.js' * @returns Newly created folder */ export async function createFolder( - client: BaseTelegramClient, + client: ITelegramClient, folder: PartialExcept, ): Promise { let id = folder.id diff --git a/packages/client/src/methods/dialogs/delete-folder.ts b/packages/core/src/highlevel/methods/dialogs/delete-folder.ts similarity index 50% rename from packages/client/src/methods/dialogs/delete-folder.ts rename to packages/core/src/highlevel/methods/dialogs/delete-folder.ts index e861639e..68baafc7 100644 --- a/packages/client/src/methods/dialogs/delete-folder.ts +++ b/packages/core/src/highlevel/methods/dialogs/delete-folder.ts @@ -1,12 +1,14 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' + +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' /** * Delete a folder by its ID * * @param id Folder ID or folder itself */ -export async function deleteFolder(client: BaseTelegramClient, id: number | tl.RawDialogFilter): Promise { +export async function deleteFolder(client: ITelegramClient, id: number | tl.RawDialogFilter): Promise { const r = await client.call({ _: 'messages.updateDialogFilter', id: typeof id === 'number' ? id : id.id, diff --git a/packages/client/src/methods/dialogs/edit-folder.ts b/packages/core/src/highlevel/methods/dialogs/edit-folder.ts similarity index 86% rename from packages/client/src/methods/dialogs/edit-folder.ts rename to packages/core/src/highlevel/methods/dialogs/edit-folder.ts index 3dfbd5cf..de75c52b 100644 --- a/packages/client/src/methods/dialogs/edit-folder.ts +++ b/packages/core/src/highlevel/methods/dialogs/edit-folder.ts @@ -1,6 +1,8 @@ -import { BaseTelegramClient, MtArgumentError, tl } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { MtArgumentError } from '../../../types/errors.js' +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { getFolders } from './get-folders.js' /** @@ -9,7 +11,7 @@ import { getFolders } from './get-folders.js' * @returns Modified folder */ export async function editFolder( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** * Folder, folder ID or name. diff --git a/packages/client/src/methods/dialogs/find-folder.ts b/packages/core/src/highlevel/methods/dialogs/find-folder.ts similarity index 86% rename from packages/client/src/methods/dialogs/find-folder.ts rename to packages/core/src/highlevel/methods/dialogs/find-folder.ts index 655f60a9..009efd63 100644 --- a/packages/client/src/methods/dialogs/find-folder.ts +++ b/packages/core/src/highlevel/methods/dialogs/find-folder.ts @@ -1,5 +1,7 @@ -import { BaseTelegramClient, MtArgumentError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MtArgumentError } from '../../../types/errors.js' +import { ITelegramClient } from '../../client.types.js' import { getFolders } from './get-folders.js' /** @@ -12,7 +14,7 @@ import { getFolders } from './get-folders.js' * @param params Search parameters. At least one must be set. */ export async function findFolder( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Folder title */ title?: string diff --git a/packages/client/src/methods/dialogs/get-folders.ts b/packages/core/src/highlevel/methods/dialogs/get-folders.ts similarity index 74% rename from packages/client/src/methods/dialogs/get-folders.ts rename to packages/core/src/highlevel/methods/dialogs/get-folders.ts index cd709fd0..5e21e461 100644 --- a/packages/client/src/methods/dialogs/get-folders.ts +++ b/packages/core/src/highlevel/methods/dialogs/get-folders.ts @@ -1,11 +1,13 @@ -import { BaseTelegramClient, MtArgumentError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MtArgumentError } from '../../../types/errors.js' +import { ITelegramClient } from '../../client.types.js' import { InputDialogFolder } from '../../types/index.js' /** * Get list of folders. */ -export async function getFolders(client: BaseTelegramClient): Promise { +export async function getFolders(client: ITelegramClient): Promise { return client.call({ _: 'messages.getDialogFilters', }) @@ -13,7 +15,7 @@ export async function getFolders(client: BaseTelegramClient): Promise { if (typeof folder === 'string' || typeof folder === 'number') { diff --git a/packages/client/src/methods/dialogs/get-peer-dialogs.ts b/packages/core/src/highlevel/methods/dialogs/get-peer-dialogs.ts similarity index 73% rename from packages/client/src/methods/dialogs/get-peer-dialogs.ts rename to packages/core/src/highlevel/methods/dialogs/get-peer-dialogs.ts index 85ea32a4..96e80deb 100644 --- a/packages/client/src/methods/dialogs/get-peer-dialogs.ts +++ b/packages/core/src/highlevel/methods/dialogs/get-peer-dialogs.ts @@ -1,5 +1,5 @@ -import { BaseTelegramClient, MaybeArray } from '@mtcute/core' - +import { MaybeArray } from '../../../types/utils.js' +import { ITelegramClient } from '../../client.types.js' import { Dialog } from '../../types/messages/dialog.js' import { InputPeerLike } from '../../types/peers/index.js' import { resolvePeerMany } from '../users/resolve-peer-many.js' @@ -9,7 +9,7 @@ import { resolvePeerMany } from '../users/resolve-peer-many.js' * * @param peers Peers for which to fetch dialogs. */ -export async function getPeerDialogs(client: BaseTelegramClient, peers: MaybeArray): Promise { +export async function getPeerDialogs(client: ITelegramClient, peers: MaybeArray): Promise { if (!Array.isArray(peers)) peers = [peers] const res = await client.call({ diff --git a/packages/client/src/methods/dialogs/iter-dialogs.ts b/packages/core/src/highlevel/methods/dialogs/iter-dialogs.ts similarity index 97% rename from packages/client/src/methods/dialogs/iter-dialogs.ts rename to packages/core/src/highlevel/methods/dialogs/iter-dialogs.ts index 170754a3..2613068e 100644 --- a/packages/client/src/methods/dialogs/iter-dialogs.ts +++ b/packages/core/src/highlevel/methods/dialogs/iter-dialogs.ts @@ -1,5 +1,9 @@ -import { BaseTelegramClient, Long, MtUnsupportedError, tl } from '@mtcute/core' +import Long from 'long' +import { tl } from '@mtcute/tl' + +import { MtUnsupportedError } from '../../../types/errors.js' +import { ITelegramClient } from '../../client.types.js' import { Dialog, InputDialogFolder } from '../../types/index.js' import { normalizeDate } from '../../utils/misc-utils.js' import { _normalizeInputFolder } from './get-folders.js' @@ -15,7 +19,7 @@ import { _normalizeInputFolder } from './get-folders.js' * @param params Fetch parameters */ export async function* iterDialogs( - client: BaseTelegramClient, + client: ITelegramClient, params?: { /** * Offset message date used as an anchor for pagination. diff --git a/packages/client/src/methods/dialogs/set-folders-order.ts b/packages/core/src/highlevel/methods/dialogs/set-folders-order.ts similarity index 54% rename from packages/client/src/methods/dialogs/set-folders-order.ts rename to packages/core/src/highlevel/methods/dialogs/set-folders-order.ts index 9e1a39b3..4a78e3e6 100644 --- a/packages/client/src/methods/dialogs/set-folders-order.ts +++ b/packages/core/src/highlevel/methods/dialogs/set-folders-order.ts @@ -1,12 +1,12 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' /** * Reorder folders * * @param order New order of folders (folder IDs, where default = 0) */ -export async function setFoldersOrder(client: BaseTelegramClient, order: number[]): Promise { +export async function setFoldersOrder(client: ITelegramClient, order: number[]): Promise { const r = await client.call({ _: 'messages.updateDialogFiltersOrder', order, diff --git a/packages/client/src/methods/files/_platform.ts b/packages/core/src/highlevel/methods/files/_platform.ts similarity index 100% rename from packages/client/src/methods/files/_platform.ts rename to packages/core/src/highlevel/methods/files/_platform.ts diff --git a/packages/client/src/methods/files/_platform.web.ts b/packages/core/src/highlevel/methods/files/_platform.web.ts similarity index 88% rename from packages/client/src/methods/files/_platform.web.ts rename to packages/core/src/highlevel/methods/files/_platform.web.ts index 3fad2b9f..600b1cad 100644 --- a/packages/client/src/methods/files/_platform.web.ts +++ b/packages/core/src/highlevel/methods/files/_platform.web.ts @@ -1,4 +1,4 @@ -import { MtArgumentError } from '@mtcute/core' +import { MtArgumentError } from '../../../types/errors.js' /** @internal */ export function _createFileStream(): never { diff --git a/packages/client/src/methods/files/download-buffer.ts b/packages/core/src/highlevel/methods/files/download-buffer.ts similarity index 84% rename from packages/client/src/methods/files/download-buffer.ts rename to packages/core/src/highlevel/methods/files/download-buffer.ts index 85099991..9b1e0754 100644 --- a/packages/client/src/methods/files/download-buffer.ts +++ b/packages/core/src/highlevel/methods/files/download-buffer.ts @@ -1,6 +1,5 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { concatBuffers } from '@mtcute/core/utils.js' - +import { concatBuffers } from '../../../utils/buffer-utils.js' +import { ITelegramClient } from '../../client.types.js' import { FileDownloadLocation, FileDownloadParameters, FileLocation } from '../../types/index.js' import { downloadAsIterable } from './download-iterable.js' @@ -13,7 +12,7 @@ import { downloadAsIterable } from './download-iterable.js' * @param params File download parameters */ export async function downloadAsBuffer( - client: BaseTelegramClient, + client: ITelegramClient, location: FileDownloadLocation, params?: FileDownloadParameters, ): Promise { diff --git a/packages/client/src/methods/files/download-file.ts b/packages/core/src/highlevel/methods/files/download-file.ts similarity index 93% rename from packages/client/src/methods/files/download-file.ts rename to packages/core/src/highlevel/methods/files/download-file.ts index 331dd6a6..01c127e5 100644 --- a/packages/client/src/methods/files/download-file.ts +++ b/packages/core/src/highlevel/methods/files/download-file.ts @@ -2,8 +2,7 @@ import { createWriteStream, rmSync } from 'fs' import { writeFile } from 'fs/promises' -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { FileDownloadLocation, FileDownloadParameters, FileLocation } from '../../types/index.js' import { downloadAsIterable } from './download-iterable.js' @@ -15,7 +14,7 @@ import { downloadAsIterable } from './download-iterable.js' * @param params File download parameters */ export async function downloadToFile( - client: BaseTelegramClient, + client: ITelegramClient, filename: string, location: FileDownloadLocation, params?: FileDownloadParameters, diff --git a/packages/client/src/methods/files/download-file.web.ts b/packages/core/src/highlevel/methods/files/download-file.web.ts similarity index 66% rename from packages/client/src/methods/files/download-file.web.ts rename to packages/core/src/highlevel/methods/files/download-file.web.ts index 0855eca7..cdf92fed 100644 --- a/packages/client/src/methods/files/download-file.web.ts +++ b/packages/core/src/highlevel/methods/files/download-file.web.ts @@ -1,4 +1,4 @@ -import { MtUnsupportedError } from '@mtcute/core' +import { MtUnsupportedError } from '../../../types/errors.js' export function downloadToFile() { throw new MtUnsupportedError('Downloading to file is only supported in NodeJS') diff --git a/packages/client/src/methods/files/download-iterable.ts b/packages/core/src/highlevel/methods/files/download-iterable.ts similarity index 91% rename from packages/client/src/methods/files/download-iterable.ts rename to packages/core/src/highlevel/methods/files/download-iterable.ts index fcfcc34a..8f4302cb 100644 --- a/packages/client/src/methods/files/download-iterable.ts +++ b/packages/core/src/highlevel/methods/files/download-iterable.ts @@ -1,8 +1,12 @@ -import { BaseTelegramClient, ConnectionKind, MtArgumentError, MtUnsupportedError, tl } from '@mtcute/core' -import { ConditionVariable } from '@mtcute/core/utils.js' -import { fileIdToInputFileLocation, fileIdToInputWebFileLocation, parseFileId } from '@mtcute/file-id' +import { parseFileId } from '@mtcute/file-id' +import { tl } from '@mtcute/tl' +import { ConnectionKind } from '../../../network/network-manager.js' +import { MtArgumentError, MtUnsupportedError } from '../../../types/errors.js' +import { ConditionVariable } from '../../../utils/condition-variable.js' +import { ITelegramClient } from '../../client.types.js' import { FileDownloadLocation, FileDownloadParameters, FileLocation } from '../../types/index.js' +import { fileIdToInputFileLocation, fileIdToInputWebFileLocation } from '../../utils/convert-file-id.js' import { determinePartSize } from '../../utils/file-utils.js' // small files (less than 128 kb) are downloaded using the "downloadSmall" pool @@ -19,7 +23,7 @@ const REQUESTS_PER_CONNECTION = 3 // some arbitrary magic value that seems to wo * @param params Download parameters */ export async function* downloadAsIterable( - client: BaseTelegramClient, + client: ITelegramClient, input: FileDownloadLocation, params?: FileDownloadParameters, ): AsyncIterableIterator { @@ -64,7 +68,8 @@ export async function* downloadAsIterable( const isWeb = tl.isAnyInputWebFileLocation(location) // we will receive a FileMigrateError in case this is invalid - if (!dcId) dcId = client.network.getPrimaryDcId() + const primaryDcId = await client.getPrimaryDcId() + if (!dcId) dcId = primaryDcId const partSizeKb = params?.partSize ?? (fileSize ? determinePartSize(fileSize) : 64) @@ -88,11 +93,11 @@ export async function* downloadAsIterable( let connectionKind: ConnectionKind if (isSmall) { - connectionKind = dcId === client.network.getPrimaryDcId() ? 'main' : 'downloadSmall' + connectionKind = dcId === primaryDcId ? 'main' : 'downloadSmall' } else { connectionKind = 'download' } - const poolSize = client.network.getPoolSize(connectionKind, dcId) + const poolSize = await client.getPoolSize(connectionKind, dcId) client.log.debug( 'Downloading file of size %d from dc %d using %s connection pool (pool size: %d)', diff --git a/packages/client/src/methods/files/download-stream.ts b/packages/core/src/highlevel/methods/files/download-stream.ts similarity index 93% rename from packages/client/src/methods/files/download-stream.ts rename to packages/core/src/highlevel/methods/files/download-stream.ts index 5c5c52de..a0235f67 100644 --- a/packages/client/src/methods/files/download-stream.ts +++ b/packages/core/src/highlevel/methods/files/download-stream.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { FileDownloadLocation, FileDownloadParameters, FileLocation } from '../../types/index.js' import { bufferToStream } from '../../utils/stream-utils.js' import { downloadAsIterable } from './download-iterable.js' @@ -11,7 +10,7 @@ import { downloadAsIterable } from './download-iterable.js' * @param params File download parameters */ export function downloadAsStream( - client: BaseTelegramClient, + client: ITelegramClient, location: FileDownloadLocation, params?: FileDownloadParameters, ): ReadableStream { diff --git a/packages/client/src/methods/files/normalize-file-to-document.ts b/packages/core/src/highlevel/methods/files/normalize-file-to-document.ts similarity index 81% rename from packages/client/src/methods/files/normalize-file-to-document.ts rename to packages/core/src/highlevel/methods/files/normalize-file-to-document.ts index 3b49c420..a10ae664 100644 --- a/packages/client/src/methods/files/normalize-file-to-document.ts +++ b/packages/core/src/highlevel/methods/files/normalize-file-to-document.ts @@ -1,6 +1,7 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputFileLike } from '../../types/index.js' import { _normalizeInputMedia } from './normalize-input-media.js' @@ -8,7 +9,7 @@ import { _normalizeInputMedia } from './normalize-input-media.js' * @internal */ export async function _normalizeFileToDocument( - client: BaseTelegramClient, + client: ITelegramClient, file: InputFileLike | tl.TypeInputDocument, params: { progressCallback?: (uploaded: number, total: number) => void diff --git a/packages/client/src/methods/files/normalize-input-file.ts b/packages/core/src/highlevel/methods/files/normalize-input-file.ts similarity index 88% rename from packages/client/src/methods/files/normalize-input-file.ts rename to packages/core/src/highlevel/methods/files/normalize-input-file.ts index c3e35b9e..82b2a79a 100644 --- a/packages/client/src/methods/files/normalize-input-file.ts +++ b/packages/core/src/highlevel/methods/files/normalize-input-file.ts @@ -1,6 +1,8 @@ -import { BaseTelegramClient, MtArgumentError, tl } from '@mtcute/core' import { tdFileId } from '@mtcute/file-id' +import { tl } from '@mtcute/tl' +import { MtArgumentError } from '../../../types/errors.js' +import { ITelegramClient } from '../../client.types.js' import { InputFileLike, isUploadedFile } from '../../types/files/index.js' import { uploadFile } from './upload-file.js' @@ -9,7 +11,7 @@ import { uploadFile } from './upload-file.js' * uploading it if needed. */ export async function _normalizeInputFile( - client: BaseTelegramClient, + client: ITelegramClient, input: InputFileLike, params: { progressCallback?: (uploaded: number, total: number) => void diff --git a/packages/client/src/methods/files/normalize-input-media.ts b/packages/core/src/highlevel/methods/files/normalize-input-media.ts similarity index 97% rename from packages/client/src/methods/files/normalize-input-media.ts rename to packages/core/src/highlevel/methods/files/normalize-input-media.ts index 93747f42..0e411c21 100644 --- a/packages/client/src/methods/files/normalize-input-media.ts +++ b/packages/core/src/highlevel/methods/files/normalize-input-media.ts @@ -1,10 +1,14 @@ -import { BaseTelegramClient, Long, tl } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' -import { fileIdToInputDocument, fileIdToInputPhoto, parseFileId, tdFileId } from '@mtcute/file-id' +import Long from 'long' +import { parseFileId, tdFileId } from '@mtcute/file-id' +import { tl } from '@mtcute/tl' + +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { isUploadedFile } from '../../types/files/uploaded-file.js' import { UploadFileLike } from '../../types/files/utils.js' import { InputMediaLike } from '../../types/media/input-media.js' +import { fileIdToInputDocument, fileIdToInputPhoto } from '../../utils/convert-file-id.js' import { extractFileName } from '../../utils/file-utils.js' import { normalizeDate } from '../../utils/misc-utils.js' import { encodeWaveform } from '../../utils/voice-utils.js' @@ -18,7 +22,7 @@ import { uploadFile } from './upload-file.js' * uploading the file if needed. */ export async function _normalizeInputMedia( - client: BaseTelegramClient, + client: ITelegramClient, media: InputMediaLike, params: { progressCallback?: (uploaded: number, total: number) => void diff --git a/packages/client/src/methods/files/upload-file.ts b/packages/core/src/highlevel/methods/files/upload-file.ts similarity index 96% rename from packages/client/src/methods/files/upload-file.ts rename to packages/core/src/highlevel/methods/files/upload-file.ts index 19ed541f..8a98e898 100644 --- a/packages/client/src/methods/files/upload-file.ts +++ b/packages/core/src/highlevel/methods/files/upload-file.ts @@ -1,6 +1,8 @@ -import { BaseTelegramClient, MtArgumentError, tl } from '@mtcute/core' -import { randomLong } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { MtArgumentError } from '../../../types/errors.js' +import { randomLong } from '../../../utils/long-utils.js' +import { ITelegramClient } from '../../client.types.js' import { UploadedFile, UploadFileLike } from '../../types/index.js' import { guessFileMime } from '../../utils/file-type.js' import { determinePartSize, isProbablyPlainText } from '../../utils/file-utils.js' @@ -31,7 +33,7 @@ const MAX_PART_COUNT_PREMIUM = 8000 // 512 kb * 8000 = 4000 MiB * @param params Upload parameters */ export async function uploadFile( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** * Upload file source. @@ -194,7 +196,7 @@ export async function uploadFile( const partSize = partSizeKb * 1024 let partCount = fileSize === -1 ? -1 : ~~((fileSize + partSize - 1) / partSize) - const maxPartCount = client.network.params.isPremium ? MAX_PART_COUNT_PREMIUM : MAX_PART_COUNT + const maxPartCount = client.storage.self.getCached()?.isPremium ? MAX_PART_COUNT_PREMIUM : MAX_PART_COUNT if (partCount > maxPartCount) { throw new MtArgumentError(`File is too large (max ${maxPartCount} parts, got ${partCount})`) @@ -204,7 +206,7 @@ export async function uploadFile( const isSmall = fileSize !== -1 && fileSize < SMALL_FILE_MAX_SIZE const connectionKind = isSmall ? 'main' : 'upload' // streamed uploads must be serialized, otherwise we'll get FILE_PART_SIZE_INVALID - const connectionPoolSize = Math.min(client.network.getPoolSize(connectionKind), partCount) + const connectionPoolSize = Math.min(await client.getPoolSize(connectionKind), partCount) const requestsPerConnection = params.requestsPerConnection ?? REQUESTS_PER_CONNECTION client.log.debug( diff --git a/packages/client/src/methods/files/upload-media.ts b/packages/core/src/highlevel/methods/files/upload-media.ts similarity index 90% rename from packages/client/src/methods/files/upload-media.ts rename to packages/core/src/highlevel/methods/files/upload-media.ts index d1842d87..f72c2bdd 100644 --- a/packages/client/src/methods/files/upload-media.ts +++ b/packages/core/src/highlevel/methods/files/upload-media.ts @@ -1,6 +1,7 @@ -import { assertNever, BaseTelegramClient, MtArgumentError } from '@mtcute/core' -import { assertTypeIs, assertTypeIsNot } from '@mtcute/core/utils.js' - +import { MtArgumentError } from '../../../types/errors.js' +import { assertNever } from '../../../types/utils.js' +import { assertTypeIs, assertTypeIsNot } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputMediaLike, InputPeerLike, MessageMedia, Photo, RawDocument } from '../../types/index.js' import { parseDocument } from '../../types/media/document-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -18,7 +19,7 @@ import { _normalizeInputMedia } from './normalize-input-media.js' * @param params Upload parameters */ export async function uploadMedia( - client: BaseTelegramClient, + client: ITelegramClient, media: InputMediaLike, params: { /** diff --git a/packages/client/src/methods/forums/create-forum-topic.ts b/packages/core/src/highlevel/methods/forums/create-forum-topic.ts similarity index 90% rename from packages/client/src/methods/forums/create-forum-topic.ts rename to packages/core/src/highlevel/methods/forums/create-forum-topic.ts index 8c5b138b..16ab1c2d 100644 --- a/packages/client/src/methods/forums/create-forum-topic.ts +++ b/packages/core/src/highlevel/methods/forums/create-forum-topic.ts @@ -1,6 +1,7 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' -import { randomLong } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { randomLong } from '../../../utils/long-utils.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, Message } from '../../types/index.js' import { toInputChannel } from '../../utils/peer-utils.js' import { _findMessageInUpdate } from '../messages/find-in-update.js' @@ -14,7 +15,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @returns Service message for the created topic */ export async function createForumTopic( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Chat ID or username */ chatId: InputPeerLike diff --git a/packages/client/src/methods/forums/delete-forum-topic-history.ts b/packages/core/src/highlevel/methods/forums/delete-forum-topic-history.ts similarity index 73% rename from packages/client/src/methods/forums/delete-forum-topic-history.ts rename to packages/core/src/highlevel/methods/forums/delete-forum-topic-history.ts index aeb686b3..e6634103 100644 --- a/packages/client/src/methods/forums/delete-forum-topic-history.ts +++ b/packages/core/src/highlevel/methods/forums/delete-forum-topic-history.ts @@ -1,9 +1,8 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { assertTypeIsNot } from '@mtcute/core/utils.js' - +import { assertTypeIsNot } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import type { ForumTopic, InputPeerLike } from '../../types/index.js' +import { createDummyUpdate } from '../../updates/utils.js' import { toInputChannel } from '../../utils/peer-utils.js' -import { createDummyUpdate } from '../../utils/updates-utils.js' import { resolvePeer } from '../users/resolve-peer.js' /** @@ -13,7 +12,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param topicId ID of the topic (i.e. its top message ID) */ export async function deleteForumTopicHistory( - client: BaseTelegramClient, + client: ITelegramClient, chat: InputPeerLike, topicId: number | ForumTopic, ): Promise { @@ -26,5 +25,5 @@ export async function deleteForumTopicHistory( topMsgId: typeof topicId === 'number' ? topicId : topicId.id, }) - client.network.handleUpdate(createDummyUpdate(res.pts, res.ptsCount, channel.channelId)) + client.handleClientUpdate(createDummyUpdate(res.pts, res.ptsCount, channel.channelId)) } diff --git a/packages/client/src/methods/forums/edit-forum-topic.ts b/packages/core/src/highlevel/methods/forums/edit-forum-topic.ts similarity index 92% rename from packages/client/src/methods/forums/edit-forum-topic.ts rename to packages/core/src/highlevel/methods/forums/edit-forum-topic.ts index 60f8889d..3504caf3 100644 --- a/packages/client/src/methods/forums/edit-forum-topic.ts +++ b/packages/core/src/highlevel/methods/forums/edit-forum-topic.ts @@ -1,5 +1,8 @@ -import { BaseTelegramClient, Long, tl } from '@mtcute/core' +import Long from 'long' +import { tl } from '@mtcute/tl' + +import { ITelegramClient } from '../../client.types.js' import type { ForumTopic, InputPeerLike, Message } from '../../types/index.js' import { toInputChannel } from '../../utils/peer-utils.js' import { _findMessageInUpdate } from '../messages/find-in-update.js' @@ -15,7 +18,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @returns Service message about the modification */ export async function editForumTopic( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Chat ID or username */ chatId: InputPeerLike diff --git a/packages/client/src/methods/forums/get-forum-topics-by-id.ts b/packages/core/src/highlevel/methods/forums/get-forum-topics-by-id.ts similarity index 82% rename from packages/client/src/methods/forums/get-forum-topics-by-id.ts rename to packages/core/src/highlevel/methods/forums/get-forum-topics-by-id.ts index 01bbb15f..32cb8ee9 100644 --- a/packages/client/src/methods/forums/get-forum-topics-by-id.ts +++ b/packages/core/src/highlevel/methods/forums/get-forum-topics-by-id.ts @@ -1,5 +1,5 @@ -import { BaseTelegramClient, MaybeArray } from '@mtcute/core' - +import { MaybeArray } from '../../../types/utils.js' +import { ITelegramClient } from '../../client.types.js' import { ForumTopic, InputPeerLike } from '../../types/index.js' import { toInputChannel } from '../../utils/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -10,7 +10,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param chatId Chat ID or username */ export async function getForumTopicsById( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, ids: MaybeArray, ): Promise { diff --git a/packages/client/src/methods/forums/get-forum-topics.ts b/packages/core/src/highlevel/methods/forums/get-forum-topics.ts similarity index 95% rename from packages/client/src/methods/forums/get-forum-topics.ts rename to packages/core/src/highlevel/methods/forums/get-forum-topics.ts index f2e870eb..e0018bea 100644 --- a/packages/client/src/methods/forums/get-forum-topics.ts +++ b/packages/core/src/highlevel/methods/forums/get-forum-topics.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { ArrayPaginated, ForumTopic, InputPeerLike } from '../../types/index.js' import { makeArrayPaginated } from '../../utils/index.js' import { toInputChannel } from '../../utils/peer-utils.js' @@ -24,7 +23,7 @@ const defaultOffset: GetForumTopicsOffset = { * @param chatId Chat ID or username */ export async function getForumTopics( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, params?: { /** diff --git a/packages/client/src/methods/forums/iter-forum-topics.ts b/packages/core/src/highlevel/methods/forums/iter-forum-topics.ts similarity index 94% rename from packages/client/src/methods/forums/iter-forum-topics.ts rename to packages/core/src/highlevel/methods/forums/iter-forum-topics.ts index ee8db2d9..28d83634 100644 --- a/packages/client/src/methods/forums/iter-forum-topics.ts +++ b/packages/core/src/highlevel/methods/forums/iter-forum-topics.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { ForumTopic, InputPeerLike } from '../../types/index.js' import { toInputChannel } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -11,7 +10,7 @@ import { getForumTopics } from './get-forum-topics.js' * @param chatId Chat ID or username */ export async function* iterForumTopics( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, params?: Parameters[2] & { /** diff --git a/packages/client/src/methods/forums/reorder-pinned-forum-topics.ts b/packages/core/src/highlevel/methods/forums/reorder-pinned-forum-topics.ts similarity index 91% rename from packages/client/src/methods/forums/reorder-pinned-forum-topics.ts rename to packages/core/src/highlevel/methods/forums/reorder-pinned-forum-topics.ts index 73e0cc2c..4ee86846 100644 --- a/packages/client/src/methods/forums/reorder-pinned-forum-topics.ts +++ b/packages/core/src/highlevel/methods/forums/reorder-pinned-forum-topics.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import type { ForumTopic, InputPeerLike } from '../../types/index.js' import { toInputChannel } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -10,7 +9,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * Only admins with `manageTopics` permission can do this. */ export async function reorderPinnedForumTopics( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Chat ID or username */ chatId: InputPeerLike diff --git a/packages/client/src/methods/forums/toggle-forum-topic-closed.ts b/packages/core/src/highlevel/methods/forums/toggle-forum-topic-closed.ts similarity index 93% rename from packages/client/src/methods/forums/toggle-forum-topic-closed.ts rename to packages/core/src/highlevel/methods/forums/toggle-forum-topic-closed.ts index db0730ed..34f35935 100644 --- a/packages/client/src/methods/forums/toggle-forum-topic-closed.ts +++ b/packages/core/src/highlevel/methods/forums/toggle-forum-topic-closed.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import type { ForumTopic, InputPeerLike, Message } from '../../types/index.js' import { toInputChannel } from '../../utils/peer-utils.js' import { _findMessageInUpdate } from '../messages/find-in-update.js' @@ -13,7 +12,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @returns Service message about the modification */ export async function toggleForumTopicClosed( - client: BaseTelegramClient, + client: ITelegramClient, parmas: { /** Chat ID or username */ chatId: InputPeerLike diff --git a/packages/client/src/methods/forums/toggle-forum-topic-pinned.ts b/packages/core/src/highlevel/methods/forums/toggle-forum-topic-pinned.ts similarity index 91% rename from packages/client/src/methods/forums/toggle-forum-topic-pinned.ts rename to packages/core/src/highlevel/methods/forums/toggle-forum-topic-pinned.ts index 903bd856..42ef5ead 100644 --- a/packages/client/src/methods/forums/toggle-forum-topic-pinned.ts +++ b/packages/core/src/highlevel/methods/forums/toggle-forum-topic-pinned.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { ForumTopic, InputPeerLike } from '../../types/index.js' import { toInputChannel } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -10,7 +9,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * Only admins with `manageTopics` permission can do this. */ export async function toggleForumTopicPinned( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Chat ID or username */ chatId: InputPeerLike diff --git a/packages/client/src/methods/forums/toggle-forum.ts b/packages/core/src/highlevel/methods/forums/toggle-forum.ts similarity index 72% rename from packages/client/src/methods/forums/toggle-forum.ts rename to packages/core/src/highlevel/methods/forums/toggle-forum.ts index 186422d7..7d53a09e 100644 --- a/packages/client/src/methods/forums/toggle-forum.ts +++ b/packages/core/src/highlevel/methods/forums/toggle-forum.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { toInputChannel } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -12,11 +11,11 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param chatId Chat ID or username * @param enabled Whether the supergroup should be a forum */ -export async function toggleForum(client: BaseTelegramClient, chatId: InputPeerLike, enabled = false): Promise { +export async function toggleForum(client: ITelegramClient, chatId: InputPeerLike, enabled = false): Promise { const res = await client.call({ _: 'channels.toggleForum', channel: toInputChannel(await resolvePeer(client, chatId), chatId), enabled, }) - client.network.handleUpdate(res) + client.handleClientUpdate(res) } diff --git a/packages/client/src/methods/forums/toggle-general-topic-hidden.ts b/packages/core/src/highlevel/methods/forums/toggle-general-topic-hidden.ts similarity index 93% rename from packages/client/src/methods/forums/toggle-general-topic-hidden.ts rename to packages/core/src/highlevel/methods/forums/toggle-general-topic-hidden.ts index f7a1427b..d9f631eb 100644 --- a/packages/client/src/methods/forums/toggle-general-topic-hidden.ts +++ b/packages/core/src/highlevel/methods/forums/toggle-general-topic-hidden.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, Message } from '../../types/index.js' import { toInputChannel } from '../../utils/peer-utils.js' import { _findMessageInUpdate } from '../messages/find-in-update.js' @@ -13,7 +12,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @returns Service message about the modification */ export async function toggleGeneralTopicHidden( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Chat ID or username */ chatId: InputPeerLike diff --git a/packages/client/src/methods/invite-links/create-invite-link.ts b/packages/core/src/highlevel/methods/invite-links/create-invite-link.ts similarity index 94% rename from packages/client/src/methods/invite-links/create-invite-link.ts rename to packages/core/src/highlevel/methods/invite-links/create-invite-link.ts index 3efc4eef..0ac56e0b 100644 --- a/packages/client/src/methods/invite-links/create-invite-link.ts +++ b/packages/core/src/highlevel/methods/invite-links/create-invite-link.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { ChatInviteLink, InputPeerLike } from '../../types/index.js' import { normalizeDate } from '../../utils/misc-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -13,7 +12,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param params */ export async function createInviteLink( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, params?: { /** diff --git a/packages/client/src/methods/invite-links/edit-invite-link.ts b/packages/core/src/highlevel/methods/invite-links/edit-invite-link.ts similarity index 95% rename from packages/client/src/methods/invite-links/edit-invite-link.ts rename to packages/core/src/highlevel/methods/invite-links/edit-invite-link.ts index ea22269e..3df68628 100644 --- a/packages/client/src/methods/invite-links/edit-invite-link.ts +++ b/packages/core/src/highlevel/methods/invite-links/edit-invite-link.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { ChatInviteLink, InputPeerLike, PeersIndex } from '../../types/index.js' import { normalizeDate } from '../../utils/misc-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -16,7 +15,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @returns Modified invite link */ export async function editInviteLink( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Chat ID */ chatId: InputPeerLike diff --git a/packages/client/src/methods/invite-links/export-invite-link.ts b/packages/core/src/highlevel/methods/invite-links/export-invite-link.ts similarity index 76% rename from packages/client/src/methods/invite-links/export-invite-link.ts rename to packages/core/src/highlevel/methods/invite-links/export-invite-link.ts index ef63c1f8..209ad3fb 100644 --- a/packages/client/src/methods/invite-links/export-invite-link.ts +++ b/packages/core/src/highlevel/methods/invite-links/export-invite-link.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { ChatInviteLink, InputPeerLike } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -12,7 +11,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * * @param chatId Chat IDs */ -export async function exportInviteLink(client: BaseTelegramClient, chatId: InputPeerLike): Promise { +export async function exportInviteLink(client: ITelegramClient, chatId: InputPeerLike): Promise { const res = await client.call({ _: 'messages.exportChatInvite', peer: await resolvePeer(client, chatId), diff --git a/packages/client/src/methods/invite-links/get-invite-link-members.ts b/packages/core/src/highlevel/methods/invite-links/get-invite-link-members.ts similarity index 95% rename from packages/client/src/methods/invite-links/get-invite-link-members.ts rename to packages/core/src/highlevel/methods/invite-links/get-invite-link-members.ts index 0fd4acb7..5b4e473e 100644 --- a/packages/client/src/methods/invite-links/get-invite-link-members.ts +++ b/packages/core/src/highlevel/methods/invite-links/get-invite-link-members.ts @@ -1,5 +1,6 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { ArrayPaginated, ChatInviteLink, ChatInviteLinkMember, InputPeerLike, PeersIndex } from '../../types/index.js' import { makeArrayPaginated, normalizeDate, toInputUser } from '../../utils/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -12,7 +13,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param params Additional params */ export async function getInviteLinkMembers( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, params?: { /** diff --git a/packages/client/src/methods/invite-links/get-invite-link.ts b/packages/core/src/highlevel/methods/invite-links/get-invite-link.ts similarity index 87% rename from packages/client/src/methods/invite-links/get-invite-link.ts rename to packages/core/src/highlevel/methods/invite-links/get-invite-link.ts index 3485e78c..c7dfca86 100644 --- a/packages/client/src/methods/invite-links/get-invite-link.ts +++ b/packages/core/src/highlevel/methods/invite-links/get-invite-link.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { ChatInviteLink, InputPeerLike, PeersIndex } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -10,7 +9,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param link The invite link */ export async function getInviteLink( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, link: string, ): Promise { diff --git a/packages/client/src/methods/invite-links/get-invite-links.ts b/packages/core/src/highlevel/methods/invite-links/get-invite-links.ts similarity index 96% rename from packages/client/src/methods/invite-links/get-invite-links.ts rename to packages/core/src/highlevel/methods/invite-links/get-invite-links.ts index 58990952..97d327bd 100644 --- a/packages/client/src/methods/invite-links/get-invite-links.ts +++ b/packages/core/src/highlevel/methods/invite-links/get-invite-links.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { ArrayPaginated, ChatInviteLink, InputPeerLike, PeersIndex } from '../../types/index.js' import { makeArrayPaginated } from '../../utils/index.js' import { toInputUser } from '../../utils/peer-utils.js' @@ -23,7 +22,7 @@ export interface GetInviteLinksOffset { * @param params */ export async function getInviteLinks( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, params?: { /** diff --git a/packages/client/src/methods/invite-links/get-primary-invite-link.ts b/packages/core/src/highlevel/methods/invite-links/get-primary-invite-link.ts similarity index 79% rename from packages/client/src/methods/invite-links/get-primary-invite-link.ts rename to packages/core/src/highlevel/methods/invite-links/get-primary-invite-link.ts index c34d95a2..2100ffae 100644 --- a/packages/client/src/methods/invite-links/get-primary-invite-link.ts +++ b/packages/core/src/highlevel/methods/invite-links/get-primary-invite-link.ts @@ -1,5 +1,5 @@ -import { BaseTelegramClient, MtTypeAssertionError } from '@mtcute/core' - +import { MtTypeAssertionError } from '../../../types/errors.js' +import { ITelegramClient } from '../../client.types.js' import { ChatInviteLink, InputPeerLike, PeersIndex } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -8,7 +8,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * * @param chatId Chat ID */ -export async function getPrimaryInviteLink(client: BaseTelegramClient, chatId: InputPeerLike): Promise { +export async function getPrimaryInviteLink(client: ITelegramClient, chatId: InputPeerLike): Promise { const res = await client.call({ _: 'messages.getExportedChatInvites', peer: await resolvePeer(client, chatId), diff --git a/packages/client/src/methods/invite-links/hide-all-join-requests.ts b/packages/core/src/highlevel/methods/invite-links/hide-all-join-requests.ts similarity index 90% rename from packages/client/src/methods/invite-links/hide-all-join-requests.ts rename to packages/core/src/highlevel/methods/invite-links/hide-all-join-requests.ts index b1a479c1..ff765938 100644 --- a/packages/client/src/methods/invite-links/hide-all-join-requests.ts +++ b/packages/core/src/highlevel/methods/invite-links/hide-all-join-requests.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import type { ChatInviteLink, InputPeerLike } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -7,7 +6,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * Approve or decline multiple join requests to a chat. */ export async function hideAllJoinRequests( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Chat/channel ID */ chatId: InputPeerLike diff --git a/packages/client/src/methods/invite-links/hide-join-request.ts b/packages/core/src/highlevel/methods/invite-links/hide-join-request.ts similarity index 90% rename from packages/client/src/methods/invite-links/hide-join-request.ts rename to packages/core/src/highlevel/methods/invite-links/hide-join-request.ts index 0ee2029e..66257157 100644 --- a/packages/client/src/methods/invite-links/hide-join-request.ts +++ b/packages/core/src/highlevel/methods/invite-links/hide-join-request.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { toInputUser } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -8,7 +7,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * Approve or decline join request to a chat. */ export async function hideJoinRequest( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Chat/channel ID */ chatId: InputPeerLike diff --git a/packages/client/src/methods/invite-links/iter-invite-link-members.ts b/packages/core/src/highlevel/methods/invite-links/iter-invite-link-members.ts similarity index 95% rename from packages/client/src/methods/invite-links/iter-invite-link-members.ts rename to packages/core/src/highlevel/methods/invite-links/iter-invite-link-members.ts index d0edcfa4..a4213b38 100644 --- a/packages/client/src/methods/invite-links/iter-invite-link-members.ts +++ b/packages/core/src/highlevel/methods/invite-links/iter-invite-link-members.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { ChatInviteLinkMember, InputPeerLike } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' import { getInviteLinkMembers } from './get-invite-link-members.js' @@ -12,7 +11,7 @@ import { getInviteLinkMembers } from './get-invite-link-members.js' * @param params Additional params */ export async function* iterInviteLinkMembers( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, params?: Parameters[2] & { /** diff --git a/packages/client/src/methods/invite-links/iter-invite-links.ts b/packages/core/src/highlevel/methods/invite-links/iter-invite-links.ts similarity index 95% rename from packages/client/src/methods/invite-links/iter-invite-links.ts rename to packages/core/src/highlevel/methods/invite-links/iter-invite-links.ts index 7d3d92d7..96acb43c 100644 --- a/packages/client/src/methods/invite-links/iter-invite-links.ts +++ b/packages/core/src/highlevel/methods/invite-links/iter-invite-links.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { ChatInviteLink, InputPeerLike } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' import { getInviteLinks } from './get-invite-links.js' @@ -16,7 +15,7 @@ import { getInviteLinks } from './get-invite-links.js' * @param params */ export async function* iterInviteLinks( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, params?: Parameters[2] & { /** diff --git a/packages/client/src/methods/invite-links/revoke-invite-link.ts b/packages/core/src/highlevel/methods/invite-links/revoke-invite-link.ts similarity index 92% rename from packages/client/src/methods/invite-links/revoke-invite-link.ts rename to packages/core/src/highlevel/methods/invite-links/revoke-invite-link.ts index 14df0ef9..da9f5891 100644 --- a/packages/client/src/methods/invite-links/revoke-invite-link.ts +++ b/packages/core/src/highlevel/methods/invite-links/revoke-invite-link.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { ChatInviteLink, InputPeerLike, PeersIndex } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -14,7 +13,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @returns If `link` is a primary invite, newly generated invite link, otherwise the revoked link */ export async function revokeInviteLink( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, link: string | ChatInviteLink, ): Promise { diff --git a/packages/client/src/methods/messages/close-poll.ts b/packages/core/src/highlevel/methods/messages/close-poll.ts similarity index 79% rename from packages/client/src/methods/messages/close-poll.ts rename to packages/core/src/highlevel/methods/messages/close-poll.ts index bb0ead38..8518920f 100644 --- a/packages/client/src/methods/messages/close-poll.ts +++ b/packages/core/src/highlevel/methods/messages/close-poll.ts @@ -1,8 +1,10 @@ -import { BaseTelegramClient, Long, MtTypeAssertionError } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' +import Long from 'long' +import { MtTypeAssertionError } from '../../../types/errors.js' +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputMessageId, normalizeInputMessageId, PeersIndex, Poll } from '../../types/index.js' -import { assertIsUpdatesGroup } from '../../utils/updates-utils.js' +import { assertIsUpdatesGroup } from '../../updates/utils.js' import { resolvePeer } from '../users/resolve-peer.js' /** @@ -12,7 +14,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * will be able to vote in it */ export async function closePoll( - client: BaseTelegramClient, + client: ITelegramClient, params: InputMessageId & { /** * Whether to dispatch the edit message event @@ -41,7 +43,7 @@ export async function closePoll( assertIsUpdatesGroup('messages.editMessage', res) - client.network.handleUpdate(res, !params.shouldDispatch) + client.handleClientUpdate(res, !params.shouldDispatch) const upd = res.updates[0] assertTypeIs('messages.editMessage (@ .updates[0])', upd, 'updateMessagePoll') diff --git a/packages/client/src/methods/messages/delete-messages.ts b/packages/core/src/highlevel/methods/messages/delete-messages.ts similarity index 90% rename from packages/client/src/methods/messages/delete-messages.ts rename to packages/core/src/highlevel/methods/messages/delete-messages.ts index 98cb3093..cb4f8967 100644 --- a/packages/client/src/methods/messages/delete-messages.ts +++ b/packages/core/src/highlevel/methods/messages/delete-messages.ts @@ -1,8 +1,9 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, Message } from '../../types/index.js' +import { createDummyUpdate } from '../../updates/utils.js' import { isInputPeerChannel, toInputChannel } from '../../utils/peer-utils.js' -import { createDummyUpdate } from '../../utils/updates-utils.js' import { resolvePeer } from '../users/resolve-peer.js' import { deleteScheduledMessages } from './delete-scheduled-messages.js' @@ -24,7 +25,7 @@ export interface DeleteMessagesParams { * @param ids Message(s) ID(s) to delete. */ export async function deleteMessagesById( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, ids: number[], params?: DeleteMessagesParams, @@ -52,7 +53,7 @@ export async function deleteMessagesById( upd = createDummyUpdate(res.pts, res.ptsCount) } - client.network.handleUpdate(upd) + client.handleClientUpdate(upd) } /** @@ -61,7 +62,7 @@ export async function deleteMessagesById( * @param messages Message(s) to delete */ export async function deleteMessages( - client: BaseTelegramClient, + client: ITelegramClient, messages: Message[], params?: DeleteMessagesParams, ): Promise { diff --git a/packages/client/src/methods/messages/delete-scheduled-messages.ts b/packages/core/src/highlevel/methods/messages/delete-scheduled-messages.ts similarity index 82% rename from packages/client/src/methods/messages/delete-scheduled-messages.ts rename to packages/core/src/highlevel/methods/messages/delete-scheduled-messages.ts index 07ee3ca1..9a5d9961 100644 --- a/packages/client/src/methods/messages/delete-scheduled-messages.ts +++ b/packages/core/src/highlevel/methods/messages/delete-scheduled-messages.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -10,7 +9,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param ids Message(s) ID(s) to delete. */ export async function deleteScheduledMessages( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, ids: number[], ): Promise { @@ -22,5 +21,5 @@ export async function deleteScheduledMessages( id: ids, }) - client.network.handleUpdate(res) + client.handleClientUpdate(res) } diff --git a/packages/client/src/methods/messages/edit-inline-message.ts b/packages/core/src/highlevel/methods/messages/edit-inline-message.ts similarity index 96% rename from packages/client/src/methods/messages/edit-inline-message.ts rename to packages/core/src/highlevel/methods/messages/edit-inline-message.ts index e5870fa5..463a4ffc 100644 --- a/packages/client/src/methods/messages/edit-inline-message.ts +++ b/packages/core/src/highlevel/methods/messages/edit-inline-message.ts @@ -1,5 +1,6 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { BotKeyboard, InputMediaLike, InputText, ReplyMarkup } from '../../types/index.js' import { normalizeInlineId } from '../../utils/inline-utils.js' import { _normalizeInputMedia } from '../files/normalize-input-media.js' @@ -14,7 +15,7 @@ import { _normalizeInputText } from '../misc/normalize-text.js' * @param params */ export async function editInlineMessage( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** * Inline message ID, either as a TL object, or as a diff --git a/packages/client/src/methods/messages/edit-message.ts b/packages/core/src/highlevel/methods/messages/edit-message.ts similarity index 96% rename from packages/client/src/methods/messages/edit-message.ts rename to packages/core/src/highlevel/methods/messages/edit-message.ts index 9ed80c1b..57208451 100644 --- a/packages/client/src/methods/messages/edit-message.ts +++ b/packages/core/src/highlevel/methods/messages/edit-message.ts @@ -1,5 +1,6 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { BotKeyboard, InputMediaLike, @@ -22,7 +23,7 @@ import { _findMessageInUpdate } from './find-in-update.js' * @param params */ export async function editMessage( - client: BaseTelegramClient, + client: ITelegramClient, params: InputMessageId & { /** * New message text diff --git a/packages/client/src/methods/messages/find-in-update.ts b/packages/core/src/highlevel/methods/messages/find-in-update.ts similarity index 80% rename from packages/client/src/methods/messages/find-in-update.ts rename to packages/core/src/highlevel/methods/messages/find-in-update.ts index 3018f178..b49c2458 100644 --- a/packages/client/src/methods/messages/find-in-update.ts +++ b/packages/core/src/highlevel/methods/messages/find-in-update.ts @@ -1,16 +1,18 @@ /* eslint-disable max-params */ -import { BaseTelegramClient, MtTypeAssertionError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MtTypeAssertionError } from '../../../types/errors.js' +import { ITelegramClient } from '../../client.types.js' import { Message } from '../../types/messages/index.js' import { PeersIndex } from '../../types/peers/index.js' -import { assertIsUpdatesGroup } from '../../utils/updates-utils.js' +import { assertIsUpdatesGroup } from '../../updates/utils.js' /** * @internal * @noemit */ export function _findMessageInUpdate( - client: BaseTelegramClient, + client: ITelegramClient, res: tl.TypeUpdates, isEdit?: boolean, noDispatch?: boolean, @@ -21,7 +23,7 @@ export function _findMessageInUpdate( * @noemit */ export function _findMessageInUpdate( - client: BaseTelegramClient, + client: ITelegramClient, res: tl.TypeUpdates, isEdit?: boolean, noDispatch?: boolean, @@ -33,7 +35,7 @@ export function _findMessageInUpdate( * @noemit */ export function _findMessageInUpdate( - client: BaseTelegramClient, + client: ITelegramClient, res: tl.TypeUpdates, isEdit = false, noDispatch = true, @@ -41,7 +43,7 @@ export function _findMessageInUpdate( ): Message | null { assertIsUpdatesGroup('_findMessageInUpdate', res) - client.network.handleUpdate(res, noDispatch) + client.handleClientUpdate(res, noDispatch) for (const u of res.updates) { if ( diff --git a/packages/client/src/methods/messages/forward-messages.ts b/packages/core/src/highlevel/methods/messages/forward-messages.ts similarity index 92% rename from packages/client/src/methods/messages/forward-messages.ts rename to packages/core/src/highlevel/methods/messages/forward-messages.ts index 797ca44c..a108f586 100644 --- a/packages/client/src/methods/messages/forward-messages.ts +++ b/packages/core/src/highlevel/methods/messages/forward-messages.ts @@ -1,9 +1,9 @@ -import { BaseTelegramClient, MtArgumentError } from '@mtcute/core' -import { randomLong } from '@mtcute/core/utils.js' - +import { MtArgumentError } from '../../../types/errors.js' +import { randomLong } from '../../../utils/long-utils.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, Message, PeersIndex } from '../../types/index.js' +import { assertIsUpdatesGroup } from '../../updates/utils.js' import { normalizeDate } from '../../utils/misc-utils.js' -import { assertIsUpdatesGroup } from '../../utils/updates-utils.js' import { resolvePeer } from '../users/resolve-peer.js' // @exported @@ -74,7 +74,7 @@ export interface ForwardMessageOptions { * @returns Newly sent, forwarded messages in the destination chat. */ export async function forwardMessagesById( - client: BaseTelegramClient, + client: ITelegramClient, params: ForwardMessageOptions & { /** Source chat ID, username, phone, `"me"` or `"self"` */ fromChatId: InputPeerLike @@ -109,7 +109,7 @@ export async function forwardMessagesById( assertIsUpdatesGroup('messages.forwardMessages', res) - client.network.handleUpdate(res, !params.shouldDispatch) + client.handleClientUpdate(res, !params.shouldDispatch) const peers = PeersIndex.from(res) @@ -133,7 +133,7 @@ export async function forwardMessagesById( * > **Note**: all messages must be from the same chat. */ export async function forwardMessages( - client: BaseTelegramClient, + client: ITelegramClient, params: ForwardMessageOptions & { messages: Message[] }, diff --git a/packages/client/src/methods/messages/get-callback-query-message.ts b/packages/core/src/highlevel/methods/messages/get-callback-query-message.ts similarity index 91% rename from packages/client/src/methods/messages/get-callback-query-message.ts rename to packages/core/src/highlevel/methods/messages/get-callback-query-message.ts index 339c3266..83872c7f 100644 --- a/packages/client/src/methods/messages/get-callback-query-message.ts +++ b/packages/core/src/highlevel/methods/messages/get-callback-query-message.ts @@ -1,6 +1,7 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' -import { assertTypeIsNot } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { assertTypeIsNot } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { Message } from '../../types/messages/message.js' import { InputPeerLike, PeersIndex } from '../../types/peers/index.js' import type { CallbackQuery } from '../../types/updates/callback-query.js' @@ -13,7 +14,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * in the given callback query. */ export async function getCallbackQueryMessage( - client: BaseTelegramClient, + client: ITelegramClient, id: | CallbackQuery | tl.RawUpdateBotCallbackQuery diff --git a/packages/client/src/methods/messages/get-discussion-message.ts b/packages/core/src/highlevel/methods/messages/get-discussion-message.ts similarity index 93% rename from packages/client/src/methods/messages/get-discussion-message.ts rename to packages/core/src/highlevel/methods/messages/get-discussion-message.ts index 784c43b5..31a53b4e 100644 --- a/packages/client/src/methods/messages/get-discussion-message.ts +++ b/packages/core/src/highlevel/methods/messages/get-discussion-message.ts @@ -1,12 +1,13 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { InputMessageId, Message, normalizeInputMessageId } from '../../types/messages/index.js' import { InputPeerLike, PeersIndex } from '../../types/peers/index.js' import { resolvePeer } from '../users/resolve-peer.js' /** @internal */ export async function _getDiscussionMessage( - client: BaseTelegramClient, + client: ITelegramClient, peer: InputPeerLike, message: number, ): Promise<[tl.TypeInputPeer, number]> { @@ -53,7 +54,7 @@ export async function _getDiscussionMessage( * @param message ID of the channel post */ export async function getDiscussionMessage( - client: BaseTelegramClient, + client: ITelegramClient, params: InputMessageId, ): Promise { const { chatId, message } = normalizeInputMessageId(params) diff --git a/packages/client/src/methods/messages/get-history.ts b/packages/core/src/highlevel/methods/messages/get-history.ts similarity index 94% rename from packages/client/src/methods/messages/get-history.ts rename to packages/core/src/highlevel/methods/messages/get-history.ts index 71f25bdc..7c1ffbb1 100644 --- a/packages/client/src/methods/messages/get-history.ts +++ b/packages/core/src/highlevel/methods/messages/get-history.ts @@ -1,6 +1,9 @@ -import { BaseTelegramClient, Long, tl } from '@mtcute/core' -import { assertTypeIsNot } from '@mtcute/core/utils.js' +import Long from 'long' +import { tl } from '@mtcute/tl' + +import { assertTypeIsNot } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { ArrayPaginated, InputPeerLike, Message, PeersIndex } from '../../types/index.js' import { makeArrayPaginated } from '../../utils/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -23,7 +26,7 @@ const defaultOffset: GetHistoryOffset = { * @param params Additional fetch parameters */ export async function getHistory( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, params?: { /** diff --git a/packages/client/src/methods/messages/get-message-by-link.ts b/packages/core/src/highlevel/methods/messages/get-message-by-link.ts similarity index 67% rename from packages/client/src/methods/messages/get-message-by-link.ts rename to packages/core/src/highlevel/methods/messages/get-message-by-link.ts index 9500da95..5adb6415 100644 --- a/packages/client/src/methods/messages/get-message-by-link.ts +++ b/packages/core/src/highlevel/methods/messages/get-message-by-link.ts @@ -1,7 +1,8 @@ -import { BaseTelegramClient, MtArgumentError, toggleChannelIdMark } from '@mtcute/core' -import { links } from '@mtcute/core/utils.js' - -import { Message } from '../../index.js' +import { MtArgumentError } from '../../../types/errors.js' +import { links } from '../../../utils/links/index.js' +import { toggleChannelIdMark } from '../../../utils/peer-utils.js' +import { ITelegramClient } from '../../client.types.js' +import { Message } from '../../types/messages/index.js' import { resolvePeer } from '../users/resolve-peer.js' import { _getDiscussionMessage } from './get-discussion-message.js' import { getMessages } from './get-messages.js' @@ -9,7 +10,7 @@ import { getMessages } from './get-messages.js' /** * Given a message link (e.g. `t.me/durov/1`), fetch the relevant message. */ -export async function getMessageByLink(client: BaseTelegramClient, link: string): Promise { +export async function getMessageByLink(client: ITelegramClient, link: string): Promise { const parsed = links.message.parse(link) if (!parsed) { diff --git a/packages/client/src/methods/messages/get-message-group.ts b/packages/core/src/highlevel/methods/messages/get-message-group.ts similarity index 80% rename from packages/client/src/methods/messages/get-message-group.ts rename to packages/core/src/highlevel/methods/messages/get-message-group.ts index 13aed8d6..7ed26ec3 100644 --- a/packages/client/src/methods/messages/get-message-group.ts +++ b/packages/core/src/highlevel/methods/messages/get-message-group.ts @@ -1,6 +1,6 @@ -import { BaseTelegramClient, MtArgumentError } from '@mtcute/core' -import { isPresent } from '@mtcute/core/utils.js' - +import { MtArgumentError } from '../../../types/errors.js' +import { isPresent } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputMessageId, Message, normalizeInputMessageId } from '../../types/index.js' import { isInputPeerChannel } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -12,7 +12,7 @@ import { getMessages } from './get-messages.js' * @param chatId Chat ID * @param message ID of one of the messages in the group */ -export async function getMessageGroup(client: BaseTelegramClient, params: InputMessageId): Promise { +export async function getMessageGroup(client: ITelegramClient, params: InputMessageId): Promise { const { chatId, message } = normalizeInputMessageId(params) // awesome hack stolen from pyrogram diff --git a/packages/client/src/methods/messages/get-message-reactions.ts b/packages/core/src/highlevel/methods/messages/get-message-reactions.ts similarity index 87% rename from packages/client/src/methods/messages/get-message-reactions.ts rename to packages/core/src/highlevel/methods/messages/get-message-reactions.ts index 1f49d6d7..23ce59eb 100644 --- a/packages/client/src/methods/messages/get-message-reactions.ts +++ b/packages/core/src/highlevel/methods/messages/get-message-reactions.ts @@ -1,8 +1,8 @@ -import { BaseTelegramClient, getMarkedPeerId } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' - +import { getMarkedPeerId } from '../../../utils/peer-utils.js' +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, Message, MessageReactions, PeersIndex } from '../../types/index.js' -import { assertIsUpdatesGroup } from '../../utils/updates-utils.js' +import { assertIsUpdatesGroup } from '../../updates/utils.js' import { resolvePeer } from '../users/resolve-peer.js' /** @@ -17,7 +17,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @returns Reactions to corresponding messages, or `null` if there are none */ export async function getMessageReactionsById( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, messages: number[], ): Promise<(MessageReactions | null)[]> { @@ -62,7 +62,7 @@ export async function getMessageReactionsById( * @returns Reactions to corresponding messages, or `null` if there are none */ export async function getMessageReactions( - client: BaseTelegramClient, + client: ITelegramClient, messages: Message[], ): Promise<(MessageReactions | null)[]> { return getMessageReactionsById( diff --git a/packages/client/src/methods/messages/get-messages-unsafe.ts b/packages/core/src/highlevel/methods/messages/get-messages-unsafe.ts similarity index 84% rename from packages/client/src/methods/messages/get-messages-unsafe.ts rename to packages/core/src/highlevel/methods/messages/get-messages-unsafe.ts index a4542953..b69cf050 100644 --- a/packages/client/src/methods/messages/get-messages-unsafe.ts +++ b/packages/core/src/highlevel/methods/messages/get-messages-unsafe.ts @@ -1,6 +1,8 @@ -import { BaseTelegramClient, MaybeArray, tl } from '@mtcute/core' -import { assertTypeIsNot } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { MaybeArray } from '../../../types/utils.js' +import { assertTypeIsNot } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { Message, PeersIndex } from '../../types/index.js' /** @@ -19,7 +21,7 @@ import { Message, PeersIndex } from '../../types/index.js' * (i.e. `getMessages(msg.chat.id, msg.id, true).id === msg.replyToMessageId`) */ export async function getMessagesUnsafe( - client: BaseTelegramClient, + client: ITelegramClient, messageIds: MaybeArray, fromReply = false, ): Promise<(Message | null)[]> { diff --git a/packages/client/src/methods/messages/get-messages.ts b/packages/core/src/highlevel/methods/messages/get-messages.ts similarity index 92% rename from packages/client/src/methods/messages/get-messages.ts rename to packages/core/src/highlevel/methods/messages/get-messages.ts index d5df1c19..969a0ca1 100644 --- a/packages/client/src/methods/messages/get-messages.ts +++ b/packages/core/src/highlevel/methods/messages/get-messages.ts @@ -1,6 +1,8 @@ -import { BaseTelegramClient, MaybeArray, tl } from '@mtcute/core' -import { assertTypeIsNot } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { MaybeArray } from '../../../types/utils.js' +import { assertTypeIsNot } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { Message } from '../../types/messages/index.js' import { InputPeerLike, PeersIndex } from '../../types/peers/index.js' import { isInputPeerChannel, toInputChannel } from '../../utils/peer-utils.js' @@ -20,7 +22,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * (i.e. `getMessages(msg.chat.id, msg.id, true).id === msg.replyToMessageId`) */ export async function getMessages( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, messageIds: MaybeArray, fromReply = false, diff --git a/packages/client/src/methods/messages/get-reaction-users.ts b/packages/core/src/highlevel/methods/messages/get-reaction-users.ts similarity index 94% rename from packages/client/src/methods/messages/get-reaction-users.ts rename to packages/core/src/highlevel/methods/messages/get-reaction-users.ts index 2f23eb45..4c7753dd 100644 --- a/packages/client/src/methods/messages/get-reaction-users.ts +++ b/packages/core/src/highlevel/methods/messages/get-reaction-users.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { ArrayPaginated, InputMessageId, @@ -21,7 +20,7 @@ export type GetReactionUsersOffset = string * @param params */ export async function getReactionUsers( - client: BaseTelegramClient, + client: ITelegramClient, params: InputMessageId & { /** * Get only reactions with the specified emoji diff --git a/packages/client/src/methods/messages/get-reply-to.ts b/packages/core/src/highlevel/methods/messages/get-reply-to.ts similarity index 82% rename from packages/client/src/methods/messages/get-reply-to.ts rename to packages/core/src/highlevel/methods/messages/get-reply-to.ts index bf107dd5..caac3236 100644 --- a/packages/client/src/methods/messages/get-reply-to.ts +++ b/packages/core/src/highlevel/methods/messages/get-reply-to.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { Message } from '../../types/messages/index.js' import { getMessages } from './get-messages.js' import { getMessagesUnsafe } from './get-messages-unsafe.js' @@ -11,7 +10,7 @@ import { getMessagesUnsafe } from './get-messages-unsafe.js' * the message itself may have been deleted, in which case * this method will also return `null`. */ -export async function getReplyTo(client: BaseTelegramClient, message: Message): Promise { +export async function getReplyTo(client: ITelegramClient, message: Message): Promise { if (!message.replyToMessage?.id) { return null } diff --git a/packages/client/src/methods/messages/get-scheduled-messages.ts b/packages/core/src/highlevel/methods/messages/get-scheduled-messages.ts similarity index 83% rename from packages/client/src/methods/messages/get-scheduled-messages.ts rename to packages/core/src/highlevel/methods/messages/get-scheduled-messages.ts index 17e6d1e7..24f6df23 100644 --- a/packages/client/src/methods/messages/get-scheduled-messages.ts +++ b/packages/core/src/highlevel/methods/messages/get-scheduled-messages.ts @@ -1,6 +1,6 @@ -import { BaseTelegramClient, MaybeArray } from '@mtcute/core' -import { assertTypeIsNot } from '@mtcute/core/utils.js' - +import { MaybeArray } from '../../../types/utils.js' +import { assertTypeIsNot } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, Message, PeersIndex } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -14,7 +14,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param messageIds Scheduled messages IDs */ export async function getScheduledMessages( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, messageIds: MaybeArray, ): Promise<(Message | null)[]> { diff --git a/packages/client/src/methods/messages/iter-history.ts b/packages/core/src/highlevel/methods/messages/iter-history.ts similarity index 94% rename from packages/client/src/methods/messages/iter-history.ts rename to packages/core/src/highlevel/methods/messages/iter-history.ts index 988f6fa7..6bb80793 100644 --- a/packages/client/src/methods/messages/iter-history.ts +++ b/packages/core/src/highlevel/methods/messages/iter-history.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, Message } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' import { getHistory } from './get-history.js' @@ -11,7 +10,7 @@ import { getHistory } from './get-history.js' * @param params Additional fetch parameters */ export async function* iterHistory( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, params?: Parameters[2] & { /** diff --git a/packages/client/src/methods/messages/iter-reaction-users.ts b/packages/core/src/highlevel/methods/messages/iter-reaction-users.ts similarity index 94% rename from packages/client/src/methods/messages/iter-reaction-users.ts rename to packages/core/src/highlevel/methods/messages/iter-reaction-users.ts index c30c3c02..7bb8c556 100644 --- a/packages/client/src/methods/messages/iter-reaction-users.ts +++ b/packages/core/src/highlevel/methods/messages/iter-reaction-users.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { normalizeInputMessageId, normalizeInputReaction, PeerReaction } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' import { getReactionUsers } from './get-reaction-users.js' @@ -14,7 +13,7 @@ import { getReactionUsers } from './get-reaction-users.js' * @param params */ export async function* iterReactionUsers( - client: BaseTelegramClient, + client: ITelegramClient, params: Parameters[1] & { /** * Limit the number of events returned. diff --git a/packages/client/src/methods/messages/iter-search-global.ts b/packages/core/src/highlevel/methods/messages/iter-search-global.ts similarity index 95% rename from packages/client/src/methods/messages/iter-search-global.ts rename to packages/core/src/highlevel/methods/messages/iter-search-global.ts index 7f63a1d1..55ee704a 100644 --- a/packages/client/src/methods/messages/iter-search-global.ts +++ b/packages/core/src/highlevel/methods/messages/iter-search-global.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { Message, SearchFilters } from '../../types/index.js' import { normalizeDate } from '../../utils/index.js' import { searchGlobal } from './search-global.js' @@ -14,7 +13,7 @@ import { searchGlobal } from './search-global.js' * @param params Search parameters */ export async function* iterSearchGlobal( - client: BaseTelegramClient, + client: ITelegramClient, params?: Parameters[1] & { /** * Limits the number of messages to be retrieved. diff --git a/packages/client/src/methods/messages/iter-search-messages.ts b/packages/core/src/highlevel/methods/messages/iter-search-messages.ts similarity index 96% rename from packages/client/src/methods/messages/iter-search-messages.ts rename to packages/core/src/highlevel/methods/messages/iter-search-messages.ts index b61c2799..cc5dd005 100644 --- a/packages/client/src/methods/messages/iter-search-messages.ts +++ b/packages/core/src/highlevel/methods/messages/iter-search-messages.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { Message, SearchFilters } from '../../types/index.js' import { normalizeDate } from '../../utils/misc-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -14,7 +13,7 @@ import { searchMessages } from './search-messages.js' * @param params Additional search parameters */ export async function* iterSearchMessages( - client: BaseTelegramClient, + client: ITelegramClient, params?: Parameters[1] & { /** * Limits the number of messages to be retrieved. diff --git a/packages/client/src/methods/messages/pin-message.ts b/packages/core/src/highlevel/methods/messages/pin-message.ts similarity index 94% rename from packages/client/src/methods/messages/pin-message.ts rename to packages/core/src/highlevel/methods/messages/pin-message.ts index 2e92840b..da8e15b1 100644 --- a/packages/client/src/methods/messages/pin-message.ts +++ b/packages/core/src/highlevel/methods/messages/pin-message.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputMessageId, Message, normalizeInputMessageId } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' import { _findMessageInUpdate } from './find-in-update.js' @@ -13,7 +12,7 @@ import { _findMessageInUpdate } from './find-in-update.js' * @returns Service message about pinned message, if one was generated. */ export async function pinMessage( - client: BaseTelegramClient, + client: ITelegramClient, params: InputMessageId & { /** Whether to send a notification (only for legacy groups and supergroups) */ notify?: boolean diff --git a/packages/client/src/methods/messages/read-history.ts b/packages/core/src/highlevel/methods/messages/read-history.ts similarity index 74% rename from packages/client/src/methods/messages/read-history.ts rename to packages/core/src/highlevel/methods/messages/read-history.ts index 5fd1569b..09e3f042 100644 --- a/packages/client/src/methods/messages/read-history.ts +++ b/packages/core/src/highlevel/methods/messages/read-history.ts @@ -1,9 +1,8 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' - +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' +import { createDummyUpdate } from '../../updates/utils.js' import { isInputPeerChannel, toInputChannel } from '../../utils/peer-utils.js' -import { createDummyUpdate } from '../../utils/updates-utils.js' import { resolvePeer } from '../users/resolve-peer.js' /** @@ -12,7 +11,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param chatId Chat ID */ export async function readHistory( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, params?: { /** @@ -39,9 +38,9 @@ export async function readHistory( }) if (isInputPeerChannel(peer)) { - client.network.handleUpdate(createDummyUpdate(res.pts, res.ptsCount, peer.channelId)) + client.handleClientUpdate(createDummyUpdate(res.pts, res.ptsCount, peer.channelId)) } else { - client.network.handleUpdate(createDummyUpdate(res.pts, res.ptsCount)) + client.handleClientUpdate(createDummyUpdate(res.pts, res.ptsCount)) } } @@ -59,6 +58,6 @@ export async function readHistory( peer, maxId, }) - client.network.handleUpdate(createDummyUpdate(res.pts, res.ptsCount)) + client.handleClientUpdate(createDummyUpdate(res.pts, res.ptsCount)) } } diff --git a/packages/client/src/methods/messages/read-reactions.ts b/packages/core/src/highlevel/methods/messages/read-reactions.ts similarity index 51% rename from packages/client/src/methods/messages/read-reactions.ts rename to packages/core/src/highlevel/methods/messages/read-reactions.ts index d0fbdd06..94f55430 100644 --- a/packages/client/src/methods/messages/read-reactions.ts +++ b/packages/core/src/highlevel/methods/messages/read-reactions.ts @@ -1,7 +1,6 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' -import { createDummyUpdate } from '../../utils/updates-utils.js' +import { createDummyUpdate } from '../../updates/utils.js' import { resolvePeer } from '../users/resolve-peer.js' /** @@ -9,10 +8,10 @@ import { resolvePeer } from '../users/resolve-peer.js' * * @param chatId Chat ID */ -export async function readReactions(client: BaseTelegramClient, chatId: InputPeerLike): Promise { +export async function readReactions(client: ITelegramClient, chatId: InputPeerLike): Promise { const res = await client.call({ _: 'messages.readReactions', peer: await resolvePeer(client, chatId), }) - client.network.handleUpdate(createDummyUpdate(res.pts, res.ptsCount)) + client.handleClientUpdate(createDummyUpdate(res.pts, res.ptsCount)) } diff --git a/packages/client/src/methods/messages/search-global.ts b/packages/core/src/highlevel/methods/messages/search-global.ts similarity index 93% rename from packages/client/src/methods/messages/search-global.ts rename to packages/core/src/highlevel/methods/messages/search-global.ts index d49d6009..db12fa5f 100644 --- a/packages/client/src/methods/messages/search-global.ts +++ b/packages/core/src/highlevel/methods/messages/search-global.ts @@ -1,6 +1,7 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' -import { assertTypeIsNot } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { assertTypeIsNot } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { ArrayPaginated, Message, PeersIndex, SearchFilters } from '../../types/index.js' import { makeArrayPaginated, normalizeDate } from '../../utils/index.js' @@ -25,7 +26,7 @@ const defaultOffset: SearchGlobalOffset = { * @param params Search parameters */ export async function searchGlobal( - client: BaseTelegramClient, + client: ITelegramClient, params?: { /** * Text query string. Use `"@"` to search for mentions. diff --git a/packages/client/src/methods/messages/search-messages.ts b/packages/core/src/highlevel/methods/messages/search-messages.ts similarity index 95% rename from packages/client/src/methods/messages/search-messages.ts rename to packages/core/src/highlevel/methods/messages/search-messages.ts index 592e2a5e..977909b6 100644 --- a/packages/client/src/methods/messages/search-messages.ts +++ b/packages/core/src/highlevel/methods/messages/search-messages.ts @@ -1,6 +1,9 @@ -import { BaseTelegramClient, Long, tl } from '@mtcute/core' -import { assertTypeIsNot } from '@mtcute/core/utils.js' +import Long from 'long' +import { tl } from '@mtcute/tl' + +import { assertTypeIsNot } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { ArrayPaginated, InputPeerLike, Message, PeersIndex, SearchFilters } from '../../types/index.js' import { makeArrayPaginated, normalizeDate } from '../../utils/misc-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -15,7 +18,7 @@ export type SearchMessagesOffset = number * @param params Additional search parameters */ export async function searchMessages( - client: BaseTelegramClient, + client: ITelegramClient, params?: { /** * Text query string. Required for text-only messages, diff --git a/packages/client/src/methods/messages/send-answer.ts b/packages/core/src/highlevel/methods/messages/send-answer.ts similarity index 92% rename from packages/client/src/methods/messages/send-answer.ts rename to packages/core/src/highlevel/methods/messages/send-answer.ts index a7dfa3bd..1b63c147 100644 --- a/packages/client/src/methods/messages/send-answer.ts +++ b/packages/core/src/highlevel/methods/messages/send-answer.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { Message } from '../../types/messages/message.js' import { ParametersSkip2 } from '../../types/utils.js' import { sendMedia } from './send-media.js' @@ -8,7 +7,7 @@ import { sendText } from './send-text.js' /** Send a text to the same chat (and topic, if applicable) as a given message */ export function answerText( - client: BaseTelegramClient, + client: ITelegramClient, message: Message, ...params: ParametersSkip2 ): ReturnType { @@ -24,7 +23,7 @@ export function answerText( /** Send a media to the same chat (and topic, if applicable) as a given message */ export function answerMedia( - client: BaseTelegramClient, + client: ITelegramClient, message: Message, ...params: ParametersSkip2 ): ReturnType { @@ -40,7 +39,7 @@ export function answerMedia( /** Send a media group to the same chat (and topic, if applicable) as a given message */ export function answerMediaGroup( - client: BaseTelegramClient, + client: ITelegramClient, message: Message, ...params: ParametersSkip2 ): ReturnType { diff --git a/packages/client/src/methods/messages/send-comment.ts b/packages/core/src/highlevel/methods/messages/send-comment.ts similarity index 93% rename from packages/client/src/methods/messages/send-comment.ts rename to packages/core/src/highlevel/methods/messages/send-comment.ts index c1c378f4..e61dc532 100644 --- a/packages/client/src/methods/messages/send-comment.ts +++ b/packages/core/src/highlevel/methods/messages/send-comment.ts @@ -1,5 +1,5 @@ -import { BaseTelegramClient, MtArgumentError } from '@mtcute/core' - +import { MtArgumentError } from '../../../types/errors.js' +import { ITelegramClient } from '../../client.types.js' import { Message } from '../../types/messages/message.js' import { ParametersSkip2 } from '../../types/utils.js' import { sendMedia } from './send-media.js' @@ -18,7 +18,7 @@ import { sendText } from './send-text.js' * To check if a post has comments, use {@link Message#replies}.hasComments */ export function commentText( - client: BaseTelegramClient, + client: ITelegramClient, message: Message, ...params: ParametersSkip2 ): ReturnType { @@ -47,7 +47,7 @@ export function commentText( * To check if a post has comments, use {@link Message#replies}.hasComments */ export function commentMedia( - client: BaseTelegramClient, + client: ITelegramClient, message: Message, ...params: ParametersSkip2 ): ReturnType { @@ -76,7 +76,7 @@ export function commentMedia( * To check if a post has comments, use {@link Message#replies}.hasComments */ export function commentMediaGroup( - client: BaseTelegramClient, + client: ITelegramClient, message: Message, ...params: ParametersSkip2 ): ReturnType { diff --git a/packages/client/src/methods/messages/send-common.ts b/packages/core/src/highlevel/methods/messages/send-common.ts similarity index 95% rename from packages/client/src/methods/messages/send-common.ts rename to packages/core/src/highlevel/methods/messages/send-common.ts index f779d62e..6d22eed8 100644 --- a/packages/client/src/methods/messages/send-common.ts +++ b/packages/core/src/highlevel/methods/messages/send-common.ts @@ -1,5 +1,8 @@ -import { BaseTelegramClient, getMarkedPeerId, MtArgumentError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MtArgumentError } from '../../../types/errors.js' +import { getMarkedPeerId } from '../../../utils/peer-utils.js' +import { ITelegramClient } from '../../client.types.js' import { MtMessageNotFoundError } from '../../types/errors.js' import { Message } from '../../types/messages/message.js' import { TextWithEntities } from '../../types/misc/entities.js' @@ -109,7 +112,7 @@ export interface CommonSendParams { * @noemit */ export async function _processCommonSendParameters( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, params: CommonSendParams, ) { diff --git a/packages/client/src/methods/messages/send-copy-group.ts b/packages/core/src/highlevel/methods/messages/send-copy-group.ts similarity index 89% rename from packages/client/src/methods/messages/send-copy-group.ts rename to packages/core/src/highlevel/methods/messages/send-copy-group.ts index dfb4f175..6b295a4b 100644 --- a/packages/client/src/methods/messages/send-copy-group.ts +++ b/packages/core/src/highlevel/methods/messages/send-copy-group.ts @@ -1,6 +1,8 @@ -import { BaseTelegramClient, MtArgumentError, tl } from '@mtcute/core' -import { isPresent } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { MtArgumentError } from '../../../types/errors.js' +import { isPresent } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { Message } from '../../types/messages/message.js' import { InputPeerLike } from '../../types/peers/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -20,7 +22,7 @@ export interface SendCopyGroupParams extends CommonSendParams { * Note that all the provided messages must be in the same message group */ export async function sendCopyGroup( - client: BaseTelegramClient, + client: ITelegramClient, params: SendCopyGroupParams & ( | { diff --git a/packages/client/src/methods/messages/send-copy.ts b/packages/core/src/highlevel/methods/messages/send-copy.ts similarity index 92% rename from packages/client/src/methods/messages/send-copy.ts rename to packages/core/src/highlevel/methods/messages/send-copy.ts index 033b1eb1..d6ffd771 100644 --- a/packages/client/src/methods/messages/send-copy.ts +++ b/packages/core/src/highlevel/methods/messages/send-copy.ts @@ -1,5 +1,6 @@ -import { BaseTelegramClient, getMarkedPeerId, MtArgumentError } from '@mtcute/core' - +import { MtArgumentError } from '../../../types/errors.js' +import { getMarkedPeerId } from '../../../utils/peer-utils.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, InputText, Message, MtMessageNotFoundError, ReplyMarkup } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' import { getMessages } from './get-messages.js' @@ -33,7 +34,7 @@ export interface SendCopyParams extends CommonSendParams { * it can't be copied. */ export async function sendCopy( - client: BaseTelegramClient, + client: ITelegramClient, params: SendCopyParams & ( | { diff --git a/packages/client/src/methods/messages/send-media-group.ts b/packages/core/src/highlevel/methods/messages/send-media-group.ts similarity index 93% rename from packages/client/src/methods/messages/send-media-group.ts rename to packages/core/src/highlevel/methods/messages/send-media-group.ts index 18b748ff..8eab137e 100644 --- a/packages/client/src/methods/messages/send-media-group.ts +++ b/packages/core/src/highlevel/methods/messages/send-media-group.ts @@ -1,10 +1,11 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' -import { randomLong } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { randomLong } from '../../../utils/long-utils.js' +import { ITelegramClient } from '../../client.types.js' import { InputMediaLike } from '../../types/media/input-media.js' import { Message } from '../../types/messages/message.js' import { InputPeerLike, PeersIndex } from '../../types/peers/index.js' -import { assertIsUpdatesGroup } from '../../utils/updates-utils.js' +import { assertIsUpdatesGroup } from '../../updates/utils.js' import { _normalizeInputMedia } from '../files/normalize-input-media.js' import { _normalizeInputText } from '../misc/normalize-text.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -23,7 +24,7 @@ import { _processCommonSendParameters, CommonSendParams } from './send-common.js * @link InputMedia */ export async function sendMediaGroup( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, medias: (InputMediaLike | string)[], params?: CommonSendParams & { @@ -109,7 +110,7 @@ export async function sendMediaGroup( ) assertIsUpdatesGroup('sendMediaGroup', res) - client.network.handleUpdate(res, true) + client.handleClientUpdate(res) const peers = PeersIndex.from(res) diff --git a/packages/client/src/methods/messages/send-media.ts b/packages/core/src/highlevel/methods/messages/send-media.ts similarity index 96% rename from packages/client/src/methods/messages/send-media.ts rename to packages/core/src/highlevel/methods/messages/send-media.ts index b954117a..414ecdeb 100644 --- a/packages/client/src/methods/messages/send-media.ts +++ b/packages/core/src/highlevel/methods/messages/send-media.ts @@ -1,6 +1,5 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { randomLong } from '@mtcute/core/utils.js' - +import { randomLong } from '../../../utils/long-utils.js' +import { ITelegramClient } from '../../client.types.js' import { BotKeyboard, ReplyMarkup } from '../../types/bots/keyboards.js' import { InputMediaLike } from '../../types/media/input-media.js' import { Message } from '../../types/messages/message.js' @@ -25,7 +24,7 @@ import { _processCommonSendParameters, CommonSendParams } from './send-common.js * @link InputMedia */ export async function sendMedia( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, media: InputMediaLike | string, params?: CommonSendParams & { diff --git a/packages/client/src/methods/messages/send-quote.ts b/packages/core/src/highlevel/methods/messages/send-quote.ts similarity index 90% rename from packages/client/src/methods/messages/send-quote.ts rename to packages/core/src/highlevel/methods/messages/send-quote.ts index ce68e01d..caa4eb47 100644 --- a/packages/client/src/methods/messages/send-quote.ts +++ b/packages/core/src/highlevel/methods/messages/send-quote.ts @@ -1,7 +1,10 @@ -import { BaseTelegramClient, MtArgumentError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' -import { InputPeerLike, TextWithEntities } from '../../index.js' +import { MtArgumentError } from '../../../types/errors.js' +import { ITelegramClient } from '../../client.types.js' import { Message } from '../../types/messages/message.js' +import { TextWithEntities } from '../../types/misc/entities.js' +import { InputPeerLike } from '../../types/peers/peer.js' import { sendMedia } from './send-media.js' import { sendMediaGroup } from './send-media-group.js' import { sendText } from './send-text.js' @@ -56,7 +59,7 @@ function extractQuote(message: Message, from: number, to: number): TextWithEntit /** Send a text in reply to a given quote */ export function quoteWithText( - client: BaseTelegramClient, + client: ITelegramClient, message: Message, params: QuoteParamsFrom[3]> & { /** Text to send */ @@ -74,7 +77,7 @@ export function quoteWithText( /** Send a media in reply to a given quote */ export function quoteWithMedia( - client: BaseTelegramClient, + client: ITelegramClient, message: Message, params: QuoteParamsFrom[3]> & { /** Media to send */ @@ -92,7 +95,7 @@ export function quoteWithMedia( /** Send a media group in reply to a given quote */ export function quoteWithMediaGroup( - client: BaseTelegramClient, + client: ITelegramClient, message: Message, params: QuoteParamsFrom[3]> & { /** Media group to send */ diff --git a/packages/client/src/methods/messages/send-reaction.ts b/packages/core/src/highlevel/methods/messages/send-reaction.ts similarity index 90% rename from packages/client/src/methods/messages/send-reaction.ts rename to packages/core/src/highlevel/methods/messages/send-reaction.ts index 0af47a5b..c781b02e 100644 --- a/packages/client/src/methods/messages/send-reaction.ts +++ b/packages/core/src/highlevel/methods/messages/send-reaction.ts @@ -1,5 +1,5 @@ -import { BaseTelegramClient, MaybeArray } from '@mtcute/core' - +import { MaybeArray } from '../../../types/utils.js' +import { ITelegramClient } from '../../client.types.js' import { InputMessageId, InputReaction, @@ -7,7 +7,7 @@ import { normalizeInputMessageId, normalizeInputReaction, } from '../../types/index.js' -import { assertIsUpdatesGroup } from '../../utils/updates-utils.js' +import { assertIsUpdatesGroup } from '../../updates/utils.js' import { resolvePeer } from '../users/resolve-peer.js' import { _findMessageInUpdate } from './find-in-update.js' @@ -19,7 +19,7 @@ import { _findMessageInUpdate } from './find-in-update.js' * The message is normally available for users, but may not be available for bots in PMs. */ export async function sendReaction( - client: BaseTelegramClient, + client: ITelegramClient, params: InputMessageId & { /** Reaction emoji (or `null` to remove reaction) */ emoji?: MaybeArray | null diff --git a/packages/client/src/methods/messages/send-reply.ts b/packages/core/src/highlevel/methods/messages/send-reply.ts similarity index 89% rename from packages/client/src/methods/messages/send-reply.ts rename to packages/core/src/highlevel/methods/messages/send-reply.ts index 87348a6f..2f64b325 100644 --- a/packages/client/src/methods/messages/send-reply.ts +++ b/packages/core/src/highlevel/methods/messages/send-reply.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { Message } from '../../types/messages/message.js' import { ParametersSkip2 } from '../../types/utils.js' import { sendMedia } from './send-media.js' @@ -8,7 +7,7 @@ import { sendText } from './send-text.js' /** Send a text in reply to a given message */ export function replyText( - client: BaseTelegramClient, + client: ITelegramClient, message: Message, ...params: ParametersSkip2 ): ReturnType { @@ -20,7 +19,7 @@ export function replyText( /** Send a media in reply to a given message */ export function replyMedia( - client: BaseTelegramClient, + client: ITelegramClient, message: Message, ...params: ParametersSkip2 ): ReturnType { @@ -32,7 +31,7 @@ export function replyMedia( /** Send a media group in reply to a given message */ export function replyMediaGroup( - client: BaseTelegramClient, + client: ITelegramClient, message: Message, ...params: ParametersSkip2 ): ReturnType { diff --git a/packages/client/src/methods/messages/send-scheduled.ts b/packages/core/src/highlevel/methods/messages/send-scheduled.ts similarity index 80% rename from packages/client/src/methods/messages/send-scheduled.ts rename to packages/core/src/highlevel/methods/messages/send-scheduled.ts index 2cc1828d..3b3777a5 100644 --- a/packages/client/src/methods/messages/send-scheduled.ts +++ b/packages/core/src/highlevel/methods/messages/send-scheduled.ts @@ -1,7 +1,9 @@ -import { BaseTelegramClient, MaybeArray, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MaybeArray } from '../../../types/utils.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, Message, PeersIndex } from '../../types/index.js' -import { assertIsUpdatesGroup } from '../../utils/updates-utils.js' +import { assertIsUpdatesGroup } from '../../updates/utils.js' import { resolvePeer } from '../users/resolve-peer.js' /** @@ -15,7 +17,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param ids ID(s) of the messages */ export async function sendScheduled( - client: BaseTelegramClient, + client: ITelegramClient, peer: InputPeerLike, ids: MaybeArray, ): Promise { @@ -28,7 +30,7 @@ export async function sendScheduled( }) assertIsUpdatesGroup('sendScheduled', res) - client.network.handleUpdate(res, true) + client.handleClientUpdate(res, true) const peers = PeersIndex.from(res) diff --git a/packages/client/src/methods/messages/send-text.test.ts b/packages/core/src/highlevel/methods/messages/send-text.test.ts similarity index 92% rename from packages/client/src/methods/messages/send-text.test.ts rename to packages/core/src/highlevel/methods/messages/send-text.test.ts index c5f17fb7..7066817a 100644 --- a/packages/client/src/methods/messages/send-text.test.ts +++ b/packages/core/src/highlevel/methods/messages/send-text.test.ts @@ -1,8 +1,9 @@ +import Long from 'long' import { describe, expect, it, vi } from 'vitest' -import { Long, toggleChannelIdMark } from '@mtcute/core' import { createStub, StubTelegramClient } from '@mtcute/test' +import { toggleChannelIdMark } from '../../../utils/peer-utils.js' import { sendText } from './send-text.js' const stubUser = createStub('user', { @@ -102,7 +103,12 @@ describe('sendText', () => { it('should correctly handle updateShortSentMessage with cached peer', async () => { const client = new StubTelegramClient() - client.storage.self.store({ userId: stubUser.id, isBot: false }) + client.storage.self.store({ + userId: stubUser.id, + isBot: false, + isPremium: false, + usernames: [], + }) await client.registerPeers(stubUser) client.respondWith('messages.sendMessage', () => @@ -126,7 +132,12 @@ describe('sendText', () => { it('should correctly handle updateShortSentMessage without cached peer', async () => { const client = new StubTelegramClient() - client.storage.self.store({ userId: stubUser.id, isBot: false }) + client.storage.self.store({ + userId: stubUser.id, + isBot: false, + isPremium: false, + usernames: [], + }) const getUsersFn = client.respondWith( 'users.getUsers', diff --git a/packages/client/src/methods/messages/send-text.ts b/packages/core/src/highlevel/methods/messages/send-text.ts similarity index 91% rename from packages/client/src/methods/messages/send-text.ts rename to packages/core/src/highlevel/methods/messages/send-text.ts index 3e15ee23..daaf2092 100644 --- a/packages/client/src/methods/messages/send-text.ts +++ b/packages/core/src/highlevel/methods/messages/send-text.ts @@ -1,12 +1,15 @@ -import { BaseTelegramClient, getMarkedPeerId, MtTypeAssertionError, tl } from '@mtcute/core' -import { randomLong } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { MtTypeAssertionError } from '../../../types/errors.js' +import { randomLong } from '../../../utils/long-utils.js' +import { getMarkedPeerId } from '../../../utils/peer-utils.js' +import { ITelegramClient } from '../../client.types.js' import { BotKeyboard, ReplyMarkup } from '../../types/bots/keyboards.js' import { Message } from '../../types/messages/message.js' import { InputText } from '../../types/misc/entities.js' import { InputPeerLike, PeersIndex } from '../../types/peers/index.js' +import { createDummyUpdate } from '../../updates/utils.js' import { inputPeerToPeer } from '../../utils/peer-utils.js' -import { createDummyUpdate } from '../../utils/updates-utils.js' import { _getRawPeerBatched } from '../chats/batched-queries.js' import { _normalizeInputText } from '../misc/normalize-text.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -22,7 +25,7 @@ import { _processCommonSendParameters, CommonSendParams } from './send-common.js * @param params Additional sending parameters */ export async function sendText( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, text: InputText, params?: CommonSendParams & { @@ -89,7 +92,7 @@ export async function sendText( // is this needed? // this._date = res.date - client.network.handleUpdate(createDummyUpdate(res.pts, res.ptsCount)) + client.handleClientUpdate(createDummyUpdate(res.pts, res.ptsCount)) const peers = new PeersIndex() diff --git a/packages/client/src/methods/messages/send-typing.ts b/packages/core/src/highlevel/methods/messages/send-typing.ts similarity index 89% rename from packages/client/src/methods/messages/send-typing.ts rename to packages/core/src/highlevel/methods/messages/send-typing.ts index 3cb16fc4..5855f6d1 100644 --- a/packages/client/src/methods/messages/send-typing.ts +++ b/packages/core/src/highlevel/methods/messages/send-typing.ts @@ -1,6 +1,8 @@ -import { assertNever, BaseTelegramClient, tl } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { assertNever } from '../../../types/utils.js' +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, TypingStatus } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -17,9 +19,9 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param params */ export async function sendTyping( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, - status: TypingStatus | tl.TypeSendMessageAction = 'typing', + status: Exclude | tl.TypeSendMessageAction = 'typing', params?: { /** * For `upload_*` and history import actions, progress of the upload diff --git a/packages/client/src/methods/messages/send-vote.ts b/packages/core/src/highlevel/methods/messages/send-vote.ts similarity index 82% rename from packages/client/src/methods/messages/send-vote.ts rename to packages/core/src/highlevel/methods/messages/send-vote.ts index 0afcf767..4327b5ac 100644 --- a/packages/client/src/methods/messages/send-vote.ts +++ b/packages/core/src/highlevel/methods/messages/send-vote.ts @@ -1,8 +1,10 @@ -import { BaseTelegramClient, getMarkedPeerId, MaybeArray, MtArgumentError, MtTypeAssertionError } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' - +import { MtArgumentError, MtTypeAssertionError } from '../../../types/errors.js' +import { MaybeArray } from '../../../types/utils.js' +import { getMarkedPeerId } from '../../../utils/peer-utils.js' +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputMessageId, MtMessageNotFoundError, normalizeInputMessageId, PeersIndex, Poll } from '../../types/index.js' -import { assertIsUpdatesGroup } from '../../utils/updates-utils.js' +import { assertIsUpdatesGroup } from '../../updates/utils.js' import { resolvePeer } from '../users/resolve-peer.js' import { getMessages } from './get-messages.js' @@ -10,7 +12,7 @@ import { getMessages } from './get-messages.js' * Send or retract a vote in a poll. */ export async function sendVote( - client: BaseTelegramClient, + client: ITelegramClient, params: InputMessageId & { /** * Selected options, or `null` to retract. @@ -61,7 +63,7 @@ export async function sendVote( assertIsUpdatesGroup('messages.sendVote', res) - client.network.handleUpdate(res, true) + client.handleClientUpdate(res, true) const upd = res.updates[0] assertTypeIs('messages.sendVote (@ .updates[0])', upd, 'updateMessagePoll') diff --git a/packages/client/src/methods/messages/translate-message.ts b/packages/core/src/highlevel/methods/messages/translate-message.ts similarity index 90% rename from packages/client/src/methods/messages/translate-message.ts rename to packages/core/src/highlevel/methods/messages/translate-message.ts index bc54d4b5..c61b6cbc 100644 --- a/packages/client/src/methods/messages/translate-message.ts +++ b/packages/core/src/highlevel/methods/messages/translate-message.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputMessageId, normalizeInputMessageId, TextWithEntities } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -9,7 +8,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * Returns `null` if it could not translate the message. */ export async function translateMessage( - client: BaseTelegramClient, + client: ITelegramClient, params: InputMessageId & { /** Target language (two-letter ISO 639-1 language code) */ toLanguage: string diff --git a/packages/client/src/methods/messages/translate-text.ts b/packages/core/src/highlevel/methods/messages/translate-text.ts similarity index 86% rename from packages/client/src/methods/messages/translate-text.ts rename to packages/core/src/highlevel/methods/messages/translate-text.ts index bdfce90a..22b5706c 100644 --- a/packages/client/src/methods/messages/translate-text.ts +++ b/packages/core/src/highlevel/methods/messages/translate-text.ts @@ -1,5 +1,5 @@ -import { BaseTelegramClient, MtTypeAssertionError } from '@mtcute/core' - +import { MtTypeAssertionError } from '../../../types/errors.js' +import { ITelegramClient } from '../../client.types.js' import { InputText, TextWithEntities } from '../../types/misc/entities.js' import { _normalizeInputText } from '../misc/normalize-text.js' @@ -10,7 +10,7 @@ import { _normalizeInputText } from '../misc/normalize-text.js' * @param toLanguage Target language (two-letter ISO 639-1 language code) */ export async function translateText( - client: BaseTelegramClient, + client: ITelegramClient, text: InputText, toLanguage: string, ): Promise { diff --git a/packages/client/src/methods/messages/unpin-all-messages.ts b/packages/core/src/highlevel/methods/messages/unpin-all-messages.ts similarity index 70% rename from packages/client/src/methods/messages/unpin-all-messages.ts rename to packages/core/src/highlevel/methods/messages/unpin-all-messages.ts index 6645ac47..a9e187a9 100644 --- a/packages/client/src/methods/messages/unpin-all-messages.ts +++ b/packages/core/src/highlevel/methods/messages/unpin-all-messages.ts @@ -1,8 +1,7 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' +import { createDummyUpdate } from '../../updates/utils.js' import { isInputPeerChannel } from '../../utils/peer-utils.js' -import { createDummyUpdate } from '../../utils/updates-utils.js' import { resolvePeer } from '../users/resolve-peer.js' /** @@ -11,7 +10,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param chatId Chat or user ID */ export async function unpinAllMessages( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, params?: { /** @@ -31,8 +30,8 @@ export async function unpinAllMessages( }) if (isInputPeerChannel(peer)) { - client.network.handleUpdate(createDummyUpdate(res.pts, res.ptsCount, peer.channelId)) + client.handleClientUpdate(createDummyUpdate(res.pts, res.ptsCount, peer.channelId)) } else { - client.network.handleUpdate(createDummyUpdate(res.pts, res.ptsCount)) + client.handleClientUpdate(createDummyUpdate(res.pts, res.ptsCount)) } } diff --git a/packages/client/src/methods/messages/unpin-message.ts b/packages/core/src/highlevel/methods/messages/unpin-message.ts similarity index 77% rename from packages/client/src/methods/messages/unpin-message.ts rename to packages/core/src/highlevel/methods/messages/unpin-message.ts index 84c46389..04b940fd 100644 --- a/packages/client/src/methods/messages/unpin-message.ts +++ b/packages/core/src/highlevel/methods/messages/unpin-message.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputMessageId, normalizeInputMessageId } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -12,7 +11,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param chatId Chat ID, username, phone number, `"self"` or `"me"` * @param messageId Message ID */ -export async function unpinMessage(client: BaseTelegramClient, params: InputMessageId): Promise { +export async function unpinMessage(client: ITelegramClient, params: InputMessageId): Promise { const { chatId, message } = normalizeInputMessageId(params) const res = await client.call({ @@ -22,5 +21,5 @@ export async function unpinMessage(client: BaseTelegramClient, params: InputMess unpin: true, }) - client.network.handleUpdate(res) + client.handleClientUpdate(res) } diff --git a/packages/core/src/highlevel/methods/misc/chain-id.ts b/packages/core/src/highlevel/methods/misc/chain-id.ts new file mode 100644 index 00000000..c9a786fb --- /dev/null +++ b/packages/core/src/highlevel/methods/misc/chain-id.ts @@ -0,0 +1,11 @@ +import { tl } from '@mtcute/tl' + +import { getMarkedPeerId } from '../../../utils/peer-utils.js' +import { ITelegramClient } from '../../client.types.js' + +/** @internal */ +export function _getPeerChainId(client: ITelegramClient, peer: tl.TypeInputPeer, prefix = 'peer') { + const id = peer._ === 'inputPeerSelf' ? client.storage.self.getCached()!.userId : getMarkedPeerId(peer) + + return `${prefix}:${id}` +} diff --git a/packages/client/src/methods/misc/init-takeout-session.ts b/packages/core/src/highlevel/methods/misc/init-takeout-session.ts similarity index 79% rename from packages/client/src/methods/misc/init-takeout-session.ts rename to packages/core/src/highlevel/methods/misc/init-takeout-session.ts index c30699e6..4df278db 100644 --- a/packages/client/src/methods/misc/init-takeout-session.ts +++ b/packages/core/src/highlevel/methods/misc/init-takeout-session.ts @@ -1,5 +1,6 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { TakeoutSession } from '../../types/index.js' /** @@ -8,7 +9,7 @@ import { TakeoutSession } from '../../types/index.js' * @param params Takeout session parameters */ export async function initTakeoutSession( - client: BaseTelegramClient, + client: ITelegramClient, params: Omit, ): Promise { return new TakeoutSession( diff --git a/packages/client/src/methods/misc/normalize-privacy-rules.ts b/packages/core/src/highlevel/methods/misc/normalize-privacy-rules.ts similarity index 92% rename from packages/client/src/methods/misc/normalize-privacy-rules.ts rename to packages/core/src/highlevel/methods/misc/normalize-privacy-rules.ts index 60100f31..8c9edcf3 100644 --- a/packages/client/src/methods/misc/normalize-privacy-rules.ts +++ b/packages/core/src/highlevel/methods/misc/normalize-privacy-rules.ts @@ -1,5 +1,6 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { InputPrivacyRule } from '../../types/index.js' import { toInputUser } from '../../utils/index.js' import { resolvePeerMany } from '../users/resolve-peer-many.js' @@ -9,7 +10,7 @@ import { resolvePeerMany } from '../users/resolve-peer-many.js' * resolving the peers if needed. */ export async function _normalizePrivacyRules( - client: BaseTelegramClient, + client: ITelegramClient, rules: InputPrivacyRule[], ): Promise { const res: tl.TypeInputPrivacyRule[] = [] diff --git a/packages/client/src/methods/misc/normalize-text.ts b/packages/core/src/highlevel/methods/misc/normalize-text.ts similarity index 91% rename from packages/client/src/methods/misc/normalize-text.ts rename to packages/core/src/highlevel/methods/misc/normalize-text.ts index cce1afec..42ab4388 100644 --- a/packages/client/src/methods/misc/normalize-text.ts +++ b/packages/core/src/highlevel/methods/misc/normalize-text.ts @@ -1,5 +1,6 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { InputText } from '../../types/misc/entities.js' import { toInputUser } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -8,7 +9,7 @@ const empty: [string, undefined] = ['', undefined] /** @internal */ export async function _normalizeInputText( - client: BaseTelegramClient, + client: ITelegramClient, input?: InputText, ): Promise<[string, tl.TypeMessageEntity[] | undefined]> { if (!input) { diff --git a/packages/client/src/methods/password/change-cloud-password.ts b/packages/core/src/highlevel/methods/password/change-cloud-password.ts similarity index 64% rename from packages/client/src/methods/password/change-cloud-password.ts rename to packages/core/src/highlevel/methods/password/change-cloud-password.ts index b563136f..102e4daf 100644 --- a/packages/client/src/methods/password/change-cloud-password.ts +++ b/packages/core/src/highlevel/methods/password/change-cloud-password.ts @@ -1,11 +1,11 @@ -import { BaseTelegramClient, MtArgumentError } from '@mtcute/core' -import { assertTypeIs, computeNewPasswordHash, computeSrpParams } from '@mtcute/core/utils.js' +import { MtArgumentError } from '../../../types/errors.js' +import { ITelegramClient } from '../../client.types.js' /** * Change your 2FA password */ export async function changeCloudPassword( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Current password as plaintext */ currentPassword: string @@ -24,10 +24,9 @@ export async function changeCloudPassword( } const algo = pwd.newAlgo - assertTypeIs('account.getPassword', algo, 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow') - const oldSrp = await computeSrpParams(client.crypto, pwd, currentPassword) - const newHash = await computeNewPasswordHash(client.crypto, algo, newPassword) + const oldSrp = await client.computeSrpParams(pwd, currentPassword) + const newHash = await client.computeNewPasswordHash(algo, newPassword) await client.call({ _: 'account.updatePasswordSettings', diff --git a/packages/client/src/methods/password/enable-cloud-password.ts b/packages/core/src/highlevel/methods/password/enable-cloud-password.ts similarity index 81% rename from packages/client/src/methods/password/enable-cloud-password.ts rename to packages/core/src/highlevel/methods/password/enable-cloud-password.ts index 9111eac8..518eecf4 100644 --- a/packages/client/src/methods/password/enable-cloud-password.ts +++ b/packages/core/src/highlevel/methods/password/enable-cloud-password.ts @@ -1,5 +1,6 @@ -import { BaseTelegramClient, MtArgumentError } from '@mtcute/core' -import { assertTypeIs, computeNewPasswordHash } from '@mtcute/core/utils.js' +import { MtArgumentError } from '../../../types/errors.js' +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' /** * Enable 2FA password on your account @@ -10,7 +11,7 @@ import { assertTypeIs, computeNewPasswordHash } from '@mtcute/core/utils.js' * and the call this method again */ export async function enableCloudPassword( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** 2FA password as plaintext */ password: string @@ -31,7 +32,7 @@ export async function enableCloudPassword( const algo = pwd.newAlgo assertTypeIs('account.getPassword', algo, 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow') - const newHash = await computeNewPasswordHash(client.crypto, algo, password) + const newHash = await client.computeNewPasswordHash(algo, password) await client.call({ _: 'account.updatePasswordSettings', diff --git a/packages/client/src/methods/password/password-email.ts b/packages/core/src/highlevel/methods/password/password-email.ts similarity index 64% rename from packages/client/src/methods/password/password-email.ts rename to packages/core/src/highlevel/methods/password/password-email.ts index 51ba89e6..a721a418 100644 --- a/packages/client/src/methods/password/password-email.ts +++ b/packages/core/src/highlevel/methods/password/password-email.ts @@ -1,12 +1,12 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' /** * Verify an email to use as 2FA recovery method * * @param code Code which was sent via email */ -export async function verifyPasswordEmail(client: BaseTelegramClient, code: string): Promise { +export async function verifyPasswordEmail(client: ITelegramClient, code: string): Promise { const r = await client.call({ _: 'account.confirmPasswordEmail', code, @@ -18,7 +18,7 @@ export async function verifyPasswordEmail(client: BaseTelegramClient, code: stri /** * Resend the code to verify an email to use as 2FA recovery method. */ -export async function resendPasswordEmail(client: BaseTelegramClient): Promise { +export async function resendPasswordEmail(client: ITelegramClient): Promise { const r = await client.call({ _: 'account.resendPasswordEmail', }) @@ -29,7 +29,7 @@ export async function resendPasswordEmail(client: BaseTelegramClient): Promise { +export async function cancelPasswordEmail(client: ITelegramClient): Promise { const r = await client.call({ _: 'account.cancelPasswordEmail', }) diff --git a/packages/client/src/methods/password/remove-cloud-password.ts b/packages/core/src/highlevel/methods/password/remove-cloud-password.ts similarity index 65% rename from packages/client/src/methods/password/remove-cloud-password.ts rename to packages/core/src/highlevel/methods/password/remove-cloud-password.ts index fabbeb8a..e9936207 100644 --- a/packages/client/src/methods/password/remove-cloud-password.ts +++ b/packages/core/src/highlevel/methods/password/remove-cloud-password.ts @@ -1,19 +1,19 @@ -import { BaseTelegramClient, MtArgumentError } from '@mtcute/core' -import { computeSrpParams } from '@mtcute/core/utils.js' +import { MtArgumentError } from '../../../types/errors.js' +import { ITelegramClient } from '../../client.types.js' /** * Remove 2FA password from your account * * @param password 2FA password as plaintext */ -export async function removeCloudPassword(client: BaseTelegramClient, password: string): Promise { +export async function removeCloudPassword(client: ITelegramClient, password: string): Promise { const pwd = await client.call({ _: 'account.getPassword' }) if (!pwd.hasPassword) { throw new MtArgumentError('Cloud password is not enabled') } - const oldSrp = await computeSrpParams(client.crypto, pwd, password) + const oldSrp = await client.computeSrpParams(pwd, password) await client.call({ _: 'account.updatePasswordSettings', diff --git a/packages/client/src/methods/premium/apply-boost.ts b/packages/core/src/highlevel/methods/premium/apply-boost.ts similarity index 65% rename from packages/client/src/methods/premium/apply-boost.ts rename to packages/core/src/highlevel/methods/premium/apply-boost.ts index 3dc7618c..59851546 100644 --- a/packages/client/src/methods/premium/apply-boost.ts +++ b/packages/core/src/highlevel/methods/premium/apply-boost.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -8,7 +7,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * * @param peerId Peer ID to boost */ -export async function applyBoost(client: BaseTelegramClient, peerId: InputPeerLike): Promise { +export async function applyBoost(client: ITelegramClient, peerId: InputPeerLike): Promise { await client.call({ _: 'premium.applyBoost', peer: await resolvePeer(client, peerId), diff --git a/packages/client/src/methods/premium/can-apply-boost.ts b/packages/core/src/highlevel/methods/premium/can-apply-boost.ts similarity index 91% rename from packages/client/src/methods/premium/can-apply-boost.ts rename to packages/core/src/highlevel/methods/premium/can-apply-boost.ts index 0c221a3e..71148135 100644 --- a/packages/client/src/methods/premium/can-apply-boost.ts +++ b/packages/core/src/highlevel/methods/premium/can-apply-boost.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { Chat } from '../../types/index.js' import { BoostSlot } from '../../types/premium/boost-slot.js' import { getMyBoostSlots } from './get-my-boost-slots.js' @@ -22,7 +21,7 @@ export type CanApplyBoostResult = * - `.reason == "need_premium"` if the user needs Premium to boost * - In all cases, `slots` will contain all the current user's boost slots */ -export async function canApplyBoost(client: BaseTelegramClient): Promise { +export async function canApplyBoost(client: ITelegramClient): Promise { const myBoosts = await getMyBoostSlots(client) if (!myBoosts.length) { diff --git a/packages/client/src/methods/premium/get-boost-stats.ts b/packages/core/src/highlevel/methods/premium/get-boost-stats.ts similarity index 73% rename from packages/client/src/methods/premium/get-boost-stats.ts rename to packages/core/src/highlevel/methods/premium/get-boost-stats.ts index 193e04ba..4324def0 100644 --- a/packages/client/src/methods/premium/get-boost-stats.ts +++ b/packages/core/src/highlevel/methods/premium/get-boost-stats.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { BoostStats } from '../../types/premium/boost-stats.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -10,7 +9,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * * @returns IDs of stories that were removed */ -export async function getBoostStats(client: BaseTelegramClient, peerId: InputPeerLike): Promise { +export async function getBoostStats(client: ITelegramClient, peerId: InputPeerLike): Promise { const res = await client.call({ _: 'premium.getBoostsStatus', peer: await resolvePeer(client, peerId), diff --git a/packages/client/src/methods/premium/get-boosts.ts b/packages/core/src/highlevel/methods/premium/get-boosts.ts similarity index 92% rename from packages/client/src/methods/premium/get-boosts.ts rename to packages/core/src/highlevel/methods/premium/get-boosts.ts index ba56ac1c..e920063f 100644 --- a/packages/client/src/methods/premium/get-boosts.ts +++ b/packages/core/src/highlevel/methods/premium/get-boosts.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { ArrayPaginated, InputPeerLike, PeersIndex } from '../../types/index.js' import { Boost } from '../../types/premium/boost.js' import { makeArrayPaginated } from '../../utils/index.js' @@ -9,7 +8,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * Get boosts of a channel */ export async function getBoosts( - client: BaseTelegramClient, + client: ITelegramClient, peerId: InputPeerLike, params?: { /** diff --git a/packages/client/src/methods/premium/get-my-boost-slots.ts b/packages/core/src/highlevel/methods/premium/get-my-boost-slots.ts similarity index 77% rename from packages/client/src/methods/premium/get-my-boost-slots.ts rename to packages/core/src/highlevel/methods/premium/get-my-boost-slots.ts index d5cddc9f..e7a57c76 100644 --- a/packages/client/src/methods/premium/get-my-boost-slots.ts +++ b/packages/core/src/highlevel/methods/premium/get-my-boost-slots.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { PeersIndex } from '../../types/index.js' import { BoostSlot } from '../../types/premium/boost-slot.js' @@ -9,7 +8,7 @@ import { BoostSlot } from '../../types/premium/boost-slot.js' * Includes information about the currently boosted channels, * as well as the slots that can be used to boost other channels. */ -export async function getMyBoostSlots(client: BaseTelegramClient): Promise { +export async function getMyBoostSlots(client: ITelegramClient): Promise { const res = await client.call({ _: 'premium.getMyBoosts', }) diff --git a/packages/client/src/methods/premium/iter-boosters.ts b/packages/core/src/highlevel/methods/premium/iter-boosters.ts similarity index 94% rename from packages/client/src/methods/premium/iter-boosters.ts rename to packages/core/src/highlevel/methods/premium/iter-boosters.ts index 76f62f51..fa6a4b31 100644 --- a/packages/client/src/methods/premium/iter-boosters.ts +++ b/packages/core/src/highlevel/methods/premium/iter-boosters.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { Boost } from '../../types/premium/boost.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -13,7 +12,7 @@ import { getBoosts } from './get-boosts.js' * @returns IDs of stories that were removed */ export async function* iterBoosters( - client: BaseTelegramClient, + client: ITelegramClient, peerId: InputPeerLike, params?: Parameters[2] & { /** diff --git a/packages/client/src/methods/stickers/add-sticker-to-set.ts b/packages/core/src/highlevel/methods/stickers/add-sticker-to-set.ts similarity index 95% rename from packages/client/src/methods/stickers/add-sticker-to-set.ts rename to packages/core/src/highlevel/methods/stickers/add-sticker-to-set.ts index 53a1b109..d2ccd9bf 100644 --- a/packages/client/src/methods/stickers/add-sticker-to-set.ts +++ b/packages/core/src/highlevel/methods/stickers/add-sticker-to-set.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputStickerSet, InputStickerSetItem, @@ -21,7 +20,7 @@ import { _normalizeFileToDocument } from '../files/normalize-file-to-document.js * @returns Modfiied sticker set */ export async function addStickerToSet( - client: BaseTelegramClient, + client: ITelegramClient, setId: InputStickerSet, sticker: InputStickerSetItem, params?: { diff --git a/packages/client/src/methods/stickers/create-sticker-set.ts b/packages/core/src/highlevel/methods/stickers/create-sticker-set.ts similarity index 97% rename from packages/client/src/methods/stickers/create-sticker-set.ts rename to packages/core/src/highlevel/methods/stickers/create-sticker-set.ts index 419abdee..e343bd97 100644 --- a/packages/client/src/methods/stickers/create-sticker-set.ts +++ b/packages/core/src/highlevel/methods/stickers/create-sticker-set.ts @@ -1,5 +1,6 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { InputFileLike, InputPeerLike, @@ -23,7 +24,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @returns Newly created sticker set */ export async function createStickerSet( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** * Owner of the sticker set (must be user). diff --git a/packages/client/src/methods/stickers/delete-sticker-from-set.ts b/packages/core/src/highlevel/methods/stickers/delete-sticker-from-set.ts similarity index 75% rename from packages/client/src/methods/stickers/delete-sticker-from-set.ts rename to packages/core/src/highlevel/methods/stickers/delete-sticker-from-set.ts index 6e08fcdd..2ba7f0be 100644 --- a/packages/client/src/methods/stickers/delete-sticker-from-set.ts +++ b/packages/core/src/highlevel/methods/stickers/delete-sticker-from-set.ts @@ -1,7 +1,9 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' -import { fileIdToInputDocument, tdFileId } from '@mtcute/file-id' +import { tdFileId } from '@mtcute/file-id' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { StickerSet } from '../../types/index.js' +import { fileIdToInputDocument } from '../../utils/convert-file-id.js' /** * Delete a sticker from a sticker set @@ -15,7 +17,7 @@ import { StickerSet } from '../../types/index.js' * @returns Modfiied sticker set */ export async function deleteStickerFromSet( - client: BaseTelegramClient, + client: ITelegramClient, sticker: string | tdFileId.RawFullRemoteFileLocation | tl.TypeInputDocument, ): Promise { if (tdFileId.isFileIdLike(sticker)) { diff --git a/packages/client/src/methods/stickers/get-custom-emojis.ts b/packages/core/src/highlevel/methods/stickers/get-custom-emojis.ts similarity index 74% rename from packages/client/src/methods/stickers/get-custom-emojis.ts rename to packages/core/src/highlevel/methods/stickers/get-custom-emojis.ts index e68cff5b..2ef7ed34 100644 --- a/packages/client/src/methods/stickers/get-custom-emojis.ts +++ b/packages/core/src/highlevel/methods/stickers/get-custom-emojis.ts @@ -1,6 +1,10 @@ -import { BaseTelegramClient, MaybeArray, MtTypeAssertionError, tl } from '@mtcute/core' -import { assertTypeIs, LongSet } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { MtTypeAssertionError } from '../../../types/errors.js' +import { MaybeArray } from '../../../types/utils.js' +import { LongSet } from '../../../utils/long-utils.js' +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { Message, Sticker } from '../../types/index.js' import { parseDocument } from '../../types/media/document-utils.js' @@ -9,7 +13,7 @@ import { parseDocument } from '../../types/media/document-utils.js' * * @param ids IDs of the stickers (as defined in {@link MessageEntity.emojiId}) */ -export async function getCustomEmojis(client: BaseTelegramClient, ids: tl.Long[]): Promise { +export async function getCustomEmojis(client: ITelegramClient, ids: tl.Long[]): Promise { const res = await client.call({ _: 'messages.getCustomEmojiDocuments', documentId: ids, @@ -32,7 +36,7 @@ export async function getCustomEmojis(client: BaseTelegramClient, ids: tl.Long[] * Given one or more messages, extract all unique custom emojis from it and fetch them */ export async function getCustomEmojisFromMessages( - client: BaseTelegramClient, + client: ITelegramClient, messages: MaybeArray, ): Promise { const set = new LongSet() diff --git a/packages/client/src/methods/stickers/get-installed-stickers.ts b/packages/core/src/highlevel/methods/stickers/get-installed-stickers.ts similarity index 71% rename from packages/client/src/methods/stickers/get-installed-stickers.ts rename to packages/core/src/highlevel/methods/stickers/get-installed-stickers.ts index 115959f6..5f310198 100644 --- a/packages/client/src/methods/stickers/get-installed-stickers.ts +++ b/packages/core/src/highlevel/methods/stickers/get-installed-stickers.ts @@ -1,6 +1,7 @@ -import { BaseTelegramClient, Long } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' +import Long from 'long' +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { StickerSet } from '../../types/index.js' /** @@ -11,7 +12,7 @@ import { StickerSet } from '../../types/index.js' * > Use {@link StickerSet.getFull} or {@link getStickerSet} * > to get a stickerset that will include the stickers */ -export async function getInstalledStickers(client: BaseTelegramClient): Promise { +export async function getInstalledStickers(client: ITelegramClient): Promise { const res = await client.call({ _: 'messages.getAllStickers', hash: Long.ZERO, diff --git a/packages/client/src/methods/stickers/get-sticker-set.ts b/packages/core/src/highlevel/methods/stickers/get-sticker-set.ts similarity index 72% rename from packages/client/src/methods/stickers/get-sticker-set.ts rename to packages/core/src/highlevel/methods/stickers/get-sticker-set.ts index 7bb7e0ea..8b1f616a 100644 --- a/packages/client/src/methods/stickers/get-sticker-set.ts +++ b/packages/core/src/highlevel/methods/stickers/get-sticker-set.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputStickerSet, normalizeInputStickerSet, StickerSet } from '../../types/index.js' /** @@ -7,7 +6,7 @@ import { InputStickerSet, normalizeInputStickerSet, StickerSet } from '../../typ * * @param setId Sticker pack short name, dice emoji, `"emoji"` for animated emojis or input ID */ -export async function getStickerSet(client: BaseTelegramClient, setId: InputStickerSet): Promise { +export async function getStickerSet(client: ITelegramClient, setId: InputStickerSet): Promise { const res = await client.call({ _: 'messages.getStickerSet', stickerset: normalizeInputStickerSet(setId), diff --git a/packages/client/src/methods/stickers/move-sticker-in-set.ts b/packages/core/src/highlevel/methods/stickers/move-sticker-in-set.ts similarity index 78% rename from packages/client/src/methods/stickers/move-sticker-in-set.ts rename to packages/core/src/highlevel/methods/stickers/move-sticker-in-set.ts index ae2dfe89..24114d27 100644 --- a/packages/client/src/methods/stickers/move-sticker-in-set.ts +++ b/packages/core/src/highlevel/methods/stickers/move-sticker-in-set.ts @@ -1,7 +1,9 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' -import { fileIdToInputDocument, tdFileId } from '@mtcute/file-id' +import { tdFileId } from '@mtcute/file-id' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { StickerSet } from '../../types/index.js' +import { fileIdToInputDocument } from '../../utils/convert-file-id.js' /** * Move a sticker in a sticker set @@ -18,7 +20,7 @@ import { StickerSet } from '../../types/index.js' */ export async function moveStickerInSet( - client: BaseTelegramClient, + client: ITelegramClient, sticker: string | tdFileId.RawFullRemoteFileLocation | tl.TypeInputDocument, position: number, ): Promise { diff --git a/packages/client/src/methods/stickers/set-chat-sticker-set.ts b/packages/core/src/highlevel/methods/stickers/set-chat-sticker-set.ts similarity index 78% rename from packages/client/src/methods/stickers/set-chat-sticker-set.ts rename to packages/core/src/highlevel/methods/stickers/set-chat-sticker-set.ts index a7ca956e..d230c51f 100644 --- a/packages/client/src/methods/stickers/set-chat-sticker-set.ts +++ b/packages/core/src/highlevel/methods/stickers/set-chat-sticker-set.ts @@ -1,7 +1,7 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, InputStickerSet, normalizeInputStickerSet } from '../../types/index.js' -import { assertTrue, toInputChannel } from '../../utils/index.js' +import { toInputChannel } from '../../utils/index.js' import { resolvePeer } from '../users/resolve-peer.js' /** @@ -13,7 +13,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @returns Modified sticker set */ export async function setChatStickerSet( - client: BaseTelegramClient, + client: ITelegramClient, chatId: InputPeerLike, setId: InputStickerSet, ): Promise { diff --git a/packages/client/src/methods/stickers/set-sticker-set-thumb.ts b/packages/core/src/highlevel/methods/stickers/set-sticker-set-thumb.ts similarity index 89% rename from packages/client/src/methods/stickers/set-sticker-set-thumb.ts rename to packages/core/src/highlevel/methods/stickers/set-sticker-set-thumb.ts index 6487c46b..82922d10 100644 --- a/packages/client/src/methods/stickers/set-sticker-set-thumb.ts +++ b/packages/core/src/highlevel/methods/stickers/set-sticker-set-thumb.ts @@ -1,5 +1,6 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { InputFileLike, InputStickerSet, normalizeInputStickerSet, StickerSet } from '../../types/index.js' import { _normalizeFileToDocument } from '../files/normalize-file-to-document.js' @@ -12,7 +13,7 @@ import { _normalizeFileToDocument } from '../files/normalize-file-to-document.js * @returns Modified sticker set */ export async function setStickerSetThumb( - client: BaseTelegramClient, + client: ITelegramClient, id: InputStickerSet, thumb: InputFileLike | tl.TypeInputDocument, params?: { diff --git a/packages/client/src/methods/stories/can-send-story.ts b/packages/core/src/highlevel/methods/stories/can-send-story.ts similarity index 82% rename from packages/client/src/methods/stories/can-send-story.ts rename to packages/core/src/highlevel/methods/stories/can-send-story.ts index 2a962dcf..34fd33cf 100644 --- a/packages/client/src/methods/stories/can-send-story.ts +++ b/packages/core/src/highlevel/methods/stories/can-send-story.ts @@ -1,5 +1,6 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -15,7 +16,7 @@ export type CanSendStoryResult = true | 'need_admin' | 'need_boosts' * - `"need_admin"` if the user is not an admin in the chat * - `"need_boosts"` if the channel doesn't have enough boosts */ -export async function canSendStory(client: BaseTelegramClient, peerId: InputPeerLike): Promise { +export async function canSendStory(client: ITelegramClient, peerId: InputPeerLike): Promise { try { const res = await client.call({ _: 'stories.canSendStory', diff --git a/packages/client/src/methods/stories/delete-stories.ts b/packages/core/src/highlevel/methods/stories/delete-stories.ts similarity index 83% rename from packages/client/src/methods/stories/delete-stories.ts rename to packages/core/src/highlevel/methods/stories/delete-stories.ts index f8c5cfb4..c3fed166 100644 --- a/packages/client/src/methods/stories/delete-stories.ts +++ b/packages/core/src/highlevel/methods/stories/delete-stories.ts @@ -1,5 +1,5 @@ -import { BaseTelegramClient, MaybeArray } from '@mtcute/core' - +import { MaybeArray } from '../../../types/utils.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -9,7 +9,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @returns IDs of stories that were removed */ export async function deleteStories( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** * Story IDs to delete diff --git a/packages/client/src/methods/stories/edit-story.ts b/packages/core/src/highlevel/methods/stories/edit-story.ts similarity index 95% rename from packages/client/src/methods/stories/edit-story.ts rename to packages/core/src/highlevel/methods/stories/edit-story.ts index f3eee571..70a6c04b 100644 --- a/packages/client/src/methods/stories/edit-story.ts +++ b/packages/core/src/highlevel/methods/stories/edit-story.ts @@ -1,5 +1,6 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { ITelegramClient } from '../../client.types.js' import { InputMediaLike, InputPeerLike, InputPrivacyRule, InputText, Story } from '../../types/index.js' import { _normalizeInputMedia } from '../files/normalize-input-media.js' import { _normalizePrivacyRules } from '../misc/normalize-privacy-rules.js' @@ -13,7 +14,7 @@ import { _findStoryInUpdate } from './find-in-update.js' * @returns Edited story */ export async function editStory( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** * Story ID to edit diff --git a/packages/client/src/methods/stories/find-in-update.ts b/packages/core/src/highlevel/methods/stories/find-in-update.ts similarity index 53% rename from packages/client/src/methods/stories/find-in-update.ts rename to packages/core/src/highlevel/methods/stories/find-in-update.ts index 2c02e925..8f0f6644 100644 --- a/packages/client/src/methods/stories/find-in-update.ts +++ b/packages/core/src/highlevel/methods/stories/find-in-update.ts @@ -1,14 +1,16 @@ -import { BaseTelegramClient, MtTypeAssertionError, tl } from '@mtcute/core' -import { assertTypeIs, hasValueAtKey } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { MtTypeAssertionError } from '../../../types/errors.js' +import { assertTypeIs, hasValueAtKey } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { PeersIndex, Story } from '../../types/index.js' -import { assertIsUpdatesGroup } from '../../utils/updates-utils.js' +import { assertIsUpdatesGroup } from '../../updates/utils.js' /** @internal */ -export function _findStoryInUpdate(client: BaseTelegramClient, res: tl.TypeUpdates): Story { +export function _findStoryInUpdate(client: ITelegramClient, res: tl.TypeUpdates): Story { assertIsUpdatesGroup('_findStoryInUpdate', res) - client.network.handleUpdate(res, true) + client.handleClientUpdate(res, true) const peers = PeersIndex.from(res) const updateStory = res.updates.find(hasValueAtKey('_', 'updateStory')) diff --git a/packages/client/src/methods/stories/get-all-stories.ts b/packages/core/src/highlevel/methods/stories/get-all-stories.ts similarity index 83% rename from packages/client/src/methods/stories/get-all-stories.ts rename to packages/core/src/highlevel/methods/stories/get-all-stories.ts index fdf05c9f..da22f9a1 100644 --- a/packages/client/src/methods/stories/get-all-stories.ts +++ b/packages/core/src/highlevel/methods/stories/get-all-stories.ts @@ -1,13 +1,12 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { assertTypeIsNot } from '@mtcute/core/utils.js' - +import { assertTypeIsNot } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { AllStories } from '../../types/index.js' /** * Get all stories (e.g. to load the top bar) */ export async function getAllStories( - client: BaseTelegramClient, + client: ITelegramClient, params?: { /** * Offset from which to fetch stories diff --git a/packages/client/src/methods/stories/get-peer-stories.ts b/packages/core/src/highlevel/methods/stories/get-peer-stories.ts similarity index 72% rename from packages/client/src/methods/stories/get-peer-stories.ts rename to packages/core/src/highlevel/methods/stories/get-peer-stories.ts index f3fa6082..90f88b90 100644 --- a/packages/client/src/methods/stories/get-peer-stories.ts +++ b/packages/core/src/highlevel/methods/stories/get-peer-stories.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, PeersIndex, PeerStories } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -8,7 +7,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * * @param peerId Peer ID whose stories to fetch */ -export async function getPeerStories(client: BaseTelegramClient, peerId: InputPeerLike): Promise { +export async function getPeerStories(client: ITelegramClient, peerId: InputPeerLike): Promise { const res = await client.call({ _: 'stories.getPeerStories', peer: await resolvePeer(client, peerId), diff --git a/packages/client/src/methods/stories/get-profile-stories.ts b/packages/core/src/highlevel/methods/stories/get-profile-stories.ts similarity index 91% rename from packages/client/src/methods/stories/get-profile-stories.ts rename to packages/core/src/highlevel/methods/stories/get-profile-stories.ts index 3cd11451..e3f21672 100644 --- a/packages/client/src/methods/stories/get-profile-stories.ts +++ b/packages/core/src/highlevel/methods/stories/get-profile-stories.ts @@ -1,6 +1,5 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' - +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { ArrayPaginated, InputPeerLike, PeersIndex, Story } from '../../types/index.js' import { makeArrayPaginated } from '../../utils/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -9,7 +8,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * Get profile stories */ export async function getProfileStories( - client: BaseTelegramClient, + client: ITelegramClient, peerId: InputPeerLike, params?: { /** diff --git a/packages/client/src/methods/stories/get-stories-by-id.ts b/packages/core/src/highlevel/methods/stories/get-stories-by-id.ts similarity index 79% rename from packages/client/src/methods/stories/get-stories-by-id.ts rename to packages/core/src/highlevel/methods/stories/get-stories-by-id.ts index 91ce3d52..b079dfd6 100644 --- a/packages/client/src/methods/stories/get-stories-by-id.ts +++ b/packages/core/src/highlevel/methods/stories/get-stories-by-id.ts @@ -1,6 +1,6 @@ -import { BaseTelegramClient, MaybeArray } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' - +import { MaybeArray } from '../../../types/utils.js' +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, PeersIndex, Story } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -11,7 +11,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param storyIds Story IDs */ export async function getStoriesById( - client: BaseTelegramClient, + client: ITelegramClient, peerId: InputPeerLike, storyIds: MaybeArray, ): Promise { diff --git a/packages/client/src/methods/stories/get-stories-interactions.ts b/packages/core/src/highlevel/methods/stories/get-stories-interactions.ts similarity index 84% rename from packages/client/src/methods/stories/get-stories-interactions.ts rename to packages/core/src/highlevel/methods/stories/get-stories-interactions.ts index a1cb21a7..090b74c5 100644 --- a/packages/client/src/methods/stories/get-stories-interactions.ts +++ b/packages/core/src/highlevel/methods/stories/get-stories-interactions.ts @@ -1,5 +1,5 @@ -import { BaseTelegramClient, MaybeArray } from '@mtcute/core' - +import { MaybeArray } from '../../../types/utils.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, PeersIndex, StoryInteractions } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -9,7 +9,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * The result will be in the same order as the input IDs */ export async function getStoriesInteractions( - client: BaseTelegramClient, + client: ITelegramClient, peerId: InputPeerLike, storyIds: MaybeArray, ): Promise { diff --git a/packages/client/src/methods/stories/get-story-link.ts b/packages/core/src/highlevel/methods/stories/get-story-link.ts similarity index 88% rename from packages/client/src/methods/stories/get-story-link.ts rename to packages/core/src/highlevel/methods/stories/get-story-link.ts index 8ddcf963..ed44704e 100644 --- a/packages/client/src/methods/stories/get-story-link.ts +++ b/packages/core/src/highlevel/methods/stories/get-story-link.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -12,7 +11,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * I have no idea why is this an RPC call, but whatever */ export async function getStoryLink( - client: BaseTelegramClient, + client: ITelegramClient, peerId: InputPeerLike, storyId: number, ): Promise { diff --git a/packages/client/src/methods/stories/get-story-viewers.ts b/packages/core/src/highlevel/methods/stories/get-story-viewers.ts similarity index 94% rename from packages/client/src/methods/stories/get-story-viewers.ts rename to packages/core/src/highlevel/methods/stories/get-story-viewers.ts index 6d03ecf3..317c5426 100644 --- a/packages/client/src/methods/stories/get-story-viewers.ts +++ b/packages/core/src/highlevel/methods/stories/get-story-viewers.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, StoryViewersList } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -7,7 +6,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * Get viewers list of a story */ export async function getStoryViewers( - client: BaseTelegramClient, + client: ITelegramClient, peerId: InputPeerLike, storyId: number, params?: { diff --git a/packages/client/src/methods/stories/hide-my-stories-views.ts b/packages/core/src/highlevel/methods/stories/hide-my-stories-views.ts similarity index 77% rename from packages/client/src/methods/stories/hide-my-stories-views.ts rename to packages/core/src/highlevel/methods/stories/hide-my-stories-views.ts index 42e09bcb..c552744b 100644 --- a/packages/client/src/methods/stories/hide-my-stories-views.ts +++ b/packages/core/src/highlevel/methods/stories/hide-my-stories-views.ts @@ -1,7 +1,8 @@ -import { BaseTelegramClient, MtTypeAssertionError } from '@mtcute/core' - +import { MtTypeAssertionError } from '../../../types/errors.js' +import { hasValueAtKey } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { StoriesStealthMode } from '../../types/stories/stealth-mode.js' -import { assertIsUpdatesGroup, hasValueAtKey } from '../../utils/index.js' +import { assertIsUpdatesGroup } from '../../updates/utils.js' /** * Hide own stories views (activate so called "stealth mode") @@ -9,7 +10,7 @@ import { assertIsUpdatesGroup, hasValueAtKey } from '../../utils/index.js' * Currently has a cooldown of 1 hour, and throws FLOOD_WAIT error if it is on cooldown. */ export async function hideMyStoriesViews( - client: BaseTelegramClient, + client: ITelegramClient, params?: { /** * Whether to hide views from the last 5 minutes @@ -35,7 +36,7 @@ export async function hideMyStoriesViews( }) assertIsUpdatesGroup('hideMyStoriesViews', res) - client.network.handleUpdate(res, true) + client.handleClientUpdate(res, true) const upd = res.updates.find(hasValueAtKey('_', 'updateStoriesStealthMode')) diff --git a/packages/client/src/methods/stories/increment-stories-views.ts b/packages/core/src/highlevel/methods/stories/increment-stories-views.ts similarity index 79% rename from packages/client/src/methods/stories/increment-stories-views.ts rename to packages/core/src/highlevel/methods/stories/increment-stories-views.ts index d29bf1c9..e91f24d9 100644 --- a/packages/client/src/methods/stories/increment-stories-views.ts +++ b/packages/core/src/highlevel/methods/stories/increment-stories-views.ts @@ -1,6 +1,6 @@ -import { BaseTelegramClient, MaybeArray } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' - +import { MaybeArray } from '../../../types/utils.js' +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -14,7 +14,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param ids ID(s) of the stories to increment views of (max 200) */ export async function incrementStoriesViews( - client: BaseTelegramClient, + client: ITelegramClient, peerId: InputPeerLike, ids: MaybeArray, ): Promise { diff --git a/packages/client/src/methods/stories/iter-all-stories.ts b/packages/core/src/highlevel/methods/stories/iter-all-stories.ts similarity index 91% rename from packages/client/src/methods/stories/iter-all-stories.ts rename to packages/core/src/highlevel/methods/stories/iter-all-stories.ts index 418c302e..27da69a3 100644 --- a/packages/client/src/methods/stories/iter-all-stories.ts +++ b/packages/core/src/highlevel/methods/stories/iter-all-stories.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { PeerStories } from '../../types/index.js' import { getAllStories } from './get-all-stories.js' @@ -9,7 +8,7 @@ import { getAllStories } from './get-all-stories.js' * Wrapper over {@link getAllStories} */ export async function* iterAllStories( - client: BaseTelegramClient, + client: ITelegramClient, params?: Parameters[1] & { /** * Maximum number of stories to fetch diff --git a/packages/client/src/methods/stories/iter-profile-stories.ts b/packages/core/src/highlevel/methods/stories/iter-profile-stories.ts similarity index 94% rename from packages/client/src/methods/stories/iter-profile-stories.ts rename to packages/core/src/highlevel/methods/stories/iter-profile-stories.ts index 30a9af9a..7632b64b 100644 --- a/packages/client/src/methods/stories/iter-profile-stories.ts +++ b/packages/core/src/highlevel/methods/stories/iter-profile-stories.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, Story } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' import { getProfileStories } from './get-profile-stories.js' @@ -8,7 +7,7 @@ import { getProfileStories } from './get-profile-stories.js' * Iterate over profile stories. Wrapper over {@link getProfileStories} */ export async function* iterProfileStories( - client: BaseTelegramClient, + client: ITelegramClient, peerId: InputPeerLike, params?: Parameters[2] & { /** diff --git a/packages/client/src/methods/stories/iter-story-viewers.ts b/packages/core/src/highlevel/methods/stories/iter-story-viewers.ts similarity index 94% rename from packages/client/src/methods/stories/iter-story-viewers.ts rename to packages/core/src/highlevel/methods/stories/iter-story-viewers.ts index 66acd5fe..9c2110a8 100644 --- a/packages/client/src/methods/stories/iter-story-viewers.ts +++ b/packages/core/src/highlevel/methods/stories/iter-story-viewers.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, StoryViewer } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' import { getStoryViewers } from './get-story-viewers.js' @@ -9,7 +8,7 @@ import { getStoryViewers } from './get-story-viewers.js' * Wrapper over {@link getStoryViewers} */ export async function* iterStoryViewers( - client: BaseTelegramClient, + client: ITelegramClient, peerId: InputPeerLike, storyId: number, params?: Parameters[3] & { diff --git a/packages/client/src/methods/stories/read-stories.ts b/packages/core/src/highlevel/methods/stories/read-stories.ts similarity index 73% rename from packages/client/src/methods/stories/read-stories.ts rename to packages/core/src/highlevel/methods/stories/read-stories.ts index 53c0c459..94fd40d9 100644 --- a/packages/client/src/methods/stories/read-stories.ts +++ b/packages/core/src/highlevel/methods/stories/read-stories.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -11,7 +10,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @param peerId Peer ID whose stories to mark as read * @returns IDs of the stores that were marked as read */ -export async function readStories(client: BaseTelegramClient, peerId: InputPeerLike, maxId: number): Promise { +export async function readStories(client: ITelegramClient, peerId: InputPeerLike, maxId: number): Promise { return client.call({ _: 'stories.readStories', peer: await resolvePeer(client, peerId), diff --git a/packages/client/src/methods/stories/report-story.ts b/packages/core/src/highlevel/methods/stories/report-story.ts similarity index 79% rename from packages/client/src/methods/stories/report-story.ts rename to packages/core/src/highlevel/methods/stories/report-story.ts index 324773d9..682e87a4 100644 --- a/packages/client/src/methods/stories/report-story.ts +++ b/packages/core/src/highlevel/methods/stories/report-story.ts @@ -1,6 +1,8 @@ -import { BaseTelegramClient, MaybeArray, tl } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { MaybeArray } from '../../../types/utils.js' +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -8,7 +10,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * Report a story (or multiple stories) to the moderation team */ export async function reportStory( - client: BaseTelegramClient, + client: ITelegramClient, peerId: InputPeerLike, storyIds: MaybeArray, params?: { diff --git a/packages/client/src/methods/stories/send-story-reaction.ts b/packages/core/src/highlevel/methods/stories/send-story-reaction.ts similarity index 85% rename from packages/client/src/methods/stories/send-story-reaction.ts rename to packages/core/src/highlevel/methods/stories/send-story-reaction.ts index 9921b8a7..b44a1c1a 100644 --- a/packages/client/src/methods/stories/send-story-reaction.ts +++ b/packages/core/src/highlevel/methods/stories/send-story-reaction.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, InputReaction, normalizeInputReaction } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -7,7 +6,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * Send (or remove) a reaction to a story */ export async function sendStoryReaction( - client: BaseTelegramClient, + client: ITelegramClient, params: { peerId: InputPeerLike storyId: number @@ -28,5 +27,5 @@ export async function sendStoryReaction( addToRecent, }) - client.network.handleUpdate(res, true) + client.handleClientUpdate(res, true) } diff --git a/packages/client/src/methods/stories/send-story.ts b/packages/core/src/highlevel/methods/stories/send-story.ts similarity index 94% rename from packages/client/src/methods/stories/send-story.ts rename to packages/core/src/highlevel/methods/stories/send-story.ts index 6adac18f..87457ba2 100644 --- a/packages/client/src/methods/stories/send-story.ts +++ b/packages/core/src/highlevel/methods/stories/send-story.ts @@ -1,6 +1,7 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' -import { randomLong } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { randomLong } from '../../../utils/long-utils.js' +import { ITelegramClient } from '../../client.types.js' import { InputMediaLike, InputPeerLike, InputPrivacyRule, InputText, Story } from '../../types/index.js' import { _normalizeInputMedia } from '../files/normalize-input-media.js' import { _normalizePrivacyRules } from '../misc/normalize-privacy-rules.js' @@ -14,7 +15,7 @@ import { _findStoryInUpdate } from './find-in-update.js' * @returns Created story */ export async function sendStory( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** * Peer ID to send story as diff --git a/packages/client/src/methods/stories/toggle-peer-stories-archived.ts b/packages/core/src/highlevel/methods/stories/toggle-peer-stories-archived.ts similarity index 79% rename from packages/client/src/methods/stories/toggle-peer-stories-archived.ts rename to packages/core/src/highlevel/methods/stories/toggle-peer-stories-archived.ts index d4a85b37..00419c79 100644 --- a/packages/client/src/methods/stories/toggle-peer-stories-archived.ts +++ b/packages/core/src/highlevel/methods/stories/toggle-peer-stories-archived.ts @@ -1,6 +1,5 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' - +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -10,7 +9,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * This **does not** archive the chat with that peer, only stories. */ export async function togglePeerStoriesArchived( - client: BaseTelegramClient, + client: ITelegramClient, peerId: InputPeerLike, archived: boolean, ): Promise { diff --git a/packages/client/src/methods/stories/toggle-stories-pinned.ts b/packages/core/src/highlevel/methods/stories/toggle-stories-pinned.ts similarity index 86% rename from packages/client/src/methods/stories/toggle-stories-pinned.ts rename to packages/core/src/highlevel/methods/stories/toggle-stories-pinned.ts index 02faa14c..1dbf803c 100644 --- a/packages/client/src/methods/stories/toggle-stories-pinned.ts +++ b/packages/core/src/highlevel/methods/stories/toggle-stories-pinned.ts @@ -1,5 +1,5 @@ -import { BaseTelegramClient, MaybeArray } from '@mtcute/core' - +import { MaybeArray } from '../../../types/utils.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { resolvePeer } from '../users/resolve-peer.js' @@ -9,7 +9,7 @@ import { resolvePeer } from '../users/resolve-peer.js' * @returns IDs of stories that were toggled */ export async function toggleStoriesPinned( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** * Story ID(s) to toggle diff --git a/packages/client/src/methods/users/block-user.ts b/packages/core/src/highlevel/methods/users/block-user.ts similarity index 60% rename from packages/client/src/methods/users/block-user.ts rename to packages/core/src/highlevel/methods/users/block-user.ts index baecc529..11c6274a 100644 --- a/packages/client/src/methods/users/block-user.ts +++ b/packages/core/src/highlevel/methods/users/block-user.ts @@ -1,6 +1,5 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' - +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { resolvePeer } from './resolve-peer.js' @@ -9,7 +8,7 @@ import { resolvePeer } from './resolve-peer.js' * * @param id User ID, username or phone number */ -export async function blockUser(client: BaseTelegramClient, id: InputPeerLike): Promise { +export async function blockUser(client: ITelegramClient, id: InputPeerLike): Promise { const r = await client.call({ _: 'contacts.block', id: await resolvePeer(client, id), diff --git a/packages/client/src/methods/users/delete-profile-photos.ts b/packages/core/src/highlevel/methods/users/delete-profile-photos.ts similarity index 68% rename from packages/client/src/methods/users/delete-profile-photos.ts rename to packages/core/src/highlevel/methods/users/delete-profile-photos.ts index d1bb3861..656af894 100644 --- a/packages/client/src/methods/users/delete-profile-photos.ts +++ b/packages/core/src/highlevel/methods/users/delete-profile-photos.ts @@ -1,5 +1,8 @@ -import { BaseTelegramClient, MaybeArray, tl } from '@mtcute/core' -import { fileIdToInputPhoto } from '@mtcute/file-id' +import { tl } from '@mtcute/tl' + +import { MaybeArray } from '../../../types/utils.js' +import { ITelegramClient } from '../../client.types.js' +import { fileIdToInputPhoto } from '../../utils/convert-file-id.js' /** * Delete your own profile photos @@ -7,7 +10,7 @@ import { fileIdToInputPhoto } from '@mtcute/file-id' * @param ids ID(s) of the photos. Can be file IDs or raw TL objects */ export async function deleteProfilePhotos( - client: BaseTelegramClient, + client: ITelegramClient, ids: MaybeArray, ): Promise { if (!Array.isArray(ids)) ids = [ids] diff --git a/packages/client/src/methods/users/edit-close-friends.ts b/packages/core/src/highlevel/methods/users/edit-close-friends.ts similarity index 67% rename from packages/client/src/methods/users/edit-close-friends.ts rename to packages/core/src/highlevel/methods/users/edit-close-friends.ts index 337bc6d7..5859650d 100644 --- a/packages/client/src/methods/users/edit-close-friends.ts +++ b/packages/core/src/highlevel/methods/users/edit-close-friends.ts @@ -1,7 +1,7 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' -import { assertTrue, toInputUser } from '../../utils/index.js' +import { toInputUser } from '../../utils/index.js' import { resolvePeerMany } from './resolve-peer-many.js' /** @@ -9,7 +9,7 @@ import { resolvePeerMany } from './resolve-peer-many.js' * * @param ids User IDs */ -export async function editCloseFriendsRaw(client: BaseTelegramClient, ids: number[]): Promise { +export async function editCloseFriendsRaw(client: ITelegramClient, ids: number[]): Promise { const r = await client.call({ _: 'contacts.editCloseFriends', id: ids, @@ -23,7 +23,7 @@ export async function editCloseFriendsRaw(client: BaseTelegramClient, ids: numbe * * @param ids User IDs */ -export async function editCloseFriends(client: BaseTelegramClient, ids: InputPeerLike[]): Promise { +export async function editCloseFriends(client: ITelegramClient, ids: InputPeerLike[]): Promise { const r = await client.call({ _: 'contacts.editCloseFriends', id: await resolvePeerMany(client, ids, toInputUser).then((r) => diff --git a/packages/client/src/methods/users/get-common-chats.ts b/packages/core/src/highlevel/methods/users/get-common-chats.ts similarity index 78% rename from packages/client/src/methods/users/get-common-chats.ts rename to packages/core/src/highlevel/methods/users/get-common-chats.ts index eb56194f..44050388 100644 --- a/packages/client/src/methods/users/get-common-chats.ts +++ b/packages/core/src/highlevel/methods/users/get-common-chats.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { Chat, InputPeerLike } from '../../types/index.js' import { toInputUser } from '../../utils/peer-utils.js' import { resolvePeer } from './resolve-peer.js' @@ -10,7 +9,7 @@ import { resolvePeer } from './resolve-peer.js' * @param userId User's ID, username or phone number * @throws MtInvalidPeerTypeError */ -export async function getCommonChats(client: BaseTelegramClient, userId: InputPeerLike): Promise { +export async function getCommonChats(client: ITelegramClient, userId: InputPeerLike): Promise { return client .call({ _: 'messages.getCommonChats', diff --git a/packages/client/src/methods/users/get-global-ttl.ts b/packages/core/src/highlevel/methods/users/get-global-ttl.ts similarity index 62% rename from packages/client/src/methods/users/get-global-ttl.ts rename to packages/core/src/highlevel/methods/users/get-global-ttl.ts index 76f54ed8..bdfc1f62 100644 --- a/packages/client/src/methods/users/get-global-ttl.ts +++ b/packages/core/src/highlevel/methods/users/get-global-ttl.ts @@ -1,9 +1,9 @@ -import { BaseTelegramClient } from '@mtcute/core' +import { ITelegramClient } from '../../client.types.js' /** * Gets the current default value of the Time-To-Live setting, applied to all new chats. */ -export async function getGlobalTtl(client: BaseTelegramClient): Promise { +export async function getGlobalTtl(client: ITelegramClient): Promise { return client .call({ _: 'messages.getDefaultHistoryTTL', diff --git a/packages/client/src/methods/users/get-me.ts b/packages/core/src/highlevel/methods/users/get-me.ts similarity index 53% rename from packages/client/src/methods/users/get-me.ts rename to packages/core/src/highlevel/methods/users/get-me.ts index 6f4bab59..ee44317d 100644 --- a/packages/client/src/methods/users/get-me.ts +++ b/packages/core/src/highlevel/methods/users/get-me.ts @@ -1,12 +1,11 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' - +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { User } from '../../types/index.js' /** * Get currently authorized user's full information */ -export function getMe(client: BaseTelegramClient): Promise { +export function getMe(client: ITelegramClient): Promise { return client .call({ _: 'users.getUsers', @@ -19,13 +18,7 @@ export function getMe(client: BaseTelegramClient): Promise { .then(async ([user]) => { assertTypeIs('getMe (@ users.getUsers)', user, 'user') - await client.storage.self.store({ - userId: user.id, - isBot: user.bot!, - }) - - // todo - // authState.selfUsername = user.username ?? null + await client.storage.self.storeFrom(user) return new User(user) }) diff --git a/packages/client/src/methods/users/get-my-username.ts b/packages/core/src/highlevel/methods/users/get-my-username.ts similarity index 64% rename from packages/client/src/methods/users/get-my-username.ts rename to packages/core/src/highlevel/methods/users/get-my-username.ts index 6a21d8c0..c654d1a3 100644 --- a/packages/client/src/methods/users/get-my-username.ts +++ b/packages/core/src/highlevel/methods/users/get-my-username.ts @@ -1,4 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' +import { ITelegramClient } from '../../client.types.js' /** * Get currently authorized user's username. @@ -6,7 +6,7 @@ import { BaseTelegramClient } from '@mtcute/core' * This method uses locally available information and * does not call any API methods. */ -export function getMyUsername(client: BaseTelegramClient): string | null { +export function getMyUsername(client: ITelegramClient): string | null { throw new Error('Not implemented') // return getAuthState(client).selfUsername } diff --git a/packages/client/src/methods/users/get-profile-photo.ts b/packages/core/src/highlevel/methods/users/get-profile-photo.ts similarity index 81% rename from packages/client/src/methods/users/get-profile-photo.ts rename to packages/core/src/highlevel/methods/users/get-profile-photo.ts index a2cbcce0..9cb7cf96 100644 --- a/packages/client/src/methods/users/get-profile-photo.ts +++ b/packages/core/src/highlevel/methods/users/get-profile-photo.ts @@ -1,6 +1,7 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, Photo } from '../../types/index.js' import { toInputUser } from '../../utils/peer-utils.js' import { resolvePeer } from './resolve-peer.js' @@ -13,7 +14,7 @@ import { resolvePeer } from './resolve-peer.js' * @param params */ export async function getProfilePhoto( - client: BaseTelegramClient, + client: ITelegramClient, userId: InputPeerLike, photoId: tl.Long, ): Promise { diff --git a/packages/client/src/methods/users/get-profile-photos.ts b/packages/core/src/highlevel/methods/users/get-profile-photos.ts similarity index 86% rename from packages/client/src/methods/users/get-profile-photos.ts rename to packages/core/src/highlevel/methods/users/get-profile-photos.ts index c79d2d29..9b8df9ae 100644 --- a/packages/client/src/methods/users/get-profile-photos.ts +++ b/packages/core/src/highlevel/methods/users/get-profile-photos.ts @@ -1,6 +1,9 @@ -import { BaseTelegramClient, Long, tl } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' +import Long from 'long' +import { tl } from '@mtcute/tl' + +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { ArrayPaginated, InputPeerLike, Photo } from '../../types/index.js' import { makeArrayPaginated } from '../../utils/index.js' import { toInputUser } from '../../utils/peer-utils.js' @@ -13,7 +16,7 @@ import { resolvePeer } from './resolve-peer.js' * @param params */ export async function getProfilePhotos( - client: BaseTelegramClient, + client: ITelegramClient, userId: InputPeerLike, params?: { /** diff --git a/packages/client/src/methods/users/get-users.test.ts b/packages/core/src/highlevel/methods/users/get-users.test.ts similarity index 92% rename from packages/client/src/methods/users/get-users.test.ts rename to packages/core/src/highlevel/methods/users/get-users.test.ts index 3c02fa6b..84994bb3 100644 --- a/packages/client/src/methods/users/get-users.test.ts +++ b/packages/core/src/highlevel/methods/users/get-users.test.ts @@ -1,9 +1,9 @@ +import Long from 'long' import { describe, expect, it } from 'vitest' -import { Long } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' import { createStub, StubTelegramClient } from '@mtcute/test' +import { assertTypeIs } from '../../../utils/type-assertions.js' import { User } from '../../types/index.js' import { getUsers } from './get-users.js' diff --git a/packages/client/src/methods/users/get-users.ts b/packages/core/src/highlevel/methods/users/get-users.ts similarity index 82% rename from packages/client/src/methods/users/get-users.ts rename to packages/core/src/highlevel/methods/users/get-users.ts index 61be0420..4306824a 100644 --- a/packages/client/src/methods/users/get-users.ts +++ b/packages/core/src/highlevel/methods/users/get-users.ts @@ -1,5 +1,5 @@ -import { BaseTelegramClient, MaybeArray } from '@mtcute/core' - +import { MaybeArray } from '../../../types/utils.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, User } from '../../types/index.js' import { toInputUser } from '../../utils/peer-utils.js' import { _getUsersBatched } from '../chats/batched-queries.js' @@ -14,7 +14,7 @@ import { resolvePeerMany } from './resolve-peer-many.js' * * @param ids Users' identifiers. Can be ID, username, phone number, `"me"`, `"self"` or TL object */ -export async function getUsers(client: BaseTelegramClient, ids: MaybeArray): Promise<(User | null)[]> { +export async function getUsers(client: ITelegramClient, ids: MaybeArray): Promise<(User | null)[]> { if (!Array.isArray(ids)) { // avoid unnecessary overhead of Promise.all and resolvePeerMany const res = await _getUsersBatched(client, toInputUser(await resolvePeer(client, ids))) diff --git a/packages/client/src/methods/users/iter-profile-photos.ts b/packages/core/src/highlevel/methods/users/iter-profile-photos.ts similarity index 94% rename from packages/client/src/methods/users/iter-profile-photos.ts rename to packages/core/src/highlevel/methods/users/iter-profile-photos.ts index 92ce1b0b..d076efe5 100644 --- a/packages/client/src/methods/users/iter-profile-photos.ts +++ b/packages/core/src/highlevel/methods/users/iter-profile-photos.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike, Photo } from '../../types/index.js' import { toInputUser } from '../../utils/peer-utils.js' import { getProfilePhotos } from './get-profile-photos.js' @@ -12,7 +11,7 @@ import { resolvePeer } from './resolve-peer.js' * @param params */ export async function* iterProfilePhotos( - client: BaseTelegramClient, + client: ITelegramClient, userId: InputPeerLike, params?: Parameters[2] & { /** diff --git a/packages/client/src/methods/users/resolve-peer-many.ts b/packages/core/src/highlevel/methods/users/resolve-peer-many.ts similarity index 89% rename from packages/client/src/methods/users/resolve-peer-many.ts rename to packages/core/src/highlevel/methods/users/resolve-peer-many.ts index 8263ee9e..b6a19765 100644 --- a/packages/client/src/methods/users/resolve-peer-many.ts +++ b/packages/core/src/highlevel/methods/users/resolve-peer-many.ts @@ -1,6 +1,7 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' -import { ConditionVariable } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { ConditionVariable } from '../../../utils/condition-variable.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { resolvePeer } from './resolve-peer.js' @@ -15,7 +16,7 @@ import { resolvePeer } from './resolve-peer.js' * @param normalizer Normalization function */ export async function resolvePeerMany( - client: BaseTelegramClient, + client: ITelegramClient, peerIds: InputPeerLike[], normalizer: (obj: tl.TypeInputPeer) => T | null, ): Promise @@ -27,13 +28,13 @@ export async function resolvePeerMany +export async function resolvePeerMany(client: ITelegramClient, peerIds: InputPeerLike[]): Promise /** * @internal */ export async function resolvePeerMany( - client: BaseTelegramClient, + client: ITelegramClient, peerIds: InputPeerLike[], normalizer?: (obj: tl.TypeInputPeer) => tl.TypeInputPeer | tl.TypeInputUser | tl.TypeInputChannel | null, ): Promise<(tl.TypeInputPeer | tl.TypeInputUser | tl.TypeInputChannel)[]> { diff --git a/packages/client/src/methods/users/resolve-peer.test.ts b/packages/core/src/highlevel/methods/users/resolve-peer.test.ts similarity index 99% rename from packages/client/src/methods/users/resolve-peer.test.ts rename to packages/core/src/highlevel/methods/users/resolve-peer.test.ts index 40ee5afa..6d6bec08 100644 --- a/packages/client/src/methods/users/resolve-peer.test.ts +++ b/packages/core/src/highlevel/methods/users/resolve-peer.test.ts @@ -1,6 +1,6 @@ +import Long from 'long' import { describe, expect, it, vi } from 'vitest' -import { Long } from '@mtcute/core' import { createStub, StubTelegramClient } from '@mtcute/test' import { Chat, MtPeerNotFoundError, User } from '../../types/index.js' diff --git a/packages/client/src/methods/users/resolve-peer.ts b/packages/core/src/highlevel/methods/users/resolve-peer.ts similarity index 94% rename from packages/client/src/methods/users/resolve-peer.ts rename to packages/core/src/highlevel/methods/users/resolve-peer.ts index 9360c02c..f68cb103 100644 --- a/packages/client/src/methods/users/resolve-peer.ts +++ b/packages/core/src/highlevel/methods/users/resolve-peer.ts @@ -1,13 +1,10 @@ -import { - BaseTelegramClient, - getMarkedPeerId, - Long, - MtTypeAssertionError, - parseMarkedPeerId, - tl, - toggleChannelIdMark, -} from '@mtcute/core' +import Long from 'long' +import { tl } from '@mtcute/tl' + +import { MtTypeAssertionError } from '../../../types/errors.js' +import { getMarkedPeerId, parseMarkedPeerId, toggleChannelIdMark } from '../../../utils/peer-utils.js' +import { ITelegramClient } from '../../client.types.js' import { MtPeerNotFoundError } from '../../types/errors.js' import { InputPeerLike } from '../../types/peers/index.js' import { toInputPeer } from '../../utils/peer-utils.js' @@ -21,7 +18,7 @@ import { toInputPeer } from '../../utils/peer-utils.js' * @param force Whether to force re-fetch the peer from the server (only for usernames and phone numbers) */ export async function resolvePeer( - client: BaseTelegramClient, + client: ITelegramClient, peerId: InputPeerLike, force = false, ): Promise { diff --git a/packages/client/src/methods/users/set-global-ttl.ts b/packages/core/src/highlevel/methods/users/set-global-ttl.ts similarity index 60% rename from packages/client/src/methods/users/set-global-ttl.ts rename to packages/core/src/highlevel/methods/users/set-global-ttl.ts index 264972f6..5b68fdba 100644 --- a/packages/client/src/methods/users/set-global-ttl.ts +++ b/packages/core/src/highlevel/methods/users/set-global-ttl.ts @@ -1,5 +1,5 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' /** * Changes the current default value of the Time-To-Live setting, @@ -7,7 +7,7 @@ import { assertTrue } from '@mtcute/core/utils.js' * * @param period New TTL period, in seconds (or 0 to disable) */ -export async function setGlobalTtl(client: BaseTelegramClient, period: number): Promise { +export async function setGlobalTtl(client: ITelegramClient, period: number): Promise { const r = await client.call({ _: 'messages.setDefaultHistoryTTL', period, diff --git a/packages/client/src/methods/users/set-my-emoji-status.ts b/packages/core/src/highlevel/methods/users/set-my-emoji-status.ts similarity index 80% rename from packages/client/src/methods/users/set-my-emoji-status.ts rename to packages/core/src/highlevel/methods/users/set-my-emoji-status.ts index 0294b4f9..e6fe644c 100644 --- a/packages/client/src/methods/users/set-my-emoji-status.ts +++ b/packages/core/src/highlevel/methods/users/set-my-emoji-status.ts @@ -1,6 +1,8 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' -import { assertTrue, normalizeDate } from '../../utils/index.js' +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' +import { normalizeDate } from '../../utils/index.js' /** * Set an emoji status for the current user @@ -8,7 +10,7 @@ import { assertTrue, normalizeDate } from '../../utils/index.js' * @param emoji Custom emoji ID or `null` to remove the emoji */ export async function setMyEmojiStatus( - client: BaseTelegramClient, + client: ITelegramClient, emoji: tl.Long | null, params?: { /** diff --git a/packages/client/src/methods/users/set-my-profile-photo.ts b/packages/core/src/highlevel/methods/users/set-my-profile-photo.ts similarity index 85% rename from packages/client/src/methods/users/set-my-profile-photo.ts rename to packages/core/src/highlevel/methods/users/set-my-profile-photo.ts index b88fe373..5b92f400 100644 --- a/packages/client/src/methods/users/set-my-profile-photo.ts +++ b/packages/core/src/highlevel/methods/users/set-my-profile-photo.ts @@ -1,7 +1,10 @@ -import { BaseTelegramClient, MtArgumentError, tl } from '@mtcute/core' -import { fileIdToInputPhoto, tdFileId } from '@mtcute/file-id' +import { tdFileId } from '@mtcute/file-id' +import { tl } from '@mtcute/tl' +import { MtArgumentError } from '../../../types/errors.js' +import { ITelegramClient } from '../../client.types.js' import { InputFileLike, Photo } from '../../types/index.js' +import { fileIdToInputPhoto } from '../../utils/convert-file-id.js' import { _normalizeInputFile } from '../files/normalize-input-file.js' /** @@ -10,7 +13,7 @@ import { _normalizeInputFile } from '../files/normalize-input-file.js' * You can also pass a file ID or an InputPhoto to re-use existing photo. */ export async function setMyProfilePhoto( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** Media type (photo or video) */ type: 'photo' | 'video' diff --git a/packages/client/src/methods/users/set-my-username.ts b/packages/core/src/highlevel/methods/users/set-my-username.ts similarity index 66% rename from packages/client/src/methods/users/set-my-username.ts rename to packages/core/src/highlevel/methods/users/set-my-username.ts index 4daf38b8..72b8524b 100644 --- a/packages/client/src/methods/users/set-my-username.ts +++ b/packages/core/src/highlevel/methods/users/set-my-username.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { User } from '../../types/index.js' /** @@ -10,7 +9,7 @@ import { User } from '../../types/index.js' * * @param username New username (5-32 chars, allowed chars: `a-zA-Z0-9_`), or `null` to remove */ -export async function setMyUsername(client: BaseTelegramClient, username: string | null): Promise { +export async function setMyUsername(client: ITelegramClient, username: string | null): Promise { if (username === null) username = '' const res = await client.call({ @@ -18,8 +17,7 @@ export async function setMyUsername(client: BaseTelegramClient, username: string username, }) - // todo - // getAuthState(client).selfUsername = username || null + client.storage.self.update({ username }) return new User(res) } diff --git a/packages/client/src/methods/users/set-offline.ts b/packages/core/src/highlevel/methods/users/set-offline.ts similarity index 54% rename from packages/client/src/methods/users/set-offline.ts rename to packages/core/src/highlevel/methods/users/set-offline.ts index aa650a9d..1a42c358 100644 --- a/packages/client/src/methods/users/set-offline.ts +++ b/packages/core/src/highlevel/methods/users/set-offline.ts @@ -1,12 +1,12 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' /** * Change user status to offline or online * * @param offline Whether the user is currently offline */ -export async function setOffline(client: BaseTelegramClient, offline = true): Promise { +export async function setOffline(client: ITelegramClient, offline = true): Promise { const r = await client.call({ _: 'account.updateStatus', offline, diff --git a/packages/client/src/methods/users/unblock-user.ts b/packages/core/src/highlevel/methods/users/unblock-user.ts similarity index 61% rename from packages/client/src/methods/users/unblock-user.ts rename to packages/core/src/highlevel/methods/users/unblock-user.ts index 6f092d0c..35a07a15 100644 --- a/packages/client/src/methods/users/unblock-user.ts +++ b/packages/core/src/highlevel/methods/users/unblock-user.ts @@ -1,6 +1,5 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { assertTrue } from '@mtcute/core/utils.js' - +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' import { InputPeerLike } from '../../types/index.js' import { resolvePeer } from './resolve-peer.js' @@ -9,7 +8,7 @@ import { resolvePeer } from './resolve-peer.js' * * @param id User ID, username or phone number */ -export async function unblockUser(client: BaseTelegramClient, id: InputPeerLike): Promise { +export async function unblockUser(client: ITelegramClient, id: InputPeerLike): Promise { const r = await client.call({ _: 'contacts.unblock', id: await resolvePeer(client, id), diff --git a/packages/client/src/methods/users/update-profile.ts b/packages/core/src/highlevel/methods/users/update-profile.ts similarity index 89% rename from packages/client/src/methods/users/update-profile.ts rename to packages/core/src/highlevel/methods/users/update-profile.ts index 97585db4..6e8a60ec 100644 --- a/packages/client/src/methods/users/update-profile.ts +++ b/packages/core/src/highlevel/methods/users/update-profile.ts @@ -1,5 +1,4 @@ -import { BaseTelegramClient } from '@mtcute/core' - +import { ITelegramClient } from '../../client.types.js' import { User } from '../../types/index.js' /** @@ -10,7 +9,7 @@ import { User } from '../../types/index.js' * @param params */ export async function updateProfile( - client: BaseTelegramClient, + client: ITelegramClient, params: { /** * New first name diff --git a/packages/core/src/highlevel/storage/index.ts b/packages/core/src/highlevel/storage/index.ts new file mode 100644 index 00000000..8b3460c4 --- /dev/null +++ b/packages/core/src/highlevel/storage/index.ts @@ -0,0 +1,4 @@ +export * from './provider.js' +export * from './repository/peers.js' +export * from './repository/ref-messages.js' +export * from './storage.js' diff --git a/packages/core/src/highlevel/storage/provider.ts b/packages/core/src/highlevel/storage/provider.ts new file mode 100644 index 00000000..a1ab11d8 --- /dev/null +++ b/packages/core/src/highlevel/storage/provider.ts @@ -0,0 +1,8 @@ +import { IMtStorageProvider } from '../../storage/provider.js' +import { IPeersRepository } from './repository/peers.js' +import { IReferenceMessagesRepository } from './repository/ref-messages.js' + +export interface ITelegramStorageProvider extends IMtStorageProvider { + readonly peers: IPeersRepository + readonly refMessages: IReferenceMessagesRepository +} diff --git a/packages/core/src/storage/repository/peers.test-utils.ts b/packages/core/src/highlevel/storage/repository/peers.test-utils.ts similarity index 98% rename from packages/core/src/storage/repository/peers.test-utils.ts rename to packages/core/src/highlevel/storage/repository/peers.test-utils.ts index c50524c9..59f875e4 100644 --- a/packages/core/src/storage/repository/peers.test-utils.ts +++ b/packages/core/src/highlevel/storage/repository/peers.test-utils.ts @@ -4,7 +4,7 @@ import { createStub } from '@mtcute/test' import { __tlWriterMap } from '@mtcute/tl/binary/writer.js' import { TlBinaryWriter } from '@mtcute/tl-runtime' -import { IStorageDriver } from '../driver.js' +import { IStorageDriver } from '../../../storage/driver.js' import { IPeersRepository } from './peers.js' export function fakePeersRepository(): IPeersRepository { diff --git a/packages/core/src/storage/repository/peers.ts b/packages/core/src/highlevel/storage/repository/peers.ts similarity index 90% rename from packages/core/src/storage/repository/peers.ts rename to packages/core/src/highlevel/storage/repository/peers.ts index 507f3bb2..897368fd 100644 --- a/packages/core/src/storage/repository/peers.ts +++ b/packages/core/src/highlevel/storage/repository/peers.ts @@ -1,4 +1,4 @@ -import { MaybeAsync } from '../../types/utils.js' +import { MaybeAsync } from '../../../types/utils.js' // eslint-disable-next-line @typescript-eslint/no-namespace export namespace IPeersRepository { @@ -24,7 +24,7 @@ export namespace IPeersRepository { } export interface IPeersRepository { - /** Store the given peer*/ // todo remove any reference messages linked to them + /** Store the given peer*/ store(peer: IPeersRepository.PeerInfo): MaybeAsync /** Find a peer by their `id` */ getById(id: number): MaybeAsync diff --git a/packages/core/src/storage/repository/ref-messages.test-utils.ts b/packages/core/src/highlevel/storage/repository/ref-messages.test-utils.ts similarity index 97% rename from packages/core/src/storage/repository/ref-messages.test-utils.ts rename to packages/core/src/highlevel/storage/repository/ref-messages.test-utils.ts index acdabb2d..b8ffc5de 100644 --- a/packages/core/src/storage/repository/ref-messages.test-utils.ts +++ b/packages/core/src/highlevel/storage/repository/ref-messages.test-utils.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from 'vitest' -import { IStorageDriver } from '../driver.js' +import { IStorageDriver } from '../../../storage/driver.js' import { IReferenceMessagesRepository } from './ref-messages.js' export function fakeRefMessagesRepository(): IReferenceMessagesRepository { diff --git a/packages/core/src/storage/repository/ref-messages.ts b/packages/core/src/highlevel/storage/repository/ref-messages.ts similarity index 93% rename from packages/core/src/storage/repository/ref-messages.ts rename to packages/core/src/highlevel/storage/repository/ref-messages.ts index e478f277..71035ba3 100644 --- a/packages/core/src/storage/repository/ref-messages.ts +++ b/packages/core/src/highlevel/storage/repository/ref-messages.ts @@ -1,4 +1,4 @@ -import { MaybeAsync } from '../../types/utils.js' +import { MaybeAsync } from '../../../types/utils.js' export interface IReferenceMessagesRepository { /** Store a reference message */ diff --git a/packages/core/src/highlevel/storage/service/current-user.ts b/packages/core/src/highlevel/storage/service/current-user.ts new file mode 100644 index 00000000..eb96443f --- /dev/null +++ b/packages/core/src/highlevel/storage/service/current-user.ts @@ -0,0 +1,167 @@ +import { tl } from '@mtcute/tl' +import { TlBinaryReader, TlBinaryWriter, TlSerializationCounter } from '@mtcute/tl-runtime' + +import { IKeyValueRepository } from '../../../storage/repository/key-value.js' +import { BaseService, ServiceOptions } from '../../../storage/service/base.js' +import { MtArgumentError } from '../../../types/index.js' +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { extractUsernames } from '../../utils/peer-utils.js' + +export interface CurrentUserInfo { + userId: number + isBot: boolean + isPremium: boolean + usernames: string[] +} + +const KV_CURRENT_USER = 'current_user' + +function serialize(info: CurrentUserInfo | null): Uint8Array { + if (!info) return new Uint8Array(0) + + const hasUsernames = info.usernames.length > 0 + + let usernamesOverhead = hasUsernames ? 4 : 0 + + for (const username of info.usernames) { + // since usernames are always ASCII, string length is the same as byte length + usernamesOverhead += TlSerializationCounter.countBytesOverhead(username.length) + username.length + } + + const writer = TlBinaryWriter.manual(16 + usernamesOverhead) + writer.int(1) // version + + let flags = 0 + if (info.isBot) flags |= 1 + if (hasUsernames) flags |= 2 + if (info.isPremium) flags |= 4 + + writer.int(flags) + writer.int53(info.userId) + + if (hasUsernames) { + writer.int(info.usernames.length) + + for (const username of info.usernames) { + writer.string(username) + } + } + + return writer.result() +} + +function parse(data: Uint8Array): CurrentUserInfo | null { + if (data.length === 0) return null + + const reader = TlBinaryReader.manual(data) + if (reader.int() !== 1) return null + + const flags = reader.int() + const userId = reader.int53() + + let usernames: string[] = [] + + if (flags & 2) { + const len = reader.int() + usernames = new Array(len) + + for (let i = 0; i < len; i++) { + usernames[i] = reader.string() + } + } + + return { + userId, + isBot: (flags & 1) !== 0, + isPremium: (flags & 4) !== 0, + usernames, + } +} + +// todo: add testMode here + +export class CurrentUserService extends BaseService { + constructor( + private _kv: IKeyValueRepository, + opts: ServiceOptions, + ) { + super(opts) + } + + private _cached?: CurrentUserInfo | null + + async store(info: CurrentUserInfo | null): Promise { + if (info && this._cached) { + // update the existing object so the references to it are still valid + if (this._cached.userId === info.userId) { + return + } + + this._cached.userId = info.userId + this._cached.isBot = info.isBot + } else { + this._cached = info + } + + await this._kv.set(KV_CURRENT_USER, serialize(info)) + await this._driver.save?.() + } + + async storeFrom(user: tl.TypeUser): Promise { + assertTypeIs('storeFrom', user, 'user') + + const obj: CurrentUserInfo = { + userId: user.id, + isBot: user.bot!, + isPremium: user.premium!, + usernames: extractUsernames(user), + } + await this.store(obj) + + return obj + } + + async fetch(): Promise { + if (this._cached) return this._cached + + const data = await this._kv.get(KV_CURRENT_USER) + if (!data) return null + + const info = parse(data) + this._cached = info + + return info + } + + getCached(safe = false): CurrentUserInfo | null { + if (this._cached === undefined) { + if (safe) return null + + throw new MtArgumentError('User info is not cached yet') + } + + return this._cached + } + + async update(params: { + username?: string + usernames?: string[] + isPremium?: boolean + }): Promise { + const info = await this.fetch() + if (!info) return + + const { username, usernames, isPremium } = params + + if (isPremium !== undefined) info.isPremium = isPremium + + if (username !== undefined) { + // "main" username is always the first one + info.usernames[0] = username + } else if (usernames !== undefined) { + info.usernames = usernames + } + + return this.store(info) + } +} diff --git a/packages/core/src/storage/service/peers.ts b/packages/core/src/highlevel/storage/service/peers.ts similarity index 91% rename from packages/core/src/storage/service/peers.ts rename to packages/core/src/highlevel/storage/service/peers.ts index 61950d9a..0a3b9273 100644 --- a/packages/core/src/storage/service/peers.ts +++ b/packages/core/src/highlevel/storage/service/peers.ts @@ -2,11 +2,12 @@ import Long from 'long' import { tl } from '@mtcute/tl' -import { longFromFastString, longToFastString } from '../../utils/long-utils.js' -import { LruMap } from '../../utils/lru-map.js' -import { getAllPeersFrom, parseMarkedPeerId, toggleChannelIdMark } from '../../utils/peer-utils.js' +import { BaseService, ServiceOptions } from '../../../storage/service/base.js' +import { longFromFastString, longToFastString } from '../../../utils/long-utils.js' +import { LruMap } from '../../../utils/lru-map.js' +import { getAllPeersFrom, parseMarkedPeerId, toggleChannelIdMark } from '../../../utils/peer-utils.js' +import { extractUsernames } from '../../utils/peer-utils.js' import { IPeersRepository } from '../repository/peers.js' -import { BaseService, ServiceOptions } from './base.js' import { RefMessagesService } from './ref-messages.js' interface CacheItem { @@ -19,8 +20,6 @@ export interface PeersServiceOptions { updatesWriteInterval?: number } -// todo: move into @mtcute/client somehow? - const USERNAME_TTL = 24 * 60 * 60 * 1000 // 1 day function getInputPeer(dto: IPeersRepository.PeerInfo): tl.TypeInputPeer { @@ -47,21 +46,14 @@ function getInputPeer(dto: IPeersRepository.PeerInfo): tl.TypeInputPeer { } } -function getUsernames(obj: tl.RawUser | tl.RawChannel) { - if (obj.usernames?.length) return obj.usernames.map((x) => x.username.toLowerCase()) - if (obj.username) return [obj.username.toLowerCase()] - - return [] -} - export class PeersService extends BaseService { private _cache: LruMap private _pendingWrites = new Map() constructor( - readonly options: PeersServiceOptions, - readonly _peers: IPeersRepository, - readonly _refs: RefMessagesService, + private options: PeersServiceOptions, + private _peers: IPeersRepository, + private _refs: RefMessagesService, common: ServiceOptions, ) { super(common) @@ -107,7 +99,7 @@ export class PeersService extends BaseService { id: peer.id, accessHash: longToFastString(peer.accessHash), phone: peer.phone, - usernames: getUsernames(peer), + usernames: extractUsernames(peer), updated: Date.now(), complete: this._serializeTl(peer), } @@ -137,7 +129,7 @@ export class PeersService extends BaseService { dto = { id: toggleChannelIdMark(peer.id), accessHash: longToFastString(peer.accessHash), - usernames: getUsernames(peer as tl.RawChannel), + usernames: extractUsernames(peer as tl.RawChannel), updated: Date.now(), complete: this._serializeTl(peer), } diff --git a/packages/core/src/storage/service/ref-messages.ts b/packages/core/src/highlevel/storage/service/ref-messages.ts similarity index 91% rename from packages/core/src/storage/service/ref-messages.ts rename to packages/core/src/highlevel/storage/service/ref-messages.ts index 92bb9e8c..67d32d08 100644 --- a/packages/core/src/storage/service/ref-messages.ts +++ b/packages/core/src/highlevel/storage/service/ref-messages.ts @@ -1,6 +1,6 @@ -import { LruMap } from '../../utils/lru-map.js' +import { BaseService, ServiceOptions } from '../../../storage/service/base.js' +import { LruMap } from '../../../utils/lru-map.js' import { IReferenceMessagesRepository } from '../repository/ref-messages.js' -import { BaseService, ServiceOptions } from './base.js' export interface RefMessagesServiceOptions { cacheSize?: number diff --git a/packages/core/src/storage/service/updates.ts b/packages/core/src/highlevel/storage/service/updates.ts similarity index 91% rename from packages/core/src/storage/service/updates.ts rename to packages/core/src/highlevel/storage/service/updates.ts index 396e71c3..621339ca 100644 --- a/packages/core/src/storage/service/updates.ts +++ b/packages/core/src/highlevel/storage/service/updates.ts @@ -1,6 +1,6 @@ -import { dataViewFromBuffer } from '../../utils/buffer-utils.js' -import { IKeyValueRepository } from '../repository/key-value.js' -import { BaseService, ServiceOptions } from './base.js' +import { IKeyValueRepository } from '../../../storage/repository/key-value.js' +import { BaseService, ServiceOptions } from '../../../storage/service/base.js' +import { dataViewFromBuffer } from '../../../utils/buffer-utils.js' const KV_PTS = 'updates_pts' const KV_QTS = 'updates_qts' diff --git a/packages/core/src/highlevel/storage/storage.ts b/packages/core/src/highlevel/storage/storage.ts new file mode 100644 index 00000000..5840a39f --- /dev/null +++ b/packages/core/src/highlevel/storage/storage.ts @@ -0,0 +1,46 @@ +import { StorageManager } from '../../storage/storage.js' +import { PublicPart } from '../../types/utils.js' +import { ITelegramStorageProvider } from './provider.js' +import { CurrentUserService } from './service/current-user.js' +import { PeersService, PeersServiceOptions } from './service/peers.js' +import { RefMessagesService, RefMessagesServiceOptions } from './service/ref-messages.js' +import { UpdatesStateService } from './service/updates.js' + +interface TelegramStorageManagerOptions { + provider: ITelegramStorageProvider +} + +/** @internal */ +export interface TelegramStorageManagerExtraOptions { + refMessages?: RefMessagesServiceOptions + peers?: PeersServiceOptions +} + +export class TelegramStorageManager { + constructor( + private mt: StorageManager, + private options: TelegramStorageManagerOptions & TelegramStorageManagerExtraOptions, + ) {} + + private provider = this.options.provider + + readonly updates = new UpdatesStateService(this.provider.kv, this.mt._serviceOptions) + readonly self: PublicPart = new CurrentUserService(this.provider.kv, this.mt._serviceOptions) + readonly refMsgs = new RefMessagesService( + this.options.refMessages ?? {}, + this.provider.refMessages, + this.mt._serviceOptions, + ) + readonly peers: PublicPart = new PeersService( + this.options.peers ?? {}, + this.provider.peers, + this.refMsgs, + this.mt._serviceOptions, + ) + + async clear() { + await this.provider.peers.deleteAll() + await this.provider.refMessages.deleteAll() + await this.mt.clear() + } +} diff --git a/packages/client/src/types/auth/index.ts b/packages/core/src/highlevel/types/auth/index.ts similarity index 100% rename from packages/client/src/types/auth/index.ts rename to packages/core/src/highlevel/types/auth/index.ts diff --git a/packages/client/src/types/auth/sent-code.ts b/packages/core/src/highlevel/types/auth/sent-code.ts similarity index 98% rename from packages/client/src/types/auth/sent-code.ts rename to packages/core/src/highlevel/types/auth/sent-code.ts index 900f20d2..1c67d75e 100644 --- a/packages/client/src/types/auth/sent-code.ts +++ b/packages/core/src/highlevel/types/auth/sent-code.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' diff --git a/packages/client/src/types/bots/command-scope.ts b/packages/core/src/highlevel/types/bots/command-scope.ts similarity index 98% rename from packages/client/src/types/bots/command-scope.ts rename to packages/core/src/highlevel/types/bots/command-scope.ts index 01ba9b69..3e840668 100644 --- a/packages/client/src/types/bots/command-scope.ts +++ b/packages/core/src/highlevel/types/bots/command-scope.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { InputPeerLike } from '../peers/index.js' diff --git a/packages/client/src/types/bots/game-high-score.ts b/packages/core/src/highlevel/types/bots/game-high-score.ts similarity index 95% rename from packages/client/src/types/bots/game-high-score.ts rename to packages/core/src/highlevel/types/bots/game-high-score.ts index 0a9cd53f..89a21611 100644 --- a/packages/client/src/types/bots/game-high-score.ts +++ b/packages/core/src/highlevel/types/bots/game-high-score.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/bots/index.ts b/packages/core/src/highlevel/types/bots/index.ts similarity index 100% rename from packages/client/src/types/bots/index.ts rename to packages/core/src/highlevel/types/bots/index.ts diff --git a/packages/client/src/types/bots/input/index.ts b/packages/core/src/highlevel/types/bots/input/index.ts similarity index 100% rename from packages/client/src/types/bots/input/index.ts rename to packages/core/src/highlevel/types/bots/input/index.ts diff --git a/packages/client/src/types/bots/input/input-inline-message.ts b/packages/core/src/highlevel/types/bots/input/input-inline-message.ts similarity index 98% rename from packages/client/src/types/bots/input/input-inline-message.ts rename to packages/core/src/highlevel/types/bots/input/input-inline-message.ts index 5913d480..e60d7128 100644 --- a/packages/client/src/types/bots/input/input-inline-message.ts +++ b/packages/core/src/highlevel/types/bots/input/input-inline-message.ts @@ -1,5 +1,7 @@ -import { assertNever, BaseTelegramClient, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { assertNever } from '../../../../types/utils.js' +import { ITelegramClient } from '../../../client.types.js' import { _normalizeInputText } from '../../../methods/misc/normalize-text.js' import { InputText } from '../../../types/misc/entities.js' import { @@ -246,7 +248,7 @@ export namespace BotInlineMessage { /** @internal */ export async function _convertToTl( - client: BaseTelegramClient, + client: ITelegramClient, obj: InputInlineMessage, ): Promise { switch (obj.type) { diff --git a/packages/client/src/types/bots/input/input-inline-result.ts b/packages/core/src/highlevel/types/bots/input/input-inline-result.ts similarity index 99% rename from packages/client/src/types/bots/input/input-inline-result.ts rename to packages/core/src/highlevel/types/bots/input/input-inline-result.ts index a4421fd4..023dcc40 100644 --- a/packages/client/src/types/bots/input/input-inline-result.ts +++ b/packages/core/src/highlevel/types/bots/input/input-inline-result.ts @@ -1,6 +1,8 @@ -import { BaseTelegramClient, MtArgumentError, tl } from '@mtcute/core' -import { fileIdToInputDocument, fileIdToInputPhoto } from '@mtcute/file-id' +import { tl } from '@mtcute/tl' +import { MtArgumentError } from '../../../../types/errors.js' +import { ITelegramClient } from '../../../client.types.js' +import { fileIdToInputDocument, fileIdToInputPhoto } from '../../../utils/convert-file-id.js' import { extractFileName } from '../../../utils/file-utils.js' import { BotInlineMessage, InputInlineMessage } from './input-inline-message.js' @@ -723,7 +725,7 @@ export namespace BotInline { /** @internal */ export async function _convertToTl( - client: BaseTelegramClient, + client: ITelegramClient, results: InputInlineResult[], ): Promise<[boolean, tl.TypeInputBotInlineResult[]]> { const normalizeThumb = (obj: InputInlineResult, fallback?: string): tl.RawInputWebDocument | undefined => { diff --git a/packages/client/src/types/bots/keyboard-builder.test.ts b/packages/core/src/highlevel/types/bots/keyboard-builder.test.ts similarity index 100% rename from packages/client/src/types/bots/keyboard-builder.test.ts rename to packages/core/src/highlevel/types/bots/keyboard-builder.test.ts diff --git a/packages/client/src/types/bots/keyboard-builder.ts b/packages/core/src/highlevel/types/bots/keyboard-builder.ts similarity index 98% rename from packages/client/src/types/bots/keyboard-builder.ts rename to packages/core/src/highlevel/types/bots/keyboard-builder.ts index f46b8b3d..fe1c00e5 100644 --- a/packages/client/src/types/bots/keyboard-builder.ts +++ b/packages/core/src/highlevel/types/bots/keyboard-builder.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import type { InlineKeyboardMarkup, ReplyKeyboardMarkup } from './keyboards.js' diff --git a/packages/client/src/types/bots/keyboards.test.ts b/packages/core/src/highlevel/types/bots/keyboards.test.ts similarity index 99% rename from packages/client/src/types/bots/keyboards.test.ts rename to packages/core/src/highlevel/types/bots/keyboards.test.ts index be1f75fe..bffecd6f 100644 --- a/packages/client/src/types/bots/keyboards.test.ts +++ b/packages/core/src/highlevel/types/bots/keyboards.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { BotKeyboard } from './keyboards.js' diff --git a/packages/client/src/types/bots/keyboards.ts b/packages/core/src/highlevel/types/bots/keyboards.ts similarity index 98% rename from packages/client/src/types/bots/keyboards.ts rename to packages/core/src/highlevel/types/bots/keyboards.ts index 62fc7818..f5943232 100644 --- a/packages/client/src/types/bots/keyboards.ts +++ b/packages/core/src/highlevel/types/bots/keyboards.ts @@ -1,6 +1,7 @@ -import { assertNever, tl } from '@mtcute/core' -import { utf8EncodeToBuffer } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { utf8EncodeToBuffer } from '@mtcute/tl-runtime' +import { assertNever } from '../../../types/utils.js' import { toInputUser } from '../../utils/peer-utils.js' import { BotKeyboardBuilder } from './keyboard-builder.js' diff --git a/packages/client/src/types/calls/discard-reason.ts b/packages/core/src/highlevel/types/calls/discard-reason.ts similarity index 93% rename from packages/client/src/types/calls/discard-reason.ts rename to packages/core/src/highlevel/types/calls/discard-reason.ts index 83dc9a66..839e7b8a 100644 --- a/packages/client/src/types/calls/discard-reason.ts +++ b/packages/core/src/highlevel/types/calls/discard-reason.ts @@ -1,4 +1,6 @@ -import { assertNever, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' + +import { assertNever } from '../../../types/utils.js' /** * Phone call discard reason. Can be: diff --git a/packages/client/src/types/calls/index.ts b/packages/core/src/highlevel/types/calls/index.ts similarity index 100% rename from packages/client/src/types/calls/index.ts rename to packages/core/src/highlevel/types/calls/index.ts diff --git a/packages/client/src/types/conversation.ts b/packages/core/src/highlevel/types/conversation.ts similarity index 95% rename from packages/client/src/types/conversation.ts rename to packages/core/src/highlevel/types/conversation.ts index d001a7a5..06d12be6 100644 --- a/packages/client/src/types/conversation.ts +++ b/packages/core/src/highlevel/types/conversation.ts @@ -1,6 +1,12 @@ -import { BaseTelegramClient, getMarkedPeerId, MaybeAsync, MtArgumentError, MtTimeoutError, tl } from '@mtcute/core' -import { AsyncLock, ControllablePromise, createControllablePromise, Deque } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { MtArgumentError, MtTimeoutError } from '../../types/errors.js' +import { MaybeAsync } from '../../types/utils.js' +import { AsyncLock } from '../../utils/async-lock.js' +import { ControllablePromise, createControllablePromise } from '../../utils/controllable-promise.js' +import { Deque } from '../../utils/deque.js' +import { getMarkedPeerId } from '../../utils/peer-utils.js' +import { ITelegramClient } from '../client.types.js' import { getPeerDialogs } from '../methods/dialogs/get-peer-dialogs.js' import { readHistory } from '../methods/messages/read-history.js' import { sendMedia } from '../methods/messages/send-media.js' @@ -54,7 +60,7 @@ export class Conversation { private _pendingRead: Map> = new Map() constructor( - readonly client: BaseTelegramClient, + readonly client: ITelegramClient, readonly chat: InputPeerLike, ) { if (!(CONVERSATION_SYMBOL in client)) { @@ -66,7 +72,7 @@ export class Conversation { } } - private static _getState(client: BaseTelegramClient): ConversationsState { + private static _getState(client: ITelegramClient): ConversationsState { // eslint-disable-next-line @typescript-eslint/no-explicit-any return (client as any)[CONVERSATION_SYMBOL] as ConversationsState } @@ -77,7 +83,7 @@ export class Conversation { * * @returns `true` if the update was handled by some conversation */ - static handleUpdate(client: BaseTelegramClient, update: ParsedUpdate): boolean { + static handleUpdate(client: ITelegramClient, update: ParsedUpdate): boolean { const state = Conversation._getState(client) if (!state?.hasConversations) return false @@ -172,9 +178,6 @@ export class Conversation { } else { this._lastMessage = this._lastReceivedMessage = 0 } - this.client.on('new_message', this._onNewMessage) - this.client.on('edit_message', this._onEditMessage) - this.client.on('history_read', this._onHistoryRead) const state = Conversation._getState(this.client) @@ -191,10 +194,6 @@ export class Conversation { stop(): void { if (!this._started) return - this.client.off('new_message', this._onNewMessage) - this.client.off('edit_message', this._onEditMessage) - this.client.off('history_read', this._onHistoryRead) - const state = Conversation._getState(this.client) const pending = state.pendingConversations.get(this._chatId) @@ -568,7 +567,7 @@ export class Conversation { this._queuedNewMessage.popFront() } } catch (e: unknown) { - this.client._emitError(e) + this.client.emitError(e) } this._lastMessage = this._lastReceivedMessage = msg.id @@ -598,7 +597,7 @@ export class Conversation { this._pendingEditMessage.delete(msg.id) } })().catch((e) => { - this.client._emitError(e) + this.client.emitError(e) }) } diff --git a/packages/client/src/types/errors.ts b/packages/core/src/highlevel/types/errors.ts similarity index 95% rename from packages/client/src/types/errors.ts rename to packages/core/src/highlevel/types/errors.ts index 2f5e32f4..f2e93fe6 100644 --- a/packages/client/src/types/errors.ts +++ b/packages/core/src/highlevel/types/errors.ts @@ -1,5 +1,4 @@ -import { MtcuteError } from '@mtcute/core' - +import { MtcuteError } from '../../types/errors.js' import { InputPeerLike } from './peers/index.js' /** diff --git a/packages/client/src/types/files/file-location.ts b/packages/core/src/highlevel/types/files/file-location.ts similarity index 97% rename from packages/client/src/types/files/file-location.ts rename to packages/core/src/highlevel/types/files/file-location.ts index c31fd28b..e3745137 100644 --- a/packages/client/src/types/files/file-location.ts +++ b/packages/core/src/highlevel/types/files/file-location.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' diff --git a/packages/client/src/types/files/index.ts b/packages/core/src/highlevel/types/files/index.ts similarity index 100% rename from packages/client/src/types/files/index.ts rename to packages/core/src/highlevel/types/files/index.ts diff --git a/packages/client/src/types/files/uploaded-file.ts b/packages/core/src/highlevel/types/files/uploaded-file.ts similarity index 95% rename from packages/client/src/types/files/uploaded-file.ts rename to packages/core/src/highlevel/types/files/uploaded-file.ts index a8118cb2..3e10a053 100644 --- a/packages/client/src/types/files/uploaded-file.ts +++ b/packages/core/src/highlevel/types/files/uploaded-file.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' /** * Describes a file uploaded to the Telegram servers diff --git a/packages/client/src/types/files/utils.ts b/packages/core/src/highlevel/types/files/utils.ts similarity index 99% rename from packages/client/src/types/files/utils.ts rename to packages/core/src/highlevel/types/files/utils.ts index f90ef73c..44042905 100644 --- a/packages/client/src/types/files/utils.ts +++ b/packages/core/src/highlevel/types/files/utils.ts @@ -1,8 +1,8 @@ /* eslint-disable no-restricted-imports */ import type { ReadStream } from 'fs' -import { tl } from '@mtcute/core' import { tdFileId } from '@mtcute/file-id' +import { tl } from '@mtcute/tl' import { FileLocation } from './file-location.js' import { UploadedFile } from './uploaded-file.js' diff --git a/packages/client/src/types/files/web-document.ts b/packages/core/src/highlevel/types/files/web-document.ts similarity index 94% rename from packages/client/src/types/files/web-document.ts rename to packages/core/src/highlevel/types/files/web-document.ts index b24e6958..786c7467 100644 --- a/packages/client/src/types/files/web-document.ts +++ b/packages/core/src/highlevel/types/files/web-document.ts @@ -1,5 +1,6 @@ -import { MtArgumentError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MtArgumentError } from '../../../types/errors.js' import { makeInspectable } from '../../utils/index.js' import { FileLocation } from './file-location.js' diff --git a/packages/client/src/types/index.ts b/packages/core/src/highlevel/types/index.ts similarity index 100% rename from packages/client/src/types/index.ts rename to packages/core/src/highlevel/types/index.ts diff --git a/packages/client/src/types/media/audio.ts b/packages/core/src/highlevel/types/media/audio.ts similarity index 97% rename from packages/client/src/types/media/audio.ts rename to packages/core/src/highlevel/types/media/audio.ts index 1b201fb7..d2dd8764 100644 --- a/packages/client/src/types/media/audio.ts +++ b/packages/core/src/highlevel/types/media/audio.ts @@ -1,5 +1,5 @@ -import { tl } from '@mtcute/core' import { tdFileId } from '@mtcute/file-id' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/media/contact.ts b/packages/core/src/highlevel/types/media/contact.ts similarity index 96% rename from packages/client/src/types/media/contact.ts rename to packages/core/src/highlevel/types/media/contact.ts index 885e06d4..be15062b 100644 --- a/packages/client/src/types/media/contact.ts +++ b/packages/core/src/highlevel/types/media/contact.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' diff --git a/packages/client/src/types/media/dice.ts b/packages/core/src/highlevel/types/media/dice.ts similarity index 99% rename from packages/client/src/types/media/dice.ts rename to packages/core/src/highlevel/types/media/dice.ts index 59b4180b..a1e86238 100644 --- a/packages/client/src/types/media/dice.ts +++ b/packages/core/src/highlevel/types/media/dice.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' diff --git a/packages/client/src/types/media/document-utils.ts b/packages/core/src/highlevel/types/media/document-utils.ts similarity index 97% rename from packages/client/src/types/media/document-utils.ts rename to packages/core/src/highlevel/types/media/document-utils.ts index 644fca76..b1c88b0f 100644 --- a/packages/client/src/types/media/document-utils.ts +++ b/packages/core/src/highlevel/types/media/document-utils.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { Audio } from './audio.js' import { Document } from './document.js' diff --git a/packages/client/src/types/media/document.ts b/packages/core/src/highlevel/types/media/document.ts similarity index 99% rename from packages/client/src/types/media/document.ts rename to packages/core/src/highlevel/types/media/document.ts index 14a3431f..0597d3dd 100644 --- a/packages/client/src/types/media/document.ts +++ b/packages/core/src/highlevel/types/media/document.ts @@ -1,5 +1,5 @@ -import { tl } from '@mtcute/core' import { tdFileId as td, toFileId, toUniqueFileId } from '@mtcute/file-id' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/media/game.ts b/packages/core/src/highlevel/types/media/game.ts similarity index 98% rename from packages/client/src/types/media/game.ts rename to packages/core/src/highlevel/types/media/game.ts index f6a1702b..3da8867e 100644 --- a/packages/client/src/types/media/game.ts +++ b/packages/core/src/highlevel/types/media/game.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/media/index.ts b/packages/core/src/highlevel/types/media/index.ts similarity index 100% rename from packages/client/src/types/media/index.ts rename to packages/core/src/highlevel/types/media/index.ts diff --git a/packages/client/src/types/media/input-media.ts b/packages/core/src/highlevel/types/media/input-media.ts similarity index 99% rename from packages/client/src/types/media/input-media.ts rename to packages/core/src/highlevel/types/media/input-media.ts index 03343eb0..d4e81c85 100644 --- a/packages/client/src/types/media/input-media.ts +++ b/packages/core/src/highlevel/types/media/input-media.ts @@ -1,5 +1,6 @@ -import { MaybeArray, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MaybeArray } from '../../../types/utils.js' import { InputText } from '../../types/misc/entities.js' import { InputFileLike } from '../files/index.js' import { InputPeerLike } from '../peers/index.js' diff --git a/packages/client/src/types/media/invoice.ts b/packages/core/src/highlevel/types/media/invoice.ts similarity index 98% rename from packages/client/src/types/media/invoice.ts rename to packages/core/src/highlevel/types/media/invoice.ts index 9e7d5155..92e3623d 100644 --- a/packages/client/src/types/media/invoice.ts +++ b/packages/core/src/highlevel/types/media/invoice.ts @@ -1,5 +1,6 @@ -import { MtArgumentError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MtArgumentError } from '../../../types/errors.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { WebDocument } from '../files/web-document.js' diff --git a/packages/client/src/types/media/location.ts b/packages/core/src/highlevel/types/media/location.ts similarity index 99% rename from packages/client/src/types/media/location.ts rename to packages/core/src/highlevel/types/media/location.ts index b64cf13b..b09ed6d1 100644 --- a/packages/client/src/types/media/location.ts +++ b/packages/core/src/highlevel/types/media/location.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { FileLocation } from '../files/index.js' diff --git a/packages/client/src/types/media/photo.ts b/packages/core/src/highlevel/types/media/photo.ts similarity index 98% rename from packages/client/src/types/media/photo.ts rename to packages/core/src/highlevel/types/media/photo.ts index df65c177..7e7d411a 100644 --- a/packages/client/src/types/media/photo.ts +++ b/packages/core/src/highlevel/types/media/photo.ts @@ -1,5 +1,6 @@ -import { MtArgumentError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MtArgumentError } from '../../../types/errors.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { FileLocation } from '../files/index.js' diff --git a/packages/client/src/types/media/poll.ts b/packages/core/src/highlevel/types/media/poll.ts similarity index 98% rename from packages/client/src/types/media/poll.ts rename to packages/core/src/highlevel/types/media/poll.ts index 0c0b1c21..7b8833dd 100644 --- a/packages/client/src/types/media/poll.ts +++ b/packages/core/src/highlevel/types/media/poll.ts @@ -1,4 +1,6 @@ -import { Long, tl } from '@mtcute/core' +import Long from 'long' + +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/media/sticker.ts b/packages/core/src/highlevel/types/media/sticker.ts similarity index 98% rename from packages/client/src/types/media/sticker.ts rename to packages/core/src/highlevel/types/media/sticker.ts index 9dcb7878..8a5f2604 100644 --- a/packages/client/src/types/media/sticker.ts +++ b/packages/core/src/highlevel/types/media/sticker.ts @@ -1,6 +1,7 @@ -import { MtArgumentError, tl } from '@mtcute/core' import { tdFileId } from '@mtcute/file-id' +import { tl } from '@mtcute/tl' +import { MtArgumentError } from '../../../types/errors.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { RawDocument } from './document.js' diff --git a/packages/client/src/types/media/story.ts b/packages/core/src/highlevel/types/media/story.ts similarity index 97% rename from packages/client/src/types/media/story.ts rename to packages/core/src/highlevel/types/media/story.ts index 57749d0e..845f5613 100644 --- a/packages/client/src/types/media/story.ts +++ b/packages/core/src/highlevel/types/media/story.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/inspectable.js' import { parsePeer, Peer } from '../peers/peer.js' diff --git a/packages/client/src/types/media/thumbnail.ts b/packages/core/src/highlevel/types/media/thumbnail.ts similarity index 97% rename from packages/client/src/types/media/thumbnail.ts rename to packages/core/src/highlevel/types/media/thumbnail.ts index c7b29ea5..a9d18421 100644 --- a/packages/client/src/types/media/thumbnail.ts +++ b/packages/core/src/highlevel/types/media/thumbnail.ts @@ -1,7 +1,10 @@ -import { Long, MtArgumentError, MtTypeAssertionError, tl } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' -import { tdFileId as td, toFileId, toUniqueFileId } from '@mtcute/file-id' +import Long from 'long' +import { tdFileId as td, toFileId, toUniqueFileId } from '@mtcute/file-id' +import { tl } from '@mtcute/tl' + +import { MtArgumentError, MtTypeAssertionError } from '../../../types/errors.js' +import { assertTypeIs } from '../../../utils/type-assertions.js' import { inflateSvgPath, strippedPhotoToJpg, svgPathToFile } from '../../utils/file-utils.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/media/venue.ts b/packages/core/src/highlevel/types/media/venue.ts similarity index 96% rename from packages/client/src/types/media/venue.ts rename to packages/core/src/highlevel/types/media/venue.ts index 6efb74ad..374e57ad 100644 --- a/packages/client/src/types/media/venue.ts +++ b/packages/core/src/highlevel/types/media/venue.ts @@ -1,6 +1,6 @@ -import { tl } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { assertTypeIs } from '../../../utils/type-assertions.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { Location } from './location.js' diff --git a/packages/client/src/types/media/video.ts b/packages/core/src/highlevel/types/media/video.ts similarity index 98% rename from packages/client/src/types/media/video.ts rename to packages/core/src/highlevel/types/media/video.ts index 5fac890a..ba4274f9 100644 --- a/packages/client/src/types/media/video.ts +++ b/packages/core/src/highlevel/types/media/video.ts @@ -1,5 +1,5 @@ -import { tl } from '@mtcute/core' import { tdFileId } from '@mtcute/file-id' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/media/voice.ts b/packages/core/src/highlevel/types/media/voice.ts similarity index 97% rename from packages/client/src/types/media/voice.ts rename to packages/core/src/highlevel/types/media/voice.ts index 4e019a4c..006995bd 100644 --- a/packages/client/src/types/media/voice.ts +++ b/packages/core/src/highlevel/types/media/voice.ts @@ -1,5 +1,5 @@ -import { tl } from '@mtcute/core' import { tdFileId } from '@mtcute/file-id' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/media/web-page.ts b/packages/core/src/highlevel/types/media/web-page.ts similarity index 98% rename from packages/client/src/types/media/web-page.ts rename to packages/core/src/highlevel/types/media/web-page.ts index 5ea177f9..456fee51 100644 --- a/packages/client/src/types/media/web-page.ts +++ b/packages/core/src/highlevel/types/media/web-page.ts @@ -1,5 +1,6 @@ -import { MtArgumentError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MtArgumentError } from '../../../types/errors.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { RawDocument } from './document.js' diff --git a/packages/client/src/types/messages/dialog.ts b/packages/core/src/highlevel/types/messages/dialog.ts similarity index 97% rename from packages/client/src/types/messages/dialog.ts rename to packages/core/src/highlevel/types/messages/dialog.ts index 50c40ef5..6b873dde 100644 --- a/packages/client/src/types/messages/dialog.ts +++ b/packages/core/src/highlevel/types/messages/dialog.ts @@ -1,6 +1,8 @@ -import { getMarkedPeerId, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' -import { assertTypeIsNot, hasValueAtKey, makeInspectable } from '../../utils/index.js' +import { getMarkedPeerId } from '../../../utils/peer-utils.js' +import { assertTypeIsNot, hasValueAtKey } from '../../../utils/type-assertions.js' +import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { MtMessageNotFoundError } from '../errors.js' import { Chat } from '../peers/chat.js' diff --git a/packages/client/src/types/messages/draft-message.ts b/packages/core/src/highlevel/types/messages/draft-message.ts similarity index 97% rename from packages/client/src/types/messages/draft-message.ts rename to packages/core/src/highlevel/types/messages/draft-message.ts index 5de6b437..c5c36bec 100644 --- a/packages/client/src/types/messages/draft-message.ts +++ b/packages/core/src/highlevel/types/messages/draft-message.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/messages/index.ts b/packages/core/src/highlevel/types/messages/index.ts similarity index 100% rename from packages/client/src/types/messages/index.ts rename to packages/core/src/highlevel/types/messages/index.ts diff --git a/packages/client/src/types/messages/input-message-id.ts b/packages/core/src/highlevel/types/messages/input-message-id.ts similarity index 100% rename from packages/client/src/types/messages/input-message-id.ts rename to packages/core/src/highlevel/types/messages/input-message-id.ts diff --git a/packages/client/src/types/messages/message-action.ts b/packages/core/src/highlevel/types/messages/message-action.ts similarity index 99% rename from packages/client/src/types/messages/message-action.ts rename to packages/core/src/highlevel/types/messages/message-action.ts index a09d14c5..6fad2179 100644 --- a/packages/client/src/types/messages/message-action.ts +++ b/packages/core/src/highlevel/types/messages/message-action.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { _callDiscardReasonFromTl, CallDiscardReason } from '../calls/index.js' import { Photo } from '../media/photo.js' diff --git a/packages/client/src/types/messages/message-entity.ts b/packages/core/src/highlevel/types/messages/message-entity.ts similarity index 99% rename from packages/client/src/types/messages/message-entity.ts rename to packages/core/src/highlevel/types/messages/message-entity.ts index 7135e2dc..3d31679b 100644 --- a/packages/client/src/types/messages/message-entity.ts +++ b/packages/core/src/highlevel/types/messages/message-entity.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/messages/message-forward.ts b/packages/core/src/highlevel/types/messages/message-forward.ts similarity index 95% rename from packages/client/src/types/messages/message-forward.ts rename to packages/core/src/highlevel/types/messages/message-forward.ts index e79cbb23..ad30dfce 100644 --- a/packages/client/src/types/messages/message-forward.ts +++ b/packages/core/src/highlevel/types/messages/message-forward.ts @@ -1,5 +1,6 @@ -import { MtTypeAssertionError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MtTypeAssertionError } from '../../../types/errors.js' import { makeInspectable } from '../../utils/inspectable.js' import { memoizeGetters } from '../../utils/memoize.js' import { Chat } from '../peers/chat.js' diff --git a/packages/client/src/types/messages/message-media.ts b/packages/core/src/highlevel/types/messages/message-media.ts similarity index 97% rename from packages/client/src/types/messages/message-media.ts rename to packages/core/src/highlevel/types/messages/message-media.ts index a07ee094..17ee96a2 100644 --- a/packages/client/src/types/messages/message-media.ts +++ b/packages/core/src/highlevel/types/messages/message-media.ts @@ -1,5 +1,6 @@ -import { MtTypeAssertionError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MtTypeAssertionError } from '../../../types/errors.js' import { Audio } from '../media/audio.js' import { Contact } from '../media/contact.js' import { Dice } from '../media/dice.js' diff --git a/packages/client/src/types/messages/message-reactions.ts b/packages/core/src/highlevel/types/messages/message-reactions.ts similarity index 97% rename from packages/client/src/types/messages/message-reactions.ts rename to packages/core/src/highlevel/types/messages/message-reactions.ts index 371f21d0..a8438e71 100644 --- a/packages/client/src/types/messages/message-reactions.ts +++ b/packages/core/src/highlevel/types/messages/message-reactions.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/messages/message-replies.ts b/packages/core/src/highlevel/types/messages/message-replies.ts similarity index 95% rename from packages/client/src/types/messages/message-replies.ts rename to packages/core/src/highlevel/types/messages/message-replies.ts index 652385be..7f7b6547 100644 --- a/packages/client/src/types/messages/message-replies.ts +++ b/packages/core/src/highlevel/types/messages/message-replies.ts @@ -1,5 +1,6 @@ -import { getMarkedPeerId, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { getMarkedPeerId } from '../../../utils/peer-utils.js' import { makeInspectable } from '../../utils/inspectable.js' import { memoizeGetters } from '../../utils/memoize.js' import { parsePeer, Peer } from '../peers/peer.js' diff --git a/packages/client/src/types/messages/message.ts b/packages/core/src/highlevel/types/messages/message.ts similarity index 97% rename from packages/client/src/types/messages/message.ts rename to packages/core/src/highlevel/types/messages/message.ts index fa23ee3b..38d0e516 100644 --- a/packages/client/src/types/messages/message.ts +++ b/packages/core/src/highlevel/types/messages/message.ts @@ -1,6 +1,9 @@ -import { assertNever, getMarkedPeerId, MtArgumentError, tl, toggleChannelIdMark } from '@mtcute/core' -import { assertTypeIsNot } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { MtArgumentError } from '../../../types/errors.js' +import { assertNever } from '../../../types/utils.js' +import { getMarkedPeerId, toggleChannelIdMark } from '../../../utils/peer-utils.js' +import { assertTypeIsNot } from '../../../utils/type-assertions.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { BotKeyboard, ReplyMarkup } from '../bots/keyboards.js' diff --git a/packages/client/src/types/messages/replied-message.ts b/packages/core/src/highlevel/types/messages/replied-message.ts similarity index 98% rename from packages/client/src/types/messages/replied-message.ts rename to packages/core/src/highlevel/types/messages/replied-message.ts index 13b3b517..dfa45d85 100644 --- a/packages/client/src/types/messages/replied-message.ts +++ b/packages/core/src/highlevel/types/messages/replied-message.ts @@ -1,5 +1,6 @@ -import { MtTypeAssertionError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MtTypeAssertionError } from '../../../types/errors.js' import { makeInspectable } from '../../utils/inspectable.js' import { memoizeGetters } from '../../utils/memoize.js' import { Chat } from '../peers/chat.js' diff --git a/packages/client/src/types/messages/search-filters.ts b/packages/core/src/highlevel/types/messages/search-filters.ts similarity index 100% rename from packages/client/src/types/messages/search-filters.ts rename to packages/core/src/highlevel/types/messages/search-filters.ts diff --git a/packages/client/src/types/misc/entities.ts b/packages/core/src/highlevel/types/misc/entities.ts similarity index 91% rename from packages/client/src/types/misc/entities.ts rename to packages/core/src/highlevel/types/misc/entities.ts index 363a695a..ae9800e2 100644 --- a/packages/client/src/types/misc/entities.ts +++ b/packages/core/src/highlevel/types/misc/entities.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' /** * Formatted text with entities diff --git a/packages/client/src/types/misc/index.ts b/packages/core/src/highlevel/types/misc/index.ts similarity index 100% rename from packages/client/src/types/misc/index.ts rename to packages/core/src/highlevel/types/misc/index.ts diff --git a/packages/client/src/types/misc/input-privacy-rule.ts b/packages/core/src/highlevel/types/misc/input-privacy-rule.ts similarity index 97% rename from packages/client/src/types/misc/input-privacy-rule.ts rename to packages/core/src/highlevel/types/misc/input-privacy-rule.ts index a1ffb5a2..952fe85f 100644 --- a/packages/client/src/types/misc/input-privacy-rule.ts +++ b/packages/core/src/highlevel/types/misc/input-privacy-rule.ts @@ -1,7 +1,8 @@ /* eslint-disable @typescript-eslint/no-namespace */ -import { MaybeArray, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MaybeArray } from '../../../types/utils.js' import { InputPeerLike } from '../peers/index.js' export interface InputPrivacyRuleUsers { diff --git a/packages/client/src/types/misc/sticker-set.ts b/packages/core/src/highlevel/types/misc/sticker-set.ts similarity index 98% rename from packages/client/src/types/misc/sticker-set.ts rename to packages/core/src/highlevel/types/misc/sticker-set.ts index 55de608c..4b249dff 100644 --- a/packages/client/src/types/misc/sticker-set.ts +++ b/packages/core/src/highlevel/types/misc/sticker-set.ts @@ -1,6 +1,7 @@ -import { MtTypeAssertionError, tl } from '@mtcute/core' -import { LongMap } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { MtTypeAssertionError } from '../../../types/errors.js' +import { LongMap } from '../../../utils/long-utils.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { MtEmptyError } from '../errors.js' diff --git a/packages/client/src/types/misc/takeout-session.ts b/packages/core/src/highlevel/types/misc/takeout-session.ts similarity index 86% rename from packages/client/src/types/misc/takeout-session.ts rename to packages/core/src/highlevel/types/misc/takeout-session.ts index b29caf21..137d2cd9 100644 --- a/packages/client/src/types/misc/takeout-session.ts +++ b/packages/core/src/highlevel/types/misc/takeout-session.ts @@ -1,6 +1,10 @@ -import { BaseTelegramClient, MustEqual, RpcCallOptions, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' -import { assertTrue, makeInspectable } from '../../utils/index.js' +import { RpcCallOptions } from '../../../network/network-manager.js' +import { MustEqual } from '../../../types/utils.js' +import { assertTrue } from '../../../utils/type-assertions.js' +import { ITelegramClient } from '../../client.types.js' +import { makeInspectable } from '../../utils/index.js' /** * Account takeout session @@ -12,7 +16,7 @@ export class TakeoutSession { readonly id: tl.Long constructor( - readonly client: BaseTelegramClient, + readonly client: ITelegramClient, session: tl.account.RawTakeout, ) { this.id = session.id @@ -60,7 +64,7 @@ export class TakeoutSession { * that call should be called via takeout session or not. * Returning `true` will use takeout session, `false` will not. */ - createProxy(predicate?: (obj: tl.TlObject) => boolean): BaseTelegramClient { + createProxy(predicate?: (obj: tl.TlObject) => boolean): ITelegramClient { const boundCall: TakeoutSession['call'] = predicate ? (obj, params) => { if (predicate(obj)) { diff --git a/packages/client/src/types/peers/chat-colors.ts b/packages/core/src/highlevel/types/peers/chat-colors.ts similarity index 96% rename from packages/client/src/types/peers/chat-colors.ts rename to packages/core/src/highlevel/types/peers/chat-colors.ts index db6a1150..66872463 100644 --- a/packages/client/src/types/peers/chat-colors.ts +++ b/packages/core/src/highlevel/types/peers/chat-colors.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/inspectable.js' diff --git a/packages/client/src/types/peers/chat-event/actions.ts b/packages/core/src/highlevel/types/peers/chat-event/actions.ts similarity index 99% rename from packages/client/src/types/peers/chat-event/actions.ts rename to packages/core/src/highlevel/types/peers/chat-event/actions.ts index 854cf03d..cbfced39 100644 --- a/packages/client/src/types/peers/chat-event/actions.ts +++ b/packages/core/src/highlevel/types/peers/chat-event/actions.ts @@ -1,6 +1,7 @@ -import { tl, toggleChannelIdMark } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { toggleChannelIdMark } from '../../../../utils/peer-utils.js' +import { assertTypeIs } from '../../../../utils/type-assertions.js' import { Photo } from '../../media/photo.js' import { Message } from '../../messages/message.js' import { ChatInviteLink } from '../chat-invite-link.js' diff --git a/packages/client/src/types/peers/chat-event/filters.ts b/packages/core/src/highlevel/types/peers/chat-event/filters.ts similarity index 97% rename from packages/client/src/types/peers/chat-event/filters.ts rename to packages/core/src/highlevel/types/peers/chat-event/filters.ts index 336ab2fd..9bb21c98 100644 --- a/packages/client/src/types/peers/chat-event/filters.ts +++ b/packages/core/src/highlevel/types/peers/chat-event/filters.ts @@ -1,5 +1,6 @@ -import { assertNever, MaybeArray, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { assertNever, MaybeArray } from '../../../../types/utils.js' import { ChatAction } from './actions.js' export interface ChatEventFilters { diff --git a/packages/client/src/types/peers/chat-event/index.ts b/packages/core/src/highlevel/types/peers/chat-event/index.ts similarity index 97% rename from packages/client/src/types/peers/chat-event/index.ts rename to packages/core/src/highlevel/types/peers/chat-event/index.ts index 35e53a7b..718cb7e7 100644 --- a/packages/client/src/types/peers/chat-event/index.ts +++ b/packages/core/src/highlevel/types/peers/chat-event/index.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../../utils/index.js' import { memoizeGetters } from '../../../utils/memoize.js' diff --git a/packages/client/src/types/peers/chat-invite-link-member.ts b/packages/core/src/highlevel/types/peers/chat-invite-link-member.ts similarity index 97% rename from packages/client/src/types/peers/chat-invite-link-member.ts rename to packages/core/src/highlevel/types/peers/chat-invite-link-member.ts index 9761e864..e2cac291 100644 --- a/packages/client/src/types/peers/chat-invite-link-member.ts +++ b/packages/core/src/highlevel/types/peers/chat-invite-link-member.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/peers/chat-invite-link.ts b/packages/core/src/highlevel/types/peers/chat-invite-link.ts similarity index 96% rename from packages/client/src/types/peers/chat-invite-link.ts rename to packages/core/src/highlevel/types/peers/chat-invite-link.ts index 52b689bc..1400853b 100644 --- a/packages/client/src/types/peers/chat-invite-link.ts +++ b/packages/core/src/highlevel/types/peers/chat-invite-link.ts @@ -1,6 +1,6 @@ -import { tl } from '@mtcute/core' -import { assertTypeIsNot } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { assertTypeIsNot } from '../../../utils/type-assertions.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { PeersIndex } from './index.js' @@ -17,6 +17,7 @@ export class ChatInviteLink { readonly _peers?: PeersIndex, ) { assertTypeIsNot('ChatInviteLink', raw, 'chatInvitePublicJoinRequests') + this.raw = raw } diff --git a/packages/client/src/types/peers/chat-location.ts b/packages/core/src/highlevel/types/peers/chat-location.ts similarity index 94% rename from packages/client/src/types/peers/chat-location.ts rename to packages/core/src/highlevel/types/peers/chat-location.ts index 24e8a27c..9de0f0ac 100644 --- a/packages/client/src/types/peers/chat-location.ts +++ b/packages/core/src/highlevel/types/peers/chat-location.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/peers/chat-member.ts b/packages/core/src/highlevel/types/peers/chat-member.ts similarity index 98% rename from packages/client/src/types/peers/chat-member.ts rename to packages/core/src/highlevel/types/peers/chat-member.ts index b864e139..92269535 100644 --- a/packages/client/src/types/peers/chat-member.ts +++ b/packages/core/src/highlevel/types/peers/chat-member.ts @@ -1,6 +1,6 @@ -import { tl } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { assertTypeIs } from '../../../utils/type-assertions.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { ChatPermissions } from './chat-permissions.js' diff --git a/packages/client/src/types/peers/chat-permissions.ts b/packages/core/src/highlevel/types/peers/chat-permissions.ts similarity index 99% rename from packages/client/src/types/peers/chat-permissions.ts rename to packages/core/src/highlevel/types/peers/chat-permissions.ts index 55182a37..e4c6b252 100644 --- a/packages/client/src/types/peers/chat-permissions.ts +++ b/packages/core/src/highlevel/types/peers/chat-permissions.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' diff --git a/packages/client/src/types/peers/chat-photo.ts b/packages/core/src/highlevel/types/peers/chat-photo.ts similarity index 95% rename from packages/client/src/types/peers/chat-photo.ts rename to packages/core/src/highlevel/types/peers/chat-photo.ts index 3ee3a2f3..eb6d34b1 100644 --- a/packages/client/src/types/peers/chat-photo.ts +++ b/packages/core/src/highlevel/types/peers/chat-photo.ts @@ -1,6 +1,10 @@ -import { Long, MtArgumentError, tl, toggleChannelIdMark } from '@mtcute/core' -import { tdFileId, toFileId, toUniqueFileId } from '@mtcute/file-id' +import Long from 'long' +import { tdFileId, toFileId, toUniqueFileId } from '@mtcute/file-id' +import { tl } from '@mtcute/tl' + +import { MtArgumentError } from '../../../types/errors.js' +import { toggleChannelIdMark } from '../../../utils/peer-utils.js' import { strippedPhotoToJpg } from '../../utils/file-utils.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/peers/chat-preview.ts b/packages/core/src/highlevel/types/peers/chat-preview.ts similarity index 98% rename from packages/client/src/types/peers/chat-preview.ts rename to packages/core/src/highlevel/types/peers/chat-preview.ts index 0750cbad..b47255fd 100644 --- a/packages/client/src/types/peers/chat-preview.ts +++ b/packages/core/src/highlevel/types/peers/chat-preview.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/peers/chat.ts b/packages/core/src/highlevel/types/peers/chat.ts similarity index 99% rename from packages/client/src/types/peers/chat.ts rename to packages/core/src/highlevel/types/peers/chat.ts index da74966c..e82dba5a 100644 --- a/packages/client/src/types/peers/chat.ts +++ b/packages/core/src/highlevel/types/peers/chat.ts @@ -1,5 +1,7 @@ -import { getMarkedPeerId, MtArgumentError, MtTypeAssertionError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MtArgumentError, MtTypeAssertionError } from '../../../types/errors.js' +import { getMarkedPeerId } from '../../../utils/peer-utils.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { Photo } from '../media/photo.js' diff --git a/packages/client/src/types/peers/forum-topic.ts b/packages/core/src/highlevel/types/peers/forum-topic.ts similarity index 96% rename from packages/client/src/types/peers/forum-topic.ts rename to packages/core/src/highlevel/types/peers/forum-topic.ts index b9726caf..e5409126 100644 --- a/packages/client/src/types/peers/forum-topic.ts +++ b/packages/core/src/highlevel/types/peers/forum-topic.ts @@ -1,6 +1,7 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' -import { hasValueAtKey, makeInspectable } from '../../utils/index.js' +import { hasValueAtKey } from '../../../utils/type-assertions.js' +import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { MtMessageNotFoundError } from '../errors.js' import { DraftMessage, Message } from '../messages/index.js' diff --git a/packages/client/src/types/peers/index.ts b/packages/core/src/highlevel/types/peers/index.ts similarity index 100% rename from packages/client/src/types/peers/index.ts rename to packages/core/src/highlevel/types/peers/index.ts diff --git a/packages/client/src/types/peers/peer.ts b/packages/core/src/highlevel/types/peers/peer.ts similarity index 98% rename from packages/client/src/types/peers/peer.ts rename to packages/core/src/highlevel/types/peers/peer.ts index f3e1f1b7..1169d33a 100644 --- a/packages/client/src/types/peers/peer.ts +++ b/packages/core/src/highlevel/types/peers/peer.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { Chat } from './chat.js' import { PeersIndex } from './peers-index.js' diff --git a/packages/client/src/types/peers/peers-index.test.ts b/packages/core/src/highlevel/types/peers/peers-index.test.ts similarity index 98% rename from packages/client/src/types/peers/peers-index.test.ts rename to packages/core/src/highlevel/types/peers/peers-index.test.ts index 56749751..238cdb2e 100644 --- a/packages/client/src/types/peers/peers-index.test.ts +++ b/packages/core/src/highlevel/types/peers/peers-index.test.ts @@ -1,8 +1,8 @@ import { describe, expect, it } from 'vitest' -import { MtArgumentError } from '@mtcute/core/src/index.js' import { createStub } from '@mtcute/test' +import { MtArgumentError } from '../../../types/errors.js' import { PeersIndex } from './peers-index.js' describe('PeersIndex', () => { diff --git a/packages/client/src/types/peers/peers-index.ts b/packages/core/src/highlevel/types/peers/peers-index.ts similarity index 87% rename from packages/client/src/types/peers/peers-index.ts rename to packages/core/src/highlevel/types/peers/peers-index.ts index 14401ff5..898b1891 100644 --- a/packages/client/src/types/peers/peers-index.ts +++ b/packages/core/src/highlevel/types/peers/peers-index.ts @@ -1,10 +1,14 @@ -import { MtArgumentError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' + +import { MtArgumentError } from '../../../types/errors.js' const ERROR_MSG = 'Given peer is not available in this index. This is most likely an internal library error.' export class PeersIndex { - readonly users: Map = new Map() - readonly chats: Map = new Map() + constructor( + readonly users: Map = new Map(), + readonly chats: Map = new Map(), + ) {} hasMin = false diff --git a/packages/client/src/types/peers/typing-status.ts b/packages/core/src/highlevel/types/peers/typing-status.ts similarity index 81% rename from packages/client/src/types/peers/typing-status.ts rename to packages/core/src/highlevel/types/peers/typing-status.ts index fb411a11..76ce9051 100644 --- a/packages/client/src/types/peers/typing-status.ts +++ b/packages/core/src/highlevel/types/peers/typing-status.ts @@ -17,9 +17,11 @@ * - `game`: User is playing a game * - `record_round`: User is recording a round video message * - `upload_round`: User is uploading a round video message - * - `speak_call`: *undocumented* User is speaking in a group call - * - `history_import`: *undocumented* User is importing history + * - `speak_call`: User is speaking in a group call + * - `history_import`: User is importing history * - `sticker`: User is choosing a sticker + * - `interaction`: User has sent an emoji interaction + * - `interaction_seen`: User is watching a previously sent emoji interaction */ export type TypingStatus = | 'typing' @@ -38,3 +40,5 @@ export type TypingStatus = | 'speak_call' | 'history_import' | 'sticker' + | 'interaction' + | 'interaction_seen' diff --git a/packages/client/src/types/peers/user.test.ts b/packages/core/src/highlevel/types/peers/user.test.ts similarity index 99% rename from packages/client/src/types/peers/user.test.ts rename to packages/core/src/highlevel/types/peers/user.test.ts index cd62aa67..a83e473a 100644 --- a/packages/client/src/types/peers/user.test.ts +++ b/packages/core/src/highlevel/types/peers/user.test.ts @@ -1,6 +1,6 @@ +import Long from 'long' import { describe, expect, it } from 'vitest' -import { Long } from '@mtcute/core' import { createStub } from '@mtcute/test' import { MessageEntity } from '../messages/index.js' diff --git a/packages/client/src/types/peers/user.ts b/packages/core/src/highlevel/types/peers/user.ts similarity index 98% rename from packages/client/src/types/peers/user.ts rename to packages/core/src/highlevel/types/peers/user.ts index e27d3548..9f3b5364 100644 --- a/packages/client/src/types/peers/user.ts +++ b/packages/core/src/highlevel/types/peers/user.ts @@ -1,6 +1,7 @@ -import { MtArgumentError, tl } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { MtArgumentError } from '../../../types/errors.js' +import { assertTypeIs } from '../../../utils/type-assertions.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { MessageEntity } from '../messages/message-entity.js' diff --git a/packages/client/src/types/premium/boost-slot.ts b/packages/core/src/highlevel/types/premium/boost-slot.ts similarity index 94% rename from packages/client/src/types/premium/boost-slot.ts rename to packages/core/src/highlevel/types/premium/boost-slot.ts index aa8337bb..1a4ea2bb 100644 --- a/packages/client/src/types/premium/boost-slot.ts +++ b/packages/core/src/highlevel/types/premium/boost-slot.ts @@ -1,6 +1,6 @@ -import { tl } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { assertTypeIs } from '../../../utils/type-assertions.js' import { makeInspectable } from '../../utils/inspectable.js' import { memoizeGetters } from '../../utils/memoize.js' import { Chat, PeersIndex } from '../peers/index.js' diff --git a/packages/client/src/types/premium/boost-stats.ts b/packages/core/src/highlevel/types/premium/boost-stats.ts similarity index 98% rename from packages/client/src/types/premium/boost-stats.ts rename to packages/core/src/highlevel/types/premium/boost-stats.ts index 2db47914..74c7aa2d 100644 --- a/packages/client/src/types/premium/boost-stats.ts +++ b/packages/core/src/highlevel/types/premium/boost-stats.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' diff --git a/packages/client/src/types/premium/boost.ts b/packages/core/src/highlevel/types/premium/boost.ts similarity index 96% rename from packages/client/src/types/premium/boost.ts rename to packages/core/src/highlevel/types/premium/boost.ts index 9b0ff52c..8ccccbee 100644 --- a/packages/client/src/types/premium/boost.ts +++ b/packages/core/src/highlevel/types/premium/boost.ts @@ -1,5 +1,6 @@ -import { MtUnsupportedError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MtUnsupportedError } from '../../../types/errors.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { PeersIndex, User } from '../peers/index.js' diff --git a/packages/client/src/types/premium/index.ts b/packages/core/src/highlevel/types/premium/index.ts similarity index 100% rename from packages/client/src/types/premium/index.ts rename to packages/core/src/highlevel/types/premium/index.ts diff --git a/packages/client/src/types/reactions/emoji-status.ts b/packages/core/src/highlevel/types/reactions/emoji-status.ts similarity index 94% rename from packages/client/src/types/reactions/emoji-status.ts rename to packages/core/src/highlevel/types/reactions/emoji-status.ts index a95761c2..cea50467 100644 --- a/packages/client/src/types/reactions/emoji-status.ts +++ b/packages/core/src/highlevel/types/reactions/emoji-status.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' diff --git a/packages/client/src/types/reactions/index.ts b/packages/core/src/highlevel/types/reactions/index.ts similarity index 100% rename from packages/client/src/types/reactions/index.ts rename to packages/core/src/highlevel/types/reactions/index.ts diff --git a/packages/client/src/types/reactions/peer-reaction.ts b/packages/core/src/highlevel/types/reactions/peer-reaction.ts similarity index 88% rename from packages/client/src/types/reactions/peer-reaction.ts rename to packages/core/src/highlevel/types/reactions/peer-reaction.ts index f819fd3e..61753d25 100644 --- a/packages/client/src/types/reactions/peer-reaction.ts +++ b/packages/core/src/highlevel/types/reactions/peer-reaction.ts @@ -1,6 +1,7 @@ -import { getMarkedPeerId, tl } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { getMarkedPeerId } from '../../../utils/peer-utils.js' +import { assertTypeIs } from '../../../utils/type-assertions.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { PeersIndex } from '../peers/peers-index.js' diff --git a/packages/client/src/types/reactions/reaction-count.ts b/packages/core/src/highlevel/types/reactions/reaction-count.ts similarity index 95% rename from packages/client/src/types/reactions/reaction-count.ts rename to packages/core/src/highlevel/types/reactions/reaction-count.ts index 3cef298e..09b6e1a4 100644 --- a/packages/client/src/types/reactions/reaction-count.ts +++ b/packages/core/src/highlevel/types/reactions/reaction-count.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { ReactionEmoji, toReactionEmoji } from './types.js' diff --git a/packages/client/src/types/reactions/types.ts b/packages/core/src/highlevel/types/reactions/types.ts similarity index 92% rename from packages/client/src/types/reactions/types.ts rename to packages/core/src/highlevel/types/reactions/types.ts index 96261992..f3ef305d 100644 --- a/packages/client/src/types/reactions/types.ts +++ b/packages/core/src/highlevel/types/reactions/types.ts @@ -1,4 +1,8 @@ -import { Long, MtTypeAssertionError, tl } from '@mtcute/core' +import Long from 'long' + +import { tl } from '@mtcute/tl' + +import { MtTypeAssertionError } from '../../../types/errors.js' /** * Input version of {@link ReactionEmoji}, which also accepts bare TL object diff --git a/packages/client/src/types/stories/all-stories.ts b/packages/core/src/highlevel/types/stories/all-stories.ts similarity index 97% rename from packages/client/src/types/stories/all-stories.ts rename to packages/core/src/highlevel/types/stories/all-stories.ts index 23f3747d..24cf549b 100644 --- a/packages/client/src/types/stories/all-stories.ts +++ b/packages/core/src/highlevel/types/stories/all-stories.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/stories/index.ts b/packages/core/src/highlevel/types/stories/index.ts similarity index 100% rename from packages/client/src/types/stories/index.ts rename to packages/core/src/highlevel/types/stories/index.ts diff --git a/packages/client/src/types/stories/interactive/base.ts b/packages/core/src/highlevel/types/stories/interactive/base.ts similarity index 84% rename from packages/client/src/types/stories/interactive/base.ts rename to packages/core/src/highlevel/types/stories/interactive/base.ts index 823ebbbe..ea96429b 100644 --- a/packages/client/src/types/stories/interactive/base.ts +++ b/packages/core/src/highlevel/types/stories/interactive/base.ts @@ -1,9 +1,9 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' export abstract class StoryInteractiveArea { abstract type: string - constructor(readonly raw: Exclude) { + constructor(readonly raw: tl.TypeMediaArea) { this.raw = raw } diff --git a/packages/client/src/types/stories/interactive/channel-post.ts b/packages/core/src/highlevel/types/stories/interactive/channel-post.ts similarity index 96% rename from packages/client/src/types/stories/interactive/channel-post.ts rename to packages/core/src/highlevel/types/stories/interactive/channel-post.ts index a53c6447..a0c62fd5 100644 --- a/packages/client/src/types/stories/interactive/channel-post.ts +++ b/packages/core/src/highlevel/types/stories/interactive/channel-post.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../../utils/index.js' import { memoizeGetters } from '../../../utils/memoize.js' diff --git a/packages/client/src/types/stories/interactive/index.ts b/packages/core/src/highlevel/types/stories/interactive/index.ts similarity index 92% rename from packages/client/src/types/stories/interactive/index.ts rename to packages/core/src/highlevel/types/stories/interactive/index.ts index e7b7aa9a..d1826192 100644 --- a/packages/client/src/types/stories/interactive/index.ts +++ b/packages/core/src/highlevel/types/stories/interactive/index.ts @@ -1,5 +1,6 @@ -import { MtTypeAssertionError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MtTypeAssertionError } from '../../../../types/errors.js' import { PeersIndex } from '../../peers/index.js' import { StoryInteractiveChannelPost } from './channel-post.js' import { StoryInteractiveLocation } from './location.js' diff --git a/packages/client/src/types/stories/interactive/input.ts b/packages/core/src/highlevel/types/stories/interactive/input.ts similarity index 98% rename from packages/client/src/types/stories/interactive/input.ts rename to packages/core/src/highlevel/types/stories/interactive/input.ts index 60000b0c..4bcef23c 100644 --- a/packages/client/src/types/stories/interactive/input.ts +++ b/packages/core/src/highlevel/types/stories/interactive/input.ts @@ -1,4 +1,6 @@ -import { Long, tl } from '@mtcute/core' +import Long from 'long' + +import { tl } from '@mtcute/tl' import { VenueSource } from '../../media/index.js' import { InputReaction, normalizeInputReaction } from '../../reactions/index.js' diff --git a/packages/client/src/types/stories/interactive/location.ts b/packages/core/src/highlevel/types/stories/interactive/location.ts similarity index 88% rename from packages/client/src/types/stories/interactive/location.ts rename to packages/core/src/highlevel/types/stories/interactive/location.ts index ad846dea..2108e3c1 100644 --- a/packages/client/src/types/stories/interactive/location.ts +++ b/packages/core/src/highlevel/types/stories/interactive/location.ts @@ -1,6 +1,6 @@ -import { tl } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { assertTypeIs } from '../../../../utils/type-assertions.js' import { makeInspectable } from '../../../utils/index.js' import { memoizeGetters } from '../../../utils/memoize.js' import { Location } from '../../media/location.js' diff --git a/packages/client/src/types/stories/interactive/reaction.ts b/packages/core/src/highlevel/types/stories/interactive/reaction.ts similarity index 96% rename from packages/client/src/types/stories/interactive/reaction.ts rename to packages/core/src/highlevel/types/stories/interactive/reaction.ts index 5ba50ca9..51facf4e 100644 --- a/packages/client/src/types/stories/interactive/reaction.ts +++ b/packages/core/src/highlevel/types/stories/interactive/reaction.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../../utils/index.js' import { ReactionEmoji, toReactionEmoji } from '../../reactions/index.js' diff --git a/packages/client/src/types/stories/interactive/venue.ts b/packages/core/src/highlevel/types/stories/interactive/venue.ts similarity index 93% rename from packages/client/src/types/stories/interactive/venue.ts rename to packages/core/src/highlevel/types/stories/interactive/venue.ts index 0bc8aebb..1845c1b4 100644 --- a/packages/client/src/types/stories/interactive/venue.ts +++ b/packages/core/src/highlevel/types/stories/interactive/venue.ts @@ -1,6 +1,6 @@ -import { tl } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { assertTypeIs } from '../../../../utils/type-assertions.js' import { makeInspectable } from '../../../utils/index.js' import { memoizeGetters } from '../../../utils/memoize.js' import { Location } from '../../media/location.js' diff --git a/packages/client/src/types/stories/peer-stories.ts b/packages/core/src/highlevel/types/stories/peer-stories.ts similarity index 85% rename from packages/client/src/types/stories/peer-stories.ts rename to packages/core/src/highlevel/types/stories/peer-stories.ts index 11dc6fd9..8216d623 100644 --- a/packages/client/src/types/stories/peer-stories.ts +++ b/packages/core/src/highlevel/types/stories/peer-stories.ts @@ -1,6 +1,7 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' -import { assertTypeIs, makeInspectable } from '../../utils/index.js' +import { assertTypeIs } from '../../../utils/type-assertions.js' +import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { parsePeer, Peer, PeersIndex } from '../peers/index.js' import { Story } from './story.js' diff --git a/packages/client/src/types/stories/stealth-mode.ts b/packages/core/src/highlevel/types/stories/stealth-mode.ts similarity index 94% rename from packages/client/src/types/stories/stealth-mode.ts rename to packages/core/src/highlevel/types/stories/stealth-mode.ts index 49c8fc30..a56cfb1a 100644 --- a/packages/client/src/types/stories/stealth-mode.ts +++ b/packages/core/src/highlevel/types/stories/stealth-mode.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' diff --git a/packages/client/src/types/stories/story-interactions.ts b/packages/core/src/highlevel/types/stories/story-interactions.ts similarity index 98% rename from packages/client/src/types/stories/story-interactions.ts rename to packages/core/src/highlevel/types/stories/story-interactions.ts index cf1fcee3..2ba88aca 100644 --- a/packages/client/src/types/stories/story-interactions.ts +++ b/packages/core/src/highlevel/types/stories/story-interactions.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/stories/story-viewer.ts b/packages/core/src/highlevel/types/stories/story-viewer.ts similarity index 97% rename from packages/client/src/types/stories/story-viewer.ts rename to packages/core/src/highlevel/types/stories/story-viewer.ts index 45a7736e..e20afddf 100644 --- a/packages/client/src/types/stories/story-viewer.ts +++ b/packages/core/src/highlevel/types/stories/story-viewer.ts @@ -1,5 +1,6 @@ -import { MtTypeAssertionError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MtTypeAssertionError } from '../../../types/errors.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { Message } from '../messages/index.js' diff --git a/packages/client/src/types/stories/story.ts b/packages/core/src/highlevel/types/stories/story.ts similarity index 98% rename from packages/client/src/types/stories/story.ts rename to packages/core/src/highlevel/types/stories/story.ts index d0eda855..948e0696 100644 --- a/packages/client/src/types/stories/story.ts +++ b/packages/core/src/highlevel/types/stories/story.ts @@ -1,5 +1,6 @@ -import { MtUnsupportedError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MtUnsupportedError } from '../../../types/errors.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { parseDocument } from '../media/document-utils.js' diff --git a/packages/client/src/types/updates/bot-chat-join-request.ts b/packages/core/src/highlevel/types/updates/bot-chat-join-request.ts similarity index 93% rename from packages/client/src/types/updates/bot-chat-join-request.ts rename to packages/core/src/highlevel/types/updates/bot-chat-join-request.ts index 2f285ae1..0f359199 100644 --- a/packages/client/src/types/updates/bot-chat-join-request.ts +++ b/packages/core/src/highlevel/types/updates/bot-chat-join-request.ts @@ -1,5 +1,6 @@ -import { getBarePeerId, getMarkedPeerId, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { getBarePeerId, getMarkedPeerId } from '../../../utils/peer-utils.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { Chat, ChatInviteLink, PeersIndex, User } from '../peers/index.js' diff --git a/packages/client/src/types/updates/bot-reaction.ts b/packages/core/src/highlevel/types/updates/bot-reaction.ts similarity index 98% rename from packages/client/src/types/updates/bot-reaction.ts rename to packages/core/src/highlevel/types/updates/bot-reaction.ts index dc76bfd0..56ec38d8 100644 --- a/packages/client/src/types/updates/bot-reaction.ts +++ b/packages/core/src/highlevel/types/updates/bot-reaction.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/inspectable.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/updates/bot-stopped.ts b/packages/core/src/highlevel/types/updates/bot-stopped.ts similarity index 96% rename from packages/client/src/types/updates/bot-stopped.ts rename to packages/core/src/highlevel/types/updates/bot-stopped.ts index ae4cc328..3c99fd64 100644 --- a/packages/client/src/types/updates/bot-stopped.ts +++ b/packages/core/src/highlevel/types/updates/bot-stopped.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/updates/callback-query.ts b/packages/core/src/highlevel/types/updates/callback-query.ts similarity index 95% rename from packages/client/src/types/updates/callback-query.ts rename to packages/core/src/highlevel/types/updates/callback-query.ts index a3bd9a3c..e4f165be 100644 --- a/packages/client/src/types/updates/callback-query.ts +++ b/packages/core/src/highlevel/types/updates/callback-query.ts @@ -1,6 +1,8 @@ -import { MtArgumentError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { utf8Decode } from '@mtcute/tl-runtime' -import { makeInspectable, utf8Decode } from '../../utils/index.js' +import { MtArgumentError } from '../../../types/errors.js' +import { makeInspectable } from '../../utils/index.js' import { encodeInlineMessageId } from '../../utils/inline-utils.js' import { memoizeGetters } from '../../utils/memoize.js' import { Chat } from '../peers/chat.js' diff --git a/packages/client/src/types/updates/chat-join-request.ts b/packages/core/src/highlevel/types/updates/chat-join-request.ts similarity index 93% rename from packages/client/src/types/updates/chat-join-request.ts rename to packages/core/src/highlevel/types/updates/chat-join-request.ts index 49d2904b..fc36c034 100644 --- a/packages/client/src/types/updates/chat-join-request.ts +++ b/packages/core/src/highlevel/types/updates/chat-join-request.ts @@ -1,5 +1,6 @@ -import { getBarePeerId, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { getBarePeerId } from '../../../utils/peer-utils.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { PeersIndex, User } from '../peers/index.js' diff --git a/packages/client/src/types/updates/chat-member-update.ts b/packages/core/src/highlevel/types/updates/chat-member-update.ts similarity index 98% rename from packages/client/src/types/updates/chat-member-update.ts rename to packages/core/src/highlevel/types/updates/chat-member-update.ts index abe15528..177ae32d 100644 --- a/packages/client/src/types/updates/chat-member-update.ts +++ b/packages/core/src/highlevel/types/updates/chat-member-update.ts @@ -1,5 +1,6 @@ -import { getMarkedPeerId, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { getMarkedPeerId } from '../../../utils/peer-utils.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { Chat } from '../peers/chat.js' diff --git a/packages/client/src/types/updates/chosen-inline-result.ts b/packages/core/src/highlevel/types/updates/chosen-inline-result.ts similarity index 98% rename from packages/client/src/types/updates/chosen-inline-result.ts rename to packages/core/src/highlevel/types/updates/chosen-inline-result.ts index 36d846cf..5c8e32bd 100644 --- a/packages/client/src/types/updates/chosen-inline-result.ts +++ b/packages/core/src/highlevel/types/updates/chosen-inline-result.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { encodeInlineMessageId } from '../../utils/inline-utils.js' diff --git a/packages/client/src/types/updates/delete-message-update.ts b/packages/core/src/highlevel/types/updates/delete-message-update.ts similarity index 86% rename from packages/client/src/types/updates/delete-message-update.ts rename to packages/core/src/highlevel/types/updates/delete-message-update.ts index 9357f4db..86b7a404 100644 --- a/packages/client/src/types/updates/delete-message-update.ts +++ b/packages/core/src/highlevel/types/updates/delete-message-update.ts @@ -1,5 +1,6 @@ -import { tl, toggleChannelIdMark } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { toggleChannelIdMark } from '../../../utils/peer-utils.js' import { makeInspectable } from '../../utils/index.js' /** diff --git a/packages/client/src/types/updates/delete-story-update.ts b/packages/core/src/highlevel/types/updates/delete-story-update.ts similarity index 95% rename from packages/client/src/types/updates/delete-story-update.ts rename to packages/core/src/highlevel/types/updates/delete-story-update.ts index 1075d663..c163fc6c 100644 --- a/packages/client/src/types/updates/delete-story-update.ts +++ b/packages/core/src/highlevel/types/updates/delete-story-update.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { parsePeer, Peer, PeersIndex } from '../../types/peers/index.js' import { makeInspectable } from '../../utils/index.js' diff --git a/packages/client/src/types/updates/history-read-update.ts b/packages/core/src/highlevel/types/updates/history-read-update.ts similarity index 96% rename from packages/client/src/types/updates/history-read-update.ts rename to packages/core/src/highlevel/types/updates/history-read-update.ts index 434c8c96..53ecaf1d 100644 --- a/packages/client/src/types/updates/history-read-update.ts +++ b/packages/core/src/highlevel/types/updates/history-read-update.ts @@ -1,5 +1,6 @@ -import { getMarkedPeerId, tl, toggleChannelIdMark } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { getMarkedPeerId, toggleChannelIdMark } from '../../../utils/peer-utils.js' import { makeInspectable } from '../../utils/index.js' export class HistoryReadUpdate { diff --git a/packages/client/src/types/updates/index.ts b/packages/core/src/highlevel/types/updates/index.ts similarity index 100% rename from packages/client/src/types/updates/index.ts rename to packages/core/src/highlevel/types/updates/index.ts diff --git a/packages/client/src/types/updates/inline-query.ts b/packages/core/src/highlevel/types/updates/inline-query.ts similarity index 98% rename from packages/client/src/types/updates/inline-query.ts rename to packages/core/src/highlevel/types/updates/inline-query.ts index 5d897d4e..c98a17de 100644 --- a/packages/client/src/types/updates/inline-query.ts +++ b/packages/core/src/highlevel/types/updates/inline-query.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/updates/parse-update.ts b/packages/core/src/highlevel/types/updates/parse-update.ts similarity index 99% rename from packages/client/src/types/updates/parse-update.ts rename to packages/core/src/highlevel/types/updates/parse-update.ts index 77be37e3..ff01145d 100644 --- a/packages/client/src/types/updates/parse-update.ts +++ b/packages/core/src/highlevel/types/updates/parse-update.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-argument */ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { BotChatJoinRequestUpdate, diff --git a/packages/client/src/types/updates/poll-update.ts b/packages/core/src/highlevel/types/updates/poll-update.ts similarity index 98% rename from packages/client/src/types/updates/poll-update.ts rename to packages/core/src/highlevel/types/updates/poll-update.ts index 1c04b653..c9bd652c 100644 --- a/packages/client/src/types/updates/poll-update.ts +++ b/packages/core/src/highlevel/types/updates/poll-update.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/updates/poll-vote.ts b/packages/core/src/highlevel/types/updates/poll-vote.ts similarity index 96% rename from packages/client/src/types/updates/poll-vote.ts rename to packages/core/src/highlevel/types/updates/poll-vote.ts index 476fda3f..182dc143 100644 --- a/packages/client/src/types/updates/poll-vote.ts +++ b/packages/core/src/highlevel/types/updates/poll-vote.ts @@ -1,5 +1,6 @@ -import { MtUnsupportedError, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { MtUnsupportedError } from '../../../types/errors.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { parsePeer, Peer, PeersIndex } from '../peers/index.js' diff --git a/packages/client/src/types/updates/pre-checkout-query.ts b/packages/core/src/highlevel/types/updates/pre-checkout-query.ts similarity index 97% rename from packages/client/src/types/updates/pre-checkout-query.ts rename to packages/core/src/highlevel/types/updates/pre-checkout-query.ts index f42caa9b..d46c1b58 100644 --- a/packages/client/src/types/updates/pre-checkout-query.ts +++ b/packages/core/src/highlevel/types/updates/pre-checkout-query.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/updates/story-update.ts b/packages/core/src/highlevel/types/updates/story-update.ts similarity index 86% rename from packages/client/src/types/updates/story-update.ts rename to packages/core/src/highlevel/types/updates/story-update.ts index 2d7e4db3..88738273 100644 --- a/packages/client/src/types/updates/story-update.ts +++ b/packages/core/src/highlevel/types/updates/story-update.ts @@ -1,8 +1,9 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { assertTypeIs } from '../../../utils/type-assertions.js' import { parsePeer, Peer, PeersIndex } from '../../types/peers/index.js' import { Story } from '../../types/stories/index.js' -import { assertTypeIs, makeInspectable } from '../../utils/index.js' +import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' /** diff --git a/packages/client/src/types/updates/user-status-update.ts b/packages/core/src/highlevel/types/updates/user-status-update.ts similarity index 97% rename from packages/client/src/types/updates/user-status-update.ts rename to packages/core/src/highlevel/types/updates/user-status-update.ts index 3e64c0c5..9e5bd874 100644 --- a/packages/client/src/types/updates/user-status-update.ts +++ b/packages/core/src/highlevel/types/updates/user-status-update.ts @@ -1,4 +1,4 @@ -import { tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' diff --git a/packages/client/src/types/updates/user-typing-update.ts b/packages/core/src/highlevel/types/updates/user-typing-update.ts similarity index 83% rename from packages/client/src/types/updates/user-typing-update.ts rename to packages/core/src/highlevel/types/updates/user-typing-update.ts index 3fb47262..08c237d9 100644 --- a/packages/client/src/types/updates/user-typing-update.ts +++ b/packages/core/src/highlevel/types/updates/user-typing-update.ts @@ -1,5 +1,8 @@ -import { BasicPeerType, getBarePeerId, MtUnsupportedError, tl, toggleChannelIdMark } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { BasicPeerType } from '../../../types/peers.js' +import { assertNever } from '../../../types/utils.js' +import { getBarePeerId, toggleChannelIdMark } from '../../../utils/peer-utils.js' import { makeInspectable } from '../../utils/index.js' import { TypingStatus } from '../peers/index.js' @@ -72,6 +75,8 @@ export class UserTypingUpdate { return 'upload_document' case 'sendMessageGeoLocationAction': return 'geo' + case 'sendMessageGamePlayAction': + return 'game' case 'sendMessageChooseContactAction': return 'contact' case 'sendMessageRecordRoundAction': @@ -84,9 +89,13 @@ export class UserTypingUpdate { return 'history_import' case 'sendMessageChooseStickerAction': return 'sticker' + case 'sendMessageEmojiInteraction': + return 'interaction' + case 'sendMessageEmojiInteractionSeen': + return 'interaction_seen' + default: + assertNever(this.raw.action) } - - throw new MtUnsupportedError() } } diff --git a/packages/client/src/types/utils.ts b/packages/core/src/highlevel/types/utils.ts similarity index 91% rename from packages/client/src/types/utils.ts rename to packages/core/src/highlevel/types/utils.ts index 078fb9b4..b7239235 100644 --- a/packages/client/src/types/utils.ts +++ b/packages/core/src/highlevel/types/utils.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { MaybeAsync } from '@mtcute/core' +import { MaybeAsync } from '../../types/utils.js' export type MaybeDynamic = MaybeAsync | (() => MaybeAsync) diff --git a/packages/client/src/methods/updates/index.ts b/packages/core/src/highlevel/updates/index.ts similarity index 60% rename from packages/client/src/methods/updates/index.ts rename to packages/core/src/highlevel/updates/index.ts index 09bef6a2..9ff9e1ae 100644 --- a/packages/client/src/methods/updates/index.ts +++ b/packages/core/src/highlevel/updates/index.ts @@ -1,3 +1,3 @@ export * from './manager.js' -export * from './parsed.js' +// export * from './parsed.js' todo export * from './types.js' diff --git a/packages/core/src/highlevel/updates/manager.ts b/packages/core/src/highlevel/updates/manager.ts new file mode 100644 index 00000000..3dae3ad3 --- /dev/null +++ b/packages/core/src/highlevel/updates/manager.ts @@ -0,0 +1,1856 @@ +/* eslint-disable max-depth,max-params */ +import { tl } from '@mtcute/tl' + +import { MtArgumentError } from '../../types/errors.js' +import { assertNever, MaybeAsync } from '../../types/utils.js' +import { + AsyncLock, + ConditionVariable, + Deque, + EarlyTimer, + getBarePeerId, + getMarkedPeerId, + parseMarkedPeerId, + SortedLinkedList, + toggleChannelIdMark, + toInputChannel, +} from '../../utils/index.js' +import { BaseTelegramClient } from '../base.js' +import { CurrentUserInfo } from '../storage/service/current-user.js' +import { PeersIndex } from '../types/peers/peers-index.js' +import { PendingUpdate, PendingUpdateContainer, RawUpdateHandler, UpdatesManagerParams } from './types.js' +import { + createDummyUpdatesContainer, + extractChannelIdFromUpdate, + isMessageEmpty, + messageToUpdate, + toPendingUpdate, +} from './utils.js' + +// code in this file is very bad, thanks to Telegram's awesome updates mechanism + +// todo: maybe move to another class? +// /** +// * Enable RPS meter. +// * +// * > **Note**: This may have negative impact on performance +// * +// * @param size Sampling size +// * @param time Window time +// */ +// export function enableRps(client: ITelegramClient, size?: number, time?: number): void { +// const state = getState(client) +// state.rpsIncoming = new RpsMeter(size, time) +// state.rpsProcessing = new RpsMeter(size, time) +// } + +// /** +// * Get current average incoming RPS +// * +// * Incoming RPS is calculated based on +// * incoming update containers. Normally, +// * they should be around the same, except +// * rare situations when processing rps +// * may peak. +// */ +// export function getCurrentRpsIncoming(client: ITelegramClient): number { +// const state = getState(client) + +// if (!state.rpsIncoming) { +// throw new MtArgumentError('RPS meter is not enabled, use .enableRps() first') +// } + +// return state.rpsIncoming.getRps() +// } + +// /** +// * Get current average processing RPS +// * +// * Processing RPS is calculated based on +// * dispatched updates. Normally, +// * they should be around the same, except +// * rare situations when processing rps +// * may peak. +// */ +// export function getCurrentRpsProcessing(client: ITelegramClient): number { +// const state = getState(client) + +// if (!state.rpsProcessing) { +// throw new MtArgumentError('RPS meter is not enabled, use .enableRps() first') +// } + +// return state.rpsProcessing.getRps() +// } + +const KEEP_ALIVE_INTERVAL = 15 * 60 * 1000 // 15 minutes + +// todo: fix docs +export class UpdatesManager { + updatesLoopActive = false + updatesLoopCv = new ConditionVariable() + + postponedTimer = new EarlyTimer() + hasTimedoutPostponed = false + + pendingUpdateContainers = new SortedLinkedList((a, b) => a.seqStart - b.seqStart) + pendingPtsUpdates = new SortedLinkedList((a, b) => a.ptsBefore! - b.ptsBefore!) + pendingPtsUpdatesPostponed = new SortedLinkedList((a, b) => a.ptsBefore! - b.ptsBefore!) + pendingQtsUpdates = new SortedLinkedList((a, b) => a.qtsBefore! - b.qtsBefore!) + pendingQtsUpdatesPostponed = new SortedLinkedList((a, b) => a.qtsBefore! - b.qtsBefore!) + pendingUnorderedUpdates = new Deque() + + noDispatchEnabled = !this.params.disableNoDispatch + // channel id or 0 => msg id + noDispatchMsg = new Map>() + // channel id or 0 => pts + noDispatchPts = new Map>() + noDispatchQts = new Set() + + lock = new AsyncLock() + // rpsIncoming?: RpsMeter + // rpsProcessing?: RpsMeter + + // accessing storage every time might be expensive, + // so store everything here, and load & save + // every time session is loaded & saved. + pts?: number + qts?: number + date?: number + seq?: number + + // old values of the updates state (i.e. as in DB) + // used to avoid redundant storage calls + oldPts?: number + oldQts?: number + oldDate?: number + oldSeq?: number + selfChanged = false // todo what is this? + + // whether to catch up channels from the locally stored pts + catchUpChannels = false + catchUpOnStart = this.params.catchUp ?? false + + cpts = new Map() + cptsMod = new Map() + channelDiffTimeouts = new Map() + channelsOpened = new Map() + + log = this.client.log.create('updates') + private _handler: RawUpdateHandler = () => {} + + auth?: CurrentUserInfo | null // todo: do we need a local copy? + keepAliveInterval?: NodeJS.Timeout + + constructor( + readonly client: BaseTelegramClient, + readonly params: UpdatesManagerParams = {}, + ) { + if (client.params.disableUpdates) { + throw new MtArgumentError('Updates must be enabled to use updates manager') + } + + this._onKeepAlive = this._onKeepAlive.bind(this) + + this.postponedTimer.onTimeout(() => { + this.hasTimedoutPostponed = true + this.updatesLoopCv.notify() + }) + } + + setHandler(handler: RawUpdateHandler): void { + this._handler = handler + } + + destroy() { + this.stopLoop() + } + + notifyLoggedIn(self: CurrentUserInfo): void { + this.auth = self + this._fetchUpdatesState() + .then(() => this.startLoop()) + .catch((err) => this.client.emitError(err)) + } + + notifyLoggedOut(): void { + this.stopLoop() + this.cpts.clear() + this.cptsMod.clear() + this.pts = this.qts = this.date = this.seq = undefined + } + + async prepare(): Promise { + await this._loadUpdatesStorage() + } + + private _onKeepAlive() { + this.log.debug('no updates for >15 minutes, catching up') + this.handleUpdate({ _: 'updatesTooLong' }) + } + + /** + * Start updates loop. + * + * You must first call {@link enableUpdatesProcessing} to use this method. + * + * It is recommended to use this method in callback to {@link start}, + * or otherwise make sure the user is logged in. + * + * > **Note**: If you are using {@link UpdatesManagerParams.catchUp} option, + * > catching up will be done in background, you can't await it. + */ + async startLoop(): Promise { + if (this.updatesLoopActive) return + + // otherwise we will catch up on the first update + if (!this.catchUpOnStart) { + await this._fetchUpdatesState() + } + + // start updates loop in background + this.updatesLoopActive = true + this.keepAliveInterval = setInterval(this._onKeepAlive, KEEP_ALIVE_INTERVAL) + this._loop().catch((err) => this.client.emitError(err)) + + if (this.catchUpOnStart) { + this.catchUp() + } + } + + /** + * **ADVANCED** + * + * Manually stop updates loop. + * Usually done automatically when stopping the client with {@link close} + */ + stopLoop(): void { + if (!this.updatesLoopActive) return + + clearInterval(this.keepAliveInterval) + + for (const timer of this.channelDiffTimeouts.values()) { + clearTimeout(timer) + } + this.channelDiffTimeouts.clear() + + this.updatesLoopActive = false + this.pendingUpdateContainers.clear() + this.pendingUnorderedUpdates.clear() + this.pendingPtsUpdates.clear() + this.pendingQtsUpdates.clear() + this.pendingPtsUpdatesPostponed.clear() + this.pendingQtsUpdatesPostponed.clear() + this.postponedTimer.reset() + this.updatesLoopCv.notify() + } + + /** + * Catch up with the server by loading missed updates. + * + * > **Note**: In case the storage was not properly + * > closed the last time, "catching up" might + * > result in duplicate updates. + */ + catchUp(): void { + this.log.debug('catch up requested') + + this.catchUpChannels = true + this.handleUpdate({ _: 'updatesTooLong' }) + } + + handleClientUpdate(update: tl.TypeUpdates, noDispatch = true): void { + if (noDispatch && this.noDispatchEnabled) { + this._addToNoDispatchIndex(update) + } + + this.handleUpdate(update) + } + + handleUpdate(update: tl.TypeUpdates): void { + this.log.debug( + 'received %s, queueing for processing. containers queue size: %d', + update._, + this.pendingUpdateContainers.length, + ) + // this.rpsIncoming?.hit() + + switch (update._) { + case 'updatesTooLong': + case 'updateShortMessage': + case 'updateShortChatMessage': + case 'updateShort': + case 'updateShortSentMessage': + this.pendingUpdateContainers.add({ + upd: update, + seqStart: 0, + seqEnd: 0, + }) + break + case 'updates': + case 'updatesCombined': + this.pendingUpdateContainers.add({ + upd: update, + seqStart: update._ === 'updatesCombined' ? update.seqStart : update.seq, + seqEnd: update.seq, + }) + break + default: + assertNever(update) + } + + this.updatesLoopCv.notify() + } + + /** + * **ADVANCED** + * + * Notify the updates manager that some channel was "opened". + * Channel difference for "opened" channels will be fetched on a regular basis. + * This is a low-level method, prefer using {@link openChat} instead. + * + * Channel must be resolve-able with `resolvePeer` method (i.e. be in cache); + * base chat PTS must either be passed (e.g. from {@link Dialog}), or cached in storage. + * + * @param channelId Bare ID of the channel + * @param pts PTS of the channel, if known (e.g. from {@link Dialog}) + * @returns `true` if the channel was opened for the first time, `false` if it is already opened + */ + notifyChannelOpened(channelId: number, pts?: number): boolean { + // this method is intentionally very dumb to avoid making this file even more unreadable + + if (this.channelsOpened.has(channelId)) { + this.log.debug('channel %d opened again', channelId) + this.channelsOpened.set(channelId, this.channelsOpened.get(channelId)! + 1) + + return false + } + + this.channelsOpened.set(channelId, 1) + this.log.debug('channel %d opened (pts=%d)', channelId, pts) + + // force fetch channel difference + this._fetchChannelDifferenceViaUpdate(channelId, pts) + + return true + } + + /** + * **ADVANCED** + * + * Notify the updates manager that some channel was "closed". + * Basically the opposite of {@link notifyChannelOpened}. + * This is a low-level method, prefer using {@link closeChat} instead. + * + * @param channelId Bare channel ID + * @returns `true` if the chat was closed for the last time, `false` otherwise + */ + notifyChannelClosed(channelId: number): boolean { + const opened = this.channelsOpened.get(channelId)! + + if (opened === undefined) { + return false + } + + if (opened > 1) { + this.log.debug('channel %d closed, but is opened %d more times', channelId, opened - 1) + this.channelsOpened.set(channelId, opened - 1) + + return false + } + + this.channelsOpened.delete(channelId) + this.log.debug('channel %d closed', channelId) + + return true + } + + ////////////////////////////////////////////// IMPLEMENTATION ////////////////////////////////////////////// + + async _fetchUpdatesState(): Promise { + const { client, lock, log } = this + + await lock.acquire() + + log.debug('fetching initial state') + + try { + let fetchedState = await client.call({ _: 'updates.getState' }) + + log.debug( + 'updates.getState returned state: pts=%d, qts=%d, date=%d, seq=%d', + fetchedState.pts, + fetchedState.qts, + fetchedState.date, + fetchedState.seq, + ) + + // for some unknown fucking reason getState may return old qts + // call getDifference to get actual values :shrug: + const diff = await client.call({ + _: 'updates.getDifference', + pts: fetchedState.pts, + qts: fetchedState.qts, + date: fetchedState.date, + }) + + switch (diff._) { + case 'updates.differenceEmpty': + break + case 'updates.differenceTooLong': // shouldn't happen, but who knows? + (fetchedState as tl.Mutable).pts = diff.pts + break + case 'updates.differenceSlice': + fetchedState = diff.intermediateState + break + case 'updates.difference': + fetchedState = diff.state + break + default: + assertNever(diff) + } + + this.qts = fetchedState.qts + this.pts = fetchedState.pts + this.date = fetchedState.date + this.seq = fetchedState.seq + + log.debug('loaded initial state: pts=%d, qts=%d, date=%d, seq=%d', this.pts, this.qts, this.date, this.seq) + } catch (e) { + if (this.client.isConnected) { + log.error('failed to fetch updates state: %s', e) + } + + lock.release() + throw e + } + + lock.release() + } + + async _loadUpdatesStorage(): Promise { + const storedState = await this.client.storage.updates.getState() + + if (storedState) { + this.pts = this.oldPts = storedState[0] + this.qts = this.oldQts = storedState[1] + this.date = this.oldDate = storedState[2] + this.seq = this.oldSeq = storedState[3] + + this.log.debug( + 'loaded stored state: pts=%d, qts=%d, date=%d, seq=%d', + storedState[0], + storedState[1], + storedState[2], + storedState[3], + ) + } + // if no state, don't bother initializing properties + // since that means that there is no authorization, + // and thus fetchUpdatesState will be called + } + + async _saveUpdatesStorage(save = false): Promise { + const { client } = this + + // todo: move this to updates state service + // before any authorization pts will be undefined + if (this.pts !== undefined) { + // if old* value is not available, assume it has changed. + if (this.oldPts === undefined || this.oldPts !== this.pts) { + await client.storage.updates.setPts(this.pts) + } + if (this.oldQts === undefined || this.oldQts !== this.qts) { + await client.storage.updates.setQts(this.qts!) + } + if (this.oldDate === undefined || this.oldDate !== this.date) { + await client.storage.updates.setDate(this.date!) + } + if (this.oldSeq === undefined || this.oldSeq !== this.seq) { + await client.storage.updates.setSeq(this.seq!) + } + + // update old* values + this.oldPts = this.pts + this.oldQts = this.qts + this.oldDate = this.date + this.oldSeq = this.seq + + await client.storage.updates.setManyChannelPts(this.cptsMod) + this.cptsMod.clear() + + if (save) { + await client.mt.storage.save() + } + } + } + + _addToNoDispatchIndex(updates?: tl.TypeUpdates): void { + if (!updates) return + + const { noDispatchMsg, noDispatchPts, noDispatchQts } = this + + const addUpdate = (upd: tl.TypeUpdate) => { + const channelId = extractChannelIdFromUpdate(upd) ?? 0 + const pts = 'pts' in upd ? upd.pts : undefined + + if (pts) { + const set = noDispatchPts.get(channelId) + if (!set) noDispatchPts.set(channelId, new Set([pts])) + else set.add(pts) + } + + const qts = 'qts' in upd ? upd.qts : undefined + + if (qts) { + noDispatchQts.add(qts) + } + + switch (upd._) { + case 'updateNewMessage': + case 'updateNewChannelMessage': { + const channelId = upd.message.peerId?._ === 'peerChannel' ? upd.message.peerId.channelId : 0 + + const set = noDispatchMsg.get(channelId) + if (!set) noDispatchMsg.set(channelId, new Set([upd.message.id])) + else set.add(upd.message.id) + + break + } + } + } + + switch (updates._) { + case 'updates': + case 'updatesCombined': + updates.updates.forEach(addUpdate) + break + case 'updateShortMessage': + case 'updateShortChatMessage': + case 'updateShortSentMessage': { + // these updates are only used for non-channel messages, so we use 0 + let set = noDispatchMsg.get(0) + if (!set) noDispatchMsg.set(0, new Set([updates.id])) + else set.add(updates.id) + + set = noDispatchPts.get(0) + if (!set) noDispatchPts.set(0, new Set([updates.pts])) + else set.add(updates.pts) + break + } + case 'updateShort': + addUpdate(updates.update) + break + case 'updatesTooLong': + break + default: + assertNever(updates) + } + } + + async _fetchMissingPeers(upd: tl.TypeUpdate, peers: PeersIndex, allowMissing = false): Promise> { + const { client } = this + + const missing = new Set() + + async function fetchPeer(peer?: tl.TypePeer | number) { + if (!peer) return true + + const bare = typeof peer === 'number' ? parseMarkedPeerId(peer)[1] : getBarePeerId(peer) + + const marked = typeof peer === 'number' ? peer : getMarkedPeerId(peer) + const index = marked > 0 ? peers.users : peers.chats + + if (index.has(bare)) return true + if (missing.has(marked)) return false + + const cached = await client.storage.peers.getCompleteById(marked) + + if (!cached) { + missing.add(marked) + + return allowMissing + } + + // whatever, ts is not smart enough to understand + (index as Map).set(bare, cached) + + return true + } + + switch (upd._) { + case 'updateNewMessage': + case 'updateNewChannelMessage': + case 'updateEditMessage': + case 'updateEditChannelMessage': { + const msg = upd.message + if (msg._ === 'messageEmpty') return missing + + // ref: https://github.com/tdlib/td/blob/master/td/telegram/UpdatesManager.cpp + // (search by UpdatesManager::is_acceptable_update) + if (!(await fetchPeer(msg.peerId))) return missing + if (!(await fetchPeer(msg.fromId))) return missing + + if (msg.replyTo) { + if (msg.replyTo._ === 'messageReplyHeader' && !(await fetchPeer(msg.replyTo.replyToPeerId))) { + return missing + } + if (msg.replyTo._ === 'messageReplyStoryHeader' && !(await fetchPeer(msg.replyTo.userId))) { + return missing + } + } + + if (msg._ !== 'messageService') { + if ( + msg.fwdFrom && + (!(await fetchPeer(msg.fwdFrom.fromId)) || !(await fetchPeer(msg.fwdFrom.savedFromPeer))) + ) { + return missing + } + if (!(await fetchPeer(msg.viaBotId))) return missing + + if (msg.entities) { + for (const ent of msg.entities) { + if (ent._ === 'messageEntityMentionName') { + if (!(await fetchPeer(ent.userId))) return missing + } + } + } + + if (msg.media) { + switch (msg.media._) { + case 'messageMediaContact': + if (msg.media.userId && !(await fetchPeer(msg.media.userId))) { + return missing + } + } + } + } else { + switch (msg.action._) { + case 'messageActionChatCreate': + case 'messageActionChatAddUser': + case 'messageActionInviteToGroupCall': + for (const user of msg.action.users) { + if (!(await fetchPeer(user))) return missing + } + break + case 'messageActionChatJoinedByLink': + if (!(await fetchPeer(msg.action.inviterId))) { + return missing + } + break + case 'messageActionChatDeleteUser': + if (!(await fetchPeer(msg.action.userId))) return missing + break + case 'messageActionChatMigrateTo': + if (!(await fetchPeer(toggleChannelIdMark(msg.action.channelId)))) { + return missing + } + break + case 'messageActionChannelMigrateFrom': + if (!(await fetchPeer(-msg.action.chatId))) return missing + break + case 'messageActionGeoProximityReached': + if (!(await fetchPeer(msg.action.fromId))) return missing + if (!(await fetchPeer(msg.action.toId))) return missing + break + } + } + break + } + case 'updateDraftMessage': + if ('entities' in upd.draft && upd.draft.entities) { + for (const ent of upd.draft.entities) { + if (ent._ === 'messageEntityMentionName') { + if (!(await fetchPeer(ent.userId))) return missing + } + } + } + } + + return missing + } + + async _storeMessageReferences(msg: tl.TypeMessage): Promise { + if (msg._ === 'messageEmpty') return + + const { client } = this + + const peerId = msg.peerId + if (peerId._ !== 'peerChannel') return + + const channelId = toggleChannelIdMark(peerId.channelId) + + const promises: MaybeAsync[] = [] + + function store(peer?: tl.TypePeer | number | number[]): void { + if (!peer) return + + if (Array.isArray(peer)) { + peer.forEach(store) + + return + } + + const marked = typeof peer === 'number' ? peer : getMarkedPeerId(peer) + + promises.push(client.storage.refMsgs.store(marked, channelId, msg.id)) + } + + // reference: https://github.com/tdlib/td/blob/master/td/telegram/MessagesManager.cpp + // (search by get_message_user_ids, get_message_channel_ids) + store(msg.fromId) + + if (msg._ === 'message') { + store(msg.viaBotId) + store(msg.fwdFrom?.fromId) + + if (msg.media) { + switch (msg.media._) { + case 'messageMediaWebPage': + if (msg.media.webpage._ === 'webPage' && msg.media.webpage.attributes) { + for (const attr of msg.media.webpage.attributes) { + if (attr._ === 'webPageAttributeStory') { + store(attr.peer) + } + } + } + break + case 'messageMediaContact': + store(msg.media.userId) + break + case 'messageMediaStory': + store(msg.media.peer) + break + case 'messageMediaGiveaway': + store(msg.media.channels.map(toggleChannelIdMark)) + break + } + } + } else { + switch (msg.action._) { + case 'messageActionChatCreate': + case 'messageActionChatAddUser': + case 'messageActionInviteToGroupCall': + store(msg.action.users) + break + case 'messageActionChatDeleteUser': + store(msg.action.userId) + break + } + } + + if (msg.replyTo) { + switch (msg.replyTo._) { + case 'messageReplyHeader': + store(msg.replyTo.replyToPeerId) + store(msg.replyTo.replyFrom?.fromId) + break + case 'messageReplyStoryHeader': + store(msg.replyTo.userId) + break + } + // in fact, we can also use peers contained in the replied-to message, + // but we don't fetch it automatically, so we can't know which peers are there + } + + await Promise.all(promises) + } + + async _fetchChannelDifference(channelId: number, fallbackPts?: number): Promise { + const { channelDiffTimeouts, cpts, cptsMod, channelsOpened, client, log, pendingUnorderedUpdates } = this + + // clear timeout if any + if (channelDiffTimeouts.has(channelId)) { + clearTimeout(channelDiffTimeouts.get(channelId)) + channelDiffTimeouts.delete(channelId) + } + + let _pts: number | null | undefined = cpts.get(channelId) + + if (!_pts && this.catchUpChannels) { + _pts = await client.storage.updates.getChannelPts(channelId) + } + if (!_pts) _pts = fallbackPts + + if (!_pts) { + log.debug('fetchChannelDifference failed for channel %d: base pts not available', channelId) + + return false + } + + const channelPeer = await client.storage.peers.getById(toggleChannelIdMark(channelId)) + + if (!channelPeer) { + log.debug('fetchChannelDifference failed for channel %d: input peer not found', channelId) + + return false + } + + const channel = toInputChannel(channelPeer) + + // to make TS happy + let pts = _pts + let limit = this.auth?.isBot ? 100000 : 100 + + if (pts <= 0) { + pts = 1 + limit = 1 + } + + let lastTimeout = 0 + + for (;;) { + const diff = await client.call({ + _: 'updates.getChannelDifference', + force: true, // Set to true to skip some possibly unneeded updates and reduce server-side load + channel, + pts, + limit, + filter: { _: 'channelMessagesFilterEmpty' }, + }) + + if (diff.timeout) lastTimeout = diff.timeout + + if (diff._ === 'updates.channelDifferenceEmpty') { + log.debug('getChannelDifference (cid = %d) returned channelDifferenceEmpty', channelId) + break + } + + const peers = PeersIndex.from(diff) + + if (diff._ === 'updates.channelDifferenceTooLong') { + if (diff.dialog._ === 'dialog') { + pts = diff.dialog.pts! + } + + log.warn( + 'getChannelDifference (cid = %d) returned channelDifferenceTooLong. new pts: %d, recent msgs: %d', + channelId, + pts, + diff.messages.length, + ) + + diff.messages.forEach((message) => { + log.debug( + 'processing message %d (%s) from TooLong diff for channel %d', + message.id, + message._, + channelId, + ) + + if (message._ === 'messageEmpty') return + + pendingUnorderedUpdates.pushBack(toPendingUpdate(messageToUpdate(message), peers, true)) + }) + break + } + + log.debug( + 'getChannelDifference (cid = %d) returned %d messages, %d updates. new pts: %d, final: %b', + channelId, + diff.newMessages.length, + diff.otherUpdates.length, + diff.pts, + diff.final, + ) + + diff.newMessages.forEach((message) => { + log.debug('processing message %d (%s) from diff for channel %d', message.id, message._, channelId) + + if (message._ === 'messageEmpty') return + + pendingUnorderedUpdates.pushBack(toPendingUpdate(messageToUpdate(message), peers, true)) + }) + + diff.otherUpdates.forEach((upd) => { + const parsed = toPendingUpdate(upd, peers, true) + + log.debug( + 'processing %s from diff for channel %d, pts_before: %d, pts: %d', + upd._, + channelId, + parsed.ptsBefore, + parsed.pts, + ) + + if (isMessageEmpty(upd)) return + + pendingUnorderedUpdates.pushBack(parsed) + }) + + pts = diff.pts + + if (diff.final) break + } + + cpts.set(channelId, pts) + cptsMod.set(channelId, pts) + + // schedule next fetch + if (lastTimeout !== 0 && channelsOpened.has(channelId)) { + log.debug('scheduling next fetch for channel %d in %d seconds', channelId, lastTimeout) + channelDiffTimeouts.set( + channelId, + setTimeout(() => this._fetchChannelDifferenceViaUpdate(channelId), lastTimeout * 1000), + ) + } + + return true + } + + _fetchChannelDifferenceLater( + requestedDiff: Map>, + channelId: number, + fallbackPts?: number, + ): void { + if (!requestedDiff.has(channelId)) { + requestedDiff.set( + channelId, + this._fetchChannelDifference(channelId, fallbackPts) + .catch((err) => { + this.log.warn('error fetching difference for %d: %s', channelId, err) + }) + .then((ok) => { + requestedDiff.delete(channelId) + + if (!ok) { + this.log.debug('channel difference for %d failed, falling back to common diff', channelId) + this._fetchDifferenceLater(requestedDiff) + } + }), + ) + } + } + + _fetchChannelDifferenceViaUpdate(channelId: number, pts?: number): void { + this.handleUpdate( + createDummyUpdatesContainer([ + { + _: 'updateChannelTooLong', + channelId, + pts, + }, + ]), + ) + } + + async _fetchDifference(requestedDiff: Map>): Promise { + const { client, log, pendingPtsUpdates, pendingUnorderedUpdates } = this + + for (;;) { + const diff = await client.call({ + _: 'updates.getDifference', + pts: this.pts!, + date: this.date!, + qts: this.qts!, + }) + + switch (diff._) { + case 'updates.differenceEmpty': + log.debug('updates.getDifference returned updates.differenceEmpty') + + return + case 'updates.differenceTooLong': + this.pts = diff.pts + log.debug('updates.getDifference returned updates.differenceTooLong') + + return + } + + const fetchedState = diff._ === 'updates.difference' ? diff.state : diff.intermediateState + + log.debug( + 'updates.getDifference returned %d messages, %d updates. new pts: %d, qts: %d, seq: %d, final: %b', + diff.newMessages.length, + diff.otherUpdates.length, + fetchedState.pts, + fetchedState.qts, + fetchedState.seq, + diff._ === 'updates.difference', + ) + + const peers = PeersIndex.from(diff) + + diff.newMessages.forEach((message) => { + log.debug('processing message %d in %j (%s) from common diff', message.id, message.peerId, message._) + + if (message._ === 'messageEmpty') return + + // pts does not need to be checked for them + pendingUnorderedUpdates.pushBack(toPendingUpdate(messageToUpdate(message), peers, true)) + }) + + diff.otherUpdates.forEach((upd) => { + if (upd._ === 'updateChannelTooLong') { + log.debug( + 'received updateChannelTooLong for channel %d in common diff (pts = %d), fetching diff', + upd.channelId, + upd.pts, + ) + + this._fetchChannelDifferenceLater(requestedDiff, upd.channelId, upd.pts) + + return + } + + if (isMessageEmpty(upd)) return + + const parsed = toPendingUpdate(upd, peers, true) + + if (parsed.channelId && parsed.ptsBefore) { + // we need to check pts for these updates, put into pts queue + pendingPtsUpdates.add(parsed) + } else { + // the updates are in order already, we can treat them as unordered + pendingUnorderedUpdates.pushBack(parsed) + } + + log.debug( + 'received %s from common diff, cid: %d, pts_before: %d, pts: %d, qts_before: %d', + upd._, + parsed.channelId, + parsed.ptsBefore, + parsed.pts, + parsed.qtsBefore, + ) + }) + + this.pts = fetchedState.pts + this.qts = fetchedState.qts + this.seq = fetchedState.seq + this.date = fetchedState.date + + if (diff._ === 'updates.difference') { + return + } + } + } + + _fetchDifferenceLater(requestedDiff: Map>): void { + if (!requestedDiff.has(0)) { + requestedDiff.set( + 0, + this._fetchDifference(requestedDiff) + .catch((err) => { + if (tl.RpcError.is(err, 'AUTH_KEY_UNREGISTERED')) { + // for some reason, when logging out telegram may send updatesTooLong + // in any case, we need to stop updates loop + this.stopLoop() + + return + } + + this.log.warn('error fetching common difference: %s', err) + + if (tl.RpcError.is(err, 'PERSISTENT_TIMESTAMP_INVALID')) { + // this function never throws + return this._fetchUpdatesState() + } + }) + .then(() => { + requestedDiff.delete(0) + }), + ) + } + } + + async _onUpdate( + pending: PendingUpdate, + requestedDiff: Map>, + postponed = false, + unordered = false, + ): Promise { + const { client, log } = this + const upd = pending.update + + let missing: Set | undefined = undefined + + // it is important to do this before updating pts + if (pending.peers.hasMin || pending.peers.empty) { + // even if we have min peers in difference, we can't do anything about them. + // we still want to collect them, so we can fetch them in the background. + // we won't wait for them, since that would block the updates loop + + log.debug('loading missing peers for %s (pts = %d, cid = %d)', upd._, pending.pts, pending.channelId) + missing = await this._fetchMissingPeers(upd, pending.peers, pending.fromDifference) + + if (!pending.fromDifference && missing.size) { + log.debug( + 'fetching difference because some peers were min (%J) and not cached for %s (pts = %d, cid = %d)', + missing, + upd._, + pending.pts, + pending.channelId, + ) + + if (pending.channelId && !(upd._ === 'updateNewChannelMessage' && upd.message._ === 'messageService')) { + // don't replace service messages, because they can be about bot's kicking + this._fetchChannelDifferenceLater(requestedDiff, pending.channelId, pending.ptsBefore) + } else { + this._fetchDifferenceLater(requestedDiff) + } + + return + } + + if (missing.size) { + log.debug( + 'peers still missing after fetching difference: %J for %s (pts = %d, cid = %d)', + missing, + upd._, + pending.pts, + pending.channelId, + ) + } + } + + // apply new pts/qts, if applicable + if (!unordered) { + // because unordered may contain pts/qts values when received from diff + + if (pending.pts) { + const localPts = pending.channelId ? this.cpts.get(pending.channelId) : this.pts + + if (localPts && pending.ptsBefore !== localPts) { + log.warn( + 'pts_before does not match local_pts for %s (cid = %d, pts_before = %d, pts = %d, local_pts = %d)', + upd._, + pending.channelId, + pending.ptsBefore, + pending.pts, + localPts, + ) + } + + log.debug( + 'applying new pts (cid = %d) because received %s: %d -> %d (before: %d, count: %d) (postponed = %s)', + pending.channelId, + upd._, + localPts, + pending.pts, + pending.ptsBefore, + pending.pts - pending.ptsBefore!, + postponed, + ) + + if (pending.channelId) { + this.cpts.set(pending.channelId, pending.pts) + this.cptsMod.set(pending.channelId, pending.pts) + } else { + this.pts = pending.pts + } + } + + if (pending.qtsBefore) { + log.debug( + 'applying new qts because received %s: %d -> %d (postponed = %s)', + upd._, + this.qts, + pending.qtsBefore + 1, + postponed, + ) + + this.qts = pending.qts + } + } + + if (isMessageEmpty(upd)) return + + // this.rpsProcessing?.hit() + + // updates that are also used internally + switch (upd._) { + case 'mtcute.dummyUpdate': + // we just needed to apply new pts values + return + case 'updateDcOptions': { + const config = client.mt.network.config.getNow() + + if (config) { + client.mt.network.config.setConfig({ + ...config, + dcOptions: upd.dcOptions, + }) + } else { + client.mt.network.config.update(true).catch((err) => client.emitError(err)) + } + break + } + case 'updateConfig': + client.mt.network.config.update(true).catch((err) => client.emitError(err)) + break + case 'updateUserName': + // todo + // if (upd.userId === state.auth?.userId) { + // state.auth.selfUsername = upd.usernames.find((it) => it.active)?.username ?? null + // } + break + case 'updateDeleteChannelMessages': + if (!this.auth?.isBot) { + await client.storage.refMsgs.delete(toggleChannelIdMark(upd.channelId), upd.messages) + } + break + case 'updateNewMessage': + case 'updateEditMessage': + case 'updateNewChannelMessage': + case 'updateEditChannelMessage': + if (!this.auth?.isBot) { + await this._storeMessageReferences(upd.message) + } + break + } + + if (missing?.size) { + if (this.auth?.isBot) { + this.log.warn( + 'missing peers (%J) after getDifference for %s (pts = %d, cid = %d)', + missing, + upd._, + pending.pts, + pending.channelId, + ) + } else { + // force save storage so the min peers are stored + await client.mt.storage.save() + + for (const id of missing) { + Promise.resolve(client.storage.peers.getById(id)) + .then((peer): unknown => { + if (!peer) { + this.log.warn('cannot fetch full peer %d - getPeerById returned null', id) + + return + } + + // the peer will be automatically cached by the `.call()`, we don't have to do anything + // todo + // if (isInputPeerChannel(peer)) { + // return _getChannelsBatched(client, toInputChannel(peer)) + // } else if (isInputPeerUser(peer)) { + // return _getUsersBatched(client, toInputUser(peer)) + // } + + log.warn('cannot fetch full peer %d - unknown peer type %s', id, peer._) + }) + .catch((err) => { + log.warn('error fetching full peer %d: %s', id, err) + }) + } + } + } + + // dispatch the update + if (this.noDispatchEnabled) { + const channelId = pending.channelId ?? 0 + const msgId = + upd._ === 'updateNewMessage' || upd._ === 'updateNewChannelMessage' ? upd.message.id : undefined + + // we first need to remove it from each index, and then check if it was there + const foundByMsgId = msgId && this.noDispatchMsg.get(channelId)?.delete(msgId) + const foundByPts = this.noDispatchPts.get(channelId)?.delete(pending.pts!) + const foundByQts = this.noDispatchQts.delete(pending.qts!) + + if (foundByMsgId || foundByPts || foundByQts) { + log.debug('not dispatching %s because it is in no_dispatch index', upd._) + + return + } + } + + log.debug('dispatching %s (postponed = %s)', upd._, postponed) + this._handler(upd, pending.peers) + } + + async _loop(): Promise { + const { + log, + client, + cpts, + cptsMod, + pendingUpdateContainers, + pendingPtsUpdates, + pendingPtsUpdatesPostponed, + pendingQtsUpdates, + pendingQtsUpdatesPostponed, + pendingUnorderedUpdates, + updatesLoopCv, + postponedTimer, + } = this + + log.debug('updates loop started, state available? %b', this.pts) + + try { + if (!this.pts) { + await this._fetchUpdatesState() + } + + while (this.updatesLoopActive) { + if ( + !( + pendingUpdateContainers.length || + pendingPtsUpdates.length || + pendingQtsUpdates.length || + pendingUnorderedUpdates.length || + this.hasTimedoutPostponed + ) + ) { + await updatesLoopCv.wait() + } + if (!this.updatesLoopActive) break + + log.debug( + 'updates loop tick. pending containers: %d, pts: %d, pts_postponed: %d, qts: %d, qts_postponed: %d, unordered: %d', + pendingUpdateContainers.length, + pendingPtsUpdates.length, + pendingPtsUpdatesPostponed.length, + pendingQtsUpdates.length, + pendingQtsUpdatesPostponed.length, + pendingUnorderedUpdates.length, + ) + + const requestedDiff = new Map>() + + // first process pending containers + while (pendingUpdateContainers.length) { + const { upd, seqStart, seqEnd } = pendingUpdateContainers.popFront()! + + switch (upd._) { + case 'updatesTooLong': + log.debug('received updatesTooLong, fetching difference') + this._fetchDifferenceLater(requestedDiff) + break + case 'updatesCombined': + case 'updates': { + if (seqStart !== 0) { + // https://t.me/tdlibchat/5843 + const nextLocalSeq = this.seq! + 1 + log.debug( + 'received seq-ordered %s (seq_start = %d, seq_end = %d, size = %d)', + upd._, + seqStart, + seqEnd, + upd.updates.length, + ) + + if (nextLocalSeq > seqStart) { + log.debug( + 'ignoring updates group because already applied (by seq: exp %d, got %d)', + nextLocalSeq, + seqStart, + ) + // "the updates were already applied, and must be ignored" + continue + } + + if (nextLocalSeq < seqStart) { + log.debug( + 'fetching difference because gap detected (by seq: exp %d, got %d)', + nextLocalSeq, + seqStart, + ) + // "there's an updates gap that must be filled" + this._fetchDifferenceLater(requestedDiff) + } + } else { + log.debug('received %s (size = %d)', upd._, upd.updates.length) + } + + await client.storage.peers.updatePeersFrom(upd) + + const peers = PeersIndex.from(upd) + + for (const update of upd.updates) { + switch (update._) { + case 'updateChannelTooLong': + log.debug( + 'received updateChannelTooLong for channel %d (pts = %d) in container, fetching diff', + update.channelId, + update.pts, + ) + this._fetchChannelDifferenceLater(requestedDiff, update.channelId, update.pts) + continue + case 'updatePtsChanged': + // see https://github.com/tdlib/td/blob/07c1d53a6d3cb1fad58d2822e55eef6d57363581/td/telegram/UpdatesManager.cpp#L4051 + if (client.mt.network.getPoolSize('main') > 1) { + // highload bot + log.debug( + 'updatePtsChanged received, resetting pts to 1 and fetching difference', + ) + this.pts = 1 + this._fetchDifferenceLater(requestedDiff) + } else { + log.debug('updatePtsChanged received, fetching updates state') + await this._fetchUpdatesState() + } + continue + } + + const parsed = toPendingUpdate(update, peers) + + if (parsed.ptsBefore !== undefined) { + pendingPtsUpdates.add(parsed) + } else if (parsed.qtsBefore !== undefined) { + pendingQtsUpdates.add(parsed) + } else { + pendingUnorderedUpdates.pushBack(parsed) + } + } + + if (seqEnd !== 0 && seqEnd > this.seq!) { + this.seq = seqEnd + this.date = upd.date + } + + break + } + case 'updateShort': { + log.debug('received short %s', upd._) + + const parsed = toPendingUpdate(upd.update, new PeersIndex()) + + if (parsed.ptsBefore !== undefined) { + pendingPtsUpdates.add(parsed) + } else if (parsed.qtsBefore !== undefined) { + pendingQtsUpdates.add(parsed) + } else { + pendingUnorderedUpdates.pushBack(parsed) + } + + break + } + case 'updateShortMessage': { + log.debug('received updateShortMessage') + + const message: tl.RawMessage = { + _: 'message', + out: upd.out, + mentioned: upd.mentioned, + mediaUnread: upd.mediaUnread, + silent: upd.silent, + id: upd.id, + fromId: { + _: 'peerUser', + userId: upd.out ? this.auth!.userId : upd.userId, + }, + peerId: { + _: 'peerUser', + userId: upd.userId, + }, + fwdFrom: upd.fwdFrom, + viaBotId: upd.viaBotId, + replyTo: upd.replyTo, + date: upd.date, + message: upd.message, + entities: upd.entities, + ttlPeriod: upd.ttlPeriod, + } + + const update: tl.RawUpdateNewMessage = { + _: 'updateNewMessage', + message, + pts: upd.pts, + ptsCount: upd.ptsCount, + } + + pendingPtsUpdates.add({ + update, + ptsBefore: upd.pts - upd.ptsCount, + pts: upd.pts, + peers: new PeersIndex(), + fromDifference: false, + }) + + break + } + case 'updateShortChatMessage': { + log.debug('received updateShortChatMessage') + + const message: tl.RawMessage = { + _: 'message', + out: upd.out, + mentioned: upd.mentioned, + mediaUnread: upd.mediaUnread, + silent: upd.silent, + id: upd.id, + fromId: { + _: 'peerUser', + userId: upd.fromId, + }, + peerId: { + _: 'peerChat', + chatId: upd.chatId, + }, + fwdFrom: upd.fwdFrom, + viaBotId: upd.viaBotId, + replyTo: upd.replyTo, + date: upd.date, + message: upd.message, + entities: upd.entities, + ttlPeriod: upd.ttlPeriod, + } + + const update: tl.RawUpdateNewMessage = { + _: 'updateNewMessage', + message, + pts: upd.pts, + ptsCount: upd.ptsCount, + } + + pendingPtsUpdates.add({ + update, + ptsBefore: upd.pts - upd.ptsCount, + pts: upd.pts, + peers: new PeersIndex(), + fromDifference: false, + }) + + break + } + case 'updateShortSentMessage': { + // should not happen + log.warn('received updateShortSentMessage') + break + } + default: + assertNever(upd) + } + } + + // process pts-ordered updates + while (pendingPtsUpdates.length) { + const pending = pendingPtsUpdates.popFront()! + const upd = pending.update + + // check pts + + let localPts: number | null = null + + if (!pending.channelId) localPts = this.pts! + else if (cpts.has(pending.channelId)) { + localPts = cpts.get(pending.channelId)! + } else if (this.catchUpChannels) { + // only load stored channel pts in case + // the user has enabled catching up. + // not loading stored pts effectively disables + // catching up, but doesn't interfere with further + // update gaps (i.e. first update received is considered + // to be the base state) + + const saved = await client.storage.updates.getChannelPts(pending.channelId) + + if (saved) { + cpts.set(pending.channelId, saved) + localPts = saved + } + } + + if (localPts) { + const diff = localPts - pending.ptsBefore! + // PTS can only go up or drop cardinally + const isPtsDrop = diff > 1000009 + + if (diff > 0 && !isPtsDrop) { + // "the update was already applied, and must be ignored" + log.debug( + 'ignoring %s (cid = %d) because already applied (by pts: exp %d, got %d)', + upd._, + pending.channelId, + localPts, + pending.ptsBefore, + ) + continue + } + if (diff < 0) { + // "there's an update gap that must be filled" + // if the gap is less than 3, put the update into postponed queue + // otherwise, call getDifference + if (diff > -3) { + log.debug( + 'postponing %s for 0.5s (cid = %d) because small gap detected (by pts: exp %d, got %d, diff=%d)', + upd._, + pending.channelId, + localPts, + pending.ptsBefore, + diff, + ) + pending.timeout = Date.now() + 500 + pendingPtsUpdatesPostponed.add(pending) + postponedTimer.emitBefore(pending.timeout) + } else if (diff > -1000000) { + log.debug( + 'fetching difference after %s (cid = %d) because pts gap detected (by pts: exp %d, got %d, diff=%d)', + upd._, + pending.channelId, + localPts, + pending.ptsBefore, + diff, + ) + + if (pending.channelId) { + this._fetchChannelDifferenceLater(requestedDiff, pending.channelId) + } else { + this._fetchDifferenceLater(requestedDiff) + } + } else { + log.debug( + 'skipping all updates because pts gap is too big (by pts: exp %d, got %d, diff=%d)', + localPts, + pending.ptsBefore, + diff, + ) + + if (pending.channelId) { + cpts.set(pending.channelId, 0) + cptsMod.set(pending.channelId, 0) + } else { + await this._fetchUpdatesState() + } + } + continue + } + + if (isPtsDrop) { + log.debug('pts drop detected (%d -> %d)', localPts, pending.ptsBefore) + } + } + + await this._onUpdate(pending, requestedDiff) + } + + // process postponed pts-ordered updates + for (let item = pendingPtsUpdatesPostponed._first; item; item = item.n) { + // awesome fucking iteration because i'm so fucking tired and wanna kms + const pending = item.v + + const upd = pending.update + + let localPts + + if (!pending.channelId) localPts = this.pts! + else if (cpts.has(pending.channelId)) { + localPts = cpts.get(pending.channelId) + } + + // channel pts from storage will be available because we loaded it earlier + if (!localPts) { + log.warn( + 'local pts not available for postponed %s (cid = %d), skipping', + upd._, + pending.channelId, + ) + continue + } + + // check the pts to see if the gap was filled + if (localPts > pending.ptsBefore!) { + // "the update was already applied, and must be ignored" + log.debug( + 'ignoring postponed %s (cid = %d) because already applied (by pts: exp %d, got %d)', + upd._, + pending.channelId, + localPts, + pending.ptsBefore, + ) + pendingPtsUpdatesPostponed._remove(item) + continue + } + if (localPts < pending.ptsBefore!) { + // "there's an update gap that must be filled" + // if the timeout has not expired yet, keep the update in the queue + // otherwise, fetch diff + const now = Date.now() + + if (now < pending.timeout!) { + log.debug( + 'postponed %s (cid = %d) is still waiting (%dms left) (current pts %d, need %d)', + upd._, + pending.channelId, + pending.timeout! - now, + localPts, + pending.ptsBefore, + ) + } else { + log.debug( + "gap for postponed %s (cid = %d) wasn't filled, fetching diff (current pts %d, need %d)", + upd._, + pending.channelId, + localPts, + pending.ptsBefore, + ) + pendingPtsUpdatesPostponed._remove(item) + + if (pending.channelId) { + this._fetchChannelDifferenceLater(requestedDiff, pending.channelId) + } else { + this._fetchDifferenceLater(requestedDiff) + } + } + continue + } + + await this._onUpdate(pending, requestedDiff, true) + pendingPtsUpdatesPostponed._remove(item) + } + + // process qts-ordered updates + while (pendingQtsUpdates.length) { + const pending = pendingQtsUpdates.popFront()! + const upd = pending.update + + // check qts + const diff = this.qts! - pending.qtsBefore! + const isQtsDrop = diff > 1000009 + + if (diff > 0 && !isQtsDrop) { + // "the update was already applied, and must be ignored" + log.debug( + 'ignoring %s because already applied (by qts: exp %d, got %d)', + upd._, + this.qts!, + pending.qtsBefore, + ) + continue + } + if (this.qts! < pending.qtsBefore!) { + // "there's an update gap that must be filled" + // if the gap is less than 3, put the update into postponed queue + // otherwise, call getDifference + if (diff > -3) { + log.debug( + 'postponing %s for 0.5s because small gap detected (by qts: exp %d, got %d, diff=%d)', + upd._, + this.qts!, + pending.qtsBefore, + diff, + ) + pending.timeout = Date.now() + 500 + pendingQtsUpdatesPostponed.add(pending) + postponedTimer.emitBefore(pending.timeout) + } else { + log.debug( + 'fetching difference after %s because qts gap detected (by qts: exp %d, got %d, diff=%d)', + upd._, + this.qts!, + pending.qtsBefore, + diff, + ) + this._fetchDifferenceLater(requestedDiff) + } + continue + } + + if (isQtsDrop) { + log.debug('qts drop detected (%d -> %d)', this.qts, pending.qtsBefore) + } + + await this._onUpdate(pending, requestedDiff) + } + + // process postponed qts-ordered updates + for (let item = pendingQtsUpdatesPostponed._first; item; item = item.n) { + // awesome fucking iteration because i'm so fucking tired and wanna kms + const pending = item.v + const upd = pending.update + + // check the pts to see if the gap was filled + if (this.qts! > pending.qtsBefore!) { + // "the update was already applied, and must be ignored" + log.debug( + 'ignoring postponed %s because already applied (by qts: exp %d, got %d)', + upd._, + this.qts!, + pending.qtsBefore, + ) + continue + } + if (this.qts! < pending.qtsBefore!) { + // "there's an update gap that must be filled" + // if the timeout has not expired yet, keep the update in the queue + // otherwise, fetch diff + const now = Date.now() + + if (now < pending.timeout!) { + log.debug( + 'postponed %s is still waiting (%dms left) (current qts %d, need %d)', + upd._, + pending.timeout! - now, + this.qts!, + pending.qtsBefore, + ) + } else { + log.debug( + "gap for postponed %s wasn't filled, fetching diff (current qts %d, need %d)", + upd._, + this.qts!, + pending.qtsBefore, + ) + pendingQtsUpdatesPostponed._remove(item) + this._fetchDifferenceLater(requestedDiff) + } + continue + } + + // gap was filled, and the update can be applied + await this._onUpdate(pending, requestedDiff, true) + pendingQtsUpdatesPostponed._remove(item) + } + + this.hasTimedoutPostponed = false + + // wait for all pending diffs to load + while (requestedDiff.size) { + log.debug( + 'waiting for %d pending diffs before processing unordered: %J', + requestedDiff.size, + requestedDiff.keys(), + ) + + await Promise.all([...requestedDiff.values()]) + + // diff results may as well contain new diffs to be requested + log.debug( + 'pending diffs awaited, new diffs requested: %d (%J)', + requestedDiff.size, + requestedDiff.keys(), + ) + } + + // process unordered updates (or updates received from diff) + while (pendingUnorderedUpdates.length) { + const pending = pendingUnorderedUpdates.popFront()! + + await this._onUpdate(pending, requestedDiff, false, true) + } + + // onUpdate may also call getDiff in some cases, so we also need to check + // diff may also contain new updates, which will be processed in the next tick, + // but we don't want to postpone diff fetching + while (requestedDiff.size) { + log.debug( + 'waiting for %d pending diffs after processing unordered: %J', + requestedDiff.size, + requestedDiff.keys(), + ) + + await Promise.all([...requestedDiff.values()]) + + // diff results may as well contain new diffs to be requested + log.debug( + 'pending diffs awaited, new diffs requested: %d (%j)', + requestedDiff.size, + requestedDiff.keys(), + ) + } + + // save new update state + await this._saveUpdatesStorage(true) + } + + log.debug('updates loop stopped') + } catch (e) { + log.error('updates loop encountered error, restarting: %s', e) + + return this._loop() + } + } +} diff --git a/packages/client/src/methods/updates/parsed.ts b/packages/core/src/highlevel/updates/parsed.ts similarity index 93% rename from packages/client/src/methods/updates/parsed.ts rename to packages/core/src/highlevel/updates/parsed.ts index 5934e791..df97d7b2 100644 --- a/packages/client/src/methods/updates/parsed.ts +++ b/packages/core/src/highlevel/updates/parsed.ts @@ -1,6 +1,6 @@ -import { Message } from '../../types/messages/index.js' -import { ParsedUpdate } from '../../types/updates/index.js' -import { _parseUpdate } from '../../types/updates/parse-update.js' +import { Message } from '../types/messages/index.js' +import { ParsedUpdate } from '../types/updates/index.js' +import { _parseUpdate } from '../types/updates/parse-update.js' import { RawUpdateHandler } from './types.js' export interface ParsedUpdateHandlerParams { diff --git a/packages/client/src/methods/updates/types.ts b/packages/core/src/highlevel/updates/types.ts similarity index 56% rename from packages/client/src/methods/updates/types.ts rename to packages/core/src/highlevel/updates/types.ts index 36d50997..95b688d2 100644 --- a/packages/client/src/methods/updates/types.ts +++ b/packages/core/src/highlevel/updates/types.ts @@ -1,10 +1,8 @@ -import { BaseTelegramClient, tl } from '@mtcute/core' -import type { CurrentUserInfo } from '@mtcute/core/src/storage/service/current-user.js' -import { AsyncLock, ConditionVariable, Deque, EarlyTimer, Logger, SortedLinkedList } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' -import { PeersIndex } from '../../types/index.js' -import { RpsMeter } from '../../utils/index.js' -import { extractChannelIdFromUpdate } from './utils.js' +import { AsyncLock, ConditionVariable, Deque, EarlyTimer, Logger, SortedLinkedList } from '../../utils/index.js' +import { CurrentUserInfo } from '../storage/service/current-user.js' +import { PeersIndex } from '../types/peers/peers-index.js' /** * Function to be called for each update. @@ -53,11 +51,6 @@ export interface UpdatesManagerParams { * > "catching up" might result in duplicate updates. */ catchUp?: boolean - - /** - * Handler for raw updates. - */ - onUpdate: RawUpdateHandler } /** @internal */ @@ -103,8 +96,8 @@ export interface UpdatesState { noDispatchQts: Set lock: AsyncLock - rpsIncoming?: RpsMeter - rpsProcessing?: RpsMeter + // rpsIncoming?: RpsMeter + // rpsProcessing?: RpsMeter // accessing storage every time might be expensive, // so store everything here, and load & save @@ -136,71 +129,3 @@ export interface UpdatesState { handler: RawUpdateHandler auth: CurrentUserInfo | null } - -/** - * @internal - * @noemit - */ -export function createUpdatesState( - client: BaseTelegramClient, - authState: CurrentUserInfo | null, - opts: UpdatesManagerParams, -): UpdatesState { - return { - updatesLoopActive: false, - updatesLoopCv: new ConditionVariable(), - postponedTimer: new EarlyTimer(), - hasTimedoutPostponed: false, - pendingUpdateContainers: new SortedLinkedList((a, b) => a.seqStart - b.seqStart), - pendingPtsUpdates: new SortedLinkedList((a, b) => a.ptsBefore! - b.ptsBefore!), - pendingPtsUpdatesPostponed: new SortedLinkedList((a, b) => a.ptsBefore! - b.ptsBefore!), - pendingQtsUpdates: new SortedLinkedList((a, b) => a.qtsBefore! - b.qtsBefore!), - pendingQtsUpdatesPostponed: new SortedLinkedList((a, b) => a.qtsBefore! - b.qtsBefore!), - pendingUnorderedUpdates: new Deque(), - noDispatchEnabled: !opts.disableNoDispatch, - noDispatchMsg: new Map(), - noDispatchPts: new Map(), - noDispatchQts: new Set(), - lock: new AsyncLock(), - pts: undefined, - qts: undefined, - date: undefined, - seq: undefined, - oldPts: undefined, - oldQts: undefined, - oldDate: undefined, - oldSeq: undefined, - rpsIncoming: undefined, - rpsProcessing: undefined, - selfChanged: false, - catchUpChannels: false, - catchUpOnStart: opts.catchUp ?? false, - cpts: new Map(), - cptsMod: new Map(), - channelDiffTimeouts: new Map(), - channelsOpened: new Map(), - log: client.log.create('updates'), - stop: () => {}, // will be set later - handler: opts.onUpdate, - auth: authState, - } -} - -export function toPendingUpdate(upd: tl.TypeUpdate, peers: PeersIndex, fromDifference = false): PendingUpdate { - const channelId = extractChannelIdFromUpdate(upd) || 0 - const pts = 'pts' in upd ? upd.pts : undefined - // eslint-disable-next-line no-nested-ternary - const ptsCount = 'ptsCount' in upd ? upd.ptsCount : pts ? 0 : undefined - const qts = 'qts' in upd ? upd.qts : undefined - - return { - update: upd, - channelId, - pts, - ptsBefore: pts ? pts - ptsCount! : undefined, - qts, - qtsBefore: qts ? qts - 1 : undefined, - peers, - fromDifference, - } -} diff --git a/packages/core/src/highlevel/updates/utils.ts b/packages/core/src/highlevel/updates/utils.ts new file mode 100644 index 00000000..117317c9 --- /dev/null +++ b/packages/core/src/highlevel/updates/utils.ts @@ -0,0 +1,117 @@ +import { tl } from '@mtcute/tl' + +import { MtTypeAssertionError } from '../../types/errors.js' +import { PeersIndex } from '../types/peers/peers-index.js' +import type { PendingUpdate } from './types.js' + +export function messageToUpdate(message: tl.TypeMessage): tl.TypeUpdate { + switch (message.peerId!._) { + case 'peerUser': + case 'peerChat': + return { + _: 'updateNewMessage', + message, + pts: 0, + ptsCount: 0, + } + case 'peerChannel': + return { + _: 'updateNewChannelMessage', + message, + pts: 0, + ptsCount: 0, + } + } +} + +export function extractChannelIdFromUpdate(upd: tl.TypeUpdate): number | undefined { + // holy shit + let res = 0 + + if ('channelId' in upd) { + res = upd.channelId + } else if ( + 'message' in upd && + typeof upd.message !== 'string' && + 'peerId' in upd.message && + upd.message.peerId && + 'channelId' in upd.message.peerId + ) { + res = upd.message.peerId.channelId + } + + if (res === 0) return undefined + + return res +} + +export function toPendingUpdate(upd: tl.TypeUpdate, peers: PeersIndex, fromDifference = false): PendingUpdate { + const channelId = extractChannelIdFromUpdate(upd) || 0 + const pts = 'pts' in upd ? upd.pts : undefined + // eslint-disable-next-line no-nested-ternary + const ptsCount = 'ptsCount' in upd ? upd.ptsCount : pts ? 0 : undefined + const qts = 'qts' in upd ? upd.qts : undefined + + return { + update: upd, + channelId, + pts, + ptsBefore: pts ? pts - ptsCount! : undefined, + qts, + qtsBefore: qts ? qts - 1 : undefined, + peers, + fromDifference, + } +} + +export function isMessageEmpty(upd: tl.TypeUpdate): boolean { + return (upd as Extract).message?._ === 'messageEmpty' +} + +// dummy updates which are used for methods that return messages.affectedHistory. +// that is not an update, but it carries info about pts, and we need to handle it + +/** + * Create a dummy `updates` container with given updates. + */ +export function createDummyUpdatesContainer(updates: tl.TypeUpdate[], seq = 0): tl.TypeUpdates { + return { + _: 'updates', + seq, + date: 0, + chats: [], + users: [], + updates, + } +} + +/** + * Create a dummy update from PTS and PTS count. + * + * @param pts PTS + * @param ptsCount PTS count + * @param channelId Channel ID (bare), if applicable + */ +export function createDummyUpdate(pts: number, ptsCount: number, channelId = 0): tl.TypeUpdates { + return createDummyUpdatesContainer([ + { + _: 'mtcute.dummyUpdate', + channelId, + pts, + ptsCount, + }, + ]) +} + +/** @internal */ +export function assertIsUpdatesGroup( + ctx: string, + upd: tl.TypeUpdates, +): asserts upd is tl.RawUpdates | tl.RawUpdatesCombined { + switch (upd._) { + case 'updates': + case 'updatesCombined': + return + } + throw new MtTypeAssertionError(ctx, 'updates | updatesCombined', upd._) +} diff --git a/packages/file-id/src/convert.ts b/packages/core/src/highlevel/utils/convert-file-id.ts similarity index 97% rename from packages/file-id/src/convert.ts rename to packages/core/src/highlevel/utils/convert-file-id.ts index 8198ba40..0d7276f3 100644 --- a/packages/file-id/src/convert.ts +++ b/packages/core/src/highlevel/utils/convert-file-id.ts @@ -1,8 +1,12 @@ -import { assertNever, Long, parseMarkedPeerId, tl } from '@mtcute/core' +import Long from 'long' + +import { parseFileId, tdFileId as td } from '@mtcute/file-id' +import { tl } from '@mtcute/tl' + +import { parseMarkedPeerId } from '../../utils/peer-utils.js' -import { parseFileId } from './parse.js' -import { tdFileId as td } from './types.js' import FileType = td.FileType +import { assertNever } from '../../types/utils.js' const EMPTY_BUFFER = new Uint8Array(0) diff --git a/packages/client/src/utils/file-type.test.ts b/packages/core/src/highlevel/utils/file-type.test.ts similarity index 98% rename from packages/client/src/utils/file-type.test.ts rename to packages/core/src/highlevel/utils/file-type.test.ts index f0fa9d5e..8549c554 100644 --- a/packages/client/src/utils/file-type.test.ts +++ b/packages/core/src/highlevel/utils/file-type.test.ts @@ -1,7 +1,8 @@ import { describe, expect, it } from 'vitest' +import { hexDecodeToBuffer } from '@mtcute/tl-runtime' + import { guessFileMime } from './file-type.js' -import { hexDecodeToBuffer } from './index.js' describe('guessFileMime', () => { it.each([ diff --git a/packages/client/src/utils/file-type.ts b/packages/core/src/highlevel/utils/file-type.ts similarity index 100% rename from packages/client/src/utils/file-type.ts rename to packages/core/src/highlevel/utils/file-type.ts diff --git a/packages/client/src/utils/file-utils.test.ts b/packages/core/src/highlevel/utils/file-utils.test.ts similarity index 99% rename from packages/client/src/utils/file-utils.test.ts rename to packages/core/src/highlevel/utils/file-utils.test.ts index 47b27cc3..aeb31ad2 100644 --- a/packages/client/src/utils/file-utils.test.ts +++ b/packages/core/src/highlevel/utils/file-utils.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' -import { hexDecodeToBuffer, hexEncode, utf8Decode, utf8EncodeToBuffer } from '@mtcute/core/utils.js' +import { hexDecodeToBuffer, hexEncode, utf8Decode, utf8EncodeToBuffer } from '@mtcute/tl-runtime' import { extractFileName, diff --git a/packages/client/src/utils/file-utils.ts b/packages/core/src/highlevel/utils/file-utils.ts similarity index 95% rename from packages/client/src/utils/file-utils.ts rename to packages/core/src/highlevel/utils/file-utils.ts index ed6b8fdb..345843dd 100644 --- a/packages/client/src/utils/file-utils.ts +++ b/packages/core/src/highlevel/utils/file-utils.ts @@ -1,5 +1,7 @@ -import { MtArgumentError } from '@mtcute/core' -import { concatBuffers, hexDecodeToBuffer, utf8EncodeToBuffer } from '@mtcute/core/utils.js' +import { hexDecodeToBuffer, utf8EncodeToBuffer } from '@mtcute/tl-runtime' + +import { MtArgumentError } from '../../types/errors.js' +import { concatBuffers } from '../../utils/buffer-utils.js' /** * Given file size, determine the appropriate chunk size (in KB) diff --git a/packages/client/src/utils/index.ts b/packages/core/src/highlevel/utils/index.ts similarity index 78% rename from packages/client/src/utils/index.ts rename to packages/core/src/highlevel/utils/index.ts index 2c595af5..661c2378 100644 --- a/packages/client/src/utils/index.ts +++ b/packages/core/src/highlevel/utils/index.ts @@ -1,3 +1,5 @@ +// todo: merge this with the main utils dir + export * from './file-utils.js' export * from './inline-utils.js' export * from './inspectable.js' @@ -5,6 +7,4 @@ export * from './misc-utils.js' export * from './peer-utils.js' export * from './rps-meter.js' export * from './stream-utils.js' -export * from './updates-utils.js' export * from './voice-utils.js' -export * from '@mtcute/core/utils.js' diff --git a/packages/client/src/utils/inline-utils.test.ts b/packages/core/src/highlevel/utils/inline-utils.test.ts similarity index 96% rename from packages/client/src/utils/inline-utils.test.ts rename to packages/core/src/highlevel/utils/inline-utils.test.ts index 4defb711..c628e7c3 100644 --- a/packages/client/src/utils/inline-utils.test.ts +++ b/packages/core/src/highlevel/utils/inline-utils.test.ts @@ -1,6 +1,7 @@ +import Long from 'long' import { describe, expect, it } from 'vitest' -import { Long, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' import { encodeInlineMessageId, normalizeInlineId, parseInlineMessageId } from './inline-utils.js' diff --git a/packages/client/src/utils/inline-utils.ts b/packages/core/src/highlevel/utils/inline-utils.ts similarity index 93% rename from packages/client/src/utils/inline-utils.ts rename to packages/core/src/highlevel/utils/inline-utils.ts index 22240acb..61b782ca 100644 --- a/packages/client/src/utils/inline-utils.ts +++ b/packages/core/src/highlevel/utils/inline-utils.ts @@ -1,5 +1,7 @@ -import { assertNever, tl } from '@mtcute/core' -import { base64DecodeToBuffer, base64Encode, TlBinaryReader, TlBinaryWriter } from '@mtcute/core/utils.js' +import { tl } from '@mtcute/tl' +import { base64DecodeToBuffer, base64Encode, TlBinaryReader, TlBinaryWriter } from '@mtcute/tl-runtime' + +import { assertNever } from '../../types/utils.js' /** * Parse TDLib style inline message ID diff --git a/packages/client/src/utils/inspectable.test.ts b/packages/core/src/highlevel/utils/inspectable.test.ts similarity index 100% rename from packages/client/src/utils/inspectable.test.ts rename to packages/core/src/highlevel/utils/inspectable.test.ts diff --git a/packages/client/src/utils/inspectable.ts b/packages/core/src/highlevel/utils/inspectable.ts similarity index 97% rename from packages/client/src/utils/inspectable.ts rename to packages/core/src/highlevel/utils/inspectable.ts index dc9ef932..6aa86749 100644 --- a/packages/client/src/utils/inspectable.ts +++ b/packages/core/src/highlevel/utils/inspectable.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-argument */ -import { base64Encode } from '@mtcute/core/utils.js' +import { base64Encode } from '@mtcute/tl-runtime' const customInspectSymbol = Symbol.for('nodejs.util.inspect.custom') diff --git a/packages/client/src/utils/memoize.test.ts b/packages/core/src/highlevel/utils/memoize.test.ts similarity index 100% rename from packages/client/src/utils/memoize.test.ts rename to packages/core/src/highlevel/utils/memoize.test.ts diff --git a/packages/client/src/utils/memoize.ts b/packages/core/src/highlevel/utils/memoize.ts similarity index 100% rename from packages/client/src/utils/memoize.ts rename to packages/core/src/highlevel/utils/memoize.ts diff --git a/packages/client/src/utils/misc-utils.ts b/packages/core/src/highlevel/utils/misc-utils.ts similarity index 96% rename from packages/client/src/utils/misc-utils.ts rename to packages/core/src/highlevel/utils/misc-utils.ts index b54a0422..f7cddf25 100644 --- a/packages/client/src/utils/misc-utils.ts +++ b/packages/core/src/highlevel/utils/misc-utils.ts @@ -1,5 +1,4 @@ -import { MtArgumentError } from '@mtcute/core' - +import { MtArgumentError } from '../../types/errors.js' import { ArrayPaginated, ArrayWithTotal, MaybeDynamic, Message } from '../types/index.js' /** diff --git a/packages/client/src/utils/peer-utils.test.ts b/packages/core/src/highlevel/utils/peer-utils.test.ts similarity index 100% rename from packages/client/src/utils/peer-utils.test.ts rename to packages/core/src/highlevel/utils/peer-utils.test.ts diff --git a/packages/client/src/utils/peer-utils.ts b/packages/core/src/highlevel/utils/peer-utils.ts similarity index 93% rename from packages/client/src/utils/peer-utils.ts rename to packages/core/src/highlevel/utils/peer-utils.ts index 02adc632..407b2b4f 100644 --- a/packages/client/src/utils/peer-utils.ts +++ b/packages/core/src/highlevel/utils/peer-utils.ts @@ -1,5 +1,6 @@ -import { assertNever, tl } from '@mtcute/core' +import { tl } from '@mtcute/tl' +import { assertNever } from '../../types/utils.js' import { MtInvalidPeerTypeError } from '../types/errors.js' import { InputPeerLike } from '../types/peers/index.js' @@ -143,3 +144,10 @@ export function inputPeerToPeer(inp: tl.TypeInputPeer): tl.TypePeer { throw new MtInvalidPeerTypeError(inp, `Cannot convert ${inp._} to peer`) } } + +export function extractUsernames(obj: tl.RawUser | tl.RawChannel) { + if (obj.usernames?.length) return obj.usernames.map((x) => x.username.toLowerCase()) + if (obj.username) return [obj.username.toLowerCase()] + + return [] +} diff --git a/packages/core/src/highlevel/utils/platform/storage.ts b/packages/core/src/highlevel/utils/platform/storage.ts new file mode 100644 index 00000000..d65566e3 --- /dev/null +++ b/packages/core/src/highlevel/utils/platform/storage.ts @@ -0,0 +1,7 @@ +import { MtUnsupportedError } from '../../../types/errors.js' +import { ITelegramStorageProvider } from '../../storage/provider.js' + +/** @internal */ +export const _defaultStorageFactory = (_name: string): ITelegramStorageProvider => { + throw new MtUnsupportedError('Please provide a storage explicitly (e.g. @mtcute/sqlite)') +} diff --git a/packages/client/src/utils/platform/storage.web.ts b/packages/core/src/highlevel/utils/platform/storage.web.ts similarity index 65% rename from packages/client/src/utils/platform/storage.web.ts rename to packages/core/src/highlevel/utils/platform/storage.web.ts index 4c830aa0..faed84bd 100644 --- a/packages/client/src/utils/platform/storage.web.ts +++ b/packages/core/src/highlevel/utils/platform/storage.web.ts @@ -1,6 +1,5 @@ -import { IdbStorage } from '@mtcute/core' - -import { MtUnsupportedError } from '../../index.js' +import { IdbStorage } from '../../../storage/index.js' +import { MtUnsupportedError } from '../../../types/errors.js' /** @internal */ export const _defaultStorageFactory = (name: string) => { diff --git a/packages/client/src/utils/query-batcher.test.ts b/packages/core/src/highlevel/utils/query-batcher.test.ts similarity index 96% rename from packages/client/src/utils/query-batcher.test.ts rename to packages/core/src/highlevel/utils/query-batcher.test.ts index f92f1450..3e269230 100644 --- a/packages/client/src/utils/query-batcher.test.ts +++ b/packages/core/src/highlevel/utils/query-batcher.test.ts @@ -1,9 +1,9 @@ import { describe, expect, it } from 'vitest' -import { BaseTelegramClient } from '@mtcute/core' -import { sleep } from '@mtcute/core/utils.js' import { StubTelegramClient } from '@mtcute/test' +import { sleep } from '../../utils/misc-utils.js' +import { ITelegramClient } from '../client.types.js' import { batchedQuery } from './query-batcher.js' describe('batchedQuery', () => { @@ -13,7 +13,7 @@ describe('batchedQuery', () => { it('should correctly batch requests', async () => { const log: string[] = [] - const fetch = async (client: BaseTelegramClient, items: number[]) => { + const fetch = async (client: ITelegramClient, items: number[]) => { log.push(`[start] fetch() ${items.join(', ')}`) await sleep(10) @@ -83,7 +83,7 @@ describe('batchedQuery', () => { it('should correctly limit batch size', async () => { const log: string[] = [] - const fetch = async (client: BaseTelegramClient, items: number[]) => { + const fetch = async (client: ITelegramClient, items: number[]) => { log.push(`[start] fetch() ${items.join(', ')}`) await sleep(10) @@ -164,7 +164,7 @@ describe('batchedQuery', () => { it('should correctly do concurrent requests', async () => { const log: string[] = [] - const fetch = async (client: BaseTelegramClient, items: number[]) => { + const fetch = async (client: ITelegramClient, items: number[]) => { log.push(`[start] fetch() ${items.join(', ')}`) await sleep(10) @@ -248,7 +248,7 @@ describe('batchedQuery', () => { it('should correctly handle errors', async () => { const log: string[] = [] - const fetch = async (client: BaseTelegramClient, items: number[]) => { + const fetch = async (client: ITelegramClient, items: number[]) => { log.push(`[start] fetch() ${items.join(', ')}`) await sleep(10) @@ -314,7 +314,7 @@ describe('batchedQuery', () => { const log: string[] = [] - const fetch = async (client: BaseTelegramClient, items: number[]) => { + const fetch = async (client: ITelegramClient, items: number[]) => { log.push(`[start] ${client.log.prefix} fetch() ${items.join(', ')}`) await sleep(10) @@ -397,7 +397,7 @@ describe('batchedQuery', () => { it('should correctly handle fetcher omitting some items', async () => { const log: string[] = [] - const fetch = async (client: BaseTelegramClient, items: number[]) => { + const fetch = async (client: ITelegramClient, items: number[]) => { log.push(`[start] fetch() ${items.join(', ')}`) await sleep(10) @@ -473,7 +473,7 @@ describe('batchedQuery', () => { it('should correctly retry failed batches one by one entirely', async () => { const log: string[] = [] - const fetch = async (client: BaseTelegramClient, items: number[]) => { + const fetch = async (client: ITelegramClient, items: number[]) => { log.push(`[start] fetch() ${items.join(', ')}`) await sleep(10) @@ -540,7 +540,7 @@ describe('batchedQuery', () => { it('should correctly retry failed batches one by one partially', async () => { const log: string[] = [] - const fetch = async (client: BaseTelegramClient, items: number[]) => { + const fetch = async (client: ITelegramClient, items: number[]) => { log.push(`[start] fetch() ${items.join(', ')}`) await sleep(10) diff --git a/packages/client/src/utils/query-batcher.ts b/packages/core/src/highlevel/utils/query-batcher.ts similarity index 90% rename from packages/client/src/utils/query-batcher.ts rename to packages/core/src/highlevel/utils/query-batcher.ts index dc39ed45..abc5e811 100644 --- a/packages/client/src/utils/query-batcher.ts +++ b/packages/core/src/highlevel/utils/query-batcher.ts @@ -1,5 +1,5 @@ -import { BaseTelegramClient } from '@mtcute/core' -import { Deque } from '@mtcute/core/utils.js' +import { Deque } from '../../utils/deque.js' +import { ITelegramClient } from '../client.types.js' type Resolve = (value: T | PromiseLike) => void type Reject = (err?: unknown) => void @@ -12,6 +12,8 @@ interface InternalState { numRunning: number } +// todo: should it be MtClient? + /** * Helper function for building batched queries. * @@ -28,12 +30,12 @@ export function batchedQuery(params: { * If some item is not found, it should be omitted from the result array, * this way the corresponding request will be resolved with `null`. */ - fetch: (client: BaseTelegramClient, items: T[]) => Promise + fetch: (client: ITelegramClient, items: T[]) => Promise /** Key derivation function for input items */ - inputKey: (item: T, client: BaseTelegramClient) => K + inputKey: (item: T, client: ITelegramClient) => K /** Key derivation function for output items */ - outputKey: (item: U, client: BaseTelegramClient) => K + outputKey: (item: U, client: ITelegramClient) => K /** * Maximum number of items to be passed to the `fetcher` function at once. @@ -62,12 +64,12 @@ export function batchedQuery(params: { * or an array of items for which the query should be retried (waiters for other items will throw `err`). */ retrySingleOnError?: (items: T[], err: unknown) => boolean | T[] -}): (client: BaseTelegramClient, item: T) => Promise { +}): (client: ITelegramClient, item: T) => Promise { const { inputKey, outputKey, fetch, maxBatchSize = Infinity, maxConcurrent = 1, retrySingleOnError } = params const symbol = Symbol('batchedQueryState') - function getState(client_: BaseTelegramClient) { + function getState(client_: ITelegramClient) { const client = client_ as { [symbol]?: InternalState } if (!client[symbol]) { @@ -82,7 +84,7 @@ export function batchedQuery(params: { return client[symbol] } - function addWaiter(client: BaseTelegramClient, waiters: WaitersMap, item: T) { + function addWaiter(client: ITelegramClient, waiters: WaitersMap, item: T) { const key = inputKey(item, client) let arr = waiters.get(key) @@ -106,13 +108,13 @@ export function batchedQuery(params: { return arr } - function startLoops(client: BaseTelegramClient, state: InternalState) { + function startLoops(client: ITelegramClient, state: InternalState) { for (let i = state.numRunning; i <= maxConcurrent; i++) { processPending(client, state) } } - function processPending(client: BaseTelegramClient, state: InternalState) { + function processPending(client: ITelegramClient, state: InternalState) { const { waiters, fetchingKeys, retryQueue } = state if (state.numRunning >= maxConcurrent) return diff --git a/packages/client/src/utils/rps-meter.ts b/packages/core/src/highlevel/utils/rps-meter.ts similarity index 96% rename from packages/client/src/utils/rps-meter.ts rename to packages/core/src/highlevel/utils/rps-meter.ts index 28525053..8fdf2a9c 100644 --- a/packages/client/src/utils/rps-meter.ts +++ b/packages/core/src/highlevel/utils/rps-meter.ts @@ -1,4 +1,4 @@ -import { Deque } from '@mtcute/core/utils.js' +import { Deque } from '../../utils/deque.js' export class RpsMeter { _hits: Deque diff --git a/packages/client/src/utils/stream-utils.test.ts b/packages/core/src/highlevel/utils/stream-utils.test.ts similarity index 100% rename from packages/client/src/utils/stream-utils.test.ts rename to packages/core/src/highlevel/utils/stream-utils.test.ts diff --git a/packages/client/src/utils/stream-utils.ts b/packages/core/src/highlevel/utils/stream-utils.ts similarity index 97% rename from packages/client/src/utils/stream-utils.ts rename to packages/core/src/highlevel/utils/stream-utils.ts index 7355dde1..a600adc5 100644 --- a/packages/client/src/utils/stream-utils.ts +++ b/packages/core/src/highlevel/utils/stream-utils.ts @@ -1,4 +1,5 @@ -import { AsyncLock, concatBuffers } from '@mtcute/core/utils.js' +import { AsyncLock } from '../../utils/async-lock.js' +import { concatBuffers } from '../../utils/buffer-utils.js' export function bufferToStream(buf: Uint8Array): ReadableStream { return new ReadableStream({ diff --git a/packages/client/src/utils/voice-utils.test.ts b/packages/core/src/highlevel/utils/voice-utils.test.ts similarity index 96% rename from packages/client/src/utils/voice-utils.test.ts rename to packages/core/src/highlevel/utils/voice-utils.test.ts index 78414a74..60036d4a 100644 --- a/packages/client/src/utils/voice-utils.test.ts +++ b/packages/core/src/highlevel/utils/voice-utils.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' -import { hexDecodeToBuffer, hexEncode } from '@mtcute/core/utils.js' +import { hexDecodeToBuffer, hexEncode } from '@mtcute/tl-runtime' import { decodeWaveform, encodeWaveform } from './voice-utils.js' diff --git a/packages/client/src/utils/voice-utils.ts b/packages/core/src/highlevel/utils/voice-utils.ts similarity index 97% rename from packages/client/src/utils/voice-utils.ts rename to packages/core/src/highlevel/utils/voice-utils.ts index e6f2a677..ee8a83b9 100644 --- a/packages/client/src/utils/voice-utils.ts +++ b/packages/core/src/highlevel/utils/voice-utils.ts @@ -1,4 +1,4 @@ -import { dataViewFromBuffer } from '@mtcute/core/utils.js' +import { dataViewFromBuffer } from '../../utils/buffer-utils.js' /** * Decode 5-bit encoded voice message waveform into diff --git a/packages/core/src/highlevel/worker/errors.ts b/packages/core/src/highlevel/worker/errors.ts new file mode 100644 index 00000000..7c650c29 --- /dev/null +++ b/packages/core/src/highlevel/worker/errors.ts @@ -0,0 +1,130 @@ +import { tl } from '@mtcute/tl' + +import { + MtArgumentError, + MtcuteError, + MtSecurityError, + MtTimeoutError, + MtTypeAssertionError, + MtUnsupportedError, +} from '../../types/errors.js' +import { MtEmptyError, MtInvalidPeerTypeError, MtMessageNotFoundError, MtPeerNotFoundError } from '../types/errors.js' + +export interface SerializedError { + name: string + message: string + stack?: string + custom?: Record +} + +export function serializeError(error: unknown): SerializedError { + if (!(error instanceof Error)) return { name: 'Error', message: String(error) } + + const res: SerializedError = { + name: 'Error', + message: error.message, + stack: error.stack, + } + + const ctor = error.constructor + + if (ctor === MtTypeAssertionError) { + const _error = error as MtTypeAssertionError + res.name = 'MtTypeAssertionError' + res.custom = { + context: _error.context, + expected: _error.expected, + actual: _error.actual, + } + } else if (ctor === MtTimeoutError) { + const _error = error as MtTimeoutError + res.name = 'MtTimeoutError' + res.custom = { + timeout: _error.timeout, + } + } else if (ctor === MtMessageNotFoundError) { + const _error = error as MtMessageNotFoundError + res.name = 'MtMessageNotFoundError' + res.custom = { + peerId: _error.peerId, + messageId: _error.messageId, + context: _error.context, + } + } else if (ctor === tl.RpcError) { + res.name = 'RpcError' + res.custom = { ...error } + } else if (ctor === MtArgumentError) res.name = 'MtArgumentError' + else if (ctor === MtSecurityError) res.name = 'MtSecurityError' + else if (ctor === MtUnsupportedError) res.name = 'MtUnsupportedError' + else if (ctor === MtPeerNotFoundError) res.name = 'MtPeerNotFoundError' + else if (ctor === MtInvalidPeerTypeError) res.name = 'MtInvalidPeerTypeError' + else if (ctor === MtEmptyError) res.name = 'MtEmptyError' + else if (ctor instanceof MtcuteError) res.name = 'MtcuteError' + + return res +} + +export function deserializeError(error: SerializedError): Error { + let err2: Error + + switch (error.name) { + case 'MtTypeAssertionError': { + const custom = error.custom as { context: string; expected: string; actual: string } + + err2 = new MtTypeAssertionError(custom.context, custom.expected, custom.actual) + break + } + case 'MtTimeoutError': { + const custom = error.custom as { timeout?: number } + + err2 = new MtTimeoutError(custom.timeout) + break + } + case 'MtMessageNotFoundError': { + const custom = error.custom as { peerId: number; messageId: number; context?: string } + + err2 = new MtMessageNotFoundError(custom.peerId, custom.messageId, custom.context) + break + } + case 'RpcError': { + const custom = error.custom as unknown as tl.RpcError + err2 = new tl.RpcError(custom.code, custom.text) + err2.message = error.message // may have been formatted + + for (const key in custom) { + if (key === 'code' || key === 'text') continue + // @ts-expect-error lol + err2[key] = custom[key] + } + break + } + case 'MtArgumentError': + err2 = new MtArgumentError() + break + case 'MtSecurityError': + err2 = new MtSecurityError() + break + case 'MtUnsupportedError': + err2 = new MtUnsupportedError() + break + case 'MtPeerNotFoundError': + err2 = new MtPeerNotFoundError() + break + case 'MtInvalidPeerTypeError': + err2 = new MtInvalidPeerTypeError('', '') + err2.message = error.message // lol + break + case 'MtEmptyError': + err2 = new MtEmptyError() + break + case 'MtcuteError': + err2 = new MtcuteError() + break + default: + err2 = new Error(error.message) + } + + err2.stack = error.stack + + return err2 +} diff --git a/packages/core/src/highlevel/worker/invoker.ts b/packages/core/src/highlevel/worker/invoker.ts new file mode 100644 index 00000000..a2b644cf --- /dev/null +++ b/packages/core/src/highlevel/worker/invoker.ts @@ -0,0 +1,75 @@ +import { ControllablePromise, createControllablePromise } from '../../utils/controllable-promise.js' +import { deserializeError } from './errors.js' +import { SendFn, WorkerInboundMessage, WorkerOutboundMessage } from './protocol.js' + +export type InvokeTarget = Extract['target'] + +export class WorkerInvoker { + constructor(private send: SendFn) {} + + private _nextId = 0 + private _pending = new Map>() + + private _invoke( + target: InvokeTarget, + method: string, + args: unknown[], + isVoid: boolean, + ) { + const id = this._nextId++ + + this.send({ + type: 'invoke', + id, + target, + method, + args, + void: isVoid, + }) + + if (!isVoid) { + const promise = createControllablePromise() + + this._pending.set(id, promise) + + return promise + } + } + + invoke( + target: InvokeTarget, + method: string, + args: unknown[], + ): Promise { + return this._invoke(target, method, args, false) as Promise + } + + invokeVoid(target: InvokeTarget, method: string, args: unknown[]): void { + this._invoke(target, method, args, true) + } + + handleResult(msg: Extract) { + const promise = this._pending.get(msg.id) + if (!promise) return + + if (msg.error) { + promise.reject(deserializeError(msg.error)) + } else { + promise.resolve(msg.result) + } + } + + makeBinder(target: InvokeTarget) { + return (method: K, isVoid = false) => { + let fn + + if (isVoid) { + fn = (...args: unknown[]) => this.invokeVoid(target, method as string, args) + } else { + fn = (...args: unknown[]) => this.invoke(target, method as string, args) + } + + return fn as T[K] + } + } +} diff --git a/packages/core/src/highlevel/worker/platform/connect.ts b/packages/core/src/highlevel/worker/platform/connect.ts new file mode 100644 index 00000000..cfeafbdd --- /dev/null +++ b/packages/core/src/highlevel/worker/platform/connect.ts @@ -0,0 +1,20 @@ +import { Worker } from 'worker_threads' + +import { ClientMessageHandler, SendFn, SomeWorker } from '../protocol.js' + +export function connectToWorker(worker: SomeWorker, handler: ClientMessageHandler): [SendFn, () => void] { + if (!(worker instanceof Worker)) { + throw new Error('Only worker_threads are supported') + } + + const send: SendFn = worker.postMessage.bind(worker) + + worker.on('message', handler) + + return [ + send, + () => { + worker.off('message', handler) + }, + ] +} diff --git a/packages/core/src/highlevel/worker/platform/connect.web.ts b/packages/core/src/highlevel/worker/platform/connect.web.ts new file mode 100644 index 00000000..b77e468a --- /dev/null +++ b/packages/core/src/highlevel/worker/platform/connect.web.ts @@ -0,0 +1,59 @@ +import { beforeExit } from '../../../utils/platform/exit-hook.js' +import { ClientMessageHandler, SendFn, SomeWorker } from '../protocol.js' + +export function connectToWorker(worker: SomeWorker, handler: ClientMessageHandler): [SendFn, () => void] { + if (worker instanceof Worker) { + const send: SendFn = worker.postMessage.bind(worker) + + const messageHandler = (ev: MessageEvent) => { + handler(ev.data) + } + + worker.addEventListener('message', messageHandler) + + return [ + send, + () => { + worker.removeEventListener('message', messageHandler) + }, + ] + } + + if (worker instanceof SharedWorker) { + const send: SendFn = worker.port.postMessage.bind(worker.port) + + const pingInterval = setInterval(() => { + worker.port.postMessage({ __type__: 'ping' }) + }, 10000) + + const messageHandler = (ev: MessageEvent) => { + if (ev.data.__type__ === 'timeout') { + // we got disconnected from the worker due to timeout + // if the page is still alive (which is unlikely), we should reconnect + // however it's not really possible with SharedWorker API without re-creating the worker + // so we just reload the page for now + location.reload() + + return + } + + handler(ev.data) + } + + worker.port.addEventListener('message', messageHandler) + worker.port.start() + + const close = () => { + clearInterval(pingInterval) + worker.port.postMessage({ __type__: 'close' }) + worker.port.removeEventListener('message', messageHandler) + worker.port.close() + } + + beforeExit(close) + + return [send, close] + } + + throw new Error('Only workers and shared workers are supported') +} diff --git a/packages/core/src/highlevel/worker/platform/register.ts b/packages/core/src/highlevel/worker/platform/register.ts new file mode 100644 index 00000000..b64091a3 --- /dev/null +++ b/packages/core/src/highlevel/worker/platform/register.ts @@ -0,0 +1,22 @@ +import { parentPort } from 'worker_threads' + +import { RespondFn, WorkerMessageHandler } from '../protocol.js' + +const registered = false + +export function registerWorker(handler: WorkerMessageHandler): RespondFn { + if (!parentPort) { + throw new Error('registerWorker() must be called from a worker thread') + } + if (registered) { + throw new Error('registerWorker() must be called only once') + } + + const port = parentPort + + const respond: RespondFn = port.postMessage.bind(port) + + parentPort.on('message', (message) => handler(message, respond)) + + return respond +} diff --git a/packages/core/src/highlevel/worker/platform/register.web.ts b/packages/core/src/highlevel/worker/platform/register.web.ts new file mode 100644 index 00000000..196f0019 --- /dev/null +++ b/packages/core/src/highlevel/worker/platform/register.web.ts @@ -0,0 +1,78 @@ +import { RespondFn, WorkerMessageHandler } from '../protocol.js' + +const registered = false + +export function registerWorker(handler: WorkerMessageHandler): RespondFn { + if (registered) { + throw new Error('registerWorker() must be called only once') + } + + if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) { + const respond: RespondFn = self.postMessage.bind(self) + + self.addEventListener('message', (message) => handler(message.data, respond)) + + return respond + } + + if (typeof SharedWorkerGlobalScope !== 'undefined' && self instanceof SharedWorkerGlobalScope) { + const connections: MessagePort[] = [] + + const broadcast = (message: unknown) => { + for (const port of connections) { + port.postMessage(message) + } + } + + self.onconnect = (event) => { + const port = event.ports[0] + connections.push(port) + + const respond = port.postMessage.bind(port) + + // not very reliable, but better than nothing + // SharedWorker API doesn't provide a way to detect when the client closes the connection + // so we just assume that the client is done when it sends a 'close' message + // and keep a timeout for the case when the client closes without sending a 'close' message + const onClose = () => { + port.close() + const idx = connections.indexOf(port) + + if (idx >= 0) { + connections.splice(connections.indexOf(port), 1) + } + } + + const onTimeout = () => { + console.warn('some connection timed out!') + respond({ __type__: 'timeout' }) + onClose() + } + + // 60s should be a reasonable timeout considering that the client should send a ping every 10s + // so even if the browser has suspended the timers, we should still get a ping within a minute + let timeout = setTimeout(onTimeout, 60000) + + port.addEventListener('message', async (message) => { + if (message.data.__type__ === 'close') { + onClose() + + return + } + + if (message.data.__type__ === 'ping') { + clearTimeout(timeout) + timeout = setTimeout(onTimeout, 60000) + + return + } + + handler(message.data, respond) + }) + } + + return broadcast + } + + throw new Error('registerWorker() must be called from a worker') +} diff --git a/packages/core/src/highlevel/worker/port.ts b/packages/core/src/highlevel/worker/port.ts new file mode 100644 index 00000000..2b0a6db4 --- /dev/null +++ b/packages/core/src/highlevel/worker/port.ts @@ -0,0 +1,106 @@ +import { tl } from '@mtcute/tl' + +import { LogManager } from '../../utils/logger.js' +import { ITelegramClient } from '../client.types.js' +import { PeersIndex } from '../types/peers/peers-index.js' +import { RawUpdateHandler } from '../updates/types.js' +import { WorkerInvoker } from './invoker.js' +import { connectToWorker } from './platform/connect.js' +import { ClientMessageHandler, SomeWorker, WorkerCustomMethods } from './protocol.js' +import { TelegramStorageProxy } from './storage.js' + +export interface TelegramWorkerPortOptions { + worker: SomeWorker +} + +export class TelegramWorkerPort implements ITelegramClient { + constructor(readonly options: TelegramWorkerPortOptions) {} + + readonly log = new LogManager('worker') + + private _serverUpdatesHandler: (updates: tl.TypeUpdates) => void = () => {} + onServerUpdate(handler: (updates: tl.TypeUpdates) => void): void { + this._serverUpdatesHandler = handler + } + + private _errorHandler: (err: unknown) => void = () => {} + onError(handler: (err: unknown) => void): void { + this._errorHandler = handler + } + emitError(err: unknown): void { + this._errorHandler(err) + } + + private _updateHandler: RawUpdateHandler = () => {} + onUpdate(handler: RawUpdateHandler): void { + this._updateHandler = handler + } + + private _onMessage: ClientMessageHandler = (message) => { + switch (message.type) { + case 'log': + this.log.handler(message.color, message.level, message.tag, message.fmt, message.args) + break + case 'server_update': + this._serverUpdatesHandler(message.update) + break + case 'update': { + const peers = new PeersIndex(message.users, message.chats) + peers.hasMin = message.hasMin + this._updateHandler(message.update, peers) + break + } + case 'result': + this._invoker.handleResult(message) + break + case 'error': + this.emitError(message.error) + break + } + } + + private _connection = connectToWorker(this.options.worker, this._onMessage) + private _invoker = new WorkerInvoker(this._connection[0]) + private _bind = this._invoker.makeBinder('client') + + readonly storage = new TelegramStorageProxy(this._invoker) + + private _destroyed = false + destroy(terminate = false): void { + if (this._destroyed) return + this._connection[1]() + this._destroyed = true + + if (terminate && 'terminate' in this.options.worker) { + this.options.worker.terminate() + } + } + + invokeCustom( + method: T, + ...args: Parameters + ): Promise> { + return this._invoker.invoke('custom', method as string, args) as Promise> + } + + readonly prepare = this._bind('prepare') + private _connect = this._bind('connect') + async connect(): Promise { + await this._connect() + await this.storage.self.fetch() // force cache self locally + } + readonly close = this._bind('close') + readonly notifyLoggedIn = this._bind('notifyLoggedIn') + readonly notifyLoggedOut = this._bind('notifyLoggedOut') + readonly notifyChannelOpened = this._bind('notifyChannelOpened') + readonly notifyChannelClosed = this._bind('notifyChannelClosed') + readonly call = this._bind('call') + readonly importSession = this._bind('importSession') + readonly exportSession = this._bind('exportSession') + readonly handleClientUpdate = this._bind('handleClientUpdate', true) + readonly getApiCrenetials = this._bind('getApiCrenetials') + readonly getPoolSize = this._bind('getPoolSize') + readonly getPrimaryDcId = this._bind('getPrimaryDcId') + readonly computeSrpParams = this._bind('computeSrpParams') + readonly computeNewPasswordHash = this._bind('computeNewPasswordHash') +} diff --git a/packages/core/src/highlevel/worker/protocol.ts b/packages/core/src/highlevel/worker/protocol.ts new file mode 100644 index 00000000..b607f0d7 --- /dev/null +++ b/packages/core/src/highlevel/worker/protocol.ts @@ -0,0 +1,55 @@ +import type { Worker as NodeWorker } from 'worker_threads' + +import { tl } from '@mtcute/tl' + +import { SerializedError } from './errors.js' + +export type WorkerInboundMessage = + | { + type: 'invoke' + id: number + target: + | 'custom' + | 'client' + | 'storage' + | 'storage-self' + | 'storage-peers' + method: string + args: unknown[] + void: boolean + } + +export type WorkerOutboundMessage = + | { type: 'server_update'; update: tl.TypeUpdates } + | { + type: 'update' + update: tl.TypeUpdate + users: Map + chats: Map + hasMin: boolean + } + | { type: 'error'; error: unknown } + | { + type: 'log' + color: number + level: number + tag: string + fmt: string + args: unknown[] + } + | { + type: 'result' + id: number + result?: unknown + error?: SerializedError + } + +export type SomeWorker = NodeWorker | Worker | SharedWorker + +export type SendFn = (message: WorkerInboundMessage) => void +export type ClientMessageHandler = (message: WorkerOutboundMessage) => void + +export type RespondFn = (message: WorkerOutboundMessage) => void +export type WorkerMessageHandler = (message: WorkerInboundMessage, respond: RespondFn) => void + +export type WorkerCustomMethods = Record Promise> diff --git a/packages/core/src/highlevel/worker/storage.ts b/packages/core/src/highlevel/worker/storage.ts new file mode 100644 index 00000000..49252410 --- /dev/null +++ b/packages/core/src/highlevel/worker/storage.ts @@ -0,0 +1,80 @@ +import { tl } from '@mtcute/tl' + +import { MtArgumentError } from '../../types/errors.js' +import { PublicPart } from '../../types/utils.js' +import type { CurrentUserInfo, CurrentUserService } from '../storage/service/current-user.js' +import type { PeersService } from '../storage/service/peers.js' +import { TelegramStorageManager } from '../storage/storage.js' +import { WorkerInvoker } from './invoker.js' + +class CurrentUserServiceProxy implements PublicPart { + constructor(private _invoker: WorkerInvoker) {} + private _bind = this._invoker.makeBinder('storage-self') + + private _cached?: CurrentUserInfo | null + + private _store = this._bind('store') + async store(info: CurrentUserInfo | null): Promise { + await this._store(info) + this._cached = info + } + + private _storeFrom = this._bind('storeFrom') + async storeFrom(user: tl.TypeUser): Promise { + this._cached = await this._storeFrom(user) + + return this._cached + } + + private _fetch = this._bind('fetch') + async fetch(): Promise { + if (this._cached) return this._cached + + this._cached = await this._fetch() + + return this._cached + } + + getCached(safe?: boolean): CurrentUserInfo | null { + if (this._cached === undefined) { + if (safe) return null + + throw new MtArgumentError('User info is not cached yet') + } + + return this._cached + } + + private _update = this._bind('update') + async update(params: Parameters[0]): Promise { + await this._update(params) + this._cached = await this._fetch() + } +} + +class PeersServiceProxy implements PublicPart { + constructor(private _invoker: WorkerInvoker) {} + private _bind = this._invoker.makeBinder('storage-peers') + + readonly updatePeersFrom = this._bind('updatePeersFrom') + readonly store = this._bind('store') + readonly getById = this._bind('getById') + readonly getByPhone = this._bind('getByPhone') + readonly getByUsername = this._bind('getByUsername') + readonly getCompleteById = this._bind('getCompleteById') +} + +export class TelegramStorageProxy implements PublicPart { + constructor(private _invoker: WorkerInvoker) {} + + private _bind = this._invoker.makeBinder('storage') + + // todo - remove once we move these to updates manager + readonly updates = null as never + readonly refMsgs = null as never + + readonly self = new CurrentUserServiceProxy(this._invoker) + readonly peers = new PeersServiceProxy(this._invoker) + + readonly clear = this._bind('clear') +} diff --git a/packages/core/src/highlevel/worker/worker.ts b/packages/core/src/highlevel/worker/worker.ts new file mode 100644 index 00000000..06eb89a0 --- /dev/null +++ b/packages/core/src/highlevel/worker/worker.ts @@ -0,0 +1,123 @@ +import { BaseTelegramClient, BaseTelegramClientOptions } from '../base.js' +import { serializeError } from './errors.js' +import { registerWorker } from './platform/register.js' +import { RespondFn, WorkerCustomMethods, WorkerInboundMessage, WorkerMessageHandler } from './protocol.js' + +export interface TelegramWorkerOptions { + client: BaseTelegramClient | BaseTelegramClientOptions + customMethods?: T +} + +export function makeTelegramWorker(params: TelegramWorkerOptions) { + const { client: client_, customMethods } = params + + const client = client_ instanceof BaseTelegramClient ? client_ : new BaseTelegramClient(client_) + + const onInvoke = (msg: Extract, respond: RespondFn) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let target: any + + switch (msg.target) { + case 'custom': + target = customMethods + break + case 'client': + target = client + break + case 'storage': + target = client.storage + break + case 'storage-self': + target = client.storage.self + break + case 'storage-peers': + target = client.storage.peers + break + + default: { + respond({ + type: 'result', + id: msg.id, + error: new Error(`Unknown target ${msg.target}`), + }) + + return + } + } + + const method = target[msg.method] + + if (!method) { + respond({ + type: 'result', + id: msg.id, + error: new Error(`Method ${msg.method} not found on ${msg.target}`), + }) + + return + } + + Promise.resolve(method.apply(target, msg.args)) + .then((res) => { + if (msg.void) return + + respond({ + type: 'result', + id: msg.id, + result: res, + }) + }) + .catch((err) => { + respond({ + type: 'result', + id: msg.id, + error: serializeError(err), + }) + }) + } + + const onMessage: WorkerMessageHandler = (message, respond) => { + switch (message.type) { + case 'invoke': + onInvoke(message, respond) + break + } + } + + const broadcast = registerWorker(onMessage) + + client.log.mgr.handler = (color, level, tag, fmt, args) => + broadcast({ + type: 'log', + color, + level, + tag, + fmt, + args, + }) + client.onError((err) => + broadcast({ + type: 'error', + error: err, + }), + ) + + if (client.updates) { + client.onUpdate((update, peers) => + broadcast({ + type: 'update', + update, + users: peers.users, + chats: peers.chats, + hasMin: peers.hasMin, + }), + ) + } else { + client.onServerUpdate((update) => + broadcast({ + type: 'server_update', + update, + }), + ) + } +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index a5a1e4fe..564cca66 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,5 +1,4 @@ -export * from './base-client.js' -export * from './base-client.types.js' +export * from './highlevel/index.js' export * from './network/index.js' export * from './storage/index.js' export * from './types/index.js' diff --git a/packages/core/src/network/client.ts b/packages/core/src/network/client.ts new file mode 100644 index 00000000..9e1c087f --- /dev/null +++ b/packages/core/src/network/client.ts @@ -0,0 +1,393 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import EventEmitter from 'events' + +import { tl } from '@mtcute/tl' +import { __tlReaderMap as defaultReaderMap } from '@mtcute/tl/binary/reader.js' +import { __tlWriterMap as defaultWriterMap } from '@mtcute/tl/binary/writer.js' +import { TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime' + +import { IMtStorageProvider } from '../storage/provider.js' +import { StorageManager, StorageManagerExtraOptions } from '../storage/storage.js' +import { MustEqual } from '../types/index.js' +import { + asyncResettable, + CryptoProviderFactory, + DcOptions, + defaultCryptoProviderFactory, + defaultProductionDc, + defaultProductionIpv6Dc, + defaultTestDc, + defaultTestIpv6Dc, + ICryptoProvider, + Logger, + LogManager, +} from '../utils/index.js' +import { ConfigManager } from './config-manager.js' +import { NetworkManager, NetworkManagerExtraParams, RpcCallOptions } from './network-manager.js' +import { PersistentConnectionParams } from './persistent-connection.js' +import { ReconnectionStrategy } from './reconnection.js' +import { SessionConnection } from './session-connection.js' +import { TransportFactory } from './transports/index.js' + +/** Options for {@link MtClient} */ +export interface MtClientOptions { + /** + * API ID from my.telegram.org + */ + apiId: number + /** + * API hash from my.telegram.org + */ + apiHash: string + + /** + * Storage to use for this client. + */ + storage: IMtStorageProvider + + /** Additional options for the storage manager */ + storageOptions?: StorageManagerExtraOptions + + /** + * Cryptography provider factory to allow delegating + * crypto to native addon, worker, etc. + */ + crypto?: CryptoProviderFactory + + /** + * Whether to use IPv6 datacenters + * (IPv6 will be preferred when choosing a DC by id) + * (default: false) + */ + useIpv6?: boolean + + /** + * Primary DC to use for initial connection. + * This does not mean this will be the only DC used, + * nor that this DC will actually be primary, this only + * determines the first DC the library will try to connect to. + * Can be used to connect to other networks (like test DCs). + * + * When session already contains primary DC, this parameter is ignored. + * + * @default Production DC 2. + */ + defaultDcs?: DcOptions + + /** + * Whether to connect to test servers. + * + * If passed, {@link defaultDc} defaults to Test DC 2. + * + * **Must** be passed if using test servers, even if + * you passed custom {@link defaultDc} + */ + testMode?: boolean + + /** + * Additional options for initConnection call. + * `apiId` and `query` are not available and will be ignored. + * Omitted values will be filled with defaults + */ + initConnectionOptions?: Partial> + + /** + * Transport factory to use in the client. + * + * @default platform-specific transport: WebSocket on the web, TCP in node + */ + transport?: TransportFactory + + /** + * Reconnection strategy. + * + * @default simple reconnection strategy: first 0ms, then up to 5s (increasing by 1s) + */ + reconnectionStrategy?: ReconnectionStrategy + + /** + * Maximum duration of a flood_wait that will be waited automatically. + * Flood waits above this threshold will throw a FloodWaitError. + * Set to 0 to disable. Can be overridden with `throwFlood` parameter in call() params + * + * @default 10000 + */ + floodSleepThreshold?: number + + /** + * Maximum number of retries when calling RPC methods. + * Call is retried when InternalError or FloodWaitError is encountered. + * Can be set to Infinity. + * + * @default 5 + */ + maxRetryCount?: number + + /** + * If true, every single API call will be wrapped with `tl.invokeWithoutUpdates`, + * effectively disabling the server-sent events for the clients. + * May be useful in some cases. + * + * Note that this only wraps calls made with `.call()` within the primary + * connection. Additional connections and direct `.sendForResult()` calls + * must be wrapped manually. + * + * @default false + */ + disableUpdates?: boolean + + /** + * mtcute can send all unknown RPC errors to [danog](https://github.com/danog)'s + * [error reporting service](https://rpc.pwrtelegram.xyz/). + * + * This is fully anonymous (except maybe IP) and is only used to improve the library + * and developer experience for everyone working with MTProto. This is fully opt-in, + * and if you're too paranoid, you can disable it by manually passing `enableErrorReporting: false` to the client. + * + * @default false + */ + enableErrorReporting?: boolean + + /** + * If true, RPC errors will have a stack trace of the initial `.call()` + * or `.sendForResult()` call position, which drastically improves + * debugging experience.
+ * If false, they will have a stack trace of mtcute internals. + * + * Internally this creates a stack capture before every RPC call + * and stores it until the result is received. This might + * use a lot more memory than normal, thus can be disabled here. + * + * @default true + */ + niceStacks?: boolean + + /** + * Extra parameters for {@link NetworkManager} + */ + network?: NetworkManagerExtraParams + + /** + * Logger instance for the client. + * If not passed, a new one will be created. + */ + logger?: Logger + + /** + * Set logging level for the client. + * Shorthand for `client.log.level = level`. + * + * See static members of {@link LogManager} for possible values. + */ + logLevel?: number + + /** + * **EXPERT USE ONLY!** + * + * Override TL layer used for the connection. + * + * **Does not** change the schema used. + */ + overrideLayer?: number + + /** + * **EXPERT USE ONLY** + * + * Override reader map used for the connection. + */ + readerMap?: TlReaderMap + + /** + * **EXPERT USE ONLY** + * + * Override writer map used for the connection. + */ + writerMap?: TlWriterMap +} + +/** + * Basic MTProto client implementation, only doing the bare minimum + * to make RPC calls and receive low-level updates, as well as providing + * some APIs to manage that. + */ +export class MtClient extends EventEmitter { + /** + * Crypto provider taken from {@link MtClientOptions.crypto} + */ + readonly crypto: ICryptoProvider + + /** Storage manager */ + readonly storage: StorageManager + + /** + * "Test mode" taken from {@link MtClientOptions.testMode} + */ + protected readonly _testMode: boolean + + /** + * Primary DCs taken from {@link MtClientOptions.defaultDcs}, + * loaded from session or changed by other means (like redirecting). + */ + _defaultDcs: DcOptions + + private _niceStacks: boolean + /** TL layer used by the client */ + readonly _layer: number + /** TL readers map used by the client */ + readonly _readerMap: TlReaderMap + /** TL writers map used by the client */ + readonly _writerMap: TlWriterMap + + readonly _config = new ConfigManager(() => this.call({ _: 'help.getConfig' })) + + emitError: (err: unknown, connection?: SessionConnection) => void = console.error.bind(console) + + readonly log: Logger + readonly network: NetworkManager + + constructor(readonly params: MtClientOptions) { + super() + + this.log = params.logger ?? new LogManager() + + if (params.logLevel !== undefined) { + this.log.mgr.level = params.logLevel + } + + this.crypto = (params.crypto ?? defaultCryptoProviderFactory)() + this._testMode = Boolean(params.testMode) + + let dc = params.defaultDcs + + if (!dc) { + if (params.testMode) { + dc = params.useIpv6 ? defaultTestIpv6Dc : defaultTestDc + } else { + dc = params.useIpv6 ? defaultProductionIpv6Dc : defaultProductionDc + } + } + + this._defaultDcs = dc + this._niceStacks = params.niceStacks ?? true + + this._layer = params.overrideLayer ?? tl.LAYER + this._readerMap = params.readerMap ?? defaultReaderMap + this._writerMap = params.writerMap ?? defaultWriterMap + + this.storage = new StorageManager({ + provider: params.storage, + log: this.log, + readerMap: this._readerMap, + writerMap: this._writerMap, + ...params.storageOptions, + }) + + this.network = new NetworkManager( + { + apiId: params.apiId, + crypto: this.crypto, + disableUpdates: params.disableUpdates ?? false, + initConnectionOptions: params.initConnectionOptions, + layer: this._layer, + log: this.log, + readerMap: this._readerMap, + writerMap: this._writerMap, + reconnectionStrategy: params.reconnectionStrategy, + storage: this.storage, + testMode: Boolean(params.testMode), + transport: params.transport, + emitError: this.emitError.bind(this), + floodSleepThreshold: params.floodSleepThreshold ?? 10000, + maxRetryCount: params.maxRetryCount ?? 5, + isPremium: false, + useIpv6: Boolean(params.useIpv6), + enableErrorReporting: params.enableErrorReporting ?? false, + onUsable: () => this.emit('usable'), + onUpdate: (upd) => this.emit('update', upd), + ...params.network, + }, + this._config, + ) + } + + private _prepare = asyncResettable(async () => { + await this.crypto.initialize?.() + await this.storage.load() + + const primaryDc = await this.storage.dcs.fetch() + if (primaryDc !== null) this._defaultDcs = primaryDc + }) + + /** + * **ADVANCED** + * + * Do all the preparations, but don't connect just yet. + * Useful when you want to do some preparations before + * connecting, like setting up session. + * + * Call {@link connect} to actually connect. + */ + prepare() { + return this._prepare.run() + } + + private _connect = asyncResettable(async () => { + await this._prepare.run() + await this.network.connect(this._defaultDcs) + }) + + /** + * Initialize the connection to the primary DC. + * + * You shouldn't usually call this method directly as it is called + * implicitly the first time you call {@link call}. + */ + async connect(): Promise { + return this._connect.run() + } + + /** + * Close all connections and finalize the client. + */ + async close(): Promise { + this._config.destroy() + this.network.destroy() + + await this.storage.save() + await this.storage.destroy?.() + + this._prepare.reset() + this._connect.reset() + } + + /** + * Make an RPC call. + * + * The connection must have been {@link connect}-ed + * before calling this method. + * + * This method is still quite low-level and you shouldn't use this + * when using high-level API provided by `@mtcute/client`. + * + * @param message RPC method to call + * @param params Additional call parameters + */ + async call( + message: MustEqual, + params?: RpcCallOptions, + ): Promise { + const stack = this._niceStacks ? new Error().stack : undefined + + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return this.network.call(message, params, stack) + } + + /** + * Register an error handler for the client + * + * @param handler Error handler. + */ + onError(handler: (err: unknown) => void): void { + this.emitError = handler + } +} diff --git a/packages/core/src/network/index.ts b/packages/core/src/network/index.ts index 7057a4f0..a1dba3f2 100644 --- a/packages/core/src/network/index.ts +++ b/packages/core/src/network/index.ts @@ -1,3 +1,4 @@ +export * from './client.js' export type { ConnectionKind, NetworkManagerExtraParams, RpcCallOptions } from './network-manager.js' export * from './reconnection.js' export * from './session-connection.js' diff --git a/packages/core/src/network/network-manager.ts b/packages/core/src/network/network-manager.ts index 74874138..ad68e6d1 100644 --- a/packages/core/src/network/network-manager.ts +++ b/packages/core/src/network/network-manager.ts @@ -2,7 +2,7 @@ import { mtp, tl } from '@mtcute/tl' import { TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime' import { StorageManager } from '../storage/storage.js' -import { MtArgumentError, MtcuteError, MtTimeoutError } from '../types/index.js' +import { MtArgumentError, MtcuteError, MtTimeoutError, MtUnsupportedError } from '../types/index.js' import { ControllablePromise, createControllablePromise, DcOptions, ICryptoProvider, Logger, sleep } from '../utils/index.js' import { assertTypeIs } from '../utils/type-assertions.js' import { ConfigManager } from './config-manager.js' @@ -48,7 +48,8 @@ export interface NetworkManagerParams { readerMap: TlReaderMap writerMap: TlWriterMap isPremium: boolean - _emitError: (err: Error, connection?: SessionConnection) => void + emitError: (err: Error, connection?: SessionConnection) => void + onUpdate: (upd: tl.TypeUpdates) => void onUsable: () => void } @@ -283,7 +284,7 @@ export class DcConnectionManager { this.download.notifyKeyChange() this.downloadSmall.notifyKeyChange() }) - .catch((e: Error) => this.manager.params._emitError(e)) + .catch((e: Error) => this.manager.params.emitError(e)) }) connection.on('tmp-key-change', (idx: number, key: Uint8Array | null, expires: number) => { if (kind !== 'main') { @@ -305,11 +306,11 @@ export class DcConnectionManager { this.download.notifyKeyChange() this.downloadSmall.notifyKeyChange() }) - .catch((e: Error) => this.manager.params._emitError(e)) + .catch((e: Error) => this.manager.params.emitError(e)) }) connection.on('future-salts', (salts: mtp.RawMt_future_salt[]) => { Promise.resolve(this.manager._storage.salts.store(this.dcId, salts)).catch((e: Error) => - this.manager.params._emitError(e), + this.manager.params.emitError(e), ) }) @@ -341,7 +342,7 @@ export class DcConnectionManager { }) connection.on('error', (err: Error, conn: SessionConnection) => { - this.manager.params._emitError(err, conn) + this.manager.params.emitError(err, conn) }) } @@ -425,7 +426,7 @@ export class NetworkManager { protected readonly _dcConnections = new Map() protected _primaryDc?: DcConnectionManager - private _updateHandler: (upd: tl.TypeUpdates, fromClient: boolean) => void = () => {} + private _updateHandler: (upd: tl.TypeUpdates, fromClient: boolean) => void constructor( readonly params: NetworkManagerParams & NetworkManagerExtraParams, @@ -458,6 +459,7 @@ export class NetworkManager { this._transportFactory = params.transport ?? defaultTransportFactory this._reconnectionStrategy = params.reconnectionStrategy ?? defaultReconnectionStrategy this._connectionCount = params.connectionCount ?? defaultConnectionCountDelegate + this._updateHandler = params.onUpdate this._onConfigChanged = this._onConfigChanged.bind(this) config.onConfigUpdate(this._onConfigChanged) @@ -498,15 +500,6 @@ export class NetworkManager { dc.main.on('usable', () => { this.params.onUsable() - - Promise.resolve(this._storage.self.fetch()) - .then((self) => { - if (self?.isBot) { - // bots may receive tmpSessions, which we should respect - return this.config.update(true) - } - }) - .catch((e: Error) => this.params._emitError(e)) }) dc.main.on('update', (update: tl.TypeUpdates) => { this._updateHandler(update, false) @@ -616,16 +609,44 @@ export class NetworkManager { } } - notifyLoggedIn(auth: tl.auth.TypeAuthorization): void { - if (auth._ === 'auth.authorizationSignUpRequired' || auth.user._ === 'userEmpty') { - return + notifyLoggedIn(auth: tl.auth.TypeAuthorization | tl.RawUser): tl.RawUser { + if (auth._ === 'auth.authorizationSignUpRequired') { + throw new MtUnsupportedError( + 'Signup is no longer supported by Telegram for non-official clients. Please use your mobile device to sign up.', + ) } - if (auth.tmpSessions) { - this._primaryDc?.main.setCount(auth.tmpSessions) + let user: tl.RawUser + + if (auth._ === 'auth.authorization') { + if (auth.tmpSessions) { + this._primaryDc?.main.setCount(auth.tmpSessions) + } + + user = auth.user as tl.RawUser + } else { + if (auth.bot) { + // bots may receive tmpSessions, which we should respect + this.config.update(true) + .catch((e: Error) => this.params.emitError(e)) + } + + user = auth } - this.setIsPremium(auth.user.premium!) + this.setIsPremium(user.premium!) + + // telegram ignores invokeWithoutUpdates for auth methods + if (auth._ === 'auth.authorization' && this.params.disableUpdates) { + this.resetSessions() + } + + return user + } + + notifyLoggedOut(): void { + this.setIsPremium(false) + this.resetSessions() } resetSessions(): void { @@ -781,14 +802,6 @@ export class NetworkManager { throw lastError! } - setUpdateHandler(handler: (upd: tl.TypeUpdates, fromClient: boolean) => void): void { - this._updateHandler = handler - } - - handleUpdate(update: tl.TypeUpdates, fromClient = true): void { - this._updateHandler(update, fromClient) - } - changeTransport(factory: TransportFactory): void { for (const dc of this._dcConnections.values()) { dc.main.changeTransport(factory) diff --git a/packages/core/src/storage/index.ts b/packages/core/src/storage/index.ts index 0bd39b5e..ddf40746 100644 --- a/packages/core/src/storage/index.ts +++ b/packages/core/src/storage/index.ts @@ -2,4 +2,5 @@ export * from './driver.js' export * from './provider.js' export * from './providers/idb/index.js' export * from './providers/memory/index.js' +export * from './repository/index.js' export * from './storage.js' diff --git a/packages/core/src/storage/provider.ts b/packages/core/src/storage/provider.ts index 96d9a1c2..acd2e783 100644 --- a/packages/core/src/storage/provider.ts +++ b/packages/core/src/storage/provider.ts @@ -1,8 +1,6 @@ import { IStorageDriver } from './driver.js' import { IAuthKeysRepository } from './repository/auth-keys.js' import { IKeyValueRepository } from './repository/key-value.js' -import { IPeersRepository } from './repository/peers.js' -import { IReferenceMessagesRepository } from './repository/ref-messages.js' export type IStorageProvider = T & { readonly driver: IStorageDriver @@ -11,6 +9,4 @@ export type IStorageProvider = T & { export type IMtStorageProvider = IStorageProvider<{ readonly kv: IKeyValueRepository readonly authKeys: IAuthKeysRepository - readonly peers: IPeersRepository - readonly refMessages: IReferenceMessagesRepository }> diff --git a/packages/core/src/storage/providers/idb/idb.test.ts b/packages/core/src/storage/providers/idb/idb.test.ts index c3d5a7c7..bf6b0ded 100644 --- a/packages/core/src/storage/providers/idb/idb.test.ts +++ b/packages/core/src/storage/providers/idb/idb.test.ts @@ -1,9 +1,9 @@ import { afterAll, beforeAll, describe } from 'vitest' +import { testPeersRepository } from '../../../highlevel/storage/repository/peers.test-utils.js' +import { testRefMessagesRepository } from '../../../highlevel/storage/repository/ref-messages.test-utils.js' import { testAuthKeysRepository } from '../../repository/auth-keys.test-utils.js' import { testKeyValueRepository } from '../../repository/key-value.test-utils.js' -import { testPeersRepository } from '../../repository/peers.test-utils.js' -import { testRefMessagesRepository } from '../../repository/ref-messages.test-utils.js' import { IdbStorage } from './index.js' if (import.meta.env.TEST_ENV === 'browser') { diff --git a/packages/core/src/storage/providers/idb/index.ts b/packages/core/src/storage/providers/idb/index.ts index 5cd50170..a0d36e8e 100644 --- a/packages/core/src/storage/providers/idb/index.ts +++ b/packages/core/src/storage/providers/idb/index.ts @@ -5,6 +5,8 @@ import { IdbKvRepository } from './repository/kv.js' import { IdbPeersRepository } from './repository/peers.js' import { IdbRefMsgRepository } from './repository/ref-messages.js' +export { IdbStorageDriver } from './driver.js' + /** * mtcute storage that uses IndexedDB as a backend. * diff --git a/packages/core/src/storage/providers/idb/repository/peers.ts b/packages/core/src/storage/providers/idb/repository/peers.ts index 28e955d9..4197a3f7 100644 --- a/packages/core/src/storage/providers/idb/repository/peers.ts +++ b/packages/core/src/storage/providers/idb/repository/peers.ts @@ -1,4 +1,4 @@ -import { IPeersRepository } from '../../../repository/peers.js' +import { IPeersRepository } from '../../../../highlevel/storage/repository/peers.js' import { IdbStorageDriver } from '../driver.js' import { reqToPromise } from '../utils.js' diff --git a/packages/core/src/storage/providers/idb/repository/ref-messages.ts b/packages/core/src/storage/providers/idb/repository/ref-messages.ts index 7b9c5ff0..276ab833 100644 --- a/packages/core/src/storage/providers/idb/repository/ref-messages.ts +++ b/packages/core/src/storage/providers/idb/repository/ref-messages.ts @@ -1,4 +1,4 @@ -import { IReferenceMessagesRepository } from '../../../repository/ref-messages.js' +import { IReferenceMessagesRepository } from '../../../../highlevel/storage/repository/ref-messages.js' import { IdbStorageDriver } from '../driver.js' import { cursorToIterator, reqToPromise, txToPromise } from '../utils.js' diff --git a/packages/core/src/storage/providers/memory/driver.ts b/packages/core/src/storage/providers/memory/driver.ts index 6ee842b1..36037cb8 100644 --- a/packages/core/src/storage/providers/memory/driver.ts +++ b/packages/core/src/storage/providers/memory/driver.ts @@ -3,9 +3,9 @@ import { IStorageDriver } from '../../driver.js' export class MemoryStorageDriver implements IStorageDriver { readonly states: Map = new Map() - getState(repo: string, def: T) { + getState(repo: string, def: () => T) { if (!this.states.has(repo)) { - this.states.set(repo, def) + this.states.set(repo, def()) } return this.states.get(repo) as T diff --git a/packages/core/src/storage/providers/memory/index.ts b/packages/core/src/storage/providers/memory/index.ts index 752112db..c6b1f0b4 100644 --- a/packages/core/src/storage/providers/memory/index.ts +++ b/packages/core/src/storage/providers/memory/index.ts @@ -1,3 +1,4 @@ +import { ITelegramStorageProvider } from '../../../highlevel/storage/provider.js' import { IMtStorageProvider } from '../../provider.js' import { MemoryStorageDriver } from './driver.js' import { MemoryAuthKeysRepository } from './repository/auth-keys.js' @@ -5,6 +6,8 @@ import { MemoryKeyValueRepository } from './repository/kv.js' import { MemoryPeersRepository } from './repository/peers.js' import { MemoryRefMessagesRepository } from './repository/ref-messages.js' +export { MemoryStorageDriver } from './driver.js' + /** * In-memory storage driver implementation for mtcute. * @@ -12,7 +15,7 @@ import { MemoryRefMessagesRepository } from './repository/ref-messages.js' * **will** be lost on restart. Only use this storage for testing, * or if you know exactly what you're doing. */ -export class MemoryStorage implements IMtStorageProvider { +export class MemoryStorage implements IMtStorageProvider, ITelegramStorageProvider { readonly driver = new MemoryStorageDriver() readonly kv = new MemoryKeyValueRepository(this.driver) readonly authKeys = new MemoryAuthKeysRepository(this.driver) diff --git a/packages/core/src/storage/providers/memory/memory.test.ts b/packages/core/src/storage/providers/memory/memory.test.ts index d386aa01..36c889c8 100644 --- a/packages/core/src/storage/providers/memory/memory.test.ts +++ b/packages/core/src/storage/providers/memory/memory.test.ts @@ -1,9 +1,9 @@ import { describe } from 'vitest' +import { testPeersRepository } from '../../../highlevel/storage/repository/peers.test-utils.js' +import { testRefMessagesRepository } from '../../../highlevel/storage/repository/ref-messages.test-utils.js' import { testAuthKeysRepository } from '../../repository/auth-keys.test-utils.js' import { testKeyValueRepository } from '../../repository/key-value.test-utils.js' -import { testPeersRepository } from '../../repository/peers.test-utils.js' -import { testRefMessagesRepository } from '../../repository/ref-messages.test-utils.js' import { MemoryStorage } from './index.js' describe('memory storage', () => { diff --git a/packages/core/src/storage/providers/memory/repository/auth-keys.ts b/packages/core/src/storage/providers/memory/repository/auth-keys.ts index 25842cb3..a43ec9da 100644 --- a/packages/core/src/storage/providers/memory/repository/auth-keys.ts +++ b/packages/core/src/storage/providers/memory/repository/auth-keys.ts @@ -10,11 +10,11 @@ interface AuthKeysState { export class MemoryAuthKeysRepository implements IAuthKeysRepository { constructor(readonly _driver: MemoryStorageDriver) {} - readonly state = this._driver.getState('authKeys', { + readonly state = this._driver.getState('authKeys', () => ({ authKeys: new Map(), authKeysTemp: new Map(), authKeysTempExpiry: new Map(), - }) + })) set(dc: number, key: Uint8Array | null): void { if (key) { diff --git a/packages/core/src/storage/providers/memory/repository/kv.ts b/packages/core/src/storage/providers/memory/repository/kv.ts index 1678266a..0e56f912 100644 --- a/packages/core/src/storage/providers/memory/repository/kv.ts +++ b/packages/core/src/storage/providers/memory/repository/kv.ts @@ -4,7 +4,7 @@ import { MemoryStorageDriver } from '../driver.js' export class MemoryKeyValueRepository implements IKeyValueRepository { constructor(readonly _driver: MemoryStorageDriver) {} - readonly state = this._driver.getState>('kv', new Map()) + readonly state = this._driver.getState>('kv', () => new Map()) set(key: string, value: Uint8Array): void { this.state.set(key, value) diff --git a/packages/core/src/storage/providers/memory/repository/peers.ts b/packages/core/src/storage/providers/memory/repository/peers.ts index 67a76fb3..6c5b1ba9 100644 --- a/packages/core/src/storage/providers/memory/repository/peers.ts +++ b/packages/core/src/storage/providers/memory/repository/peers.ts @@ -1,4 +1,4 @@ -import { IPeersRepository } from '../../../repository/peers.js' +import { IPeersRepository } from '../../../../highlevel/storage/repository/peers.js' import { MemoryStorageDriver } from '../driver.js' interface PeersState { @@ -10,11 +10,11 @@ interface PeersState { export class MemoryPeersRepository implements IPeersRepository { constructor(readonly _driver: MemoryStorageDriver) {} - readonly state = this._driver.getState('peers', { + readonly state = this._driver.getState('peers', () => ({ entities: new Map(), usernameIndex: new Map(), phoneIndex: new Map(), - }) + })) store(peer: IPeersRepository.PeerInfo): void { const old = this.state.entities.get(peer.id) diff --git a/packages/core/src/storage/providers/memory/repository/ref-messages.ts b/packages/core/src/storage/providers/memory/repository/ref-messages.ts index 9a567c07..d85be0c7 100644 --- a/packages/core/src/storage/providers/memory/repository/ref-messages.ts +++ b/packages/core/src/storage/providers/memory/repository/ref-messages.ts @@ -1,4 +1,4 @@ -import { IReferenceMessagesRepository } from '../../../repository/ref-messages.js' +import { IReferenceMessagesRepository } from '../../../../highlevel/storage/repository/ref-messages.js' import { MemoryStorageDriver } from '../driver.js' interface RefMessagesState { @@ -8,9 +8,9 @@ interface RefMessagesState { export class MemoryRefMessagesRepository implements IReferenceMessagesRepository { constructor(readonly _driver: MemoryStorageDriver) {} - readonly state = this._driver.getState('refMessages', { + readonly state = this._driver.getState('refMessages', () => ({ refs: new Map(), - }) + })) store(peerId: number, chatId: number, msgId: number): void { if (!this.state.refs.has(peerId)) { diff --git a/packages/core/src/storage/repository/index.ts b/packages/core/src/storage/repository/index.ts index 03df2e41..613574b8 100644 --- a/packages/core/src/storage/repository/index.ts +++ b/packages/core/src/storage/repository/index.ts @@ -1,4 +1,4 @@ +export * from '../../highlevel/storage/repository/peers.js' +export * from '../../highlevel/storage/repository/ref-messages.js' export * from './auth-keys.js' export * from './key-value.js' -export * from './peers.js' -export * from './ref-messages.js' diff --git a/packages/core/src/storage/service/base.ts b/packages/core/src/storage/service/base.ts index 48ef00c6..0a04a217 100644 --- a/packages/core/src/storage/service/base.ts +++ b/packages/core/src/storage/service/base.ts @@ -12,10 +12,10 @@ export interface ServiceOptions { } export class BaseService { - readonly _driver: IStorageDriver - readonly _readerMap: TlReaderMap - readonly _writerMap: TlWriterMap - readonly _log: Logger + protected _driver: IStorageDriver + protected _readerMap: TlReaderMap + protected _writerMap: TlWriterMap + protected _log: Logger constructor(opts: ServiceOptions) { this._driver = opts.driver diff --git a/packages/core/src/storage/service/current-user.ts b/packages/core/src/storage/service/current-user.ts deleted file mode 100644 index fd5c72fe..00000000 --- a/packages/core/src/storage/service/current-user.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { TlBinaryReader, TlBinaryWriter } from '@mtcute/tl-runtime' - -import { MtArgumentError } from '../../types/index.js' -import { IKeyValueRepository } from '../repository/key-value.js' -import { BaseService, ServiceOptions } from './base.js' - -export interface CurrentUserInfo { - userId: number - isBot: boolean -} - -// todo: do we need this in core? - -const KV_CURRENT_USER = 'current_user' - -function serialize(info: CurrentUserInfo | null): Uint8Array { - if (!info) return new Uint8Array(0) - - const writer = TlBinaryWriter.manual(16) - writer.int(1) // version - - let flags = 0 - if (info.isBot) flags |= 1 - - writer.int(flags) - writer.int53(info.userId) - - return writer.result() -} - -function parse(data: Uint8Array): CurrentUserInfo | null { - if (data.length === 0) return null - - const reader = TlBinaryReader.manual(data) - if (reader.int() !== 1) return null - - const flags = reader.int() - const userId = reader.int53() - - return { - userId, - isBot: (flags & 1) !== 0, - } -} - -// todo: add testMode here - -export class CurrentUserService extends BaseService { - constructor( - readonly _kv: IKeyValueRepository, - opts: ServiceOptions, - ) { - super(opts) - } - - private _cached?: CurrentUserInfo | null - - async store(info: CurrentUserInfo | null): Promise { - if (info && this._cached) { - // update the existing object so the references to it are still valid - if (this._cached.userId === info.userId) { - return - } - - this._cached.userId = info.userId - this._cached.isBot = info.isBot - } else { - this._cached = info - } - - await this._kv.set(KV_CURRENT_USER, serialize(info)) - await this._driver.save?.() - } - - async fetch(): Promise { - if (this._cached) return this._cached - - const data = await this._kv.get(KV_CURRENT_USER) - if (!data) return null - - const info = parse(data) - this._cached = info - - return info - } - - getCached(safe = false): CurrentUserInfo | null { - if (this._cached === undefined) { - if (safe) return null - - throw new MtArgumentError('User info is not cached yet') - } - - return this._cached - } -} diff --git a/packages/core/src/storage/service/updates.test.ts b/packages/core/src/storage/service/updates.test.ts index d2d6d6c6..82c3e699 100644 --- a/packages/core/src/storage/service/updates.test.ts +++ b/packages/core/src/storage/service/updates.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest' import { fakeKeyValueRepository } from '../repository/key-value.test-utils.js' -import { UpdatesStateService } from './updates.js' +import { UpdatesStateService } from '../../highlevel/storage/service/updates.js' import { testServiceOptions } from './utils.test-utils.js' describe('updates state service', () => { diff --git a/packages/core/src/storage/storage.ts b/packages/core/src/storage/storage.ts index f0e0b316..427c02ac 100644 --- a/packages/core/src/storage/storage.ts +++ b/packages/core/src/storage/storage.ts @@ -1,21 +1,14 @@ import { TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime' -import { beforeExit } from '../utils/index.js' +import { asyncResettable, beforeExit } from '../utils/index.js' import { Logger } from '../utils/logger.js' import { IMtStorageProvider } from './provider.js' import { AuthKeysService } from './service/auth-keys.js' import { ServiceOptions } from './service/base.js' -import { CurrentUserService } from './service/current-user.js' import { DefaultDcsService } from './service/default-dcs.js' import { FutureSaltsService } from './service/future-salts.js' -import { PeersService, PeersServiceOptions } from './service/peers.js' -import { RefMessagesService, RefMessagesServiceOptions } from './service/ref-messages.js' -import { UpdatesStateService } from './service/updates.js' -/** - * Options for {@link StorageManager}, for internal use only. - */ -export interface StorageManagerOptions { +interface StorageManagerOptions { provider: IMtStorageProvider log: Logger readerMap: TlReaderMap @@ -25,6 +18,8 @@ export interface StorageManagerOptions { /** * Additional options for {@link StorageManager}, that * can be customized by the user. + * + * @internal */ export interface StorageManagerExtraOptions { /** @@ -35,9 +30,6 @@ export interface StorageManagerExtraOptions { */ saveInterval?: number - refMessages?: RefMessagesServiceOptions - peers?: PeersServiceOptions - /** * Whether to finalize database before exiting. * @@ -53,7 +45,7 @@ export class StorageManager { readonly driver = this.provider.driver readonly log = this.options.log.create('storage') - private _serviceOptions: ServiceOptions = { + readonly _serviceOptions: ServiceOptions = { driver: this.driver, readerMap: this.options.readerMap, writerMap: this.options.writerMap, @@ -62,34 +54,21 @@ export class StorageManager { readonly dcs = new DefaultDcsService(this.provider.kv, this._serviceOptions) readonly salts = new FutureSaltsService(this.provider.kv, this._serviceOptions) - readonly updates = new UpdatesStateService(this.provider.kv, this._serviceOptions) - readonly self = new CurrentUserService(this.provider.kv, this._serviceOptions) readonly keys = new AuthKeysService(this.provider.authKeys, this.salts, this._serviceOptions) - readonly refMsgs = new RefMessagesService( - this.options.refMessages ?? {}, - this.provider.refMessages, - this._serviceOptions, - ) - readonly peers = new PeersService(this.options.peers ?? {}, this.provider.peers, this.refMsgs, this._serviceOptions) private _cleanupRestore?: () => void - private _loadPromise?: Promise | true - load(): Promise { - if (this._loadPromise === true) return Promise.resolve() - if (this._loadPromise) return this._loadPromise - + private _load = asyncResettable(async () => { this.driver.setup?.(this.log) if (this.options.cleanup ?? true) { this._cleanupRestore = beforeExit(() => this._destroy().catch((err) => this.log.error(err))) } - this._loadPromise = Promise.resolve(this.driver.load?.()).then(() => { - this._loadPromise = true - }) - - return this._loadPromise + await this.driver.load?.() + }) + load(): Promise { + return this._load.run() } async save(): Promise { @@ -101,17 +80,15 @@ export class StorageManager { await this.provider.authKeys.deleteAll() } await this.provider.kv.deleteAll() - await this.provider.peers.deleteAll() - await this.provider.refMessages.deleteAll() await this.save() } private async _destroy(): Promise { - if (!this._loadPromise) return - await this._loadPromise + if (!this._load.finished()) return + await this._load.wait() await this.driver.destroy?.() - this._loadPromise = undefined + this._load.reset() } async destroy(): Promise { diff --git a/packages/core/src/types/utils.ts b/packages/core/src/types/utils.ts index 083d8e8c..6eebe378 100644 --- a/packages/core/src/types/utils.ts +++ b/packages/core/src/types/utils.ts @@ -6,6 +6,8 @@ export type MaybeArray = T | T[] export type MustEqual = T extends V ? (V extends T ? T : V) : V +export type PublicPart = { [K in keyof T]: T[K] } + // eslint-disable-next-line @typescript-eslint/no-unused-vars export function assertNever(x: never): never { throw new Error('Illegal state') diff --git a/packages/core/src/utils/crypto/password.ts b/packages/core/src/utils/crypto/password.ts index 6f37d729..6662d628 100644 --- a/packages/core/src/utils/crypto/password.ts +++ b/packages/core/src/utils/crypto/password.ts @@ -4,6 +4,7 @@ import { utf8EncodeToBuffer } from '@mtcute/tl-runtime' import { MtSecurityError, MtUnsupportedError } from '../../types/errors.js' import { bigIntModPow, bigIntToBuffer, bufferToBigInt } from '../bigint-utils.js' import { concatBuffers } from '../buffer-utils.js' +import { assertTypeIs } from '../type-assertions.js' import { ICryptoProvider } from './abstract.js' import { xorBuffer } from './utils.js' @@ -38,9 +39,11 @@ export async function computePasswordHash( */ export async function computeNewPasswordHash( crypto: ICryptoProvider, - algo: tl.RawPasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow, + algo: tl.TypePasswordKdfAlgo, password: string, ): Promise { + assertTypeIs('account.getPassword', algo, 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow') + const salt1 = new Uint8Array(algo.salt1.length + 32) salt1.set(algo.salt1) crypto.randomFill(salt1.subarray(algo.salt1.length)) diff --git a/packages/core/src/utils/function-utils.ts b/packages/core/src/utils/function-utils.ts index 8a7fc3e2..32a6a49d 100644 --- a/packages/core/src/utils/function-utils.ts +++ b/packages/core/src/utils/function-utils.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ export type ThrottledFunction = (() => void) & { reset: () => void } @@ -38,3 +39,33 @@ export function throttle(func: () => void, delay: number): ThrottledFunction { return res } + +export function asyncResettable Promise>(func: T) { + let runningPromise: Promise | null = null + let finished = false + + const run = function (...args: any[]) { + if (finished) return Promise.resolve() + + if (runningPromise) { + return runningPromise + } + + runningPromise = func(...args) + runningPromise.then(() => { + runningPromise = null + finished = true + }) + + return runningPromise + } as T + + return { + run, + finished: () => finished, + wait: () => runningPromise, + reset: () => { + finished = false + }, + } +} diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index 27cac258..87b0e002 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -1,3 +1,4 @@ +export * from '../highlevel/utils/index.js' export * from './async-lock.js' export * from './bigint-utils.js' export * from './buffer-utils.js' diff --git a/packages/core/src/utils/string-session.test.ts b/packages/core/src/utils/string-session.test.ts index 202ed56b..767b9d6e 100644 --- a/packages/core/src/utils/string-session.test.ts +++ b/packages/core/src/utils/string-session.test.ts @@ -50,7 +50,12 @@ describe('writeStringSession', () => { testMode: false, primaryDcs: stubDcs, authKey: stubAuthKey, - self: { userId: 12345, isBot: false }, + self: { + userId: 12345, + isBot: false, + isPremium: false, + usernames: [], + }, }), ).toMatchInlineSnapshot( '"AgUAAAANobcYAAAAAAIAAAAOMTQ5LjE1NC4xNjcuNTAAuwEAAA2htxgCAAAAAgAAAA8xNDkuMTU0LjE2Ny4yMjK7AQAAOTAAAAAAAAA3l3m8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"', @@ -64,7 +69,12 @@ describe('writeStringSession', () => { testMode: true, primaryDcs: stubDcs, authKey: stubAuthKey, - self: { userId: 12345, isBot: false }, + self: { + userId: 12345, + isBot: false, + isPremium: false, + usernames: [], + }, }), ).toMatchInlineSnapshot( '"AgcAAAANobcYAAAAAAIAAAAOMTQ5LjE1NC4xNjcuNTAAuwEAAA2htxgCAAAAAgAAAA8xNDkuMTU0LjE2Ny4yMjK7AQAAOTAAAAAAAAA3l3m8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"', diff --git a/packages/core/src/utils/string-session.ts b/packages/core/src/utils/string-session.ts index 42570d39..614b7edb 100644 --- a/packages/core/src/utils/string-session.ts +++ b/packages/core/src/utils/string-session.ts @@ -1,3 +1,4 @@ +// todo move to highlevel/ import { tl } from '@mtcute/tl' import { base64DecodeToBuffer, @@ -8,7 +9,7 @@ import { TlWriterMap, } from '@mtcute/tl-runtime' -import { CurrentUserInfo } from '../storage/service/current-user.js' +import { CurrentUserInfo } from '../highlevel/storage/service/current-user.js' import { MtArgumentError } from '../types/index.js' import { DcOptions } from './dcs.js' @@ -68,7 +69,7 @@ export function readStringSession(readerMap: TlReaderMap, data: string): StringS const version = buf[0] - if (version !== 1 && version !== 2) { + if (version !== 1 && version !== 2 && version !== 3) { throw new Error(`Invalid session string (version = ${version})`) } @@ -95,6 +96,9 @@ export function readStringSession(readerMap: TlReaderMap, data: string): StringS self = { userId: selfId, isBot: selfBot, + // todo: we should make sure we fetch this from the server at first start + isPremium: false, + usernames: [], } } diff --git a/packages/dispatcher/package.json b/packages/dispatcher/package.json index 4b6dad91..56e4a3f2 100644 --- a/packages/dispatcher/package.json +++ b/packages/dispatcher/package.json @@ -20,8 +20,18 @@ "gen-updates": "node ./scripts/generate.cjs" }, "dependencies": { - "@mtcute/client": "workspace:^", - "@mtcute/test": "workspace:^", + "@mtcute/core": "workspace:^", "events": "3.2.0" + }, + "devDependencies": { + "@mtcute/test": "workspace:^" + }, + "peerDependencies": { + "@mtcute/sqlite": "workspace:^" + }, + "peerDependenciesMeta": { + "@mtcute/sqlite": { + "optional": true + } } } diff --git a/packages/dispatcher/scripts/generate.cjs b/packages/dispatcher/scripts/generate.cjs index e2620e17..59b6f58c 100644 --- a/packages/dispatcher/scripts/generate.cjs +++ b/packages/dispatcher/scripts/generate.cjs @@ -1,4 +1,4 @@ -const { types, toSentence, replaceSections, formatFile } = require('../../client/scripts/generate-updates.cjs') +const { types, toSentence, replaceSections, formatFile } = require('../../mtcute/scripts/generate-updates.cjs') function generateHandler() { const lines = [] diff --git a/packages/dispatcher/src/callback-data-builder.ts b/packages/dispatcher/src/callback-data-builder.ts index 3c639261..098c10e8 100644 --- a/packages/dispatcher/src/callback-data-builder.ts +++ b/packages/dispatcher/src/callback-data-builder.ts @@ -1,4 +1,4 @@ -import { CallbackQuery, MaybeArray, MtArgumentError } from '@mtcute/client' +import { CallbackQuery, MaybeArray, MtArgumentError } from '@mtcute/core' import { UpdateFilter } from './filters/types.js' diff --git a/packages/dispatcher/src/context/base.ts b/packages/dispatcher/src/context/base.ts index d5f9d6c0..c99ce7a2 100644 --- a/packages/dispatcher/src/context/base.ts +++ b/packages/dispatcher/src/context/base.ts @@ -1,4 +1,4 @@ -import { ParsedUpdate, TelegramClient } from '@mtcute/client' +import { ParsedUpdate, TelegramClient } from '@mtcute/core' export type UpdateContext = T & { client: TelegramClient diff --git a/packages/dispatcher/src/context/callback-query.ts b/packages/dispatcher/src/context/callback-query.ts index ba5bf468..72202071 100644 --- a/packages/dispatcher/src/context/callback-query.ts +++ b/packages/dispatcher/src/context/callback-query.ts @@ -1,4 +1,4 @@ -import { CallbackQuery, InlineCallbackQuery, MaybeAsync, Message, TelegramClient } from '@mtcute/client' +import { CallbackQuery, InlineCallbackQuery, MaybeAsync, Message, TelegramClient } from '@mtcute/core' import { UpdateContext } from './base.js' diff --git a/packages/dispatcher/src/context/chat-join-request.ts b/packages/dispatcher/src/context/chat-join-request.ts index 0bad1eda..bed8159c 100644 --- a/packages/dispatcher/src/context/chat-join-request.ts +++ b/packages/dispatcher/src/context/chat-join-request.ts @@ -1,4 +1,4 @@ -import { BotChatJoinRequestUpdate, TelegramClient } from '@mtcute/client' +import { BotChatJoinRequestUpdate, TelegramClient } from '@mtcute/core' import { UpdateContext } from './base.js' diff --git a/packages/dispatcher/src/context/chosen-inline-result.ts b/packages/dispatcher/src/context/chosen-inline-result.ts index 575d9b0e..c6c7b810 100644 --- a/packages/dispatcher/src/context/chosen-inline-result.ts +++ b/packages/dispatcher/src/context/chosen-inline-result.ts @@ -1,4 +1,4 @@ -import { ChosenInlineResult, MtArgumentError, TelegramClient } from '@mtcute/client' +import { ChosenInlineResult, MtArgumentError, TelegramClient } from '@mtcute/core' import { UpdateContext } from './base.js' diff --git a/packages/dispatcher/src/context/inline-query.ts b/packages/dispatcher/src/context/inline-query.ts index e9594f59..707770d7 100644 --- a/packages/dispatcher/src/context/inline-query.ts +++ b/packages/dispatcher/src/context/inline-query.ts @@ -1,4 +1,4 @@ -import { InlineQuery, ParametersSkip1, TelegramClient } from '@mtcute/client' +import { InlineQuery, ParametersSkip1, TelegramClient } from '@mtcute/core' import { UpdateContext } from './base.js' diff --git a/packages/dispatcher/src/context/message.ts b/packages/dispatcher/src/context/message.ts index bac6b13c..fda723f3 100644 --- a/packages/dispatcher/src/context/message.ts +++ b/packages/dispatcher/src/context/message.ts @@ -1,8 +1,9 @@ -import { Message, MtPeerNotFoundError, OmitInputMessageId, ParametersSkip1, Peer, TelegramClient } from '@mtcute/client' -import { DeleteMessagesParams } from '@mtcute/client/src/methods/messages/delete-messages.js' -import { ForwardMessageOptions } from '@mtcute/client/src/methods/messages/forward-messages.js' -import { SendCopyParams } from '@mtcute/client/src/methods/messages/send-copy.js' -import { SendCopyGroupParams } from '@mtcute/client/src/methods/messages/send-copy-group.js' +import { Message, MtPeerNotFoundError, OmitInputMessageId, ParametersSkip1, Peer, TelegramClient } from '@mtcute/core' +// todo: fix these imports when packaging +import { DeleteMessagesParams } from '@mtcute/core/src/highlevel/methods/messages/delete-messages.js' +import { ForwardMessageOptions } from '@mtcute/core/src/highlevel/methods/messages/forward-messages.js' +import { SendCopyParams } from '@mtcute/core/src/highlevel/methods/messages/send-copy.js' +import { SendCopyGroupParams } from '@mtcute/core/src/highlevel/methods/messages/send-copy-group.js' import { UpdateContext } from './base.js' diff --git a/packages/dispatcher/src/context/parse.ts b/packages/dispatcher/src/context/parse.ts index 416e9754..a63e2ba1 100644 --- a/packages/dispatcher/src/context/parse.ts +++ b/packages/dispatcher/src/context/parse.ts @@ -1,4 +1,4 @@ -import { ParsedUpdate, TelegramClient } from '@mtcute/client' +import { ParsedUpdate, TelegramClient } from '@mtcute/core' import { UpdateContextDistributed } from './base.js' import { CallbackQueryContext } from './callback-query.js' diff --git a/packages/dispatcher/src/context/pre-checkout-query.ts b/packages/dispatcher/src/context/pre-checkout-query.ts index da5f6077..05699cbb 100644 --- a/packages/dispatcher/src/context/pre-checkout-query.ts +++ b/packages/dispatcher/src/context/pre-checkout-query.ts @@ -1,4 +1,4 @@ -import { PreCheckoutQuery, TelegramClient } from '@mtcute/client' +import { PreCheckoutQuery, TelegramClient } from '@mtcute/core' import { UpdateContext } from './base.js' diff --git a/packages/dispatcher/src/dispatcher.ts b/packages/dispatcher/src/dispatcher.ts index b394f4b9..0c30704f 100644 --- a/packages/dispatcher/src/dispatcher.ts +++ b/packages/dispatcher/src/dispatcher.ts @@ -23,7 +23,7 @@ import { tl, UserStatusUpdate, UserTypingUpdate, -} from '@mtcute/client' +} from '@mtcute/core' import { UpdateContext } from './context/base.js' import { @@ -68,11 +68,11 @@ import { import { PropagationAction } from './propagation.js' import { defaultStateKeyDelegate, - isCompatibleStorage, - IStateStorage, + IStateStorageProvider, StateKeyDelegate, UpdateState, } from './state/index.js' +import { StateService } from './state/service.js' export interface DispatcherParams { /** @@ -87,7 +87,7 @@ export interface DispatcherParams { * * @default Client's storage */ - storage?: IStateStorage + storage?: IStateStorageProvider /** * Custom key delegate for the dispatcher. @@ -111,11 +111,11 @@ export class Dispatcher { private _scene?: string private _sceneScoped?: boolean - private _storage?: State extends never ? undefined : IStateStorage - private _stateKeyDelegate?: State extends never ? undefined : StateKeyDelegate + private _storage?: StateService + private _stateKeyDelegate?: StateKeyDelegate private _customStateKeyDelegate?: StateKeyDelegate - private _customStorage?: IStateStorage + private _customStorage?: StateService private _errorHandler?: ( err: Error, @@ -137,8 +137,6 @@ export class Dispatcher { protected constructor(client?: TelegramClient, params?: DispatcherParams) { this.dispatchRawUpdate = this.dispatchRawUpdate.bind(this) this.dispatchUpdate = this.dispatchUpdate.bind(this) - this._onClientBeforeConnect = this._onClientBeforeConnect.bind(this) - this._onClientBeforeClose = this._onClientBeforeClose.bind(this) // eslint-disable-next-line prefer-const let { storage, key, sceneName } = params ?? {} @@ -146,32 +144,19 @@ export class Dispatcher { if (client) { this.bindToClient(client) - if (!storage) { - const _storage = client.storage - - // if (!isCompatibleStorage(_storage)) { - // // todo: dont throw if state is never used - // throw new MtArgumentError( - // 'Storage used by the client is not compatible with the dispatcher. Please provide a compatible storage manually', - // ) - // } - - storage = _storage as any - } - if (storage) { - this._storage = storage as any - this._stateKeyDelegate = (key ?? defaultStateKeyDelegate) as any + this._storage = new StateService(storage) + this._stateKeyDelegate = (key ?? defaultStateKeyDelegate) } } else { // child dispatcher without client if (storage) { - this._customStorage = storage as any + this._customStorage = new StateService(storage) } if (key) { - this._customStateKeyDelegate = key as any + this._customStateKeyDelegate = key } if (sceneName) { @@ -213,64 +198,6 @@ export class Dispatcher { return this._scene } - private _onClientBeforeConnect() { - (async () => { - if ( - !this._parent && - this._storage && - this._storage !== (this._client!.storage as unknown as IStateStorage) - ) { - // this is a root dispatcher with custom storage - await this._storage.load?.() - } - - if (this._parent && this._customStorage) { - // this is a child dispatcher with custom storage - await this._customStorage.load?.() - } - - for (const child of this._children) { - child._onClientBeforeConnect() - } - - if (this._scenes) { - for (const scene of this._scenes.values()) { - scene._onClientBeforeConnect() - } - } - })().catch((err) => this._client!._emitError(err)) - } - - private _onClientBeforeClose() { - (async () => { - if ( - !this._parent && - this._storage && - this._storage !== (this._client!.storage as unknown as IStateStorage) - ) { - // this is a root dispatcher with custom storage - await this._storage.save?.() - await this._storage.destroy?.() - } - - if (this._parent && this._customStorage) { - // this is a child dispatcher with custom storage - await this._customStorage.save?.() - await this._customStorage.destroy?.() - } - - for (const child of this._children) { - child._onClientBeforeClose() - } - - if (this._scenes) { - for (const scene of this._scenes.values()) { - scene._onClientBeforeClose() - } - } - })().catch((err) => this._client!._emitError(err)) - } - /** * Bind the dispatcher to the client. * Called by the constructor automatically if @@ -281,8 +208,6 @@ export class Dispatcher { bindToClient(client: TelegramClient): void { client.on('update', this.dispatchUpdate) client.on('raw_update', this.dispatchRawUpdate) - client.on('before_connect', this._onClientBeforeConnect) - client.on('before_close', this._onClientBeforeClose) this._client = client } @@ -294,13 +219,35 @@ export class Dispatcher { if (this._client) { this._client.off('update', this.dispatchUpdate) this._client.off('raw_update', this.dispatchRawUpdate) - this._client.off('before_connect', this._onClientBeforeConnect) - this._client.off('before_close', this._onClientBeforeClose) this._client = undefined } } + /** + * Destroy the dispatcher and all its children. + * + * When destroying, all the registered handlers are removed, + * and the underlying storage is freed. + */ + async destroy(): Promise { + if (this._parent && this._customStorage) { + await this._customStorage.destroy() + } else if (!this._parent && this._storage) { + await this._storage.destroy() + } + + this.removeUpdateHandler('all') + + for (const child of this._children) { + await child.destroy() + } + + for (const scene of this._scenes?.values() ?? []) { + await scene.destroy() + } + } + /** * Process a raw update with this dispatcher. * Calling this method without bound client will not work. @@ -316,7 +263,7 @@ export class Dispatcher { // order does not matter in the dispatcher, // so we can handle each update in its own task - this.dispatchRawUpdateNow(update, peers).catch((err) => this._client!._emitError(err)) + this.dispatchRawUpdateNow(update, peers).catch((err) => this._client!.emitError(err)) } /** @@ -386,7 +333,7 @@ export class Dispatcher { // order does not matter in the dispatcher, // so we can handle each update in its own task - this.dispatchUpdateNow(update).catch((err) => this._client!._emitError(err)) + this.dispatchUpdateNow(update).catch((err) => this._client!.emitError(err)) } /** diff --git a/packages/dispatcher/src/filters/bots.test.ts b/packages/dispatcher/src/filters/bots.test.ts index a8c4c33b..5c6e3b09 100644 --- a/packages/dispatcher/src/filters/bots.test.ts +++ b/packages/dispatcher/src/filters/bots.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' -import { Message, PeersIndex, tl } from '@mtcute/client' +import { Message, PeersIndex, tl } from '@mtcute/core' import { createStub, StubTelegramClient } from '@mtcute/test' import { MessageContext } from '../index.js' diff --git a/packages/dispatcher/src/filters/bots.ts b/packages/dispatcher/src/filters/bots.ts index c368e6e8..ff638a27 100644 --- a/packages/dispatcher/src/filters/bots.ts +++ b/packages/dispatcher/src/filters/bots.ts @@ -1,4 +1,4 @@ -import { MaybeArray, MaybeAsync, Message } from '@mtcute/client' +import { MaybeArray, MaybeAsync, Message } from '@mtcute/core' import { MessageContext } from '../context/message.js' import { chat } from './chat.js' @@ -66,12 +66,11 @@ export const command = ( const lastGroup = m[m.length - 1] if (lastGroup) { - // const state = msg.client.getAuthState() + const self = msg.client.storage.self.getCached() - // if (state.isBot && lastGroup !== state.selfUsername) { - // return false - // } - console.log('todo') + if (self && self.isBot && !self.usernames.includes(lastGroup)) { + return false + } } const match = m.slice(1, -1) diff --git a/packages/dispatcher/src/filters/chat.ts b/packages/dispatcher/src/filters/chat.ts index 92be1e1c..d482c38f 100644 --- a/packages/dispatcher/src/filters/chat.ts +++ b/packages/dispatcher/src/filters/chat.ts @@ -9,7 +9,7 @@ import { PollVoteUpdate, User, UserTypingUpdate, -} from '@mtcute/client' +} from '@mtcute/core' import { UpdateContextDistributed } from '../context/base.js' import { EmptyObject, Modify, UpdateFilter } from './types.js' @@ -89,8 +89,7 @@ export const chatId: { case 'user_typing': { const id = upd.chatId - throw new Error('TODO') - // return (matchSelf && id === upd.client.getAuthState().userId) || indexId.has(id) + return (matchSelf && id === upd.client.storage.self.getCached()?.userId) || indexId.has(id) } } diff --git a/packages/dispatcher/src/filters/group.ts b/packages/dispatcher/src/filters/group.ts index 6ad76ce2..e0993eb2 100644 --- a/packages/dispatcher/src/filters/group.ts +++ b/packages/dispatcher/src/filters/group.ts @@ -1,4 +1,4 @@ -import { MaybeAsync, Message } from '@mtcute/client' +import { MaybeAsync, Message } from '@mtcute/core' import { MessageContext } from '../context/message.js' import { Modify, UpdateFilter } from './types.js' diff --git a/packages/dispatcher/src/filters/logic.ts b/packages/dispatcher/src/filters/logic.ts index dc8c63af..1f08c155 100644 --- a/packages/dispatcher/src/filters/logic.ts +++ b/packages/dispatcher/src/filters/logic.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ // ^^ will be looked into in MTQ-29 -import { MaybeAsync } from '@mtcute/client' +import { MaybeAsync } from '@mtcute/core' import { ExtractBaseMany, ExtractMod, Invert, UnionToIntersection, UpdateFilter } from './types.js' diff --git a/packages/dispatcher/src/filters/message.ts b/packages/dispatcher/src/filters/message.ts index b374bf9b..14f700ec 100644 --- a/packages/dispatcher/src/filters/message.ts +++ b/packages/dispatcher/src/filters/message.ts @@ -16,7 +16,7 @@ import { StickerType, User, Video, -} from '@mtcute/client' +} from '@mtcute/core' import { MessageContext } from '../index.js' import { Modify, UpdateFilter } from './types.js' diff --git a/packages/dispatcher/src/filters/state.ts b/packages/dispatcher/src/filters/state.ts index 8892eea5..5cc676c6 100644 --- a/packages/dispatcher/src/filters/state.ts +++ b/packages/dispatcher/src/filters/state.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { MaybeAsync } from '@mtcute/client' +import { MaybeAsync } from '@mtcute/core' import { UpdateFilter } from './types.js' diff --git a/packages/dispatcher/src/filters/text.ts b/packages/dispatcher/src/filters/text.ts index 2b8f3983..507d0423 100644 --- a/packages/dispatcher/src/filters/text.ts +++ b/packages/dispatcher/src/filters/text.ts @@ -1,4 +1,4 @@ -import { CallbackQuery, ChosenInlineResult, InlineCallbackQuery, InlineQuery, Message } from '@mtcute/client' +import { CallbackQuery, ChosenInlineResult, InlineCallbackQuery, InlineQuery, Message } from '@mtcute/core' import { UpdateContextDistributed } from '../context/base.js' import { UpdateFilter } from './types.js' diff --git a/packages/dispatcher/src/filters/types.ts b/packages/dispatcher/src/filters/types.ts index 7832bdf7..e15755e9 100644 --- a/packages/dispatcher/src/filters/types.ts +++ b/packages/dispatcher/src/filters/types.ts @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ // ^^ will be looked into in MTQ-29 -import { MaybeAsync } from '@mtcute/client' +import { MaybeAsync } from '@mtcute/core' import { UpdateState } from '../state/update-state.js' /** diff --git a/packages/dispatcher/src/filters/updates.ts b/packages/dispatcher/src/filters/updates.ts index 2360c19f..9fef1534 100644 --- a/packages/dispatcher/src/filters/updates.ts +++ b/packages/dispatcher/src/filters/updates.ts @@ -1,4 +1,4 @@ -import { ChatMemberUpdate, ChatMemberUpdateType, MaybeArray, UserStatus, UserStatusUpdate } from '@mtcute/client' +import { ChatMemberUpdate, ChatMemberUpdateType, MaybeArray, UserStatus, UserStatusUpdate } from '@mtcute/core' import { UpdateFilter } from './types.js' diff --git a/packages/dispatcher/src/filters/user.ts b/packages/dispatcher/src/filters/user.ts index 7b748e3f..4030e63e 100644 --- a/packages/dispatcher/src/filters/user.ts +++ b/packages/dispatcher/src/filters/user.ts @@ -14,7 +14,7 @@ import { User, UserStatusUpdate, UserTypingUpdate, -} from '@mtcute/client' +} from '@mtcute/core' import { UpdateContextDistributed } from '../context/base.js' import { UpdateFilter } from './types.js' @@ -94,9 +94,8 @@ export const userId: { case 'user_typing': { const id = upd.userId - throw new Error('TODO') - // return (matchSelf && id === upd.client.getAuthState().userId) || - // indexId.has(id) + return (matchSelf && id === upd.client.storage.self.getCached()?.userId) || + indexId.has(id) } case 'poll_vote': case 'story': @@ -111,9 +110,8 @@ export const userId: { case 'history_read': { const id = upd.chatId - throw new Error('TODO') - // return (matchSelf && id === upd.client.getAuthState().userId) || - // indexId.has(id) + return (matchSelf && id === upd.client.storage.self.getCached()?.userId) || + indexId.has(id) } } diff --git a/packages/dispatcher/src/handler.ts b/packages/dispatcher/src/handler.ts index 6f71ef64..905205f8 100644 --- a/packages/dispatcher/src/handler.ts +++ b/packages/dispatcher/src/handler.ts @@ -16,7 +16,7 @@ import { tl, UserStatusUpdate, UserTypingUpdate, -} from '@mtcute/client' +} from '@mtcute/core' import { UpdateContext } from './context/base.js' import { diff --git a/packages/dispatcher/src/state/index.ts b/packages/dispatcher/src/state/index.ts index f44fb9f0..8a615236 100644 --- a/packages/dispatcher/src/state/index.ts +++ b/packages/dispatcher/src/state/index.ts @@ -1,3 +1,5 @@ export * from './key.js' -export * from './storage.js' +export * from './provider.js' +export * from './providers/index.js' +export * from './repository.js' export * from './update-state.js' diff --git a/packages/dispatcher/src/state/key.ts b/packages/dispatcher/src/state/key.ts index 9524ef53..1423e686 100644 --- a/packages/dispatcher/src/state/key.ts +++ b/packages/dispatcher/src/state/key.ts @@ -1,4 +1,4 @@ -import { assertNever, MaybeAsync, Peer } from '@mtcute/client' +import { assertNever, MaybeAsync, Peer } from '@mtcute/core' import { CallbackQueryContext, MessageContext } from '../context/index.js' diff --git a/packages/dispatcher/src/state/provider.ts b/packages/dispatcher/src/state/provider.ts new file mode 100644 index 00000000..7ec35aac --- /dev/null +++ b/packages/dispatcher/src/state/provider.ts @@ -0,0 +1,7 @@ +import { IStorageProvider } from '@mtcute/core' + +import { IStateRepository } from './repository.js' + +export type IStateStorageProvider = IStorageProvider<{ + state: IStateRepository +}> diff --git a/packages/dispatcher/src/state/providers/index.ts b/packages/dispatcher/src/state/providers/index.ts new file mode 100644 index 00000000..05bd34a6 --- /dev/null +++ b/packages/dispatcher/src/state/providers/index.ts @@ -0,0 +1,2 @@ +export * from './memory.js' +export * from './sqlite.js' diff --git a/packages/dispatcher/src/state/providers/memory.ts b/packages/dispatcher/src/state/providers/memory.ts new file mode 100644 index 00000000..673498d8 --- /dev/null +++ b/packages/dispatcher/src/state/providers/memory.ts @@ -0,0 +1,105 @@ +import { MaybeAsync, MemoryStorageDriver } from '@mtcute/core' + +import { IStateStorageProvider } from '../provider.js' +import { IStateRepository } from '../repository.js' + +interface StateDto { + value: string + expiresAt?: number +} + +interface RateLimitDto { + reset: number + remaining: number +} + +class MemoryStateRepository implements IStateRepository { + constructor(readonly _driver: MemoryStorageDriver) {} + + readonly state = this._driver.getState>('dispatcher_fsm', () => new Map()) + + setState(key: string, state: string, ttl?: number | undefined): void { + this.state.set(key, { + value: state, + expiresAt: ttl ? Date.now() + ttl * 1000 : undefined, + }) + } + + getState(key: string, now: number): string | null { + const state = this.state.get(key) + if (!state) return null + + if (state.expiresAt && state.expiresAt < now) { + this.state.delete(key) + + return null + } + + return state.value + } + + deleteState(key: string): void { + this.state.delete(key) + } + + vacuum(now: number): void { + for (const [key, state] of this.state.entries()) { + if (state.expiresAt && state.expiresAt < now) { + this.state.delete(key) + } + } + + for (const [key, state] of this.rl.entries()) { + if (state.reset < now) { + this.rl.delete(key) + } + } + } + + readonly rl = this._driver.getState>('rl', () => new Map()) + + getRateLimit(key: string, now: number, limit: number, window: number): [number, number] { + // leaky bucket + const item = this.rl.get(key) + + if (!item) { + const state: RateLimitDto = { + reset: now + window * 1000, + remaining: limit, + } + + this.rl.set(key, state) + + return [state.remaining, state.reset] + } + + if (item.reset < now) { + // expired + + const state: RateLimitDto = { + reset: now + window * 1000, + remaining: limit, + } + + this.rl.set(key, state) + + return [state.remaining, state.reset] + } + + item.remaining = item.remaining > 0 ? item.remaining - 1 : 0 + + return [item.remaining, item.reset] + } + + resetRateLimit(key: string): MaybeAsync { + this.rl.delete(key) + } +} + +export class MemoryStateStorage implements IStateStorageProvider { + constructor( + readonly driver: MemoryStorageDriver = new MemoryStorageDriver(), + ) {} + + readonly state = new MemoryStateRepository(this.driver) +} diff --git a/packages/dispatcher/src/state/providers/sqlite.ts b/packages/dispatcher/src/state/providers/sqlite.ts new file mode 100644 index 00000000..eda52c37 --- /dev/null +++ b/packages/dispatcher/src/state/providers/sqlite.ts @@ -0,0 +1,123 @@ +import { MaybeAsync } from '@mtcute/core' +import type { SqliteStorage, SqliteStorageDriver, Statement } from '@mtcute/sqlite' + +import { IStateStorageProvider } from '../provider.js' +import { IStateRepository } from '../repository.js' + +interface StateDto { + value: string + expires_at: number | null +} + +interface RateLimitDto { + reset: number + remaining: number +} + +class SqliteStateRepository implements IStateRepository { + constructor(readonly _driver: SqliteStorageDriver) { + _driver.registerMigration('state', 1, (db) => { + db.exec(` + create table fsm_state ( + key text primary key, + value text not null, + expires_at integer + ); + create table rl_state ( + key text primary key, + reset integer not null, + remaining integer not null + ); + `) + }) + _driver.onLoad(() => { + this._setState = _driver.db.prepare( + 'insert or replace into fsm_state (key, value, expires_at) values (?, ?, ?)', + ) + this._getState = _driver.db.prepare('select value, expires_at from fsm_state where key = ?') + this._deleteState = _driver.db.prepare('delete from fsm_state where key = ?') + this._deleteOldState = _driver.db.prepare('delete from fsm_state where expires_at < ?') + + this._setRl = _driver.db.prepare('insert or replace into rl_state (key, reset, remaining) values (?, ?, ?)') + this._getRl = _driver.db.prepare('select reset, remaining from rl_state where key = ?') + this._deleteRl = _driver.db.prepare('delete from rl_state where key = ?') + this._deleteOldRl = _driver.db.prepare('delete from rl_state where reset < ?') + }) + } + + private _setState!: Statement + setState(key: string, state: string, ttl?: number | undefined): MaybeAsync { + this._setState.run(key, state, ttl ? Date.now() + ttl * 1000 : undefined) + } + + private _getState!: Statement + getState(key: string, now: number): MaybeAsync { + const res_ = this._getState.get(key) + if (!res_) return null + const res = res_ as StateDto + + if (res.expires_at && res.expires_at < now) { + this._deleteState.run(key) + + return null + } + + return res.value + } + + private _deleteState!: Statement + deleteState(key: string): MaybeAsync { + this._deleteState.run(key) + } + + private _deleteOldState!: Statement + private _deleteOldRl!: Statement + vacuum(now: number): MaybeAsync { + this._deleteOldState.run(now) + this._deleteOldRl.run(now) + } + + private _setRl!: Statement + private _getRl!: Statement + private _deleteRl!: Statement + + getRateLimit(key: string, now: number, limit: number, window: number): [number, number] { + const val = this._getRl.get(key) as RateLimitDto | undefined + + // hot path. rate limit fsm entries always have an expiration date + + if (!val || val.reset < now) { + // expired or does not exist + const item: RateLimitDto = { + reset: now + window * 1000, + remaining: limit, + } + + this._setRl.run(key, item.reset, item.remaining) + + return [item.remaining, item.reset] + } + + if (val.remaining > 0) { + val.remaining -= 1 + + this._setRl.run(key, val.reset, val.remaining) + } + + return [val.remaining, val.reset] + } + + resetRateLimit(key: string): MaybeAsync { + this._deleteRl.run(key) + } +} + +export class SqliteStateStorage implements IStateStorageProvider { + constructor(readonly driver: SqliteStorageDriver) {} + + static from(provider: SqliteStorage) { + return new SqliteStateStorage(provider.driver) + } + + readonly state = new SqliteStateRepository(this.driver) +} diff --git a/packages/dispatcher/src/state/repository.ts b/packages/dispatcher/src/state/repository.ts new file mode 100644 index 00000000..d82d9628 --- /dev/null +++ b/packages/dispatcher/src/state/repository.ts @@ -0,0 +1,68 @@ +import { MaybeAsync } from '@mtcute/core' + +/** + * Interface for FSM storage for the dispatcher. + * + * All of the officially supported storages already implement + * this interface, so you can just re-use it. + * + * Current scene is a special case of a `string` state, + * Most of the time you can just store it the same way + * as normal state, prefixing with something like `$current_state_` + * (scene name can't start with `$`). + * Alternatively, you can store them as simple strings + */ +export interface IStateRepository { + /** + * Retrieve state from the storage + * If state is not found or has expired, return `null` + * + * @param key Key of the state, as defined by {@link StateKeyDelegate} + */ + getState(key: string, now: number): MaybeAsync + + /** + * Save state to the storage + * + * @param key Key of the state, as defined by {@link StateKeyDelegate} + * @param state String representing the state + * @param ttl TTL for the state, in seconds + */ + setState(key: string, state: string, ttl?: number): MaybeAsync + + /** + * Delete state from the storage + * + * @param key Key of the state, as defined by {@link StateKeyDelegate} + */ + deleteState(key: string): MaybeAsync + + /** + * Clean up expired states and rate limits. + * + * @param now Current unix time in ms + */ + vacuum(now: number): MaybeAsync + + /** + * Get information about a rate limit. + * + * It is recommended that you use sliding window or leaky bucket + * to implement rate limiting ([learn more](https://konghq.com/blog/how-to-design-a-scalable-rate-limiting-algorithm/)), + * + * @param key Key of the rate limit + * @param now Current unix time in ms + * @param limit Maximum number of requests in `window` + * @param window Window size in seconds + * @returns Tuple containing the number of remaining and + * unix time in ms when the user can try again + */ + getRateLimit(key: string, now: number, limit: number, window: number): MaybeAsync<[number, number]> + + /** + * Reset a rate limit. + * + * @param key Key of the rate limit + */ + resetRateLimit(key: string): MaybeAsync +} diff --git a/packages/dispatcher/src/state/service.ts b/packages/dispatcher/src/state/service.ts new file mode 100644 index 00000000..a49b5a61 --- /dev/null +++ b/packages/dispatcher/src/state/service.ts @@ -0,0 +1,78 @@ +import { asyncResettable, LruMap } from '@mtcute/core/utils.js' + +import { IStateStorageProvider } from './provider.js' + +const makeCurrentSceneKey = (key: string) => `$current_scene_${key}` + +export class StateService { + constructor(readonly provider: IStateStorageProvider) {} + + private _cache: LruMap = new LruMap(100) + private _vacuumTimer?: NodeJS.Timeout + + private _loaded = false + private _load = asyncResettable(async () => { + await this.provider.driver.load?.() + this._loaded = true + }) + async load() { + await this._load.run() + this._vacuumTimer = setInterval(() => { + Promise.resolve(this.provider.state.vacuum(Date.now())) + .catch(() => {}) + }, 300_000) + } + + async destroy() { + await this.provider.driver.save?.() + await this.provider.driver.destroy?.() + clearInterval(this._vacuumTimer!) + this._loaded = false + } + + async getState(key: string): Promise { + if (!this._loaded) await this.load() + + const cached = this._cache.get(key) + if (cached) return cached as T + + const state = await this.provider.state.getState(key, Date.now()) + if (!state) return null + + return JSON.parse(state) as T + } + + async setState(key: string, state: T, ttl?: number): Promise { + if (!this._loaded) await this.load() + + this._cache.set(key, state) + await this.provider.state.setState(key, JSON.stringify(state), ttl) + } + + async deleteState(key: string): Promise { + if (!this._loaded) await this.load() + + this._cache.delete(key) + await this.provider.state.deleteState(key) + } + + getCurrentScene(key: string): Promise { + return this.getState(makeCurrentSceneKey(key)) + } + + setCurrentScene(key: string, scene: string, ttl?: number): Promise { + return this.setState(makeCurrentSceneKey(key), scene, ttl) + } + + deleteCurrentScene(key: string): Promise { + return this.deleteState(makeCurrentSceneKey(key)) + } + + getRateLimit(key: string, limit: number, window: number) { + return this.provider.state.getRateLimit(key, Date.now(), limit, window) + } + + resetRateLimit(key: string) { + return this.provider.state.resetRateLimit(key) + } +} diff --git a/packages/dispatcher/src/state/storage.ts b/packages/dispatcher/src/state/storage.ts deleted file mode 100644 index 8e0d7198..00000000 --- a/packages/dispatcher/src/state/storage.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { MaybeAsync } from '@mtcute/client' - -// ⚠️ Important: when modifying the below interface, also update it -// in packages/core/src/storage/storage.test-utils.ts - -/** - * Interface for FSM storage for the dispatcher. - * - * All of the officially supported storages already implement - * this interface, so you can just re-use it. - * - * Current scene is a special case of a `string` state, - * Most of the time you can just store it the same way - * as normal state, prefixing with something like `$current_state_` - * (scene name can't start with `$`). - * Alternatively, you can store them as simple strings - */ -export interface IStateStorage { - /** - * Load state from some external storage. - * Should be used either to load session content from file/network/etc - * to memory, or to open required connections to fetch session content later - * - * This method may be called multiple times and should handle that. - */ - load?(): MaybeAsync - /** - * Save state to some external storage. - * Should be used to commit pending changes in the session. - * For example, saving session content to file/network/etc, - * or committing a database transaction - */ - save?(): MaybeAsync - /** - * Cleanup storage and release all used resources. - * - * This method may be called multiple times and should handle that. - */ - destroy?(): MaybeAsync - - /** - * Retrieve state from the storage - * - * @param key Key of the state, as defined by {@link StateKeyDelegate} - */ - getState(key: string): MaybeAsync - - /** - * Save state to the storage - * - * @param key Key of the state, as defined by {@link StateKeyDelegate} - * @param state Object representing the state - * @param ttl TTL for the state, in seconds - */ - setState(key: string, state: unknown, ttl?: number): MaybeAsync - - /** - * Delete state from the storage - * - * @param key Key of the state, as defined by {@link StateKeyDelegate} - */ - deleteState(key: string): MaybeAsync - - /** - * Retrieve the current scene UID from the storage - * - * @param key Key of the state, as defined by {@link StateKeyDelegate} - */ - getCurrentScene(key: string): MaybeAsync - - /** - * Change current scene's UID from the storage - * - * @param key Key of the state, as defined by {@link StateKeyDelegate} - * @param scene New scene - * @param ttl TTL for the scene, in seconds - */ - setCurrentScene(key: string, scene: string, ttl?: number): MaybeAsync - - /** - * Delete current scene from the storage, effectively "exiting" to root. - * - * @param key Key of the state, as defined by {@link StateKeyDelegate} - */ - deleteCurrentScene(key: string): MaybeAsync - - /** - * Get information about a rate limit. - * - * It is recommended that you use sliding window or leaky bucket - * to implement rate limiting ([learn more](https://konghq.com/blog/how-to-design-a-scalable-rate-limiting-algorithm/)), - * - * @param key Key of the rate limit - * @param limit Maximum number of requests in `window` - * @param window Window size in seconds - * @returns Tuple containing the number of remaining and - * unix time in ms when the user can try again - */ - getRateLimit(key: string, limit: number, window: number): MaybeAsync<[number, number]> - - /** - * Reset a rate limit. - * - * @param key Key of the rate limit - */ - resetRateLimit(key: string): MaybeAsync -} - -export function isCompatibleStorage(storage: unknown): storage is IStateStorage { - return ( - typeof storage === 'object' && - storage !== null && - 'getState' in storage && - 'setState' in storage && - 'deleteState' in storage && - 'getCurrentScene' in storage && - 'setCurrentScene' in storage && - 'deleteCurrentScene' in storage && - 'getRateLimit' in storage && - 'resetRateLimit' in storage - ) -} diff --git a/packages/dispatcher/src/state/update-state.ts b/packages/dispatcher/src/state/update-state.ts index 12fbc69c..8e983b6f 100644 --- a/packages/dispatcher/src/state/update-state.ts +++ b/packages/dispatcher/src/state/update-state.ts @@ -1,10 +1,10 @@ /* eslint-disable dot-notation */ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { MtArgumentError, MtcuteError } from '@mtcute/client' -import { sleep } from '@mtcute/client/utils.js' +import { MtArgumentError, MtcuteError } from '@mtcute/core' +import { sleep } from '@mtcute/core/utils.js' import type { Dispatcher } from '../dispatcher.js' -import { IStateStorage } from './storage.js' +import { StateService } from './service.js' /** * Error thrown by `.rateLimit()` @@ -25,21 +25,21 @@ export class UpdateState { private _key: string private _localKey!: string - private _storage: IStateStorage + private _storage: StateService private _scene: string | null private _scoped?: boolean private _cached?: State | null - private _localStorage: IStateStorage + private _localStorage: StateService private _localKeyBase: string constructor( - storage: IStateStorage, + storage: StateService, key: string, scene: string | null, scoped?: boolean, - customStorage?: IStateStorage, + customStorage?: StateService, customKey?: string, ) { this._storage = storage diff --git a/packages/dispatcher/src/wizard.ts b/packages/dispatcher/src/wizard.ts index bf7e6240..eaeae547 100644 --- a/packages/dispatcher/src/wizard.ts +++ b/packages/dispatcher/src/wizard.ts @@ -1,4 +1,4 @@ -import { MaybeAsync } from '@mtcute/client' +import { MaybeAsync } from '@mtcute/core' import { MessageContext } from './context/message.js' import { Dispatcher } from './dispatcher.js' diff --git a/packages/dispatcher/tsconfig.json b/packages/dispatcher/tsconfig.json index 554df3b9..6a3f7a53 100644 --- a/packages/dispatcher/tsconfig.json +++ b/packages/dispatcher/tsconfig.json @@ -6,8 +6,5 @@ }, "include": [ "./src", - ], - "references": [ - { "path": "../client" } ] } diff --git a/packages/file-id/package.json b/packages/file-id/package.json index e4e96100..1d305ae0 100644 --- a/packages/file-id/package.json +++ b/packages/file-id/package.json @@ -19,6 +19,7 @@ } }, "dependencies": { - "@mtcute/core": "workspace:^" + "@mtcute/tl-runtime": "workspace:^", + "long": "5.2.3" } } diff --git a/packages/file-id/src/index.ts b/packages/file-id/src/index.ts index ce6e80e5..26c53e53 100644 --- a/packages/file-id/src/index.ts +++ b/packages/file-id/src/index.ts @@ -1,4 +1,3 @@ -export * from './convert.js' export * from './parse.js' export * from './serialize.js' export * from './serialize-unique.js' diff --git a/packages/file-id/src/parse.test.ts b/packages/file-id/src/parse.test.ts index a1ea0433..5ed848ea 100644 --- a/packages/file-id/src/parse.test.ts +++ b/packages/file-id/src/parse.test.ts @@ -1,7 +1,7 @@ +import Long from 'long' import { describe, expect, it } from 'vitest' -import { Long } from '@mtcute/core' -import { hexDecodeToBuffer } from '@mtcute/core/utils.js' +import { hexDecodeToBuffer } from '@mtcute/tl-runtime' import { parseFileId } from './parse.js' import { tdFileId as td } from './types.js' diff --git a/packages/file-id/src/parse.ts b/packages/file-id/src/parse.ts index 8a3bd257..1099c1a4 100644 --- a/packages/file-id/src/parse.ts +++ b/packages/file-id/src/parse.ts @@ -1,4 +1,4 @@ -import { base64DecodeToBuffer, base64Encode, TlBinaryReader } from '@mtcute/core/utils.js' +import { base64DecodeToBuffer, base64Encode, TlBinaryReader } from '@mtcute/tl-runtime' import { tdFileId as td } from './types.js' import { telegramRleDecode } from './utils.js' diff --git a/packages/file-id/src/serialize-unique.ts b/packages/file-id/src/serialize-unique.ts index e8c727af..9602ac1b 100644 --- a/packages/file-id/src/serialize-unique.ts +++ b/packages/file-id/src/serialize-unique.ts @@ -1,8 +1,7 @@ -import { assertNever } from '@mtcute/core' -import { base64Encode, byteLengthUtf8, TlBinaryWriter } from '@mtcute/core/utils.js' +import { base64Encode, byteLengthUtf8, TlBinaryWriter } from '@mtcute/tl-runtime' import { tdFileId as td } from './types.js' -import { telegramRleEncode } from './utils.js' +import { assertNever, telegramRleEncode } from './utils.js' export type InputUniqueLocation = | Pick diff --git a/packages/file-id/src/serialize.ts b/packages/file-id/src/serialize.ts index d3343d0d..6b563748 100644 --- a/packages/file-id/src/serialize.ts +++ b/packages/file-id/src/serialize.ts @@ -1,8 +1,7 @@ -import { assertNever } from '@mtcute/core' -import { base64Encode, byteLengthUtf8, concatBuffers, TlBinaryWriter } from '@mtcute/core/utils.js' +import { base64Encode, byteLengthUtf8, TlBinaryWriter } from '@mtcute/tl-runtime' import { tdFileId as td } from './types.js' -import { telegramRleEncode } from './utils.js' +import { assertNever, telegramRleEncode } from './utils.js' const SUFFIX = new Uint8Array([td.CURRENT_VERSION, td.PERSISTENT_ID_VERSION]) @@ -104,5 +103,10 @@ export function toFileId(location: Omit): str assertNever(loc) } - return base64Encode(concatBuffers([telegramRleEncode(writer.result()), SUFFIX]), true) + const result = telegramRleEncode(writer.result()) + const withSuffix = new Uint8Array(result.length + SUFFIX.length) + withSuffix.set(result) + withSuffix.set(SUFFIX, result.length) + + return base64Encode(withSuffix, true) } diff --git a/packages/file-id/src/types.ts b/packages/file-id/src/types.ts index b4e31521..924f1e1a 100644 --- a/packages/file-id/src/types.ts +++ b/packages/file-id/src/types.ts @@ -1,4 +1,4 @@ -import { Long } from '@mtcute/core' +import Long from 'long' // eslint-disable-next-line @typescript-eslint/no-namespace export namespace tdFileId { diff --git a/packages/file-id/src/utils.test.ts b/packages/file-id/src/utils.test.ts index a7602aa7..59cbccec 100644 --- a/packages/file-id/src/utils.test.ts +++ b/packages/file-id/src/utils.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' -import { hexDecodeToBuffer, hexEncode } from '@mtcute/core/utils.js' +import { hexDecodeToBuffer, hexEncode } from '@mtcute/tl-runtime' import { telegramRleDecode, telegramRleEncode } from './utils.js' diff --git a/packages/file-id/src/utils.ts b/packages/file-id/src/utils.ts index 1cc6def2..7661c34a 100644 --- a/packages/file-id/src/utils.ts +++ b/packages/file-id/src/utils.ts @@ -50,3 +50,7 @@ export function telegramRleDecode(buf: Uint8Array): Uint8Array { return new Uint8Array(ret) } + +export function assertNever(_: never): never { + throw new Error('unreachable') +} diff --git a/packages/html-parser/package.json b/packages/html-parser/package.json index 66e38449..7ef67867 100644 --- a/packages/html-parser/package.json +++ b/packages/html-parser/package.json @@ -23,6 +23,6 @@ "long": "5.2.3" }, "devDependencies": { - "@mtcute/client": "workspace:^" + "@mtcute/core": "workspace:^" } } diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 1eb766ce..6552d456 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -23,7 +23,7 @@ } }, "devDependencies": { - "@mtcute/client": "workspace:^", + "@mtcute/core": "workspace:^", "@mtcute/dispatcher": "workspace:^" } } diff --git a/packages/markdown-parser/package.json b/packages/markdown-parser/package.json index 7978a551..07ca0578 100644 --- a/packages/markdown-parser/package.json +++ b/packages/markdown-parser/package.json @@ -22,6 +22,6 @@ "long": "5.2.3" }, "devDependencies": { - "@mtcute/client": "workspace:^" + "@mtcute/core": "workspace:^" } } diff --git a/packages/node/package.json b/packages/node/package.json index 92bc5d03..79e0ca9c 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -24,7 +24,7 @@ } }, "dependencies": { - "@mtcute/client": "workspace:^", + "@mtcute/core": "workspace:^", "@mtcute/sqlite": "workspace:^", "@mtcute/markdown-parser": "workspace:^", "@mtcute/html-parser": "workspace:^" diff --git a/packages/sqlite/src/driver.ts b/packages/sqlite/src/driver.ts index 63999eb8..fadedbbb 100644 --- a/packages/sqlite/src/driver.ts +++ b/packages/sqlite/src/driver.ts @@ -1,6 +1,7 @@ import sqlite3, { Database, Options, Statement } from 'better-sqlite3' import { BaseStorageDriver, MtUnsupportedError } from '@mtcute/core' +import { beforeExit } from '@mtcute/core/utils.js' export interface SqliteStorageDriverOptions { /** @@ -43,6 +44,7 @@ export class SqliteStorageDriver extends BaseStorageDriver { private _pending: [Statement, unknown[]][] = [] private _runMany!: (stmts: [Statement, unknown[]][]) => void + private _cleanup?: () => void private _migrations: Map> = new Map() private _maxVersion: Map = new Map() @@ -153,6 +155,10 @@ export class SqliteStorageDriver extends BaseStorageDriver { }) this._initialize() + this._cleanup = beforeExit(() => { + this._save() + this._destroy() + }) for (const cb of this._onLoad) cb(this.db) } @@ -165,5 +171,7 @@ export class SqliteStorageDriver extends BaseStorageDriver { _destroy(): void { this.db.close() + this._cleanup?.() + this._cleanup = undefined } } diff --git a/packages/sqlite/src/index.ts b/packages/sqlite/src/index.ts index 3a7ad797..2fb42852 100644 --- a/packages/sqlite/src/index.ts +++ b/packages/sqlite/src/index.ts @@ -1,4 +1,4 @@ -import { IMtStorageProvider } from '@mtcute/core' +import { IMtStorageProvider, ITelegramStorageProvider } from '@mtcute/core' import { SqliteStorageDriver, SqliteStorageDriverOptions } from './driver.js' import { SqliteAuthKeysRepository } from './repository/auth-keys.js' @@ -6,12 +6,14 @@ import { SqliteKeyValueRepository } from './repository/kv.js' import { SqlitePeersRepository } from './repository/peers.js' import { SqliteRefMessagesRepository } from './repository/ref-messages.js' -export class SqliteStorage implements IMtStorageProvider { +export { SqliteStorageDriver } from './driver.js' +export type { Statement } from 'better-sqlite3' + +export class SqliteStorage implements IMtStorageProvider, ITelegramStorageProvider { constructor( readonly filename = ':memory:', readonly params?: SqliteStorageDriverOptions, - ) { - } + ) {} readonly driver = new SqliteStorageDriver(this.filename, this.params) diff --git a/packages/sqlite/src/repository/auth-keys.ts b/packages/sqlite/src/repository/auth-keys.ts index ef14f672..affe5699 100644 --- a/packages/sqlite/src/repository/auth-keys.ts +++ b/packages/sqlite/src/repository/auth-keys.ts @@ -1,6 +1,6 @@ import { Statement } from 'better-sqlite3' -import { IAuthKeysRepository } from '@mtcute/core/src/storage/repository/auth-keys.js' +import { IAuthKeysRepository } from '@mtcute/core' import { SqliteStorageDriver } from '../driver.js' diff --git a/packages/sqlite/src/repository/kv.ts b/packages/sqlite/src/repository/kv.ts index 31a567d6..01cd347a 100644 --- a/packages/sqlite/src/repository/kv.ts +++ b/packages/sqlite/src/repository/kv.ts @@ -1,6 +1,6 @@ import { Statement } from 'better-sqlite3' -import { IKeyValueRepository } from '@mtcute/core/src/storage/repository/key-value.js' +import { IKeyValueRepository } from '@mtcute/core' import { SqliteStorageDriver } from '../driver.js' diff --git a/packages/sqlite/src/repository/peers.ts b/packages/sqlite/src/repository/peers.ts index 00a01ca2..0cfb65dc 100644 --- a/packages/sqlite/src/repository/peers.ts +++ b/packages/sqlite/src/repository/peers.ts @@ -1,6 +1,6 @@ import { Statement } from 'better-sqlite3' -import { IPeersRepository } from '@mtcute/core/src/storage/repository/peers.js' +import { IPeersRepository } from '@mtcute/core' import { SqliteStorageDriver } from '../driver.js' diff --git a/packages/sqlite/src/repository/ref-messages.ts b/packages/sqlite/src/repository/ref-messages.ts index e0ca4a2c..9c139148 100644 --- a/packages/sqlite/src/repository/ref-messages.ts +++ b/packages/sqlite/src/repository/ref-messages.ts @@ -1,6 +1,6 @@ import { Statement } from 'better-sqlite3' -import { IReferenceMessagesRepository } from '@mtcute/core/src/storage/repository/ref-messages.js' +import { IReferenceMessagesRepository } from '@mtcute/core' import { SqliteStorageDriver } from '../driver.js' diff --git a/packages/test/src/client.ts b/packages/test/src/client.ts index aecebfad..9ba350d0 100644 --- a/packages/test/src/client.ts +++ b/packages/test/src/client.ts @@ -29,7 +29,7 @@ export class StubTelegramClient extends BaseTelegramClient { onMessage: (data) => { if (!this._onRawMessage) { if (this._responders.size) { - this._emitError(new Error('Unexpected outgoing message')) + this.emitError(new Error('Unexpected outgoing message')) } return @@ -281,7 +281,9 @@ export class StubTelegramClient extends BaseTelegramClient { async connectAndWait() { await this.connect() - await new Promise((resolve) => this.once('usable', resolve)) + await new Promise((resolve): void => { + this.mt.once('usable', resolve) + }) } async with(fn: () => MaybeAsync): Promise { diff --git a/packages/test/src/transport.ts b/packages/test/src/transport.ts index 562469d4..47aecfdf 100644 --- a/packages/test/src/transport.ts +++ b/packages/test/src/transport.ts @@ -1,8 +1,8 @@ import EventEmitter from 'events' +import { tl } from '@mtcute/tl' import { ITelegramTransport, TransportState } from '@mtcute/core' import { ICryptoProvider, Logger } from '@mtcute/core/utils.js' -import { tl } from '@mtcute/tl' export class StubTelegramTransport extends EventEmitter implements ITelegramTransport { constructor( diff --git a/packages/tl/scripts/fetch-api.ts b/packages/tl/scripts/fetch-api.ts index 0a0b85a9..295215a3 100644 --- a/packages/tl/scripts/fetch-api.ts +++ b/packages/tl/scripts/fetch-api.ts @@ -9,7 +9,6 @@ import { readFile, writeFile } from 'fs/promises' import { join } from 'path' import * as readline from 'readline' -import { hasPresentKey, isPresent } from '@mtcute/core/utils.js' import { generateTlSchemasDifference, mergeTlEntries, @@ -20,6 +19,7 @@ import { TlFullSchema, writeTlEntryToString, } from '@mtcute/tl-utils' +import { hasPresentKey, isPresent } from '@mtcute/core/utils.js' import { __dirname, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 242f1931..17cb9089 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -115,21 +115,11 @@ importers: specifier: 0.34.6 version: 0.34.6(@vitest/browser@0.34.6)(@vitest/ui@0.34.6)(playwright@1.40.1) - packages/client: + packages/core: dependencies: - '@mtcute/core': - specifier: workspace:^ - version: link:../core '@mtcute/file-id': specifier: workspace:^ version: link:../file-id - devDependencies: - '@mtcute/test': - specifier: workspace:^ - version: link:../test - - packages/core: - dependencies: '@mtcute/tl': specifier: workspace:^ version: link:../tl @@ -155,9 +145,6 @@ importers: '@types/ws': specifier: 8.5.4 version: 8.5.4 - node-forge: - specifier: 1.3.1 - version: 1.3.1 ws: specifier: 8.13.0 version: 8.13.0 @@ -195,22 +182,29 @@ importers: version: link:../test packages/dispatcher: - dependencies: - '@mtcute/client': - specifier: workspace:^ - version: link:../client - '@mtcute/test': - specifier: workspace:^ - version: link:../test - events: - specifier: 3.2.0 - version: 3.2.0 - - packages/file-id: dependencies: '@mtcute/core': specifier: workspace:^ version: link:../core + '@mtcute/sqlite': + specifier: workspace:^ + version: link:../sqlite + events: + specifier: 3.2.0 + version: 3.2.0 + devDependencies: + '@mtcute/test': + specifier: workspace:^ + version: link:../test + + packages/file-id: + dependencies: + '@mtcute/tl-runtime': + specifier: workspace:^ + version: link:../tl-runtime + long: + specifier: 5.2.3 + version: 5.2.3 packages/html-parser: dependencies: @@ -221,9 +215,9 @@ importers: specifier: 5.2.3 version: 5.2.3 devDependencies: - '@mtcute/client': + '@mtcute/core': specifier: workspace:^ - version: link:../client + version: link:../core packages/http-proxy: dependencies: @@ -233,9 +227,9 @@ importers: packages/i18n: devDependencies: - '@mtcute/client': + '@mtcute/core': specifier: workspace:^ - version: link:../client + version: link:../core '@mtcute/dispatcher': specifier: workspace:^ version: link:../dispatcher @@ -246,9 +240,9 @@ importers: specifier: 5.2.3 version: 5.2.3 devDependencies: - '@mtcute/client': + '@mtcute/core': specifier: workspace:^ - version: link:../client + version: link:../core packages/mtproxy: dependencies: @@ -258,9 +252,9 @@ importers: packages/node: dependencies: - '@mtcute/client': + '@mtcute/core': specifier: workspace:^ - version: link:../client + version: link:../core '@mtcute/html-parser': specifier: workspace:^ version: link:../html-parser diff --git a/scripts/build-package.js b/scripts/build-package.js index 9976e5cb..4122427f 100644 --- a/scripts/build-package.js +++ b/scripts/build-package.js @@ -26,6 +26,7 @@ const buildConfig = { removeReferenceComments: true, replaceSrcImports: true, esmOnlyDirectives: false, + esmImportDirectives: false, before: () => {}, final: () => {}, ...(() => { @@ -173,6 +174,7 @@ if (buildConfig.buildTs) { console.log('[i] Building typescript (CJS)...') const originalFiles = {} + // todo - get rid of these, use @esm-replace-import instead if (buildConfig.esmOnlyDirectives) { for (const f of glob.sync(path.join(packageDir, '**/*.ts'))) { const content = fs.readFileSync(f, 'utf8') @@ -182,6 +184,15 @@ if (buildConfig.buildTs) { fs.writeFileSync(f, content.replace(/@only-if-esm.*?@\/only-if-esm/gs, '')) } } + if (buildConfig.esmImportDirectives) { + for (const f of glob.sync(path.join(packageDir, '**/*.ts'))) { + const content = fs.readFileSync(f, 'utf8') + if (!content.includes('@esm-replace-import')) continue + originalFiles[f] = content + + fs.writeFileSync(f, content.replace(/@esm-replace-import.*?await import/gs, 'require')) + } + } let error = false diff --git a/tsconfig.json b/tsconfig.json index 2b6ef48e..d3481db6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,6 +21,12 @@ "node", "vite/client" ], + "lib": [ + "es2020", + "dom", + "dom.iterable", + "WebWorker", + ], "resolveJsonModule": true, "isolatedModules": true, },