diff --git a/packages/client/src/types/messages/message-forward.ts b/packages/client/src/types/messages/message-forward.ts index 868eeb9d..e79cbb23 100644 --- a/packages/client/src/types/messages/message-forward.ts +++ b/packages/client/src/types/messages/message-forward.ts @@ -3,8 +3,8 @@ import { MtTypeAssertionError, tl } from '@mtcute/core' import { makeInspectable } from '../../utils/inspectable.js' import { memoizeGetters } from '../../utils/memoize.js' import { Chat } from '../peers/chat.js' +import { parsePeer, PeerSender } from '../peers/peer.js' import { PeersIndex } from '../peers/peers-index.js' -import { User } from '../peers/user.js' import { _messageMediaFromTl } from './message-media.js' /** @@ -27,20 +27,16 @@ export class MessageForwardInfo { * Sender of the original message (either user or a channel) * or their name (for users with private forwards) */ - get sender(): User | Chat | string { + get sender(): PeerSender { if (this.raw.fromName) { - return this.raw.fromName + return { + type: 'anonymous', + displayName: this.raw.fromName, + } } if (this.raw.fromId) { - switch (this.raw.fromId._) { - case 'peerChannel': - return new Chat(this._peers.chat(this.raw.fromId.channelId)) - case 'peerUser': - return new User(this._peers.user(this.raw.fromId.userId)) - default: - throw new MtTypeAssertionError('raw.fwdFrom.fromId', 'peerUser | peerChannel', this.raw.fromId._) - } + return parsePeer(this.raw.fromId, this._peers) } throw new MtTypeAssertionError('MessageForwardInfo', 'to have fromId or fromName', 'neither') @@ -50,7 +46,7 @@ export class MessageForwardInfo { * For "saved" messages (i.e. messages forwarded to yourself, * "Saved Messages"), the peer where the message was originally sent */ - fromChat(): User | Chat | null { + fromChat(): Chat | null { if (!this.raw.savedFromPeer) return null return Chat._parseFromPeer(this.raw.savedFromPeer, this._peers) diff --git a/packages/client/src/types/messages/message-replies.ts b/packages/client/src/types/messages/message-replies.ts index 5e7644d4..652385be 100644 --- a/packages/client/src/types/messages/message-replies.ts +++ b/packages/client/src/types/messages/message-replies.ts @@ -2,9 +2,8 @@ import { getMarkedPeerId, tl } from '@mtcute/core' import { makeInspectable } from '../../utils/inspectable.js' import { memoizeGetters } from '../../utils/memoize.js' -import { Chat } from '../peers/chat.js' +import { parsePeer, Peer } from '../peers/peer.js' import { PeersIndex } from '../peers/peers-index.js' -import { User } from '../peers/user.js' /** * Information about replies to a message @@ -65,19 +64,8 @@ export class MessageRepliesInfo { /** * Last few commenters to the post (usually 3) */ - get repliers(): (User | Chat)[] { - return ( - this.raw.recentRepliers?.map((it) => { - switch (it._) { - case 'peerUser': - return new User(this._peers.user(it.userId)) - case 'peerChannel': - return new Chat(this._peers.chat(it.channelId)) - default: - throw new Error('Unexpected peer type: ' + it._) - } - }) ?? [] - ) + get repliers(): Peer[] { + return this.raw.recentRepliers?.map((it) => parsePeer(it, this._peers)) ?? [] } } diff --git a/packages/client/src/types/messages/message.ts b/packages/client/src/types/messages/message.ts index 22a533e0..fa23ee3b 100644 --- a/packages/client/src/types/messages/message.ts +++ b/packages/client/src/types/messages/message.ts @@ -1,11 +1,4 @@ -import { - assertNever, - getMarkedPeerId, - MtArgumentError, - MtTypeAssertionError, - tl, - toggleChannelIdMark, -} from '@mtcute/core' +import { assertNever, getMarkedPeerId, MtArgumentError, tl, toggleChannelIdMark } from '@mtcute/core' import { assertTypeIsNot } from '@mtcute/core/utils.js' import { makeInspectable } from '../../utils/index.js' @@ -13,6 +6,7 @@ import { memoizeGetters } from '../../utils/memoize.js' import { BotKeyboard, ReplyMarkup } from '../bots/keyboards.js' import { TextWithEntities } from '../misc/index.js' import { Chat } from '../peers/chat.js' +import { parsePeer, Peer } from '../peers/peer.js' import { PeersIndex } from '../peers/peers-index.js' import { User } from '../peers/user.js' import { _messageActionFromTl, MessageAction } from './message-action.js' @@ -114,7 +108,7 @@ export class Message { * If the message is a forwarded channel post, * sender is the channel itself. */ - get sender(): User | Chat { + get sender(): Peer { const from = this.raw.fromId if (!from) { @@ -125,14 +119,8 @@ export class Message { // anon admin, return the chat return this.chat } - switch (from._) { - case 'peerChannel': // forwarded channel post or anon - return new Chat(this._peers.chat(from.channelId)) - case 'peerUser': - return new User(this._peers.user(from.userId)) - default: - throw new MtTypeAssertionError('raw.fromId', 'peerUser | peerChannel', from._) - } + + return parsePeer(from, this._peers) } /** diff --git a/packages/client/src/types/messages/replied-message.ts b/packages/client/src/types/messages/replied-message.ts index 6ed02758..13b3b517 100644 --- a/packages/client/src/types/messages/replied-message.ts +++ b/packages/client/src/types/messages/replied-message.ts @@ -3,6 +3,7 @@ import { MtTypeAssertionError, tl } from '@mtcute/core' import { makeInspectable } from '../../utils/inspectable.js' import { memoizeGetters } from '../../utils/memoize.js' import { Chat } from '../peers/chat.js' +import { PeerSender } from '../peers/peer.js' import { PeersIndex } from '../peers/peers-index.js' import { User } from '../peers/user.js' import { MessageEntity } from './message-entity.js' @@ -27,12 +28,12 @@ export interface _RepliedMessageAssertionsByOrigin { other_chat: { id: number chat: Chat - sender: User | Chat | string + sender: PeerSender } private: { id: null chat: null - sender: User | Chat | string + sender: PeerSender } } @@ -90,7 +91,7 @@ export class RepliedMessageInfo { } } - return true + return false } /** @@ -128,12 +129,15 @@ export class RepliedMessageInfo { * * `null` if the sender is not available (for `same_chat` origin) */ - get sender(): User | Chat | string | null { + get sender(): PeerSender | null { const { replyFrom, replyToPeerId } = this.raw if (!replyFrom && !replyToPeerId) return null if (replyFrom?.fromName) { - return replyFrom.fromName + return { + type: 'anonymous', + displayName: replyFrom.fromName, + } } const peer = replyFrom?.fromId ?? replyToPeerId diff --git a/packages/client/src/types/peers/forum-topic.ts b/packages/client/src/types/peers/forum-topic.ts index a4bd400b..b9726caf 100644 --- a/packages/client/src/types/peers/forum-topic.ts +++ b/packages/client/src/types/peers/forum-topic.ts @@ -1,12 +1,11 @@ -import { MtTypeAssertionError, tl } from '@mtcute/core' +import { tl } from '@mtcute/core' import { hasValueAtKey, makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' import { MtMessageNotFoundError } from '../errors.js' import { DraftMessage, Message } from '../messages/index.js' -import { Chat } from './chat.js' +import { parsePeer, Peer } from './peer.js' import { PeersIndex } from './peers-index.js' -import { User } from './user.js' export class ForumTopic { static COLOR_BLUE = 0x6fb9f0 @@ -108,15 +107,8 @@ export class ForumTopic { /** * Creator of the topic */ - get creator(): User | Chat { - switch (this.raw.fromId._) { - case 'peerUser': - return new User(this._peers.user(this.raw.fromId.userId)) - case 'peerChat': - return new Chat(this._peers.chat(this.raw.fromId.chatId)) - default: - throw new MtTypeAssertionError('ForumTopic#creator', 'peerUser | peerChat', this.raw.fromId._) - } + get creator(): Peer { + return parsePeer(this.raw.fromId, this._peers) } /** diff --git a/packages/client/src/types/peers/index.ts b/packages/client/src/types/peers/index.ts index 70cae46a..c86d7ab2 100644 --- a/packages/client/src/types/peers/index.ts +++ b/packages/client/src/types/peers/index.ts @@ -1,5 +1,3 @@ -import { tl } from '@mtcute/core' - export * from './chat.js' export * from './chat-event/index.js' export * from './chat-invite-link.js' @@ -10,33 +8,7 @@ export * from './chat-permissions.js' export * from './chat-photo.js' export * from './chat-preview.js' export * from './forum-topic.js' +export * from './peer.js' export * from './peers-index.js' export * from './typing-status.js' export * from './user.js' - -/** - * More extensive peer types, that differentiate between - * users and bots, channels and supergroups. - */ -export type PeerType = 'user' | 'bot' | 'group' | 'channel' | 'supergroup' - -/** - * Type that can be used as an input peer to most of the high-level methods. Can be: - * - `number`, representing peer's marked ID* - * - `string`, representing peer's username (without preceding `@`) - * - `string`, representing user's phone number - * - `"me"` and `"self"` which will be replaced with the current user/bot - * - Any object with `inputPeer: tl.TypeInputPeer` property - * - Raw TL object - * - * > * Telegram has moved to int64 IDs. Though, Levin [has confirmed](https://t.me/tdlibchat/25071) - * > that new IDs *will* still fit into int53, meaning JS integers are fine. - */ -export type InputPeerLike = - | string - | number - | tl.TypePeer - | tl.TypeInputPeer - | tl.TypeInputUser - | tl.TypeInputChannel - | { inputPeer: tl.TypeInputPeer } diff --git a/packages/client/src/types/peers/peer.ts b/packages/client/src/types/peers/peer.ts new file mode 100644 index 00000000..f3e1f1b7 --- /dev/null +++ b/packages/client/src/types/peers/peer.ts @@ -0,0 +1,71 @@ +import { tl } from '@mtcute/core' + +import { Chat } from './chat.js' +import { PeersIndex } from './peers-index.js' +import { User } from './user.js' + +/** + * More extensive peer types, that differentiate between + * users and bots, channels and supergroups. + */ +export type PeerType = 'user' | 'bot' | 'group' | 'channel' | 'supergroup' + +/** + * Type that can be used as an input peer to most of the high-level methods. Can be: + * - `number`, representing peer's marked ID* + * - `string`, representing peer's username (without preceding `@`) + * - `string`, representing user's phone number + * - `"me"` and `"self"` which will be replaced with the current user/bot + * - Any object with `inputPeer: tl.TypeInputPeer` property + * - Raw TL object + * + * > * Telegram has moved to int64 IDs. Though, Levin [has confirmed](https://t.me/tdlibchat/25071) + * > that new IDs *will* still fit into int53, meaning JS integers are fine. + */ +export type InputPeerLike = + | string + | number + | tl.TypePeer + | tl.TypeInputPeer + | tl.TypeInputUser + | tl.TypeInputChannel + | { inputPeer: tl.TypeInputPeer } + +/** + * Peer (a user or a chat) + * + * Type of the peer can be determined by the `.type` property + */ +export type Peer = User | Chat + +/** + * An object representing an anonymous sender (e.g. for users that have forwards hidden) + */ +export interface AnonymousSender { + readonly type: 'anonymous' + + /** + * Name of the anonymous sender that should be displayed + */ + readonly displayName: string +} + +/** + * Object representing a sender of a forwarded message, + * which can be either a {@link Peer} or an {@link AnonymousSender} + */ +export type PeerSender = Peer | AnonymousSender + +/** + * Given a `tl.TypePeer`, return a {@link Peer} object ({@link User} or {@link Chat}) + */ +export function parsePeer(peer: tl.TypePeer, index: PeersIndex): Peer { + switch (peer._) { + case 'peerUser': + return new User(index.user(peer.userId)) + case 'peerChat': + return new Chat(index.chat(peer.chatId)) + case 'peerChannel': + return new Chat(index.chat(peer.channelId)) + } +} diff --git a/packages/client/src/types/stories/peer-stories.ts b/packages/client/src/types/stories/peer-stories.ts index d6a173d1..11dc6fd9 100644 --- a/packages/client/src/types/stories/peer-stories.ts +++ b/packages/client/src/types/stories/peer-stories.ts @@ -2,7 +2,7 @@ import { tl } from '@mtcute/core' import { assertTypeIs, makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' -import { Chat, PeersIndex, User } from '../peers/index.js' +import { parsePeer, Peer, PeersIndex } from '../peers/index.js' import { Story } from './story.js' export class PeerStories { @@ -14,15 +14,8 @@ export class PeerStories { /** * Peer that owns these stories. */ - get peer(): User | Chat { - switch (this.raw.peer._) { - case 'peerUser': - return new User(this._peers.user(this.raw.peer.userId)) - case 'peerChat': - return new Chat(this._peers.chat(this.raw.peer.chatId)) - case 'peerChannel': - return new Chat(this._peers.chat(this.raw.peer.channelId)) - } + get peer(): Peer { + return parsePeer(this.raw.peer, this._peers) } /** diff --git a/packages/client/src/types/updates/delete-story-update.ts b/packages/client/src/types/updates/delete-story-update.ts index eedd9023..1075d663 100644 --- a/packages/client/src/types/updates/delete-story-update.ts +++ b/packages/client/src/types/updates/delete-story-update.ts @@ -1,6 +1,6 @@ import { tl } from '@mtcute/core' -import { Chat, PeersIndex, User } from '../../types/peers/index.js' +import { parsePeer, Peer, PeersIndex } from '../../types/peers/index.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' @@ -16,15 +16,8 @@ export class DeleteStoryUpdate { /** * Peer that owns these stories. */ - get peer(): User | Chat { - switch (this.raw.peer._) { - case 'peerUser': - return new User(this._peers.user(this.raw.peer.userId)) - case 'peerChat': - return new Chat(this._peers.chat(this.raw.peer.chatId)) - case 'peerChannel': - return new Chat(this._peers.chat(this.raw.peer.channelId)) - } + get peer(): Peer { + return parsePeer(this.raw.peer, this._peers) } /** diff --git a/packages/client/src/types/updates/poll-vote.ts b/packages/client/src/types/updates/poll-vote.ts index 503d3a12..476fda3f 100644 --- a/packages/client/src/types/updates/poll-vote.ts +++ b/packages/client/src/types/updates/poll-vote.ts @@ -1,9 +1,8 @@ import { MtUnsupportedError, tl } from '@mtcute/core' -import { assertTypeIs } from '@mtcute/core/utils.js' import { makeInspectable } from '../../utils/index.js' import { memoizeGetters } from '../../utils/memoize.js' -import { Chat, PeersIndex, User } from '../peers/index.js' +import { parsePeer, Peer, PeersIndex } from '../peers/index.js' /** * Some user has voted in a public poll. @@ -27,14 +26,8 @@ export class PollVoteUpdate { /** * Peer who has voted */ - get peer(): User | Chat { - if (this.raw.peer._ === 'peerUser') { - return new User(this._peers.user(this.raw.peer.userId)) - } - - assertTypeIs('PollVoteUpdate.peer', this.raw.peer, 'peerChannel') - - return new Chat(this._peers.chat(this.raw.peer.channelId)) + get peer(): Peer { + return parsePeer(this.raw.peer, this._peers) } /** diff --git a/packages/client/src/types/updates/story-update.ts b/packages/client/src/types/updates/story-update.ts index b9f1e787..2d7e4db3 100644 --- a/packages/client/src/types/updates/story-update.ts +++ b/packages/client/src/types/updates/story-update.ts @@ -1,6 +1,6 @@ import { tl } from '@mtcute/core' -import { Chat, PeersIndex, User } from '../../types/peers/index.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 { memoizeGetters } from '../../utils/memoize.js' @@ -20,15 +20,8 @@ export class StoryUpdate { /** * Peer that owns these stories. */ - get peer(): User | Chat { - switch (this.raw.peer._) { - case 'peerUser': - return new User(this._peers.user(this.raw.peer.userId)) - case 'peerChat': - return new Chat(this._peers.chat(this.raw.peer.chatId)) - case 'peerChannel': - return new Chat(this._peers.chat(this.raw.peer.channelId)) - } + get peer(): Peer { + return parsePeer(this.raw.peer, this._peers) } /** diff --git a/packages/dispatcher/src/context/message.ts b/packages/dispatcher/src/context/message.ts index b02f45e0..bac6b13c 100644 --- a/packages/dispatcher/src/context/message.ts +++ b/packages/dispatcher/src/context/message.ts @@ -1,12 +1,4 @@ -import { - Chat, - Message, - MtPeerNotFoundError, - OmitInputMessageId, - ParametersSkip1, - TelegramClient, - User, -} from '@mtcute/client' +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' @@ -53,7 +45,7 @@ export class MessageContext extends Message implements UpdateContext { * * Learn more: [Incomplete peers](https://mtcute.dev/guide/topics/peers.html#incomplete-peers) */ - async getSender(): Promise { + async getCompleteSender(): Promise { if (!this.sender.isMin) return this.sender let res diff --git a/packages/dispatcher/src/filters/message.ts b/packages/dispatcher/src/filters/message.ts index 562900e2..b374bf9b 100644 --- a/packages/dispatcher/src/filters/message.ts +++ b/packages/dispatcher/src/filters/message.ts @@ -2,11 +2,11 @@ // ^^ will be looked into in MTQ-29 import { _RepliedMessageAssertionsByOrigin, - Chat, MaybeArray, Message, MessageAction, MessageMediaType, + Peer, RawDocument, RawLocation, RepliedMessageInfo, @@ -208,7 +208,7 @@ export const action = ['type']>( action: Extract sender: T extends 'user_joined_link' | 'user_removed' | 'history_cleared' | 'contact_joined' | 'bot_allowed' ? User - : User | Chat + : Peer } > => { if (Array.isArray(type)) { @@ -261,7 +261,7 @@ export const withCompleteSender = ): UpdateFilter => async (msg, state) => { try { - await msg.getSender() + await msg.getCompleteSender() } catch (e) { return false } diff --git a/packages/dispatcher/src/state/key.ts b/packages/dispatcher/src/state/key.ts index fab9f302..b82ca6c1 100644 --- a/packages/dispatcher/src/state/key.ts +++ b/packages/dispatcher/src/state/key.ts @@ -1,4 +1,4 @@ -import { assertNever, Chat, MaybeAsync, User } from '@mtcute/client' +import { assertNever, MaybeAsync, Peer } from '@mtcute/client' import { CallbackQueryContext, MessageContext } from '../context/index.js' @@ -10,7 +10,7 @@ import { CallbackQueryContext, MessageContext } from '../context/index.js' * @param msg Message or callback from which to derive the key * @param scene Current scene UID, or `null` if none */ -export type StateKeyDelegate = (upd: MessageContext | CallbackQueryContext | User | Chat) => MaybeAsync +export type StateKeyDelegate = (upd: MessageContext | CallbackQueryContext | Peer) => MaybeAsync /** * Default state key delegate.