feat(client): chat invite links related methods

This commit is contained in:
teidesu 2021-05-10 00:35:29 +03:00
parent ac0ddc5c6d
commit ded667b332
13 changed files with 580 additions and 14 deletions

View file

@ -204,7 +204,7 @@ async function addSingleMethod(state, fileName) {
comment: getLeadingComments(stmt),
aliases,
overload: isOverload,
hasOverloads: hasOverloads[name] && !isOverload
hasOverloads: hasOverloads[name] && !isOverload,
})
const module = `./${relPath.replace(/\.ts$/, '')}`
@ -299,7 +299,15 @@ async function main() {
const classContents = []
state.methods.list.forEach(
({ name: origName, isPrivate, func, comment, aliases, overload, hasOverloads }) => {
({
name: origName,
isPrivate,
func,
comment,
aliases,
overload,
hasOverloads,
}) => {
// create method that calls that function and passes `this`
// first let's determine the signature
const returnType = func.type ? ': ' + func.type.getText() : ''
@ -336,7 +344,9 @@ async function main() {
ts.SyntaxKind.NumericLiteral ||
(it.initializer.kind ===
ts.SyntaxKind.Identifier &&
it.initializer.escapedText === 'NaN')
(it.initializer.escapedText === 'NaN' ||
it.initializer.escapedText ===
'Infinity'))
) {
it.type = { kind: ts.SyntaxKind.NumberKeyword }
} else {

View file

@ -60,6 +60,14 @@ import { _normalizeFileToDocument } from './methods/files/normalize-file-to-docu
import { _normalizeInputFile } from './methods/files/normalize-input-file'
import { _normalizeInputMedia } from './methods/files/normalize-input-media'
import { uploadFile } from './methods/files/upload-file'
import { createInviteLink } from './methods/invite-links/create-invite-link'
import { editInviteLink } from './methods/invite-links/edit-invite-link'
import { exportInviteLink } from './methods/invite-links/export-invite-link'
import { getInviteLinkMembers } from './methods/invite-links/get-invite-link-members'
import { getInviteLink } from './methods/invite-links/get-invite-link'
import { getInviteLinks } from './methods/invite-links/get-invite-links'
import { getPrimaryInviteLink } from './methods/invite-links/get-primary-invite-link'
import { revokeInviteLink } from './methods/invite-links/revoke-invite-link'
import { closePoll } from './methods/messages/close-poll'
import { deleteMessages } from './methods/messages/delete-messages'
import { editInlineMessage } from './methods/messages/edit-inline-message'
@ -1200,6 +1208,146 @@ export interface TelegramClient extends BaseTelegramClient {
*/
progressCallback?: (uploaded: number, total: number) => void
}): Promise<UploadedFile>
/**
* Create an additional invite link for the chat.
*
* You must be an administrator and have appropriate rights.
*
* @param chatId Chat ID
* @param params
*/
createInviteLink(
chatId: InputPeerLike,
params?: {
/**
* Date when this link will expire.
* If `number` is passed, UNIX time in ms is expected.
*/
expires?: number | Date
/**
* Maximum number of users that can be members of this chat
* at the same time after joining using this link.
*
* Integer in range `[1, 99999]` or `Infinity`, defaults to `Infinity`
*/
usageLimit?: number
}
): Promise<ChatInviteLink>
/**
* Edit an invite link. You can only edit non-primary
* invite links.
*
* Only pass the fields that you want to modify.
*
* @param chatId Chat ID
* @param link Invite link to edit
* @param params
* @returns Modified invite link
*/
editInviteLink(
chatId: InputPeerLike,
link: string,
params: {
/**
* Date when this link will expire.
* If `number` is passed, UNIX time in ms is expected.
*/
expires?: number | Date
/**
* Maximum number of users that can be members of this chat
* at the same time after joining using this link.
*
* Integer in range `[1, 99999]` or `Infinity`,
*/
usageLimit?: number
}
): Promise<ChatInviteLink>
/**
* Generate a new primary invite link for a chat,
* old primary link is revoked.
*
* > **Note**: each administrator has their own primary invite link,
* > and bots by default don't have one.
*
* @param chatId Chat ID
*/
exportInviteLink(chatId: InputPeerLike): Promise<ChatInviteLink>
/**
* Iterate over users who have joined
* the chat with the given invite link.
*
* @param chatId Chat ID
* @param link Invite link
* @param limit (default: `Infinity`) Maximum number of users to return (by default returns all)
*/
getInviteLinkMembers(
chatId: InputPeerLike,
link: string,
limit?: number
): AsyncIterableIterator<ChatInviteLink.JoinedMember>
/**
* Get detailed information about an invite link
*
* @param chatId Chat ID
* @param link The invite link
*/
getInviteLink(chatId: InputPeerLike, link: string): Promise<ChatInviteLink>
/**
* Get invite links created by some administrator in the chat.
*
* As an administrator you can only get your own links
* (i.e. `adminId = "self"`), as a creator you can get
* any other admin's links.
*
* @param chatId Chat ID
* @param adminId Admin who created the links
* @param params
*/
getInviteLinks(
chatId: InputPeerLike,
adminId: InputPeerLike,
params?: {
/**
* Whether to fetch revoked invite links
*/
revoked?: boolean
/**
* Limit the number of invite links to be fetched.
* By default, all links are fetched.
*/
limit?: number
/**
* Size of chunks which are fetched. Usually not needed.
*
* Defaults to `100`
*/
chunkSize?: number
}
): AsyncIterableIterator<ChatInviteLink>
/**
* Get primary invite link of a chat
*
* @param chatId Chat ID
*/
getPrimaryInviteLink(chatId: InputPeerLike): Promise<ChatInviteLink>
/**
* Revoke an invite link.
*
* If `link` is a primary invite link, a new invite link will be
* generated automatically by Telegram
*
* @param chatId Chat ID
* @param link Invite link to revoke
* @returns If `link` is a primary invite, newly generated invite link, otherwise the revoked link
*/
revokeInviteLink(
chatId: InputPeerLike,
link: string
): Promise<ChatInviteLink>
/**
* Close a poll sent by you.
*
@ -2556,6 +2704,14 @@ export class TelegramClient extends BaseTelegramClient {
protected _normalizeInputFile = _normalizeInputFile
protected _normalizeInputMedia = _normalizeInputMedia
uploadFile = uploadFile
createInviteLink = createInviteLink
editInviteLink = editInviteLink
exportInviteLink = exportInviteLink
getInviteLinkMembers = getInviteLinkMembers
getInviteLink = getInviteLink
getInviteLinks = getInviteLinks
getPrimaryInviteLink = getPrimaryInviteLink
revokeInviteLink = revokeInviteLink
closePoll = closePoll
deleteMessages = deleteMessages
editInlineMessage = editInlineMessage

View file

@ -0,0 +1,44 @@
import { TelegramClient } from '../../client'
import { ChatInviteLink, InputPeerLike } from '../../types'
import { normalizeToInputPeer } from '../../utils/peer-utils'
import { normalizeDate } from '../../utils/misc-utils'
/**
* Create an additional invite link for the chat.
*
* You must be an administrator and have appropriate rights.
*
* @param chatId Chat ID
* @param params
* @internal
*/
export async function createInviteLink(
this: TelegramClient,
chatId: InputPeerLike,
params?: {
/**
* Date when this link will expire.
* If `number` is passed, UNIX time in ms is expected.
*/
expires?: number | Date
/**
* Maximum number of users that can be members of this chat
* at the same time after joining using this link.
*
* Integer in range `[1, 99999]` or `Infinity`, defaults to `Infinity`
*/
usageLimit?: number
}
): Promise<ChatInviteLink> {
if (!params) params = {}
const res = await this.call({
_: 'messages.exportChatInvite',
peer: normalizeToInputPeer(await this.resolvePeer(chatId)),
expireDate: normalizeDate(params.expires),
usageLimit: params.usageLimit
})
return new ChatInviteLink(this, res)
}

View file

@ -0,0 +1,49 @@
import { TelegramClient } from '../../client'
import { ChatInviteLink, InputPeerLike } from '../../types'
import { createUsersChatsIndex, normalizeToInputPeer } from '../../utils/peer-utils'
import { normalizeDate } from '../../utils/misc-utils'
/**
* Edit an invite link. You can only edit non-primary
* invite links.
*
* Only pass the fields that you want to modify.
*
* @param chatId Chat ID
* @param link Invite link to edit
* @param params
* @returns Modified invite link
* @internal
*/
export async function editInviteLink(
this: TelegramClient,
chatId: InputPeerLike,
link: string,
params: {
/**
* Date when this link will expire.
* If `number` is passed, UNIX time in ms is expected.
*/
expires?: number | Date
/**
* Maximum number of users that can be members of this chat
* at the same time after joining using this link.
*
* Integer in range `[1, 99999]` or `Infinity`,
*/
usageLimit?: number
}
): Promise<ChatInviteLink> {
const res = await this.call({
_: 'messages.editExportedChatInvite',
peer: normalizeToInputPeer(await this.resolvePeer(chatId)),
link,
expireDate: normalizeDate(params.expires),
usageLimit: params.usageLimit
})
const { users } = createUsersChatsIndex(res)
return new ChatInviteLink(this, res.invite, users)
}

View file

@ -0,0 +1,26 @@
import { TelegramClient } from '../../client'
import { ChatInviteLink, InputPeerLike } from '../../types'
import { normalizeToInputPeer } from '../../utils/peer-utils'
/**
* Generate a new primary invite link for a chat,
* old primary link is revoked.
*
* > **Note**: each administrator has their own primary invite link,
* > and bots by default don't have one.
*
* @param chatId Chat IDs
* @internal
*/
export async function exportInviteLink(
this: TelegramClient,
chatId: InputPeerLike
): Promise<ChatInviteLink> {
const res = await this.call({
_: 'messages.exportChatInvite',
peer: normalizeToInputPeer(await this.resolvePeer(chatId)),
legacyRevokePermanent: true
})
return new ChatInviteLink(this, res)
}

View file

@ -0,0 +1,63 @@
import { TelegramClient } from '../../client'
import { ChatInviteLink, InputPeerLike, User } from '../../types'
import { createUsersChatsIndex, normalizeToInputPeer } from '../../utils/peer-utils'
import { tl } from '@mtcute/tl'
/**
* Iterate over users who have joined
* the chat with the given invite link.
*
* @param chatId Chat ID
* @param link Invite link
* @param limit Maximum number of users to return (by default returns all)
* @internal
*/
export async function* getInviteLinkMembers(
this: TelegramClient,
chatId: InputPeerLike,
link: string,
limit = Infinity
): AsyncIterableIterator<ChatInviteLink.JoinedMember> {
const peer = normalizeToInputPeer(await this.resolvePeer(chatId))
let current = 0
let offsetDate = 0
let offsetUser: tl.TypeInputUser = { _: 'inputUserEmpty' }
for (;;) {
// for some reason ts needs annotation, idk
const res: tl.RpcCallReturn['messages.getChatInviteImporters'] = await this.call({
_: 'messages.getChatInviteImporters',
limit: Math.min(100, limit - current),
peer,
link,
offsetDate,
offsetUser,
})
if (!res.importers.length) break
const { users } = createUsersChatsIndex(res)
const last = res.importers[res.importers.length - 1]
offsetDate = last.date
offsetUser = {
_: 'inputUser',
userId: last.userId,
accessHash: (users[last.userId] as tl.RawUser).accessHash!
}
for (const it of res.importers) {
const user = new User(this, users[it.userId])
yield {
user,
date: new Date(it.date * 1000),
}
}
current += res.importers.length
if (current >= limit) break
}
}

View file

@ -0,0 +1,26 @@
import { TelegramClient } from '../../client'
import { ChatInviteLink, InputPeerLike } from '../../types'
import { createUsersChatsIndex, normalizeToInputPeer } from '../../utils/peer-utils'
/**
* Get detailed information about an invite link
*
* @param chatId Chat ID
* @param link The invite link
* @internal
*/
export async function getInviteLink(
this: TelegramClient,
chatId: InputPeerLike,
link: string
): Promise<ChatInviteLink> {
const res = await this.call({
_: 'messages.getExportedChatInvite',
peer: normalizeToInputPeer(await this.resolvePeer(chatId)),
link
})
const { users } = createUsersChatsIndex(res)
return new ChatInviteLink(this, res.invite, users)
}

View file

@ -0,0 +1,91 @@
import { TelegramClient } from '../../client'
import {
ChatInviteLink,
InputPeerLike,
MtCuteInvalidPeerTypeError,
} from '../../types'
import {
createUsersChatsIndex,
normalizeToInputPeer,
normalizeToInputUser,
} from '../../utils/peer-utils'
import { tl } from '@mtcute/tl'
/**
* Get invite links created by some administrator in the chat.
*
* As an administrator you can only get your own links
* (i.e. `adminId = "self"`), as a creator you can get
* any other admin's links.
*
* @param chatId Chat ID
* @param adminId Admin who created the links
* @param params
* @internal
*/
export async function* getInviteLinks(
this: TelegramClient,
chatId: InputPeerLike,
adminId: InputPeerLike,
params?: {
/**
* Whether to fetch revoked invite links
*/
revoked?: boolean
/**
* Limit the number of invite links to be fetched.
* By default, all links are fetched.
*/
limit?: number
/**
* Size of chunks which are fetched. Usually not needed.
*
* Defaults to `100`
*/
chunkSize?: number
}
): AsyncIterableIterator<ChatInviteLink> {
if (!params) params = {}
let current = 0
const total = params.limit || Infinity
const chunkSize = Math.min(params.chunkSize ?? 100, total)
const peer = normalizeToInputPeer(await this.resolvePeer(chatId))
const admin = normalizeToInputUser(await this.resolvePeer(adminId))
if (!admin) throw new MtCuteInvalidPeerTypeError(adminId, 'user')
let offsetDate: number | undefined = undefined
let offsetLink: string | undefined = undefined
for (;;) {
const res: tl.RpcCallReturn['messages.getExportedChatInvites'] = await this.call(
{
_: 'messages.getExportedChatInvites',
peer,
adminId: admin,
limit: Math.min(chunkSize, total - current),
offsetDate,
offsetLink,
}
)
if (!res.invites.length) break
const { users } = createUsersChatsIndex(res)
const last = res.invites[res.invites.length - 1]
offsetDate = last.date
offsetLink = last.link
for (const it of res.invites) {
yield new ChatInviteLink(this, it, users)
}
current += res.invites.length
if (current >= total) break
}
}

View file

@ -0,0 +1,40 @@
import { TelegramClient } from '../../client'
import {
ChatInviteLink,
InputPeerLike,
MtCuteTypeAssertionError,
} from '../../types'
import {
createUsersChatsIndex,
normalizeToInputPeer,
} from '../../utils/peer-utils'
/**
* Get primary invite link of a chat
*
* @param chatId Chat ID
* @internal
*/
export async function getPrimaryInviteLink(
this: TelegramClient,
chatId: InputPeerLike
): Promise<ChatInviteLink> {
const res = await this.call({
_: 'messages.getExportedChatInvites',
peer: normalizeToInputPeer(await this.resolvePeer(chatId)),
adminId: { _: 'inputUserSelf' },
limit: 1,
revoked: false,
})
if (!res.invites[0]?.permanent)
throw new MtCuteTypeAssertionError(
'messages.getExportedChatInvites (@ .invites[0].permanent)',
'true',
'false'
)
const { users } = createUsersChatsIndex(res)
return new ChatInviteLink(this, res.invites[0], users)
}

View file

@ -0,0 +1,33 @@
import { TelegramClient } from '../../client'
import { ChatInviteLink, InputPeerLike } from '../../types'
import { createUsersChatsIndex, normalizeToInputPeer } from '../../utils/peer-utils'
/**
* Revoke an invite link.
*
* If `link` is a primary invite link, a new invite link will be
* generated automatically by Telegram
*
* @param chatId Chat ID
* @param link Invite link to revoke
* @returns If `link` is a primary invite, newly generated invite link, otherwise the revoked link
* @internal
*/
export async function revokeInviteLink(
this: TelegramClient,
chatId: InputPeerLike,
link: string
): Promise<ChatInviteLink> {
const res = await this.call({
_: 'messages.editExportedChatInvite',
peer: normalizeToInputPeer(await this.resolvePeer(chatId)),
link,
revoked: true
})
const { users } = createUsersChatsIndex(res)
const invite = res._ === 'messages.exportedChatInviteReplaced' ? res.newInvite : res.invite
return new ChatInviteLink(this, invite, users)
}

View file

@ -69,7 +69,10 @@ export async function* iterProfilePhotos(
offset += res.photos.length
yield* res.photos.map((it) => new Photo(this, it as tl.RawPhoto))
for (const it of res.photos) {
yield new Photo(this, it as tl.RawPhoto)
}
current += res.photos.length
if (current >= total) break

View file

@ -1,6 +1,14 @@
import { makeInspectable } from '../utils'
import { TelegramClient } from '../../client'
import { tl } from '@mtcute/tl'
import { User } from './user'
export namespace ChatInviteLink {
export interface JoinedMember {
user: User
date: Date
}
}
/**
* An invite link
@ -9,9 +17,12 @@ export class ChatInviteLink {
readonly client: TelegramClient
readonly raw: tl.RawChatInviteExported
constructor (client: TelegramClient, raw: tl.RawChatInviteExported) {
readonly _users?: Record<number, tl.TypeUser>
constructor (client: TelegramClient, raw: tl.RawChatInviteExported, users?: Record<number, tl.TypeUser>) {
this.client = client
this.raw = raw
this._users = users
}
/**
@ -24,6 +35,20 @@ export class ChatInviteLink {
return this.raw.link
}
private _creator?: User
/**
* Creator of the invite link, if available
*/
get creator(): User | null {
if (!this._users) return null
if (!this._creator) {
this._creator = new User(this.client, this._users[this.raw.adminId])
}
return this._creator
}
/**
* Creation date of the link
*/
@ -73,7 +98,7 @@ export class ChatInviteLink {
* Number of users currently in the chat that joined using this link
*/
get usage(): number {
return this.raw.usageLimit ?? 0
return this.raw.usage ?? 0
}
}

View file

@ -95,12 +95,12 @@ export function peerToInputPeer(peer: tl.TypePeer, accessHash = bigInt.zero): tl
export function createUsersChatsIndex(
obj: {
users: tl.TypeUser[]
chats: tl.TypeChat[]
users?: tl.TypeUser[]
chats?: tl.TypeChat[]
},
second?: {
users: tl.TypeUser[]
chats: tl.TypeChat[]
users?: tl.TypeUser[]
chats?: tl.TypeChat[]
}
): {
users: Record<number, tl.TypeUser>
@ -108,12 +108,12 @@ export function createUsersChatsIndex(
} {
const users: Record<number, tl.TypeUser> = {}
const chats: Record<number, tl.TypeChat> = {}
obj.users.forEach((e) => (users[e.id] = e))
obj.chats.forEach((e) => (chats[e.id] = e))
obj.users?.forEach((e) => (users[e.id] = e))
obj.chats?.forEach((e) => (chats[e.id] = e))
if (second) {
second.users.forEach((e) => (users[e.id] = e))
second.chats.forEach((e) => (chats[e.id] = e))
second.users?.forEach((e) => (users[e.id] = e))
second.chats?.forEach((e) => (chats[e.id] = e))
}
return { users, chats }