feat(client): chats and chat joining related methods, bound methods and classes
This commit is contained in:
parent
6911d7d756
commit
383f133292
10 changed files with 396 additions and 2 deletions
|
@ -13,6 +13,10 @@ import { signInBot } from './methods/auth/sign-in-bot'
|
|||
import { signIn } from './methods/auth/sign-in'
|
||||
import { signUp } from './methods/auth/sign-up'
|
||||
import { start } from './methods/auth/start'
|
||||
import { getChatPreview } from './methods/chats/get-chat-preview'
|
||||
import { getChat } from './methods/chats/get-chat'
|
||||
import { getFullChat } from './methods/chats/get-full-chat'
|
||||
import { joinChat } from './methods/chats/join-chat'
|
||||
import { downloadAsBuffer } from './methods/files/download-buffer'
|
||||
import { downloadToFile } from './methods/files/download-file'
|
||||
import { downloadAsIterable } from './methods/files/download-iterable'
|
||||
|
@ -55,6 +59,7 @@ import { IMessageEntityParser } from './parser'
|
|||
import { Readable } from 'stream'
|
||||
import {
|
||||
Chat,
|
||||
ChatPreview,
|
||||
FileDownloadParameters,
|
||||
InputFileLike,
|
||||
InputMediaLike,
|
||||
|
@ -320,6 +325,50 @@ export class TelegramClient extends BaseTelegramClient {
|
|||
}): Promise<User> {
|
||||
return start.apply(this, arguments)
|
||||
}
|
||||
/**
|
||||
* Get preview information about a private chat.
|
||||
*
|
||||
* @param inviteLink Invite link
|
||||
* @throws MtCuteArgumentError In case invite link has invalid format
|
||||
* @throws MtCuteNotFoundError
|
||||
* In case you are trying to get info about private chat that you have already joined.
|
||||
* Use {@link getChat} or {@link getFullChat} instead.
|
||||
*/
|
||||
getChatPreview(inviteLink: string): Promise<ChatPreview> {
|
||||
return getChatPreview.apply(this, arguments)
|
||||
}
|
||||
/**
|
||||
* Get basic information about a chat.
|
||||
*
|
||||
* @param chatId ID of the chat, its username or invite link
|
||||
* @throws MtCuteArgumentError
|
||||
* In case you are trying to get info about private chat that you haven't joined.
|
||||
* Use {@link getChatPreview} instead.
|
||||
*/
|
||||
getChat(chatId: InputPeerLike): Promise<Chat> {
|
||||
return getChat.apply(this, arguments)
|
||||
}
|
||||
/**
|
||||
* Get full information about a chat.
|
||||
*
|
||||
* @param chatId ID of the chat, its username or invite link
|
||||
* @throws MtCuteArgumentError
|
||||
* In case you are trying to get info about private chat that you haven't joined.
|
||||
* Use {@link getChatPreview} instead.
|
||||
*/
|
||||
getFullChat(chatId: InputPeerLike): Promise<Chat> {
|
||||
return getFullChat.apply(this, arguments)
|
||||
}
|
||||
/**
|
||||
* Join a channel or supergroup
|
||||
*
|
||||
* @param chatId
|
||||
* Chat identifier. Either an invite link (`t.me/joinchat/*`), a username (`@username`)
|
||||
* or ID of the linked supergroup or channel.
|
||||
*/
|
||||
joinChat(chatId: InputPeerLike): Promise<Chat> {
|
||||
return joinChat.apply(this, arguments)
|
||||
}
|
||||
/**
|
||||
* Download a file and return its contents as a Buffer.
|
||||
*
|
||||
|
|
|
@ -10,6 +10,7 @@ import { Readable } from 'stream'
|
|||
import {
|
||||
User,
|
||||
Chat,
|
||||
ChatPreview,
|
||||
TermsOfService,
|
||||
SentCode,
|
||||
MaybeDynamic,
|
||||
|
|
35
packages/client/src/methods/chats/get-chat-preview.ts
Normal file
35
packages/client/src/methods/chats/get-chat-preview.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { MtCuteArgumentError, MtCuteNotFoundError } from '../../types'
|
||||
import { TelegramClient } from '../../client'
|
||||
import { INVITE_LINK_REGEX } from '../../utils/peer-utils'
|
||||
import { ChatPreview } from '../../types'
|
||||
|
||||
/**
|
||||
* Get preview information about a private chat.
|
||||
*
|
||||
* @param inviteLink Invite link
|
||||
* @throws MtCuteArgumentError In case invite link has invalid format
|
||||
* @throws MtCuteNotFoundError
|
||||
* In case you are trying to get info about private chat that you have already joined.
|
||||
* Use {@link getChat} or {@link getFullChat} instead.
|
||||
* @internal
|
||||
*/
|
||||
export async function getChatPreview(
|
||||
this: TelegramClient,
|
||||
inviteLink: string
|
||||
): Promise<ChatPreview> {
|
||||
const m = inviteLink.match(INVITE_LINK_REGEX)
|
||||
if (!m) throw new MtCuteArgumentError('Invalid invite link')
|
||||
|
||||
const res = await this.call({
|
||||
_: 'messages.checkChatInvite',
|
||||
hash: m[1],
|
||||
})
|
||||
|
||||
if (res._ !== 'chatInvite') {
|
||||
throw new MtCuteNotFoundError(
|
||||
`You have already joined this chat!`
|
||||
)
|
||||
}
|
||||
|
||||
return new ChatPreview(this, res, inviteLink)
|
||||
}
|
65
packages/client/src/methods/chats/get-chat.ts
Normal file
65
packages/client/src/methods/chats/get-chat.ts
Normal file
|
@ -0,0 +1,65 @@
|
|||
import { Chat, InputPeerLike, MtCuteArgumentError } from '../../types'
|
||||
import { TelegramClient } from '../../client'
|
||||
import {
|
||||
INVITE_LINK_REGEX,
|
||||
normalizeToInputChannel,
|
||||
normalizeToInputPeer,
|
||||
normalizeToInputUser,
|
||||
} from '../../utils/peer-utils'
|
||||
import { tl } from '@mtcute/tl'
|
||||
|
||||
/**
|
||||
* Get basic information about a chat.
|
||||
*
|
||||
* @param chatId ID of the chat, its username or invite link
|
||||
* @throws MtCuteArgumentError
|
||||
* In case you are trying to get info about private chat that you haven't joined.
|
||||
* Use {@link getChatPreview} instead.
|
||||
* @internal
|
||||
*/
|
||||
export async function getChat(
|
||||
this: TelegramClient,
|
||||
chatId: InputPeerLike
|
||||
): Promise<Chat> {
|
||||
if (typeof chatId === 'string') {
|
||||
const m = chatId.match(INVITE_LINK_REGEX)
|
||||
if (m) {
|
||||
const res = await this.call({
|
||||
_: 'messages.checkChatInvite',
|
||||
hash: m[1]
|
||||
})
|
||||
|
||||
if (res._ === 'chatInvite') {
|
||||
throw new MtCuteArgumentError(`You haven't joined ${JSON.stringify(res.title)}`)
|
||||
}
|
||||
|
||||
return new Chat(this, res.chat)
|
||||
}
|
||||
}
|
||||
|
||||
const peer = await this.resolvePeer(chatId)
|
||||
const input = normalizeToInputPeer(peer)
|
||||
|
||||
let res: tl.TypeChat | tl.TypeUser
|
||||
if (input._ === 'inputPeerChannel') {
|
||||
const r = await this.call({
|
||||
_: 'channels.getChannels',
|
||||
id: [normalizeToInputChannel(peer)!]
|
||||
})
|
||||
res = r.chats[0]
|
||||
} else if (input._ === 'inputPeerUser' || input._ === 'inputPeerSelf') {
|
||||
const r = await this.call({
|
||||
_: 'users.getUsers',
|
||||
id: [normalizeToInputUser(peer)!]
|
||||
})
|
||||
res = r[0]
|
||||
} else if (input._ === 'inputPeerChat') {
|
||||
const r = await this.call({
|
||||
_: 'messages.getChats',
|
||||
id: [input.chatId]
|
||||
})
|
||||
res = r.chats[0]
|
||||
} else throw new Error('should not happen')
|
||||
|
||||
return new Chat(this, res)
|
||||
}
|
63
packages/client/src/methods/chats/get-full-chat.ts
Normal file
63
packages/client/src/methods/chats/get-full-chat.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
import { Chat, InputPeerLike, MtCuteArgumentError } from '../../types'
|
||||
import { TelegramClient } from '../../client'
|
||||
import {
|
||||
INVITE_LINK_REGEX,
|
||||
normalizeToInputChannel,
|
||||
normalizeToInputPeer,
|
||||
normalizeToInputUser,
|
||||
} from '../../utils/peer-utils'
|
||||
import { tl } from '@mtcute/tl'
|
||||
|
||||
/**
|
||||
* Get full information about a chat.
|
||||
*
|
||||
* @param chatId ID of the chat, its username or invite link
|
||||
* @throws MtCuteArgumentError
|
||||
* In case you are trying to get info about private chat that you haven't joined.
|
||||
* Use {@link getChatPreview} instead.
|
||||
* @internal
|
||||
*/
|
||||
export async function getFullChat(
|
||||
this: TelegramClient,
|
||||
chatId: InputPeerLike
|
||||
): Promise<Chat> {
|
||||
if (typeof chatId === 'string') {
|
||||
const m = chatId.match(INVITE_LINK_REGEX)
|
||||
if (m) {
|
||||
const res = await this.call({
|
||||
_: 'messages.checkChatInvite',
|
||||
hash: m[1]
|
||||
})
|
||||
|
||||
if (res._ === 'chatInvite') {
|
||||
throw new MtCuteArgumentError(`You haven't joined ${JSON.stringify(res.title)}`)
|
||||
}
|
||||
|
||||
// we still need to fetch full chat info
|
||||
chatId = res.chat.id
|
||||
}
|
||||
}
|
||||
|
||||
const peer = await this.resolvePeer(chatId)
|
||||
const input = normalizeToInputPeer(peer)
|
||||
|
||||
let res: tl.messages.TypeChatFull | tl.TypeUserFull
|
||||
if (input._ === 'inputPeerChannel') {
|
||||
res = await this.call({
|
||||
_: 'channels.getFullChannel',
|
||||
channel: normalizeToInputChannel(peer)!
|
||||
})
|
||||
} else if (input._ === 'inputPeerUser' || input._ === 'inputPeerSelf') {
|
||||
res = await this.call({
|
||||
_: 'users.getFullUser',
|
||||
id: normalizeToInputUser(peer)!
|
||||
})
|
||||
} else if (input._ === 'inputPeerChat') {
|
||||
res = await this.call({
|
||||
_: 'messages.getFullChat',
|
||||
chatId: input.chatId
|
||||
})
|
||||
} else throw new Error('should not happen')
|
||||
|
||||
return Chat._parseFull(this, res)
|
||||
}
|
57
packages/client/src/methods/chats/join-chat.ts
Normal file
57
packages/client/src/methods/chats/join-chat.ts
Normal file
|
@ -0,0 +1,57 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import {
|
||||
Chat,
|
||||
InputPeerLike,
|
||||
MtCuteNotFoundError,
|
||||
MtCuteTypeAssertionError,
|
||||
} from '../../types'
|
||||
import { INVITE_LINK_REGEX, normalizeToInputChannel } from '../../utils/peer-utils'
|
||||
|
||||
/**
|
||||
* Join a channel or supergroup
|
||||
*
|
||||
* @param chatId
|
||||
* Chat identifier. Either an invite link (`t.me/joinchat/*`), a username (`@username`)
|
||||
* or ID of the linked supergroup or channel.
|
||||
* @internal
|
||||
*/
|
||||
export async function joinChat(
|
||||
this: TelegramClient,
|
||||
chatId: InputPeerLike
|
||||
): Promise<Chat> {
|
||||
if (typeof chatId === 'string') {
|
||||
const m = chatId.match(INVITE_LINK_REGEX)
|
||||
if (m) {
|
||||
const res = await this.call({
|
||||
_: 'messages.importChatInvite',
|
||||
hash: m[1],
|
||||
})
|
||||
if (!(res._ === 'updates' || res._ === 'updatesCombined')) {
|
||||
throw new MtCuteTypeAssertionError(
|
||||
'joinChat, (@ messages.importChatInvite)',
|
||||
'updates | updatesCombined',
|
||||
res._
|
||||
)
|
||||
}
|
||||
|
||||
return new Chat(this, res.chats[0])
|
||||
}
|
||||
}
|
||||
|
||||
const peer = normalizeToInputChannel(await this.resolvePeer(chatId))
|
||||
if (!peer) throw new MtCuteNotFoundError()
|
||||
|
||||
const res = await this.call({
|
||||
_: 'channels.joinChannel',
|
||||
channel: peer,
|
||||
})
|
||||
if (!(res._ === 'updates' || res._ === 'updatesCombined')) {
|
||||
throw new MtCuteTypeAssertionError(
|
||||
'joinChat, (@ channels.joinChannel)',
|
||||
'updates | updatesCombined',
|
||||
res._
|
||||
)
|
||||
}
|
||||
|
||||
return new Chat(this, res.chats[0])
|
||||
}
|
101
packages/client/src/types/peers/chat-preview.ts
Normal file
101
packages/client/src/types/peers/chat-preview.ts
Normal file
|
@ -0,0 +1,101 @@
|
|||
import { tl } from '@mtcute/tl'
|
||||
import { TelegramClient } from '../../client'
|
||||
import { makeInspectable } from '../utils'
|
||||
import { Photo } from '../media'
|
||||
import { User } from './user'
|
||||
import { Chat } from './chat'
|
||||
|
||||
export namespace ChatPreview {
|
||||
/**
|
||||
* Chat type. Can be:
|
||||
* - `group`: Legacy group
|
||||
* - `supergroup`: Supergroup
|
||||
* - `channel`: Broadcast channel
|
||||
* - `broadcast`: Broadcast group
|
||||
*/
|
||||
export type Type = 'group' | 'supergroup' | 'channel' | 'broadcast'
|
||||
}
|
||||
|
||||
export class ChatPreview {
|
||||
readonly client: TelegramClient
|
||||
readonly invite: tl.RawChatInvite
|
||||
|
||||
/**
|
||||
* Original invite link used to fetch
|
||||
* this preview
|
||||
*/
|
||||
readonly link: string
|
||||
|
||||
constructor(client: TelegramClient, raw: tl.RawChatInvite, link: string) {
|
||||
this.client = client
|
||||
this.invite = raw
|
||||
this.link = link
|
||||
}
|
||||
|
||||
/**
|
||||
* Title of the chat
|
||||
*/
|
||||
get title(): string {
|
||||
return this.invite.title
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of the chat
|
||||
*/
|
||||
get type(): ChatPreview.Type {
|
||||
if (!this.invite.channel) return 'group'
|
||||
if (this.invite.broadcast) return 'channel'
|
||||
if (this.invite.megagroup) return 'broadcast'
|
||||
return 'supergroup'
|
||||
}
|
||||
|
||||
/**
|
||||
* Total chat member count
|
||||
*/
|
||||
get memberCount(): number {
|
||||
return this.invite.participantsCount
|
||||
}
|
||||
|
||||
_photo?: Photo
|
||||
/**
|
||||
* Chat photo
|
||||
*/
|
||||
get photo(): Photo | null {
|
||||
if (this.invite.photo._ === 'photoEmpty') return null
|
||||
|
||||
if (!this._photo) {
|
||||
this._photo = new Photo(this.client, this.invite.photo)
|
||||
}
|
||||
|
||||
return this._photo
|
||||
}
|
||||
|
||||
private _someMembers?: User[]
|
||||
/**
|
||||
* Preview of some of the chat members.
|
||||
*
|
||||
* This usually contains around 10 members,
|
||||
* and members that are inside your contacts list are
|
||||
* ordered before others.
|
||||
*/
|
||||
get someMembers(): User[] {
|
||||
if (!this._someMembers) {
|
||||
this._someMembers = this.invite.participants
|
||||
? this.invite.participants.map(
|
||||
(it) => new User(this.client, it as tl.RawUser)
|
||||
)
|
||||
: []
|
||||
}
|
||||
|
||||
return this._someMembers
|
||||
}
|
||||
|
||||
/**
|
||||
* Join this chat
|
||||
*/
|
||||
async join(): Promise<Chat> {
|
||||
return this.client.joinChat(this.link)
|
||||
}
|
||||
}
|
||||
|
||||
makeInspectable(ChatPreview, ['link'])
|
|
@ -14,8 +14,15 @@ export namespace Chat {
|
|||
* - `group`: Legacy group
|
||||
* - `supergroup`: Supergroup
|
||||
* - `channel`: Broadcast channel
|
||||
* - `broadcast`: Broadcast group
|
||||
*/
|
||||
export type Type = 'private' | 'bot' | 'group' | 'supergroup' | 'channel'
|
||||
export type Type =
|
||||
| 'private'
|
||||
| 'bot'
|
||||
| 'group'
|
||||
| 'supergroup'
|
||||
| 'channel'
|
||||
| 'broadcast'
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -109,7 +116,11 @@ export class Chat {
|
|||
} else if (this.peer._ === 'chat') {
|
||||
this._type = 'group'
|
||||
} else if (this.peer._ === 'channel') {
|
||||
this._type = this.peer.broadcast ? 'channel' : 'supergroup'
|
||||
this._type = this.peer.megagroup
|
||||
? 'broadcast'
|
||||
: this.peer.broadcast
|
||||
? 'channel'
|
||||
: 'supergroup'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -379,6 +390,7 @@ export class Chat {
|
|||
return new Chat(client, chats[peer.channelId])
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
static _parseFull(
|
||||
client: TelegramClient,
|
||||
full: tl.messages.RawChatFull | tl.RawUserFull
|
||||
|
@ -409,6 +421,13 @@ export class Chat {
|
|||
}
|
||||
|
||||
// todo: bound methods https://github.com/pyrogram/pyrogram/blob/a86656aefcc93cc3d2f5c98227d5da28fcddb136/pyrogram/types/user_and_chats/chat.py#L319
|
||||
|
||||
/**
|
||||
* Join this chat.
|
||||
*/
|
||||
async join(): Promise<void> {
|
||||
await this.client.joinChat(this.inputPeer)
|
||||
}
|
||||
}
|
||||
|
||||
makeInspectable(Chat)
|
||||
|
|
|
@ -2,6 +2,7 @@ import { tl } from '@mtcute/tl'
|
|||
|
||||
export * from './user'
|
||||
export * from './chat'
|
||||
export * from './chat-preview'
|
||||
|
||||
/**
|
||||
* Peer types that have one-to-one relation to tl.Peer* types.
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import { tl } from '@mtcute/tl'
|
||||
|
||||
export const INVITE_LINK_REGEX = /^(?:https?:\/\/)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)\/joinchat\/)([\w-]+)$/i
|
||||
|
||||
|
||||
// helpers to normalize result of `resolvePeer` function
|
||||
|
||||
export function normalizeToInputPeer(
|
||||
|
|
Loading…
Reference in a new issue