chore(client)!: better parsed peer handling

breaking: anonymous sender is now represented with `AnonymousSender` interface and not `string`
This commit is contained in:
alina 🌸 2023-12-02 17:48:01 +03:00
parent 9db9411c27
commit 893a15d111
Signed by: teidesu
SSH key fingerprint: SHA256:uNeCpw6aTSU4aIObXLvHfLkDa82HWH9EiOj9AXOIRpI
14 changed files with 120 additions and 145 deletions

View file

@ -3,8 +3,8 @@ import { MtTypeAssertionError, tl } from '@mtcute/core'
import { makeInspectable } from '../../utils/inspectable.js' import { makeInspectable } from '../../utils/inspectable.js'
import { memoizeGetters } from '../../utils/memoize.js' import { memoizeGetters } from '../../utils/memoize.js'
import { Chat } from '../peers/chat.js' import { Chat } from '../peers/chat.js'
import { parsePeer, PeerSender } from '../peers/peer.js'
import { PeersIndex } from '../peers/peers-index.js' import { PeersIndex } from '../peers/peers-index.js'
import { User } from '../peers/user.js'
import { _messageMediaFromTl } from './message-media.js' import { _messageMediaFromTl } from './message-media.js'
/** /**
@ -27,20 +27,16 @@ export class MessageForwardInfo {
* Sender of the original message (either user or a channel) * Sender of the original message (either user or a channel)
* or their name (for users with private forwards) * or their name (for users with private forwards)
*/ */
get sender(): User | Chat | string { get sender(): PeerSender {
if (this.raw.fromName) { if (this.raw.fromName) {
return this.raw.fromName return {
type: 'anonymous',
displayName: this.raw.fromName,
}
} }
if (this.raw.fromId) { if (this.raw.fromId) {
switch (this.raw.fromId._) { return parsePeer(this.raw.fromId, this._peers)
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._)
}
} }
throw new MtTypeAssertionError('MessageForwardInfo', 'to have fromId or fromName', 'neither') 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, * For "saved" messages (i.e. messages forwarded to yourself,
* "Saved Messages"), the peer where the message was originally sent * "Saved Messages"), the peer where the message was originally sent
*/ */
fromChat(): User | Chat | null { fromChat(): Chat | null {
if (!this.raw.savedFromPeer) return null if (!this.raw.savedFromPeer) return null
return Chat._parseFromPeer(this.raw.savedFromPeer, this._peers) return Chat._parseFromPeer(this.raw.savedFromPeer, this._peers)

View file

@ -2,9 +2,8 @@ import { getMarkedPeerId, tl } from '@mtcute/core'
import { makeInspectable } from '../../utils/inspectable.js' import { makeInspectable } from '../../utils/inspectable.js'
import { memoizeGetters } from '../../utils/memoize.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 { PeersIndex } from '../peers/peers-index.js'
import { User } from '../peers/user.js'
/** /**
* Information about replies to a message * Information about replies to a message
@ -65,19 +64,8 @@ export class MessageRepliesInfo {
/** /**
* Last few commenters to the post (usually 3) * Last few commenters to the post (usually 3)
*/ */
get repliers(): (User | Chat)[] { get repliers(): Peer[] {
return ( return this.raw.recentRepliers?.map((it) => parsePeer(it, this._peers)) ?? []
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._)
}
}) ?? []
)
} }
} }

View file

@ -1,11 +1,4 @@
import { import { assertNever, getMarkedPeerId, MtArgumentError, tl, toggleChannelIdMark } from '@mtcute/core'
assertNever,
getMarkedPeerId,
MtArgumentError,
MtTypeAssertionError,
tl,
toggleChannelIdMark,
} from '@mtcute/core'
import { assertTypeIsNot } from '@mtcute/core/utils.js' import { assertTypeIsNot } from '@mtcute/core/utils.js'
import { makeInspectable } from '../../utils/index.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 { BotKeyboard, ReplyMarkup } from '../bots/keyboards.js'
import { TextWithEntities } from '../misc/index.js' import { TextWithEntities } from '../misc/index.js'
import { Chat } from '../peers/chat.js' import { Chat } from '../peers/chat.js'
import { parsePeer, Peer } from '../peers/peer.js'
import { PeersIndex } from '../peers/peers-index.js' import { PeersIndex } from '../peers/peers-index.js'
import { User } from '../peers/user.js' import { User } from '../peers/user.js'
import { _messageActionFromTl, MessageAction } from './message-action.js' import { _messageActionFromTl, MessageAction } from './message-action.js'
@ -114,7 +108,7 @@ export class Message {
* If the message is a forwarded channel post, * If the message is a forwarded channel post,
* sender is the channel itself. * sender is the channel itself.
*/ */
get sender(): User | Chat { get sender(): Peer {
const from = this.raw.fromId const from = this.raw.fromId
if (!from) { if (!from) {
@ -125,14 +119,8 @@ export class Message {
// anon admin, return the chat // anon admin, return the chat
return this.chat return this.chat
} }
switch (from._) {
case 'peerChannel': // forwarded channel post or anon return parsePeer(from, this._peers)
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._)
}
} }
/** /**

View file

@ -3,6 +3,7 @@ import { MtTypeAssertionError, tl } from '@mtcute/core'
import { makeInspectable } from '../../utils/inspectable.js' import { makeInspectable } from '../../utils/inspectable.js'
import { memoizeGetters } from '../../utils/memoize.js' import { memoizeGetters } from '../../utils/memoize.js'
import { Chat } from '../peers/chat.js' import { Chat } from '../peers/chat.js'
import { PeerSender } from '../peers/peer.js'
import { PeersIndex } from '../peers/peers-index.js' import { PeersIndex } from '../peers/peers-index.js'
import { User } from '../peers/user.js' import { User } from '../peers/user.js'
import { MessageEntity } from './message-entity.js' import { MessageEntity } from './message-entity.js'
@ -27,12 +28,12 @@ export interface _RepliedMessageAssertionsByOrigin {
other_chat: { other_chat: {
id: number id: number
chat: Chat chat: Chat
sender: User | Chat | string sender: PeerSender
} }
private: { private: {
id: null id: null
chat: 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) * `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 const { replyFrom, replyToPeerId } = this.raw
if (!replyFrom && !replyToPeerId) return null if (!replyFrom && !replyToPeerId) return null
if (replyFrom?.fromName) { if (replyFrom?.fromName) {
return replyFrom.fromName return {
type: 'anonymous',
displayName: replyFrom.fromName,
}
} }
const peer = replyFrom?.fromId ?? replyToPeerId const peer = replyFrom?.fromId ?? replyToPeerId

View file

@ -1,12 +1,11 @@
import { MtTypeAssertionError, tl } from '@mtcute/core' import { tl } from '@mtcute/core'
import { hasValueAtKey, makeInspectable } from '../../utils/index.js' import { hasValueAtKey, makeInspectable } from '../../utils/index.js'
import { memoizeGetters } from '../../utils/memoize.js' import { memoizeGetters } from '../../utils/memoize.js'
import { MtMessageNotFoundError } from '../errors.js' import { MtMessageNotFoundError } from '../errors.js'
import { DraftMessage, Message } from '../messages/index.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 { PeersIndex } from './peers-index.js'
import { User } from './user.js'
export class ForumTopic { export class ForumTopic {
static COLOR_BLUE = 0x6fb9f0 static COLOR_BLUE = 0x6fb9f0
@ -108,15 +107,8 @@ export class ForumTopic {
/** /**
* Creator of the topic * Creator of the topic
*/ */
get creator(): User | Chat { get creator(): Peer {
switch (this.raw.fromId._) { return parsePeer(this.raw.fromId, this._peers)
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._)
}
} }
/** /**

View file

@ -1,5 +1,3 @@
import { tl } from '@mtcute/core'
export * from './chat.js' export * from './chat.js'
export * from './chat-event/index.js' export * from './chat-event/index.js'
export * from './chat-invite-link.js' export * from './chat-invite-link.js'
@ -10,33 +8,7 @@ export * from './chat-permissions.js'
export * from './chat-photo.js' export * from './chat-photo.js'
export * from './chat-preview.js' export * from './chat-preview.js'
export * from './forum-topic.js' export * from './forum-topic.js'
export * from './peer.js'
export * from './peers-index.js' export * from './peers-index.js'
export * from './typing-status.js' export * from './typing-status.js'
export * from './user.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 }

View file

@ -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))
}
}

View file

@ -2,7 +2,7 @@ import { tl } from '@mtcute/core'
import { assertTypeIs, makeInspectable } from '../../utils/index.js' import { assertTypeIs, makeInspectable } from '../../utils/index.js'
import { memoizeGetters } from '../../utils/memoize.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' import { Story } from './story.js'
export class PeerStories { export class PeerStories {
@ -14,15 +14,8 @@ export class PeerStories {
/** /**
* Peer that owns these stories. * Peer that owns these stories.
*/ */
get peer(): User | Chat { get peer(): Peer {
switch (this.raw.peer._) { return parsePeer(this.raw.peer, this._peers)
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))
}
} }
/** /**

View file

@ -1,6 +1,6 @@
import { tl } from '@mtcute/core' 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 { makeInspectable } from '../../utils/index.js'
import { memoizeGetters } from '../../utils/memoize.js' import { memoizeGetters } from '../../utils/memoize.js'
@ -16,15 +16,8 @@ export class DeleteStoryUpdate {
/** /**
* Peer that owns these stories. * Peer that owns these stories.
*/ */
get peer(): User | Chat { get peer(): Peer {
switch (this.raw.peer._) { return parsePeer(this.raw.peer, this._peers)
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))
}
} }
/** /**

View file

@ -1,9 +1,8 @@
import { MtUnsupportedError, tl } from '@mtcute/core' import { MtUnsupportedError, tl } from '@mtcute/core'
import { assertTypeIs } from '@mtcute/core/utils.js'
import { makeInspectable } from '../../utils/index.js' import { makeInspectable } from '../../utils/index.js'
import { memoizeGetters } from '../../utils/memoize.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. * Some user has voted in a public poll.
@ -27,14 +26,8 @@ export class PollVoteUpdate {
/** /**
* Peer who has voted * Peer who has voted
*/ */
get peer(): User | Chat { get peer(): Peer {
if (this.raw.peer._ === 'peerUser') { return parsePeer(this.raw.peer, this._peers)
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))
} }
/** /**

View file

@ -1,6 +1,6 @@
import { tl } from '@mtcute/core' 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 { Story } from '../../types/stories/index.js'
import { assertTypeIs, makeInspectable } from '../../utils/index.js' import { assertTypeIs, makeInspectable } from '../../utils/index.js'
import { memoizeGetters } from '../../utils/memoize.js' import { memoizeGetters } from '../../utils/memoize.js'
@ -20,15 +20,8 @@ export class StoryUpdate {
/** /**
* Peer that owns these stories. * Peer that owns these stories.
*/ */
get peer(): User | Chat { get peer(): Peer {
switch (this.raw.peer._) { return parsePeer(this.raw.peer, this._peers)
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))
}
} }
/** /**

View file

@ -1,12 +1,4 @@
import { import { Message, MtPeerNotFoundError, OmitInputMessageId, ParametersSkip1, Peer, TelegramClient } from '@mtcute/client'
Chat,
Message,
MtPeerNotFoundError,
OmitInputMessageId,
ParametersSkip1,
TelegramClient,
User,
} from '@mtcute/client'
import { DeleteMessagesParams } from '@mtcute/client/src/methods/messages/delete-messages.js' import { DeleteMessagesParams } from '@mtcute/client/src/methods/messages/delete-messages.js'
import { ForwardMessageOptions } from '@mtcute/client/src/methods/messages/forward-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 { SendCopyParams } from '@mtcute/client/src/methods/messages/send-copy.js'
@ -53,7 +45,7 @@ export class MessageContext extends Message implements UpdateContext<Message> {
* *
* Learn more: [Incomplete peers](https://mtcute.dev/guide/topics/peers.html#incomplete-peers) * Learn more: [Incomplete peers](https://mtcute.dev/guide/topics/peers.html#incomplete-peers)
*/ */
async getSender(): Promise<User | Chat> { async getCompleteSender(): Promise<Peer> {
if (!this.sender.isMin) return this.sender if (!this.sender.isMin) return this.sender
let res let res

View file

@ -2,11 +2,11 @@
// ^^ will be looked into in MTQ-29 // ^^ will be looked into in MTQ-29
import { import {
_RepliedMessageAssertionsByOrigin, _RepliedMessageAssertionsByOrigin,
Chat,
MaybeArray, MaybeArray,
Message, Message,
MessageAction, MessageAction,
MessageMediaType, MessageMediaType,
Peer,
RawDocument, RawDocument,
RawLocation, RawLocation,
RepliedMessageInfo, RepliedMessageInfo,
@ -208,7 +208,7 @@ export const action = <T extends Exclude<MessageAction, null>['type']>(
action: Extract<MessageAction, { type: T }> action: Extract<MessageAction, { type: T }>
sender: T extends 'user_joined_link' | 'user_removed' | 'history_cleared' | 'contact_joined' | 'bot_allowed' sender: T extends 'user_joined_link' | 'user_removed' | 'history_cleared' | 'contact_joined' | 'bot_allowed'
? User ? User
: User | Chat : Peer
} }
> => { > => {
if (Array.isArray(type)) { if (Array.isArray(type)) {
@ -261,7 +261,7 @@ export const withCompleteSender =
): UpdateFilter<MessageContext, Mod, State> => ): UpdateFilter<MessageContext, Mod, State> =>
async (msg, state) => { async (msg, state) => {
try { try {
await msg.getSender() await msg.getCompleteSender()
} catch (e) { } catch (e) {
return false return false
} }

View file

@ -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' 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 msg Message or callback from which to derive the key
* @param scene Current scene UID, or `null` if none * @param scene Current scene UID, or `null` if none
*/ */
export type StateKeyDelegate = (upd: MessageContext | CallbackQueryContext | User | Chat) => MaybeAsync<string | null> export type StateKeyDelegate = (upd: MessageContext | CallbackQueryContext | Peer) => MaybeAsync<string | null>
/** /**
* Default state key delegate. * Default state key delegate.