chore: re-thought method arguments

also dropped support for registration
This commit is contained in:
alina 🌸 2023-10-07 00:22:08 +03:00 committed by Alina Tumanova
parent a6726ccb40
commit 280a98f52a
81 changed files with 1559 additions and 1454 deletions

View file

@ -147,10 +147,7 @@ module.exports = {
}, },
], ],
'space-in-parens': 2, 'space-in-parens': 2,
'key-spacing': [ 'key-spacing': [2, { beforeColon: false, afterColon: true, mode: 'strict' }],
2,
{ beforeColon: false, afterColon: true, mode: 'strict' },
],
'space-infix-ops': 2, 'space-infix-ops': 2,
'padding-line-between-statements': [ 'padding-line-between-statements': [
'error', 'error',
@ -176,10 +173,7 @@ module.exports = {
{ {
files: ['**/*.ts', '**/*.tsx'], files: ['**/*.ts', '**/*.tsx'],
env: { browser: true, es6: true, node: true }, env: { browser: true, es6: true, node: true },
extends: [ extends: ['plugin:@typescript-eslint/strict', 'plugin:import/typescript'],
'plugin:@typescript-eslint/strict',
'plugin:import/typescript',
],
globals: { Atomics: 'readonly', SharedArrayBuffer: 'readonly' }, globals: { Atomics: 'readonly', SharedArrayBuffer: 'readonly' },
parser: '@typescript-eslint/parser', parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'], plugins: ['@typescript-eslint'],
@ -234,6 +228,13 @@ module.exports = {
'no-console': 'off', 'no-console': 'off',
}, },
}, },
{
files: ['packages/client/src/methods/**/*.ts'],
rules: {
// this + max 3 more
'max-params': ['error', 4],
},
},
], ],
settings: { settings: {
'import/resolver': { 'import/resolver': {

File diff suppressed because it is too large Load diff

View file

@ -78,7 +78,6 @@ import {
StoryViewer, StoryViewer,
StoryViewersList, StoryViewersList,
TakeoutSession, TakeoutSession,
TermsOfService,
TypingStatus, TypingStatus,
UploadedFile, UploadedFile,
UploadFileLike, UploadFileLike,

View file

@ -1,5 +1,9 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
import { MtUnsupportedError, tl } from '@mtcute/core'
import { assertTypeIs } from '@mtcute/core/utils'
import { TelegramClient } from '../../client' import { TelegramClient } from '../../client'
import { User } from '../../types'
// @extension // @extension
interface AuthState { interface AuthState {
@ -20,3 +24,35 @@ function _initializeAuthState(this: TelegramClient) {
this._selfUsername = null this._selfUsername = null
this.log.prefix = '[USER N/A] ' this.log.prefix = '[USER N/A] '
} }
/** @internal */
export async function _onAuthorization(
this: TelegramClient,
auth: tl.auth.TypeAuthorization,
bot = false,
): Promise<User> {
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')
this._userId = auth.user.id
this.log.prefix = `[USER ${this._userId}] `
this._isBot = bot
this._selfUsername = auth.user.username!
this._selfChanged = true
await this.network.notifyLoggedIn(auth)
await this._fetchUpdatesState()
await this._saveStorage()
// telegram ignores invokeWithoutUpdates for auth methods
if (this.network.params.disableUpdates) this.network.resetSessions()
else this.startUpdatesLoop()
return new User(this, auth.user)
}

View file

@ -1,25 +0,0 @@
import { MtTypeAssertionError } from '@mtcute/core'
import { TelegramClient } from '../../client'
/**
* Accept the given TOS
*
* @param tosId TOS id
* @internal
*/
export async function acceptTos(this: TelegramClient, tosId: string): Promise<boolean> {
const res = await this.call({
_: 'help.acceptTermsOfService',
id: {
_: 'dataJSON',
data: tosId,
},
})
if (!res) {
throw new MtTypeAssertionError('help.acceptTermsOfService', 'true', 'false')
}
return true
}

View file

@ -1,4 +1,4 @@
import { assertTypeIs, computeSrpParams } from '@mtcute/core/utils' import { computeSrpParams } from '@mtcute/core/utils'
import { TelegramClient } from '../../client' import { TelegramClient } from '../../client'
import { User } from '../../types' import { User } from '../../types'
@ -23,22 +23,5 @@ export async function checkPassword(this: TelegramClient, password: string): Pro
), ),
}) })
assertTypeIs('checkPassword (@ auth.checkPassword)', res, 'auth.authorization') return this._onAuthorization(res)
assertTypeIs('checkPassword (@ auth.checkPassword -> user)', res.user, 'user')
this._userId = res.user.id
this.log.prefix = `[USER ${this._userId}] `
this._isBot = false
this._selfChanged = true
this._selfUsername = res.user.username ?? null
await this.network.notifyLoggedIn(res)
await this._fetchUpdatesState()
await this._saveStorage()
// telegram ignores invokeWithoutUpdates for auth methods
if (this.network.params.disableUpdates) this.network.resetSessions()
else this.startUpdatesLoop()
return new User(this, res.user)
} }

View file

@ -1,29 +1,26 @@
import { assertTypeIs } from '@mtcute/core/utils'
import { TelegramClient } from '../../client' import { TelegramClient } from '../../client'
import { User } from '../../types' import { User } from '../../types'
/** /**
* Recover your password with a recovery code and log in. * Recover your password with a recovery code and log in.
* *
* @param recoveryCode The recovery code sent via email
* @returns The authorized user * @returns The authorized user
* @throws BadRequestError In case the code is invalid * @throws BadRequestError In case the code is invalid
* @internal * @internal
*/ */
export async function recoverPassword(this: TelegramClient, recoveryCode: string): Promise<User> { export async function recoverPassword(
this: TelegramClient,
params: {
/** The recovery code sent via email */
recoveryCode: string
},
): Promise<User> {
const { recoveryCode } = params
const res = await this.call({ const res = await this.call({
_: 'auth.recoverPassword', _: 'auth.recoverPassword',
code: recoveryCode, code: recoveryCode,
}) })
assertTypeIs('recoverPassword (@ auth.recoverPassword)', res, 'auth.authorization') return this._onAuthorization(res)
assertTypeIs('recoverPassword (@ auth.recoverPassword -> user)', res.user, 'user')
this._userId = res.user.id
this._isBot = false
this._selfChanged = true
await this._saveStorage()
return new User(this, res.user)
} }

View file

@ -10,16 +10,23 @@ import { normalizePhoneNumber } from '../../utils/misc-utils'
* The type of the code to be re-sent is specified in the `nextType` attribute of * The type of the code to be re-sent is specified in the `nextType` attribute of
* {@link SentCode} object returned by {@link sendCode} * {@link SentCode} object returned by {@link sendCode}
* *
* @param phone Phone number in international format
* @param phoneCodeHash Confirmation code identifier from {@link SentCode}
* @internal * @internal
*/ */
export async function resendCode(this: TelegramClient, phone: string, phoneCodeHash: string): Promise<SentCode> { export async function resendCode(
phone = normalizePhoneNumber(phone) this: TelegramClient,
params: {
/** Phone number in international format */
phone: string
/** Confirmation code identifier from {@link SentCode} */
phoneCodeHash: string
},
): Promise<SentCode> {
const { phone, phoneCodeHash } = params
const res = await this.call({ const res = await this.call({
_: 'auth.resendCode', _: 'auth.resendCode',
phoneNumber: phone, phoneNumber: normalizePhoneNumber(phone),
phoneCodeHash, phoneCodeHash,
}) })

View file

@ -7,12 +7,17 @@ import { normalizePhoneNumber } from '../../utils/misc-utils'
/** /**
* Send the confirmation code to the given phone number * Send the confirmation code to the given phone number
* *
* @param phone Phone number in international format.
* @returns An object containing information about the sent confirmation code * @returns An object containing information about the sent confirmation code
* @internal * @internal
*/ */
export async function sendCode(this: TelegramClient, phone: string): Promise<SentCode> { export async function sendCode(
phone = normalizePhoneNumber(phone) this: TelegramClient,
params: {
/** Phone number in international format */
phone: string
},
): Promise<SentCode> {
const phone = normalizePhoneNumber(params.phone)
const res = await this.call({ const res = await this.call({
_: 'auth.sendCode', _: 'auth.sendCode',

View file

@ -1,5 +1,3 @@
import { assertTypeIs } from '@mtcute/core/utils'
import { TelegramClient } from '../../client' import { TelegramClient } from '../../client'
import { User } from '../../types' import { User } from '../../types'
@ -20,23 +18,5 @@ export async function signInBot(this: TelegramClient, token: string): Promise<Us
botAuthToken: token, botAuthToken: token,
}) })
assertTypeIs('signInBot (@ auth.importBotAuthorization)', res, 'auth.authorization') return this._onAuthorization(res, true)
assertTypeIs('signInBot (@ auth.importBotAuthorization -> user)', res.user, 'user')
this._userId = res.user.id
this.log.prefix = `[USER ${this._userId}] `
this._isBot = true
this._selfUsername = res.user.username!
this._selfChanged = true
await this.network.notifyLoggedIn(res)
await this._fetchUpdatesState()
await this._saveStorage()
// telegram ignores invokeWithoutUpdates for auth methods
if (this.network.params.disableUpdates) this.network.resetSessions()
else this.startUpdatesLoop()
return new User(this, res.user)
} }

View file

@ -1,60 +1,34 @@
import { assertTypeIs } from '@mtcute/core/utils'
import { TelegramClient } from '../../client' import { TelegramClient } from '../../client'
import { TermsOfService, User } from '../../types' import { User } from '../../types'
import { normalizePhoneNumber } from '../../utils/misc-utils' import { normalizePhoneNumber } from '../../utils/misc-utils'
/** /**
* Authorize a user in Telegram with a valid confirmation code. * Authorize a user in Telegram with a valid confirmation code.
* *
* @param phone Phone number in international format * @returns If the code was valid and authorization succeeded, the {@link User} is returned.
* @param phoneCodeHash Code identifier from {@link TelegramClient.sendCode} * @throws BadRequestError In case the arguments are invalid
* @param phoneCode The confirmation code that was received * @throws SessionPasswordNeededError In case a password is needed to sign in
* @returns
* - If the code was valid and authorization succeeded, the {@link User} is returned.
* - If the given phone number needs to be registered AND the ToS must be accepted,
* an object containing them is returned.
* - If the given phone number needs to be registered, `false` is returned.
* @throws BadRequestError In case the arguments are invalid
* @throws SessionPasswordNeededError In case a password is needed to sign in
* @internal * @internal
*/ */
export async function signIn( export async function signIn(
this: TelegramClient, this: TelegramClient,
phone: string, params: {
phoneCodeHash: string, /** Phone number in international format */
phoneCode: string, phone: string
): Promise<User | TermsOfService | false> { /** Code identifier from {@link sendCode} */
phone = normalizePhoneNumber(phone) phoneCodeHash: string
/** The confirmation code that was received */
phoneCode: string
},
): Promise<User> {
const { phone, phoneCodeHash, phoneCode } = params
const res = await this.call({ const res = await this.call({
_: 'auth.signIn', _: 'auth.signIn',
phoneNumber: phone, phoneNumber: normalizePhoneNumber(phone),
phoneCodeHash, phoneCodeHash,
phoneCode, phoneCode,
}) })
if (res._ === 'auth.authorizationSignUpRequired') { return this._onAuthorization(res)
if (res.termsOfService) return new TermsOfService(res.termsOfService)
return false
}
assertTypeIs('signIn (@ auth.signIn -> user)', res.user, 'user')
this._userId = res.user.id
this.log.prefix = `[USER ${this._userId}] `
this._isBot = false
this._selfChanged = true
this._selfUsername = res.user.username ?? null
await this.network.notifyLoggedIn(res)
await this._fetchUpdatesState()
await this._saveStorage()
// telegram ignores invokeWithoutUpdates for auth methods
if (this.network.params.disableUpdates) this.network.resetSessions()
else this.startUpdatesLoop()
return new User(this, res.user)
} }

View file

@ -1,51 +0,0 @@
import { assertTypeIs } from '@mtcute/core/utils'
import { TelegramClient } from '../../client'
import { User } from '../../types'
import { normalizePhoneNumber } from '../../utils/misc-utils'
/**
* Register a new user in Telegram.
*
* @param phone Phone number in international format
* @param phoneCodeHash Code identifier from {@link TelegramClient.sendCode}
* @param firstName New user's first name
* @param lastName New user's last name
* @internal
*/
export async function signUp(
this: TelegramClient,
phone: string,
phoneCodeHash: string,
firstName: string,
lastName = '',
): Promise<User> {
phone = normalizePhoneNumber(phone)
const res = await this.call({
_: 'auth.signUp',
phoneNumber: phone,
phoneCodeHash,
firstName,
lastName,
})
assertTypeIs('signUp (@ auth.signUp)', res, 'auth.authorization')
assertTypeIs('signUp (@ auth.signUp -> user)', res.user, 'user')
this._userId = res.user.id
this.log.prefix = `[USER ${this._userId}] `
this._isBot = false
this._selfChanged = true
await this.network.notifyLoggedIn(res)
await this._fetchUpdatesState()
await this._saveStorage()
// telegram ignores invokeWithoutUpdates for auth methods
if (this.network.params.disableUpdates) this.network.resetSessions()
else this.startUpdatesLoop()
return new User(this, res.user)
}

View file

@ -1,7 +1,7 @@
import { MtArgumentError } from '@mtcute/core' import { MtArgumentError } from '@mtcute/core'
import { TelegramClient } from '../../client' import { TelegramClient } from '../../client'
import { MaybeDynamic, User } from '../../types' import { User } from '../../types'
/** /**
* Utility function to quickly authorize on test DC * Utility function to quickly authorize on test DC
@ -35,25 +35,6 @@ export async function startTest(
* Override user's DC. Must be a valid test DC. * Override user's DC. Must be a valid test DC.
*/ */
dcId?: number dcId?: number
/**
* First name of the user (used only for sign-up, defaults to 'User')
*/
firstName?: MaybeDynamic<string>
/**
* Last name of the user (used only for sign-up, defaults to empty)
*/
lastName?: MaybeDynamic<string>
/**
* By using this method to sign up an account, you are agreeing to Telegram
* ToS. This is required and your account will be banned otherwise.
* See https://telegram.org/tos and https://core.telegram.org/api/terms.
*
* If true, TOS will not be displayed and `tosCallback` will not be called.
*/
acceptTos?: boolean
}, },
): Promise<User> { ): Promise<User> {
if (!params) params = {} if (!params) params = {}
@ -100,9 +81,6 @@ export async function startTest(
return this.start({ return this.start({
phone, phone,
code: () => code, code: () => code,
firstName: params.firstName,
lastName: params.lastName,
acceptTos: params.acceptTos,
codeSentCallback: (sent) => { codeSentCallback: (sent) => {
for (let i = 0; i < sent.length; i++) { for (let i = 0; i < sent.length; i++) {
code += phone![5] code += phone![5]

View file

@ -2,7 +2,7 @@
import { MaybeAsync, MtArgumentError, tl } from '@mtcute/core' import { MaybeAsync, MtArgumentError, tl } from '@mtcute/core'
import { TelegramClient } from '../../client' import { TelegramClient } from '../../client'
import { MaybeDynamic, SentCode, TermsOfService, User } from '../../types' import { MaybeDynamic, SentCode, User } from '../../types'
import { normalizePhoneNumber, resolveMaybeDynamic } from '../../utils/misc-utils' import { normalizePhoneNumber, resolveMaybeDynamic } from '../../utils/misc-utils'
// @available=both // @available=both
@ -75,31 +75,6 @@ export async function start(
*/ */
forceSms?: boolean forceSms?: boolean
/**
* First name of the user (used only for sign-up, defaults to 'User')
*/
firstName?: MaybeDynamic<string>
/**
* Last name of the user (used only for sign-up, defaults to empty)
*/
lastName?: MaybeDynamic<string>
/**
* By using this method to sign up an account, you are agreeing to Telegram
* ToS. This is required and your account will be banned otherwise.
* See https://telegram.org/tos and https://core.telegram.org/api/terms.
*
* If true, TOS will not be displayed and `tosCallback` will not be called.
*/
acceptTos?: boolean
/**
* Custom method to display ToS. Can be used to show a GUI alert of some kind.
* Defaults to `console.log`
*/
tosCallback?: (tos: TermsOfService) => MaybeAsync<void>
/** /**
* Custom method that is called when a code is sent. Can be used * Custom method that is called when a code is sent. Can be used
* to show a GUI alert of some kind. * to show a GUI alert of some kind.
@ -185,10 +160,10 @@ export async function start(
return await this.signInBot(botToken) return await this.signInBot(botToken)
} }
let sentCode = await this.sendCode(phone) let sentCode = await this.sendCode({ phone })
if (params.forceSms && sentCode.type === 'app') { if (params.forceSms && sentCode.type === 'app') {
sentCode = await this.resendCode(phone, sentCode.phoneCodeHash) sentCode = await this.resendCode({ phone, phoneCodeHash: sentCode.phoneCodeHash })
} }
if (params.codeSentCallback) { if (params.codeSentCallback) {
@ -199,14 +174,12 @@ export async function start(
let has2fa = false let has2fa = false
let result: User | TermsOfService | false
for (;;) { for (;;) {
const code = await resolveMaybeDynamic(params.code) const code = await resolveMaybeDynamic(params.code)
if (!code) throw new tl.RpcError(400, 'PHONE_CODE_EMPTY') if (!code) throw new tl.RpcError(400, 'PHONE_CODE_EMPTY')
try { try {
result = await this.signIn(phone, sentCode.phoneCodeHash, code) return await this.signIn({ phone, phoneCodeHash: sentCode.phoneCodeHash, phoneCode: code })
} catch (e) { } catch (e) {
if (!tl.RpcError.is(e)) throw e if (!tl.RpcError.is(e)) throw e
@ -246,7 +219,7 @@ export async function start(
const password = await resolveMaybeDynamic(params.password) const password = await resolveMaybeDynamic(params.password)
try { try {
result = await this.checkPassword(password) return await this.checkPassword(password)
} catch (e) { } catch (e) {
if (typeof params.password !== 'function') { if (typeof params.password !== 'function') {
throw new MtArgumentError('Provided password was invalid') throw new MtArgumentError('Provided password was invalid')
@ -261,41 +234,8 @@ export async function start(
continue continue
} else throw e } else throw e
} }
break
} }
} }
// to make ts happy throw new MtArgumentError('Failed to log in with provided credentials')
result = result!
if (result instanceof User) {
return result
}
let tosId: string | null = null
if (result instanceof TermsOfService && !params.acceptTos) {
if (params.tosCallback) {
await params.tosCallback(result)
} else {
console.log(result.text)
}
tosId = result.id
}
// signup
result = await this.signUp(
phone,
sentCode.phoneCodeHash,
await resolveMaybeDynamic(params.firstName ?? 'User'),
await resolveMaybeDynamic(params.lastName),
)
if (tosId) {
await this.acceptTos(tosId)
}
return result
} }

View file

@ -2,7 +2,6 @@ import { Long } from '@mtcute/core'
import { TelegramClient } from '../../client' import { TelegramClient } from '../../client'
// @available=bot
/** /**
* Send an answer to a callback query. * Send an answer to a callback query.
* *
@ -18,7 +17,7 @@ export async function answerCallbackQuery(
* Maximum amount of time in seconds for which * Maximum amount of time in seconds for which
* this result can be cached by the client (not server!). * this result can be cached by the client (not server!).
* *
* Defaults to `0` * @default 0
*/ */
cacheTime?: number cacheTime?: number
@ -33,7 +32,7 @@ export async function answerCallbackQuery(
* Whether to show an alert in the middle of the screen * Whether to show an alert in the middle of the screen
* instead of a notification at the top of the screen. * instead of a notification at the top of the screen.
* *
* Defaults to `false`. * @default false
*/ */
alert?: boolean alert?: boolean
@ -49,14 +48,14 @@ export async function answerCallbackQuery(
url?: string url?: string
}, },
): Promise<void> { ): Promise<void> {
if (!params) params = {} const { cacheTime = 0, text, alert, url } = params ?? {}
await this.call({ await this.call({
_: 'messages.setBotCallbackAnswer', _: 'messages.setBotCallbackAnswer',
queryId, queryId,
cacheTime: params.cacheTime ?? 0, cacheTime,
alert: params.alert, alert,
message: params.text, message: text,
url: params.url, url,
}) })
} }

View file

@ -20,7 +20,7 @@ export async function answerInlineQuery(
* Maximum number of time in seconds that the results of the * Maximum number of time in seconds that the results of the
* query may be cached on the server for. * query may be cached on the server for.
* *
* Defaults to `300` * @default 300
*/ */
cacheTime?: number cacheTime?: number
@ -39,7 +39,7 @@ export async function answerInlineQuery(
* Whether the results should only be cached on the server * Whether the results should only be cached on the server
* for the user who sent the query. * for the user who sent the query.
* *
* Defaults to `false` * @default false
*/ */
private?: boolean private?: boolean
@ -47,7 +47,7 @@ export async function answerInlineQuery(
* Next pagination offset (up to 64 bytes). * Next pagination offset (up to 64 bytes).
* *
* When user has reached the end of the current results, * When user has reached the end of the current results,
* it will re-send the inline query with the same text, but * the client will re-send the inline query with the same text, but
* with `offset` set to this value. * with `offset` set to this value.
* *
* If omitted or empty string is provided, it is assumed that * If omitted or empty string is provided, it is assumed that
@ -95,23 +95,23 @@ export async function answerInlineQuery(
parseMode?: string | null parseMode?: string | null
}, },
): Promise<void> { ): Promise<void> {
if (!params) params = {} const { cacheTime = 300, gallery, private: priv, nextOffset, switchPm, parseMode } = params ?? {}
const [gallery, tlResults] = await BotInline._convertToTl(this, results, params.parseMode) const [defaultGallery, tlResults] = await BotInline._convertToTl(this, results, parseMode)
await this.call({ await this.call({
_: 'messages.setInlineBotResults', _: 'messages.setInlineBotResults',
queryId, queryId,
results: tlResults, results: tlResults,
cacheTime: params.cacheTime ?? 300, cacheTime,
gallery: params.gallery ?? gallery, gallery: gallery ?? defaultGallery,
private: params.private, private: priv,
nextOffset: params.nextOffset, nextOffset,
switchPm: params.switchPm ? switchPm: switchPm ?
{ {
_: 'inlineBotSwitchPM', _: 'inlineBotSwitchPM',
text: params.switchPm.text, text: switchPm.text,
startParam: params.switchPm.parameter, startParam: switchPm.parameter,
} : } :
undefined, undefined,
}) })

View file

@ -6,10 +6,18 @@ import { TelegramClient } from '../../client'
* Answer a pre-checkout query. * Answer a pre-checkout query.
* *
* @param queryId Pre-checkout query ID * @param queryId Pre-checkout query ID
* @param error If pre-checkout is rejected, error message to show to the user
* @internal * @internal
*/ */
export async function answerPreCheckoutQuery(this: TelegramClient, queryId: tl.Long, error?: string): Promise<void> { export async function answerPreCheckoutQuery(
this: TelegramClient,
queryId: tl.Long,
params?: {
/** If pre-checkout is rejected, error message to show to the user */
error?: string
},
): Promise<void> {
const { error } = params ?? {}
await this.call({ await this.call({
_: 'messages.setBotPrecheckoutResults', _: 'messages.setBotPrecheckoutResults',
queryId, queryId,

View file

@ -8,18 +8,21 @@ import { InputPeerLike } from '../../types'
* Request a callback answer from a bot, * Request a callback answer from a bot,
* i.e. click an inline button that contains data. * i.e. click an inline button that contains data.
* *
* @param chatId Chat ID where the message was found
* @param message ID of the message containing the button
* @param data Data contained in the button
* @param params * @param params
* @internal * @internal
*/ */
export async function getCallbackAnswer( export async function getCallbackAnswer(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, params: {
message: number, /** Chat ID where the message was found */
data: string | Buffer, chatId: InputPeerLike
params?: {
/** ID of the message containing the button */
message: number
/** Data contained in the button */
data: string | Buffer
/** /**
* Timeout for the query in ms. * Timeout for the query in ms.
* *
@ -33,15 +36,16 @@ export async function getCallbackAnswer(
game?: boolean game?: boolean
/** /**
* If the button requires password entry, * If the button requires password entry, your 2FA password.
* your 2FA password.
* *
* Your password is never exposed to the * Your password is never exposed to the bot,
* bot, it is checked by Telegram. * it is checked by Telegram.
*/ */
password?: string password?: string
}, },
): Promise<tl.messages.TypeBotCallbackAnswer> { ): Promise<tl.messages.TypeBotCallbackAnswer> {
const { chatId, message, data, game, timeout = 10000 } = params
let password: tl.TypeInputCheckPasswordSRP | undefined = undefined let password: tl.TypeInputCheckPasswordSRP | undefined = undefined
if (params?.password) { if (params?.password) {
@ -56,8 +60,8 @@ export async function getCallbackAnswer(
msgId: message, msgId: message,
data: typeof data === 'string' ? Buffer.from(data) : data, data: typeof data === 'string' ? Buffer.from(data) : data,
password, password,
game: params?.game, game: game,
}, },
{ timeout: params?.timeout ?? 10000 }, { timeout },
) )
} }

View file

@ -8,17 +8,23 @@ import { normalizeToInputUser } from '../../utils/peer-utils'
/** /**
* Get high scores of a game * Get high scores of a game
* *
* @param chatId ID of the chat where the game was found
* @param message ID of the message containing the game
* @param userId ID of the user to find high scores for
* @internal * @internal
*/ */
export async function getGameHighScores( export async function getGameHighScores(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, params: {
message: number, /** ID of the chat where the game was found */
userId?: InputPeerLike, chatId: InputPeerLike
/** ID of the message containing the game */
message: number
/** ID of the user to find high scores for */
userId?: InputPeerLike
},
): Promise<GameHighScore[]> { ): Promise<GameHighScore[]> {
const { chatId, message, userId } = params
const chat = await this.resolvePeer(chatId) const chat = await this.resolvePeer(chatId)
let user: tl.TypeInputUser let user: tl.TypeInputUser

View file

@ -17,12 +17,16 @@ export async function setGameScore(
params: { params: {
/** Chat where the game was found */ /** Chat where the game was found */
chatId: InputPeerLike chatId: InputPeerLike
/** ID of the message where the game was found */ /** ID of the message where the game was found */
message: number message: number
/** ID of the user who has scored */ /** ID of the user who has scored */
userId: InputPeerLike userId: InputPeerLike
/** The new score (must be >0) */ /** The new score (must be >0) */
score: number score: number
/** /**
* When `true`, the game message will not be modified * When `true`, the game message will not be modified
* to include the new score * to include the new score

View file

@ -5,15 +5,19 @@ import { TelegramClient } from '../../client'
/** /**
* Sets the default chat permissions for the bot in the supergroup or channel. * Sets the default chat permissions for the bot in the supergroup or channel.
* *
* @param target Whether to target groups or channels.
* @param rights The default chat permissions.
* @internal * @internal
*/ */
export async function setMyDefaultRights( export async function setMyDefaultRights(
this: TelegramClient, this: TelegramClient,
target: 'channel' | 'group', params: {
rights: Omit<tl.RawChatAdminRights, '_'>, /** Whether to target groups or channels. */
target: 'channel' | 'group'
/** The default chat permissions. */
rights: Omit<tl.RawChatAdminRights, '_'>
},
): Promise<void> { ): Promise<void> {
const { target, rights } = params
await this.call({ await this.call({
_: target === 'group' ? 'bots.setBotGroupDefaultAdminRights' : 'bots.setBotBroadcastDefaultAdminRights', _: target === 'group' ? 'bots.setBotGroupDefaultAdminRights' : 'bots.setBotBroadcastDefaultAdminRights',
adminRights: { adminRights: {

View file

@ -10,21 +10,28 @@ import {
} from '../../utils/peer-utils' } from '../../utils/peer-utils'
/** /**
* Add new members to a group, supergroup or channel. * Add one or more new members to a group, supergroup or channel.
* *
* @param chatId ID of the chat or its username * @param chatId ID of the chat or its username
* @param users ID(s) of the users, their username(s) or phone(s). * @param users ID(s) of the user(s) to add
* @param forwardCount
* Number of old messages to be forwarded (0-100).
* Only applicable to legacy groups, ignored for supergroups and channels
* @internal * @internal
*/ */
export async function addChatMembers( export async function addChatMembers(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, chatId: InputPeerLike,
users: MaybeArray<InputPeerLike>, users: MaybeArray<InputPeerLike>,
forwardCount = 100, params: {
/**
* Number of old messages to be forwarded (0-100).
* Only applicable to legacy groups, ignored for supergroups and channels
*
* @default 100
*/
forwardCount?: number
},
): Promise<void> { ): Promise<void> {
const { forwardCount = 100 } = params
const chat = await this.resolvePeer(chatId) const chat = await this.resolvePeer(chatId)
if (!Array.isArray(users)) users = [users] if (!Array.isArray(users)) users = [users]

View file

@ -1,4 +1,4 @@
import { MaybeArray, tl } from '@mtcute/core' import { MaybeArray } from '@mtcute/core'
import { TelegramClient } from '../../client' import { TelegramClient } from '../../client'
import { InputPeerLike } from '../../types' import { InputPeerLike } from '../../types'
@ -12,19 +12,15 @@ import { InputPeerLike } from '../../types'
export async function archiveChats(this: TelegramClient, chats: MaybeArray<InputPeerLike>): Promise<void> { export async function archiveChats(this: TelegramClient, chats: MaybeArray<InputPeerLike>): Promise<void> {
if (!Array.isArray(chats)) chats = [chats] if (!Array.isArray(chats)) chats = [chats]
const folderPeers: tl.TypeInputFolderPeer[] = [] const resolvedPeers = await this.resolvePeerMany(chats)
for (const chat of chats) {
folderPeers.push({
_: 'inputFolderPeer',
peer: await this.resolvePeer(chat),
folderId: 1,
})
}
const updates = await this.call({ const updates = await this.call({
_: 'folders.editPeerFolders', _: 'folders.editPeerFolders',
folderPeers, folderPeers: resolvedPeers.map((peer) => ({
_: 'inputFolderPeer',
peer,
folderId: 1,
})),
}) })
this._handleUpdate(updates) this._handleUpdate(updates)
} }

View file

@ -17,18 +17,20 @@ import {
* When banning a channel, the user won't be able to use * When banning a channel, the user won't be able to use
* any of their channels to post until the ban is lifted. * any of their channels to post until the ban is lifted.
* *
* @param chatId Chat ID
* @param peerId User/Channel ID
* @returns Service message about removed user, if one was generated. * @returns Service message about removed user, if one was generated.
* @internal * @internal
*/ */
export async function banChatMember( export async function banChatMember(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, params: {
peerId: InputPeerLike, /** Chat ID */
chatId: InputPeerLike
/** ID of the user/channel to ban */
participantId: InputPeerLike
},
): Promise<Message | null> { ): Promise<Message | null> {
const chat = await this.resolvePeer(chatId) const chat = await this.resolvePeer(params.chatId)
const peer = await this.resolvePeer(peerId) const peer = await this.resolvePeer(params.participantId)
let res let res
if (isInputPeerChannel(chat)) { if (isInputPeerChannel(chat)) {
@ -49,7 +51,7 @@ export async function banChatMember(
chatId: chat.chatId, chatId: chat.chatId,
userId: normalizeToInputUser(peer), userId: normalizeToInputUser(peer),
}) })
} else throw new MtInvalidPeerTypeError(chatId, 'chat or channel') } else throw new MtInvalidPeerTypeError(params.chatId, 'chat or channel')
try { try {
return this._findMessageInUpdate(res) return this._findMessageInUpdate(res)

View file

@ -7,23 +7,34 @@ import { createDummyUpdate } from '../../utils/updates-utils'
* Delete communication history (for private chats * Delete communication history (for private chats
* and legacy groups) * and legacy groups)
* *
* @param chat Chat or user ID, username, phone number, `"me"` or `"self"`
* @param mode
* Deletion mode. Can be:
* - `delete`: delete messages (only for yourself)
* - `clear`: delete messages (only for yourself)
* - `revoke`: delete messages for all users
* - I'm not sure what's the difference between `delete` and `clear`,
* but they are in fact different flags in TL object.
* @param maxId Maximum ID of message to delete. Defaults to 0 (remove all messages)
* @internal * @internal
*/ */
export async function deleteHistory( export async function deleteHistory(
this: TelegramClient, this: TelegramClient,
chat: InputPeerLike, chat: InputPeerLike,
mode: 'delete' | 'clear' | 'revoke' = 'delete', params?: {
maxId = 0, /**
* Deletion mode. Can be:
* - `delete`: delete messages (only for yourself)
* - `clear`: delete messages (only for yourself)
* - `revoke`: delete messages for all users
* - I'm not sure what's the difference between `delete` and `clear`,
* but they are in fact different flags in TL object.
*
* @default 'delete'
*/
mode: 'delete' | 'clear' | 'revoke'
/**
* Maximum ID of message to delete.
*
* @default 0, i.e. remove all messages
*/
maxId?: number
},
): Promise<void> { ): Promise<void> {
const { mode = 'delete', maxId = 0 } = params ?? {}
const peer = await this.resolvePeer(chat) const peer = await this.resolvePeer(chat)
const res = await this.call({ const res = await this.call({

View file

@ -8,15 +8,19 @@ import { createDummyUpdate } from '../../utils/updates-utils'
/** /**
* Delete all messages of a user (or channel) in a supergroup * Delete all messages of a user (or channel) in a supergroup
* *
* @param chatId Chat ID
* @param participantId User/channel ID
* @internal * @internal
*/ */
export async function deleteUserHistory( export async function deleteUserHistory(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, params: {
participantId: InputPeerLike, /** Chat ID */
chatId: InputPeerLike
/** User/channel ID whose messages to delete */
participantId: InputPeerLike
},
): Promise<void> { ): Promise<void> {
const { chatId, participantId } = params
const channel = normalizeToInputChannel(await this.resolvePeer(chatId), chatId) const channel = normalizeToInputChannel(await this.resolvePeer(chatId), chatId)
const peer = await this.resolvePeer(participantId) const peer = await this.resolvePeer(participantId)

View file

@ -7,19 +7,23 @@ import { normalizeToInputChannel, normalizeToInputUser } from '../../utils/peer-
/** /**
* Edit supergroup/channel admin rights of a user. * Edit supergroup/channel admin rights of a user.
* *
* @param chatId Chat ID
* @param userId User ID
* @param rights New admin rights
* @param rank Custom admin rank
* @internal * @internal
*/ */
export async function editAdminRights( export async function editAdminRights(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, params: {
userId: InputPeerLike, /** Chat ID */
rights: Omit<tl.RawChatAdminRights, '_'>, chatId: InputPeerLike
rank = '', /** User ID */
userId: InputPeerLike
/** New admin rights */
rights: Omit<tl.RawChatAdminRights, '_'>
/** Custom admin rank */
rank?: string
},
): Promise<void> { ): Promise<void> {
const { chatId, userId, rights, rank = '' } = params
const chat = normalizeToInputChannel(await this.resolvePeer(chatId), chatId) const chat = normalizeToInputChannel(await this.resolvePeer(chatId), chatId)
const user = normalizeToInputUser(await this.resolvePeer(userId), userId) const user = normalizeToInputUser(await this.resolvePeer(userId), userId)

View file

@ -16,7 +16,6 @@ import { normalizeToInputChannel, normalizeToInputUser } from '../../utils/peer-
* in direct chronological order (i.e. newer * in direct chronological order (i.e. newer
* events have bigger event ID) * events have bigger event ID)
* *
* @param chatId Chat ID
* @param params * @param params
* @internal * @internal
*/ */
@ -73,12 +72,10 @@ export async function getChatEventLog(
limit?: number limit?: number
}, },
): Promise<ChatEvent[]> { ): Promise<ChatEvent[]> {
if (!params) params = {} const { maxId = Long.ZERO, minId = Long.ZERO, query = '', limit = 100, users, filters } = params ?? {}
const channel = normalizeToInputChannel(await this.resolvePeer(chatId), chatId) const channel = normalizeToInputChannel(await this.resolvePeer(chatId), chatId)
const { maxId = Long.ZERO, minId = Long.ZERO, query = '', limit = 100, users, filters } = params
const admins: tl.TypeInputUser[] | undefined = users ? const admins: tl.TypeInputUser[] | undefined = users ?
await this.resolvePeerMany(users, normalizeToInputUser) : await this.resolvePeerMany(users, normalizeToInputUser) :
undefined undefined

View file

@ -15,9 +15,15 @@ import { isInputPeerChannel, isInputPeerChat, isInputPeerUser, normalizeToInputC
*/ */
export async function getChatMember( export async function getChatMember(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, params: {
userId: InputPeerLike, /** Chat ID or username */
chatId: InputPeerLike
/** User ID, username, phone number, `"me"` or `"self"` */
userId: InputPeerLike
},
): Promise<ChatMember> { ): Promise<ChatMember> {
const { chatId, userId } = params
const user = await this.resolvePeer(userId) const user = await this.resolvePeer(userId)
const chat = await this.resolvePeer(chatId) const chat = await this.resolvePeer(chatId)

View file

@ -60,7 +60,7 @@ export async function getChatMembers(
type?: 'all' | 'banned' | 'restricted' | 'bots' | 'recent' | 'admins' | 'contacts' | 'mention' type?: 'all' | 'banned' | 'restricted' | 'bots' | 'recent' | 'admins' | 'contacts' | 'mention'
}, },
): Promise<ArrayWithTotal<ChatMember>> { ): Promise<ArrayWithTotal<ChatMember>> {
if (!params) params = {} const { query = '', offset = 0, limit = 200, type = 'recent' } = params ?? {}
const chat = await this.resolvePeer(chatId) const chat = await this.resolvePeer(chatId)
@ -75,21 +75,18 @@ export async function getChatMembers(
let members = let members =
res.fullChat.participants._ === 'chatParticipantsForbidden' ? [] : res.fullChat.participants.participants res.fullChat.participants._ === 'chatParticipantsForbidden' ? [] : res.fullChat.participants.participants
if (params.offset) members = members.slice(params.offset) if (offset) members = members.slice(offset)
if (params.limit) members = members.slice(0, params.limit) if (limit) members = members.slice(0, limit)
const peers = PeersIndex.from(res) const peers = PeersIndex.from(res)
const ret = members.map((m) => new ChatMember(this, m, peers)) as ArrayWithTotal<ChatMember> const ret = members.map((m) => new ChatMember(this, m, peers))
ret.total = ret.length return makeArrayWithTotal(ret, ret.length)
return ret
} }
if (isInputPeerChannel(chat)) { if (isInputPeerChannel(chat)) {
const q = params.query?.toLowerCase() ?? '' const q = query
const type = params.type ?? 'recent'
let filter: tl.TypeChannelParticipantsFilter let filter: tl.TypeChannelParticipantsFilter
@ -126,8 +123,8 @@ export async function getChatMembers(
_: 'channels.getParticipants', _: 'channels.getParticipants',
channel: normalizeToInputChannel(chat), channel: normalizeToInputChannel(chat),
filter, filter,
offset: params.offset ?? 0, offset,
limit: params.limit ?? 200, limit,
hash: Long.ZERO, hash: Long.ZERO,
}) })

View file

@ -9,7 +9,7 @@ import { INVITE_LINK_REGEX } from '../../utils/peer-utils'
* *
* @param inviteLink Invite link * @param inviteLink Invite link
* @throws MtArgumentError In case invite link has invalid format * @throws MtArgumentError In case invite link has invalid format
* @throws MtNotFoundError * @throws MtPeerNotFoundError
* In case you are trying to get info about private chat that you have already joined. * In case you are trying to get info about private chat that you have already joined.
* Use {@link getChat} or {@link getFullChat} instead. * Use {@link getChat} or {@link getFullChat} instead.
* @internal * @internal

View file

@ -9,24 +9,28 @@ import { isInputPeerChannel } from '../../utils/peer-utils'
* *
* This effectively bans a user and immediately unbans them. * This effectively bans a user and immediately unbans them.
* *
* @param chatId Chat ID
* @param userId User ID
* @internal * @internal
*/ */
export async function kickChatMember( export async function kickChatMember(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, params: {
userId: InputPeerLike, /** Chat ID */
chatId: InputPeerLike
/** User ID */
userId: InputPeerLike
},
): Promise<void> { ): Promise<void> {
const { chatId, userId } = params
const chat = await this.resolvePeer(chatId) const chat = await this.resolvePeer(chatId)
const user = await this.resolvePeer(userId) const user = await this.resolvePeer(userId)
await this.banChatMember(chat, user) await this.banChatMember({ chatId: chat, participantId: user })
// not needed in case this is a legacy group // not needed in case this is a legacy group
if (isInputPeerChannel(chat)) { if (isInputPeerChannel(chat)) {
// i fucking love telegram serverside race conditions // i fucking love telegram serverside race conditions
await sleep(1000) await sleep(1000)
await this.unbanChatMember(chat, user) await this.unbanChatMember({ chatId: chat, participantId: user })
} }
} }

View file

@ -6,10 +6,18 @@ import { isInputPeerChannel, isInputPeerChat, normalizeToInputChannel } from '..
* Leave a group chat, supergroup or channel * Leave a group chat, supergroup or channel
* *
* @param chatId Chat ID or username * @param chatId Chat ID or username
* @param clear Whether to clear history after leaving (only for legacy group chats)
* @internal * @internal
*/ */
export async function leaveChat(this: TelegramClient, chatId: InputPeerLike, clear = false): Promise<void> { export async function leaveChat(
this: TelegramClient,
chatId: InputPeerLike,
params?: {
/**
* Whether to clear history after leaving (only for legacy group chats)
*/
clear?: boolean
},
): Promise<void> {
const chat = await this.resolvePeer(chatId) const chat = await this.resolvePeer(chatId)
if (isInputPeerChannel(chat)) { if (isInputPeerChannel(chat)) {
@ -26,7 +34,7 @@ export async function leaveChat(this: TelegramClient, chatId: InputPeerLike, cle
}) })
this._handleUpdate(res) this._handleUpdate(res)
if (clear) { if (params?.clear) {
await this.deleteHistory(chat) await this.deleteHistory(chat)
} }
} else throw new MtInvalidPeerTypeError(chatId, 'chat or channel') } else throw new MtInvalidPeerTypeError(chatId, 'chat or channel')

View file

@ -8,27 +8,38 @@ import { isInputPeerChannel, normalizeToInputChannel } from '../../utils/peer-ut
/** /**
* Restrict a user in a supergroup. * Restrict a user in a supergroup.
* *
* @param chatId Chat ID
* @param userId User ID
* @param restrictions
* Restrictions for the user. Note that unlike Bot API, this object contains
* the restrictions, and not the permissions, i.e. to
* passing `sendMessages=true` will disallow the user to send messages,
* and passing `{}` (empty object) will lift any restrictions
* @param until
* Date when the user will be unrestricted.
* When `number` is passed, UNIX time in ms is expected.
* If this value is less than 30 seconds or more than 366 days in
* the future, user will be restricted forever. Defaults to `0` (forever)
* @internal * @internal
*/ */
export async function restrictChatMember( export async function restrictChatMember(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, params: {
userId: InputPeerLike, /** Chat ID */
restrictions: Omit<tl.RawChatBannedRights, '_' | 'untilDate'>, chatId: InputPeerLike
until?: number | Date,
/** User ID */
userId: InputPeerLike
/**
* Restrictions for the user. Note that unlike Bot API, this object contains
* the restrictions, and not the permissions, i.e.
* passing `sendMessages=true` will disallow the user to send messages,
* and passing `{}` (empty object) will lift any restrictions
*/
restrictions: Omit<tl.RawChatBannedRights, '_' | 'untilDate'>
/**
* Date when the user will be unrestricted.
* When `number` is passed, UNIX time in ms is expected.
* If this value is less than 30 seconds or more than 366 days in
* the future, user will be restricted forever.
*
* @default `0`, i.e. forever
*/
until?: number | Date
},
): Promise<void> { ): Promise<void> {
const { chatId, userId, restrictions, until = 0 } = params
const chat = await this.resolvePeer(chatId) const chat = await this.resolvePeer(chatId)
if (!isInputPeerChannel(chat)) { if (!isInputPeerChannel(chat)) {

View file

@ -12,7 +12,7 @@ import { assertIsUpdatesGroup } from '../../utils/updates-utils'
* @param chatId Chat ID or username * @param chatId Chat ID or username
* @param restrictions * @param restrictions
* Restrictions for the chat. Note that unlike Bot API, this object contains * Restrictions for the chat. Note that unlike Bot API, this object contains
* the restrictions, and not the permissions, i.e. to * the restrictions, and not the permissions, i.e.
* passing `sendMessages=true` will disallow the users to send messages, * passing `sendMessages=true` will disallow the users to send messages,
* and passing `{}` (empty object) will lift any restrictions * and passing `{}` (empty object) will lift any restrictions
* @internal * @internal

View file

@ -10,21 +10,29 @@ import { isInputPeerChannel, isInputPeerChat, normalizeToInputChannel } from '..
* *
* You must be an administrator and have the appropriate permissions. * You must be an administrator and have the appropriate permissions.
* *
* @param chatId Chat ID or username
* @param type Media type (photo or video)
* @param media Input media file
* @param previewSec
* When `type = video`, timestamp in seconds which will be shown
* as a static preview.
* @internal * @internal
*/ */
export async function setChatPhoto( export async function setChatPhoto(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, params: {
type: 'photo' | 'video', /** Chat ID or username */
media: InputFileLike, chatId: InputPeerLike
previewSec?: number,
/** Media type (photo or video) */
type: 'photo' | 'video'
/** Input media file */
media: InputFileLike
/**
* When `type = video`, timestamp in seconds which will be shown
* as a static preview.
*/
previewSec?: number
},
): Promise<void> { ): Promise<void> {
const { chatId, type, media, previewSec } = params
const chat = await this.resolvePeer(chatId) const chat = await this.resolvePeer(chatId)
if (!(isInputPeerChannel(chat) || isInputPeerChat(chat))) { if (!(isInputPeerChannel(chat) || isInputPeerChat(chat))) {

View file

@ -8,13 +8,14 @@ import { isInputPeerChannel, isInputPeerUser, normalizeToInputChannel, normalize
* > **Note**: non-collectible usernames must still be changed * > **Note**: non-collectible usernames must still be changed
* > using {@link setUsername}/{@link setChatUsername} * > using {@link setUsername}/{@link setChatUsername}
* *
* @param peerId Bot, channel or "me"/"self"
* @internal * @internal
*/ */
export async function toggleFragmentUsername( export async function toggleFragmentUsername(
this: TelegramClient, this: TelegramClient,
peerId: InputPeerLike,
params: { params: {
/** Peer ID whose username to toggle */
peerId: InputPeerLike
/** /**
* Username to toggle * Username to toggle
*/ */
@ -26,7 +27,7 @@ export async function toggleFragmentUsername(
active: boolean active: boolean
}, },
): Promise<void> { ): Promise<void> {
const { username, active } = params const { peerId, username, active } = params
const peer = await this.resolvePeer(peerId) const peer = await this.resolvePeer(peerId)

View file

@ -11,17 +11,21 @@ import { isInputPeerChannel, isInputPeerChat, normalizeToInputChannel } from '..
* *
* This method acts as a no-op in case a legacy group is passed. * This method acts as a no-op in case a legacy group is passed.
* *
* @param chatId Chat ID
* @param peerId User/channel ID
* @internal * @internal
*/ */
export async function unbanChatMember( export async function unbanChatMember(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, params: {
peerId: InputPeerLike, /** Chat ID */
chatId: InputPeerLike
/** User/channel ID who should be unbanned */
participantId: InputPeerLike
},
): Promise<void> { ): Promise<void> {
const { chatId, participantId } = params
const chat = await this.resolvePeer(chatId) const chat = await this.resolvePeer(chatId)
const peer = await this.resolvePeer(peerId) const peer = await this.resolvePeer(participantId)
if (isInputPeerChannel(chat)) { if (isInputPeerChannel(chat)) {
const res = await this.call({ const res = await this.call({

View file

@ -6,14 +6,14 @@ import { assertIsUpdatesGroup } from '../../utils/updates-utils'
/** /**
* Add an existing Telegram user as a contact * Add an existing Telegram user as a contact
* *
* @param userId User ID, username or phone number
* @param params Contact details
* @internal * @internal
*/ */
export async function addContact( export async function addContact(
this: TelegramClient, this: TelegramClient,
userId: InputPeerLike,
params: { params: {
/** User ID, username or phone number */
userId: InputPeerLike
/** /**
* First name of the contact * First name of the contact
*/ */
@ -36,15 +36,16 @@ export async function addContact(
sharePhone?: boolean sharePhone?: boolean
}, },
): Promise<User> { ): Promise<User> {
const { userId, firstName, lastName = '', phone = '', sharePhone = false } = params
const peer = normalizeToInputUser(await this.resolvePeer(userId), userId) const peer = normalizeToInputUser(await this.resolvePeer(userId), userId)
const res = await this.call({ const res = await this.call({
_: 'contacts.addContact', _: 'contacts.addContact',
id: peer, id: peer,
firstName: params.firstName, firstName,
lastName: params.lastName ?? '', lastName,
phone: params.phone ?? '', phone,
addPhonePrivacyException: Boolean(params.sharePhone), addPhonePrivacyException: sharePhone,
}) })
assertIsUpdatesGroup('contacts.addContact', res) assertIsUpdatesGroup('contacts.addContact', res)

View file

@ -5,20 +5,27 @@ import { TelegramClient } from '../../client'
/** /**
* Edit a folder with given modification * Edit a folder with given modification
* *
* @param folder
* Folder, folder ID or name.
* Note that passing an ID or name will require re-fetching all folders,
* and passing name might affect not the right folder if you have multiple
* with the same name.
* @param modification Modification that will be applied to this folder
* @returns Modified folder * @returns Modified folder
* @internal * @internal
*/ */
export async function editFolder( export async function editFolder(
this: TelegramClient, this: TelegramClient,
folder: tl.RawDialogFilter | number | string, params: {
modification: Partial<Omit<tl.RawDialogFilter, 'id' | '_'>>, /**
* Folder, folder ID or name.
* Note that passing an ID or name will require re-fetching all folders,
* and passing name might affect not the right folder if you have multiple
* with the same name.
*/
folder: tl.RawDialogFilter | number | string
/** Modification to be applied to this folder */
modification: Partial<Omit<tl.RawDialogFilter, 'id' | '_'>>
},
): Promise<tl.RawDialogFilter> { ): Promise<tl.RawDialogFilter> {
const { modification } = params
let { folder } = params
if (folder === 0) { if (folder === 0) {
throw new MtArgumentError('Cannot modify default folder') throw new MtArgumentError('Cannot modify default folder')
} }

View file

@ -15,8 +15,11 @@ import { TelegramClient } from '../../client'
export async function findFolder( export async function findFolder(
this: TelegramClient, this: TelegramClient,
params: { params: {
/** Folder title */
title?: string title?: string
/** Folder emoji */
emoji?: string emoji?: string
/** Folder ID */
id?: number id?: number
}, },
): Promise<tl.RawDialogFilter | null> { ): Promise<tl.RawDialogFilter | null> {

View file

@ -3,8 +3,7 @@ import { TelegramClient } from '../../client'
/** /**
* Reorder folders * Reorder folders
* *
* Order is folder's ID (0 = default folder) * @param order New order of folders (folder IDs, where default = 0)
*
* @internal * @internal
*/ */
export async function setFoldersOrder(this: TelegramClient, order: number[]): Promise<void> { export async function setFoldersOrder(this: TelegramClient, order: number[]): Promise<void> {

View file

@ -10,14 +10,15 @@ import { normalizeToInputChannel } from '../../utils/peer-utils'
* *
* Only admins with `manageTopics` permission can do this. * Only admins with `manageTopics` permission can do this.
* *
* @param chatId Chat ID or username
* @returns Service message for the created topic * @returns Service message for the created topic
* @internal * @internal
*/ */
export async function createForumTopic( export async function createForumTopic(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike,
params: { params: {
/** Chat ID or username */
chatId: InputPeerLike
/** /**
* Topic title * Topic title
*/ */
@ -39,7 +40,7 @@ export async function createForumTopic(
sendAs?: InputPeerLike sendAs?: InputPeerLike
}, },
): Promise<Message> { ): Promise<Message> {
const { title, icon, sendAs } = params const { chatId, title, icon, sendAs } = params
const res = await this.call({ const res = await this.call({
_: 'channels.createForumTopic', _: 'channels.createForumTopic',

View file

@ -16,9 +16,12 @@ import { normalizeToInputChannel } from '../../utils/peer-utils'
*/ */
export async function editForumTopic( export async function editForumTopic(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike,
topicId: number,
params: { params: {
/** Chat ID or username */
chatId: InputPeerLike
/** ID of the topic (i.e. its top message ID) */
topicId: number
/** /**
* New topic title * New topic title
*/ */
@ -33,7 +36,7 @@ export async function editForumTopic(
icon?: tl.Long | null icon?: tl.Long | null
}, },
): Promise<Message> { ): Promise<Message> {
const { title, icon } = params const { chatId, topicId, title, icon } = params
const res = await this.call({ const res = await this.call({
_: 'channels.editForumTopic', _: 'channels.editForumTopic',

View file

@ -7,14 +7,14 @@ import { normalizeToInputChannel } from '../../utils/peer-utils'
* *
* Only admins with `manageTopics` permission can do this. * Only admins with `manageTopics` permission can do this.
* *
* @param chatId Chat ID or username
* @param topicId ID of the topic (i.e. its top message ID)
* @internal * @internal
*/ */
export async function reorderPinnedForumTopics( export async function reorderPinnedForumTopics(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike,
params: { params: {
/** Chat ID or username */
chatId: InputPeerLike
/** /**
* Order of the pinned topics * Order of the pinned topics
*/ */
@ -26,7 +26,7 @@ export async function reorderPinnedForumTopics(
force?: boolean force?: boolean
}, },
): Promise<void> { ): Promise<void> {
const { order, force } = params const { chatId, order, force } = params
await this.call({ await this.call({
_: 'channels.reorderPinnedForumTopics', _: 'channels.reorderPinnedForumTopics',
channel: normalizeToInputChannel(await this.resolvePeer(chatId), chatId), channel: normalizeToInputChannel(await this.resolvePeer(chatId), chatId),

View file

@ -7,18 +7,24 @@ import { normalizeToInputChannel } from '../../utils/peer-utils'
* *
* Only admins with `manageTopics` permission can do this. * Only admins with `manageTopics` permission can do this.
* *
* @param chatId Chat ID or username
* @param topicId ID of the topic (i.e. its top message ID)
* @param closed Whether the topic should be closed
* @returns Service message about the modification * @returns Service message about the modification
* @internal * @internal
*/ */
export async function toggleForumTopicClosed( export async function toggleForumTopicClosed(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, parmas: {
topicId: number, /** Chat ID or username */
closed: boolean, chatId: InputPeerLike
/** ID of the topic (i.e. its top message ID) */
topicId: number
/** Whether the topic should be closed */
closed: boolean
},
): Promise<Message> { ): Promise<Message> {
const { chatId, topicId, closed } = parmas
const res = await this.call({ const res = await this.call({
_: 'channels.editForumTopic', _: 'channels.editForumTopic',
channel: normalizeToInputChannel(await this.resolvePeer(chatId), chatId), channel: normalizeToInputChannel(await this.resolvePeer(chatId), chatId),

View file

@ -7,17 +7,21 @@ import { normalizeToInputChannel } from '../../utils/peer-utils'
* *
* Only admins with `manageTopics` permission can do this. * Only admins with `manageTopics` permission can do this.
* *
* @param chatId Chat ID or username
* @param topicId ID of the topic (i.e. its top message ID)
* @param pinned Whether the topic should be pinned
* @internal * @internal
*/ */
export async function toggleForumTopicPinned( export async function toggleForumTopicPinned(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, params: {
topicId: number, /** Chat ID or username */
pinned: boolean, chatId: InputPeerLike
/** ID of the topic (i.e. its top message ID) */
topicId: number
/** Whether the topic should be pinned */
pinned: boolean
},
): Promise<void> { ): Promise<void> {
const { chatId, topicId, pinned } = params
await this.call({ await this.call({
_: 'channels.updatePinnedForumTopic', _: 'channels.updatePinnedForumTopic',
channel: normalizeToInputChannel(await this.resolvePeer(chatId), chatId), channel: normalizeToInputChannel(await this.resolvePeer(chatId), chatId),

View file

@ -16,9 +16,11 @@ import { normalizeDate } from '../../utils/misc-utils'
*/ */
export async function editInviteLink( export async function editInviteLink(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike,
link: string,
params: { params: {
/** Chat ID */
chatId: InputPeerLike
/** Invite link to edit */
link: string
/** /**
* Date when this link will expire. * Date when this link will expire.
* If `number` is passed, UNIX time in ms is expected. * If `number` is passed, UNIX time in ms is expected.
@ -40,13 +42,15 @@ export async function editInviteLink(
withApproval?: boolean withApproval?: boolean
}, },
): Promise<ChatInviteLink> { ): Promise<ChatInviteLink> {
const { chatId, link, expires, usageLimit, withApproval } = params
const res = await this.call({ const res = await this.call({
_: 'messages.editExportedChatInvite', _: 'messages.editExportedChatInvite',
peer: await this.resolvePeer(chatId), peer: await this.resolvePeer(chatId),
link, link,
expireDate: normalizeDate(params.expires), expireDate: normalizeDate(expires),
usageLimit: params.usageLimit, usageLimit,
requestNeeded: params.withApproval, requestNeeded: withApproval,
}) })
const peers = PeersIndex.from(res) const peers = PeersIndex.from(res)

View file

@ -15,7 +15,7 @@ import { makeArrayPaginated, normalizeDate, normalizeToInputUser } from '../../u
export async function getInviteLinkMembers( export async function getInviteLinkMembers(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, chatId: InputPeerLike,
params: { params?: {
/** /**
* Invite link for which to get members * Invite link for which to get members
*/ */
@ -54,6 +54,7 @@ export async function getInviteLinkMembers(
}, },
): Promise<ArrayPaginated<ChatInviteLinkMember, { date: number; user: tl.TypeInputUser }>> { ): Promise<ArrayPaginated<ChatInviteLinkMember, { date: number; user: tl.TypeInputUser }>> {
const peer = await this.resolvePeer(chatId) const peer = await this.resolvePeer(chatId)
if (!params) params = {}
const { limit = 100, link, requestedSearch, requested = Boolean(requestedSearch) } = params const { limit = 100, link, requestedSearch, requested = Boolean(requestedSearch) } = params

View file

@ -4,21 +4,27 @@ import { InputPeerLike } from '../../types'
/** /**
* Approve or deny multiple join requests to a chat. * Approve or deny multiple join requests to a chat.
* *
* @param peer Chat/channel ID
* @param action Whether to approve or deny the join requests
* @param link Invite link to target
* @internal * @internal
*/ */
export async function hideAllJoinRequests( export async function hideAllJoinRequests(
this: TelegramClient, this: TelegramClient,
peer: InputPeerLike, params: {
action: 'approve' | 'deny', /** Chat/channel ID */
link?: string, chatId: InputPeerLike
/** Whether to approve or deny the join requests */
action: 'approve' | 'deny'
/** Invite link to target */
link?: string
},
): Promise<void> { ): Promise<void> {
const { chatId, action, link } = params
await this.call({ await this.call({
_: 'messages.hideAllChatJoinRequests', _: 'messages.hideAllChatJoinRequests',
approved: action === 'approve', approved: action === 'approve',
peer: await this.resolvePeer(peer), peer: await this.resolvePeer(chatId),
link, link,
}) })
} }

View file

@ -5,23 +5,27 @@ import { normalizeToInputUser } from '../../utils/peer-utils'
/** /**
* Approve or deny join request to a chat. * Approve or deny join request to a chat.
* *
* @param peer Chat/channel ID
* @param user User ID
* @param action Whether to approve or deny the join request
* @internal * @internal
*/ */
export async function hideJoinRequest( export async function hideJoinRequest(
this: TelegramClient, this: TelegramClient,
peer: InputPeerLike, params: {
user: InputPeerLike, /** Chat/channel ID */
action: 'approve' | 'deny', chatId: InputPeerLike
/** User ID */
user: InputPeerLike
/** Whether to approve or deny the join request */
action: 'approve' | 'deny'
},
): Promise<void> { ): Promise<void> {
const { chatId, user, action } = params
const userId = normalizeToInputUser(await this.resolvePeer(user), user) const userId = normalizeToInputUser(await this.resolvePeer(user), user)
await this.call({ await this.call({
_: 'messages.hideChatJoinRequest', _: 'messages.hideChatJoinRequest',
approved: action === 'approve', approved: action === 'approve',
peer: await this.resolvePeer(peer), peer: await this.resolvePeer(chatId),
userId, userId,
}) })
} }

View file

@ -12,7 +12,7 @@ import { ChatInviteLinkMember, InputPeerLike } from '../../types'
export async function* iterInviteLinkMembers( export async function* iterInviteLinkMembers(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, chatId: InputPeerLike,
params: Parameters<TelegramClient['getInviteLinkMembers']>[1] & { params?: Parameters<TelegramClient['getInviteLinkMembers']>[1] & {
/** /**
* Maximum number of users to return * Maximum number of users to return
* *
@ -30,6 +30,7 @@ export async function* iterInviteLinkMembers(
}, },
): AsyncIterableIterator<ChatInviteLinkMember> { ): AsyncIterableIterator<ChatInviteLinkMember> {
const peer = await this.resolvePeer(chatId) const peer = await this.resolvePeer(chatId)
if (!params) params = {}
const { limit = Infinity, chunkSize = 100, link, requestedSearch, requested = Boolean(requestedSearch) } = params const { limit = Infinity, chunkSize = 100, link, requestedSearch, requested = Boolean(requestedSearch) } = params

View file

@ -11,11 +11,19 @@ import { assertIsUpdatesGroup } from '../../utils/updates-utils'
* Once closed, poll can't be re-opened, and nobody * Once closed, poll can't be re-opened, and nobody
* will be able to vote in it * will be able to vote in it
* *
* @param chatId Chat ID where this poll was found
* @param message Message ID where this poll was found
* @internal * @internal
*/ */
export async function closePoll(this: TelegramClient, chatId: InputPeerLike, message: number): Promise<Poll> { export async function closePoll(
this: TelegramClient,
params: {
/** Chat ID where this poll was found */
chatId: InputPeerLike
/** Message ID where this poll was found */
message: number
},
): Promise<Poll> {
const { chatId, message } = params
const res = await this.call({ const res = await this.call({
_: 'messages.editMessage', _: 'messages.editMessage',
peer: await this.resolvePeer(chatId), peer: await this.resolvePeer(chatId),

View file

@ -10,16 +10,24 @@ import { createDummyUpdate } from '../../utils/updates-utils'
* *
* @param chatId Chat's marked ID, its username, phone or `"me"` or `"self"`. * @param chatId Chat's marked ID, its username, phone or `"me"` or `"self"`.
* @param ids Message(s) ID(s) to delete. * @param ids Message(s) ID(s) to delete.
* @param revoke Whether to "revoke" (i.e. delete for both sides).
* Only used for chats and private chats.
* @internal * @internal
*/ */
export async function deleteMessages( export async function deleteMessages(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, chatId: InputPeerLike,
ids: MaybeArray<number>, ids: MaybeArray<number>,
revoke = true, params?: {
/**
* Whether to "revoke" (i.e. delete for both sides).
* Only used for chats and private chats.
*
* @default true
*/
revoke?: boolean
},
): Promise<void> { ): Promise<void> {
const { revoke = true } = params ?? {}
if (!Array.isArray(ids)) ids = [ids] if (!Array.isArray(ids)) ids = [ids]
const peer = await this.resolvePeer(chatId) const peer = await this.resolvePeer(chatId)

View file

@ -15,8 +15,13 @@ import { normalizeInlineId } from '../../utils/inline-utils'
*/ */
export async function editInlineMessage( export async function editInlineMessage(
this: TelegramClient, this: TelegramClient,
messageId: tl.TypeInputBotInlineMessageID | string,
params: { params: {
/**
* Inline message ID, either as a TL object, or as a
* TDLib and Bot API compatible string
*/
messageId: tl.TypeInputBotInlineMessageID | string
/** /**
* New message text * New message text
* *
@ -71,7 +76,7 @@ export async function editInlineMessage(
let entities: tl.TypeMessageEntity[] | undefined let entities: tl.TypeMessageEntity[] | undefined
let media: tl.TypeInputMedia | undefined = undefined let media: tl.TypeInputMedia | undefined = undefined
const id = normalizeInlineId(messageId) const id = normalizeInlineId(params.messageId)
if (params.media) { if (params.media) {
media = await this._normalizeInputMedia(params.media, params, true) media = await this._normalizeInputMedia(params.media, params, true)

View file

@ -13,9 +13,12 @@ import { BotKeyboard, FormattedString, InputMediaLike, InputPeerLike, Message, R
*/ */
export async function editMessage( export async function editMessage(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike,
message: number | Message,
params: { params: {
/** Chat ID */
chatId: InputPeerLike
/** Message to edit */
message: number | Message
/** /**
* New message text * New message text
* *
@ -72,6 +75,7 @@ export async function editMessage(
progressCallback?: (uploaded: number, total: number) => void progressCallback?: (uploaded: number, total: number) => void
}, },
): Promise<Message> { ): Promise<Message> {
const { chatId, message } = params
let content: string | undefined = undefined let content: string | undefined = undefined
let entities: tl.TypeMessageEntity[] | undefined let entities: tl.TypeMessageEntity[] | undefined
let media: tl.TypeInputMedia | undefined = undefined let media: tl.TypeInputMedia | undefined = undefined

View file

@ -11,8 +11,6 @@ import { assertIsUpdatesGroup } from '../../utils/updates-utils'
* *
* To forward with a caption, use another overload that takes an array of IDs. * To forward with a caption, use another overload that takes an array of IDs.
* *
* @param toChatId Destination chat ID, username, phone, `"me"` or `"self"`
* @param fromChatId Source chat ID, username, phone, `"me"` or `"self"`
* @param message Message ID * @param message Message ID
* @param params Additional sending parameters * @param params Additional sending parameters
* @returns Newly sent, forwarded messages in the destination chat * @returns Newly sent, forwarded messages in the destination chat
@ -20,10 +18,14 @@ import { assertIsUpdatesGroup } from '../../utils/updates-utils'
*/ */
export async function forwardMessages( export async function forwardMessages(
this: TelegramClient, this: TelegramClient,
toChatId: InputPeerLike, params: {
fromChatId: InputPeerLike, /** Source chat ID, username, phone, `"me"` or `"self"` */
message: number, fromChatId: InputPeerLike
params?: { /** Destination chat ID, username, phone, `"me"` or `"self"` */
toChatId: InputPeerLike
/** Message ID */
messages: number
/** /**
* Optionally, a caption for your forwarded message(s). * Optionally, a caption for your forwarded message(s).
* It will be sent as a separate message before the forwarded messages. * It will be sent as a separate message before the forwarded messages.
@ -117,10 +119,14 @@ export async function forwardMessages(
*/ */
export async function forwardMessages( export async function forwardMessages(
this: TelegramClient, this: TelegramClient,
toChatId: InputPeerLike, params: {
fromChatId: InputPeerLike, /** Source chat ID, username, phone, `"me"` or `"self"` */
messages: number[], fromChatId: InputPeerLike
params?: { /** Destination chat ID, username, phone, `"me"` or `"self"` */
toChatId: InputPeerLike
/** Message IDs */
messages: number[]
/** /**
* Optionally, a caption for your forwarded message(s). * Optionally, a caption for your forwarded message(s).
* It will be sent as a separate message before the forwarded messages. * It will be sent as a separate message before the forwarded messages.
@ -200,10 +206,11 @@ export async function forwardMessages(
/** @internal */ /** @internal */
export async function forwardMessages( export async function forwardMessages(
this: TelegramClient, this: TelegramClient,
toChatId: InputPeerLike, params: {
fromChatId: InputPeerLike, toChatId: InputPeerLike
messages: MaybeArray<number>, fromChatId: InputPeerLike
params?: { messages: MaybeArray<number>
/** /**
* Optionally, a caption for your forwarded message(s). * Optionally, a caption for your forwarded message(s).
* It will be sent as a separate message before the forwarded messages. * It will be sent as a separate message before the forwarded messages.
@ -283,7 +290,20 @@ export async function forwardMessages(
sendAs?: InputPeerLike sendAs?: InputPeerLike
}, },
): Promise<MaybeArray<Message>> { ): Promise<MaybeArray<Message>> {
if (!params) params = {} const {
toChatId,
fromChatId,
parseMode,
entities,
silent,
schedule,
clearDraft,
forbidForwards,
sendAs,
noAuthor,
noCaption,
} = params
let { messages } = params
let isSingle = false let isSingle = false
@ -309,22 +329,22 @@ export async function forwardMessages(
} }
captionMessage = await this.sendText(toPeer, params.caption, { captionMessage = await this.sendText(toPeer, params.caption, {
parseMode: params.parseMode, parseMode,
entities: params.entities, entities,
silent: params.silent, silent,
schedule: params.schedule, schedule,
clearDraft: params.clearDraft, clearDraft,
forbidForwards: params.forbidForwards, forbidForwards,
sendAs: params.sendAs, sendAs,
}) })
} else if (params.captionMedia) { } else if (params.captionMedia) {
captionMessage = await this.sendMedia(toPeer, params.captionMedia, { captionMessage = await this.sendMedia(toPeer, params.captionMedia, {
parseMode: params.parseMode, parseMode,
silent: params.silent, silent,
schedule: params.schedule, schedule,
clearDraft: params.clearDraft, clearDraft,
forbidForwards: params.forbidForwards, forbidForwards,
sendAs: params.sendAs, sendAs,
}) })
} }
@ -333,13 +353,13 @@ export async function forwardMessages(
toPeer, toPeer,
fromPeer: await this.resolvePeer(fromChatId), fromPeer: await this.resolvePeer(fromChatId),
id: messages, id: messages,
silent: params.silent, silent,
scheduleDate: normalizeDate(params.schedule), scheduleDate: normalizeDate(schedule),
randomId: Array.from({ length: messages.length }, () => randomLong()), randomId: Array.from({ length: messages.length }, () => randomLong()),
dropAuthor: params.noAuthor, dropAuthor: noAuthor,
dropMediaCaptions: params.noCaption, dropMediaCaptions: noCaption,
noforwards: params.forbidForwards, noforwards: forbidForwards,
sendAs: params.sendAs ? await this.resolvePeer(params.sendAs) : undefined, sendAs: sendAs ? await this.resolvePeer(sendAs) : undefined,
}) })
assertIsUpdatesGroup('messages.forwardMessages', res) assertIsUpdatesGroup('messages.forwardMessages', res)

View file

@ -9,17 +9,21 @@ import { InputPeerLike } from '../../types'
* *
* @param chatId Chat ID, username, phone number, `"self"` or `"me"` * @param chatId Chat ID, username, phone number, `"self"` or `"me"`
* @param messageId Message ID * @param messageId Message ID
* @param notify Whether to send a notification (only for legacy groups and supergroups)
* @param bothSides Whether to pin for both sides (only for private chats)
* @internal * @internal
*/ */
export async function pinMessage( export async function pinMessage(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, chatId: InputPeerLike,
messageId: number, messageId: number,
notify = false, params?: {
bothSides = false, /** Whether to send a notification (only for legacy groups and supergroups) */
notify?: boolean
/** Whether to pin for both sides (only for private chats) */
bothSides?: boolean
},
): Promise<void> { ): Promise<void> {
const { notify, bothSides } = params ?? {}
const res = await this.call({ const res = await this.call({
_: 'messages.updatePinnedMessage', _: 'messages.updatePinnedMessage',
peer: await this.resolvePeer(chatId), peer: await this.resolvePeer(chatId),

View file

@ -7,16 +7,27 @@ import { createDummyUpdate } from '../../utils/updates-utils'
* Mark chat history as read. * Mark chat history as read.
* *
* @param chatId Chat ID * @param chatId Chat ID
* @param message Message up until which to read history (by default everything is read)
* @param clearMentions Whether to also clear all mentions in the chat
* @internal * @internal
*/ */
export async function readHistory( export async function readHistory(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, chatId: InputPeerLike,
message = 0, params?: {
clearMentions = false, /**
* Message up until which to read history
*
* @default 0, i.e. read everything
*/
maxId?: number
/**
* Whether to also clear all mentions in the chat
*/
clearMentions?: boolean
},
): Promise<void> { ): Promise<void> {
const { maxId = 0, clearMentions } = params ?? {}
const peer = await this.resolvePeer(chatId) const peer = await this.resolvePeer(chatId)
if (clearMentions) { if (clearMentions) {
@ -36,13 +47,13 @@ export async function readHistory(
await this.call({ await this.call({
_: 'channels.readHistory', _: 'channels.readHistory',
channel: normalizeToInputChannel(peer), channel: normalizeToInputChannel(peer),
maxId: message, maxId,
}) })
} else { } else {
const res = await this.call({ const res = await this.call({
_: 'messages.readHistory', _: 'messages.readHistory',
peer, peer,
maxId: message, maxId,
}) })
this._handleUpdate(createDummyUpdate(res.pts, res.ptsCount)) this._handleUpdate(createDummyUpdate(res.pts, res.ptsCount))
} }

View file

@ -16,18 +16,18 @@ import { FormattedString, InputPeerLike, Message, MtMessageNotFoundError, ReplyM
* > use {@link Message.sendCopy} instead, since that is * > use {@link Message.sendCopy} instead, since that is
* > much more efficient, and that is what this method wraps. * > much more efficient, and that is what this method wraps.
* *
* @param toChatId Source chat ID
* @param fromChatId Target chat ID
* @param message Message ID to forward
* @param params * @param params
* @internal * @internal
*/ */
export async function sendCopy( export async function sendCopy(
this: TelegramClient, this: TelegramClient,
toChatId: InputPeerLike, params: {
fromChatId: InputPeerLike, /** Source chat ID */
message: number, fromChatId: InputPeerLike
params?: { /** Target chat ID */
toChatId: InputPeerLike
/** Message ID to forward */
message: number
/** /**
* Whether to send this message silently. * Whether to send this message silently.
*/ */
@ -97,6 +97,8 @@ export async function sendCopy(
clearDraft?: boolean clearDraft?: boolean
}, },
): Promise<Message> { ): Promise<Message> {
const { fromChatId, toChatId, message, ...rest } = params
const fromPeer = await this.resolvePeer(fromChatId) const fromPeer = await this.resolvePeer(fromChatId)
const msg = await this.getMessages(fromPeer, message) const msg = await this.getMessages(fromPeer, message)
@ -105,5 +107,5 @@ export async function sendCopy(
throw new MtMessageNotFoundError(getMarkedPeerId(fromPeer), message, 'to copy') throw new MtMessageNotFoundError(getMarkedPeerId(fromPeer), message, 'to copy')
} }
return msg.sendCopy(toChatId, params) return msg.sendCopy(toChatId, rest)
} }

View file

@ -5,20 +5,24 @@ import { assertIsUpdatesGroup } from '../../utils/updates-utils'
/** /**
* Send or remove a reaction. * Send or remove a reaction.
* *
* @param chatId Chat ID with the message to react to
* @param message Message ID to react to
* @param emoji Reaction emoji (or `null` to remove reaction)
* @param big Whether to use a big reaction
* @returns Message to which the reaction was sent * @returns Message to which the reaction was sent
* @internal * @internal
*/ */
export async function sendReaction( export async function sendReaction(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, params: {
message: number, /** Chat ID with the message to react to */
emoji?: InputReaction | null, chatId: InputPeerLike
big = false, /** Message ID to react to */
message: number
/** Reaction emoji (or `null` to remove reaction) */
emoji?: InputReaction | null
/** Whether to use a big reaction */
big?: boolean
},
): Promise<Message> { ): Promise<Message> {
const { chatId, message, emoji, big } = params
const reaction = normalizeInputReaction(emoji) const reaction = normalizeInputReaction(emoji)
const res = await this.call({ const res = await this.call({

View file

@ -8,21 +8,27 @@ import { assertIsUpdatesGroup } from '../../utils/updates-utils'
/** /**
* Send or retract a vote in a poll. * Send or retract a vote in a poll.
* *
* @param chatId Chat ID where this poll was found
* @param message Message ID where this poll was found
* @param options
* Selected options, or `null` to retract.
* You can pass indexes of the answers or the `Buffer`s
* representing them. In case of indexes, the poll will first
* be requested from the server.
* @internal * @internal
*/ */
export async function sendVote( export async function sendVote(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, params: {
message: number, /** Chat ID where this poll was found */
options: null | MaybeArray<number | Buffer>, chatId: InputPeerLike
/** Message ID where this poll was found */
message: number
/**
* Selected options, or `null` to retract.
* You can pass indexes of the answers or the `Buffer`s
* representing them. In case of indexes, the poll will first
* be requested from the server.
*/
options: null | MaybeArray<number | Buffer>
},
): Promise<Poll> { ): Promise<Poll> {
const { chatId, message } = params
let { options } = params
if (options === null) options = [] if (options === null) options = []
if (!Array.isArray(options)) options = [options] if (!Array.isArray(options)) options = [options]

View file

@ -6,19 +6,21 @@ import { InputPeerLike, MessageEntity } from '../../types'
* *
* Returns `null` if it could not translate the message. * Returns `null` if it could not translate the message.
* *
* > **Note**: For now doesn't seem to work, returns null for all messages.
*
* @param chatId Chat or user ID
* @param messageId Identifier of the message to translate
* @param toLanguage Target language (two-letter ISO 639-1 language code)
* @internal * @internal
*/ */
export async function translateMessage( export async function translateMessage(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, params: {
messageId: number, /** Chat or user ID */
toLanguage: string, chatId: InputPeerLike
/** Identifier of the message to translate */
messageId: number
/** Target language (two-letter ISO 639-1 language code) */
toLanguage: string
},
): Promise<[string, MessageEntity[]] | null> { ): Promise<[string, MessageEntity[]] | null> {
const { chatId, messageId, toLanguage } = params
const res = await this.call({ const res = await this.call({
_: 'messages.translateText', _: 'messages.translateText',
peer: await this.resolvePeer(chatId), peer: await this.resolvePeer(chatId),

View file

@ -6,17 +6,21 @@ import { TelegramClient } from '../../client'
/** /**
* Change your 2FA password * Change your 2FA password
* *
* @param currentPassword Current password as plaintext
* @param newPassword New password as plaintext
* @param hint Hint for the new password
* @internal * @internal
*/ */
export async function changeCloudPassword( export async function changeCloudPassword(
this: TelegramClient, this: TelegramClient,
currentPassword: string, params: {
newPassword: string, /** Current password as plaintext */
hint?: string, currentPassword: string
/** New password as plaintext */
newPassword: string
/** Hint for the new password */
hint?: string
},
): Promise<void> { ): Promise<void> {
const { currentPassword, newPassword, hint } = params
const pwd = await this.call({ _: 'account.getPassword' }) const pwd = await this.call({ _: 'account.getPassword' })
if (!pwd.hasPassword) { if (!pwd.hasPassword) {

View file

@ -11,17 +11,21 @@ import { TelegramClient } from '../../client'
* {@link resendPasswordEmail} or {@link cancelPasswordEmail}, * {@link resendPasswordEmail} or {@link cancelPasswordEmail},
* and the call this method again * and the call this method again
* *
* @param password 2FA password as plaintext
* @param hint Hint for the new password
* @param email Recovery email
* @internal * @internal
*/ */
export async function enableCloudPassword( export async function enableCloudPassword(
this: TelegramClient, this: TelegramClient,
password: string, params: {
hint?: string, /** 2FA password as plaintext */
email?: string, password: string
/** Hint for the new password */
hint?: string
/** Recovery email */
email?: string
},
): Promise<void> { ): Promise<void> {
const { password, hint, email } = params
const pwd = await this.call({ _: 'account.getPassword' }) const pwd = await this.call({ _: 'account.getPassword' })
if (pwd.hasPassword) { if (pwd.hasPassword) {

View file

@ -13,7 +13,7 @@ import {
* Only for bots, and the sticker set must * Only for bots, and the sticker set must
* have been created by this bot. * have been created by this bot.
* *
* @param id Sticker set short name or TL object with input sticker set * @param setId Sticker set short name or TL object with input sticker set
* @param sticker Sticker to be added * @param sticker Sticker to be added
* @param params * @param params
* @returns Modfiied sticker set * @returns Modfiied sticker set
@ -21,7 +21,7 @@ import {
*/ */
export async function addStickerToSet( export async function addStickerToSet(
this: TelegramClient, this: TelegramClient,
id: InputStickerSet, setId: InputStickerSet,
sticker: InputStickerSetItem, sticker: InputStickerSetItem,
params?: { params?: {
/** /**
@ -35,7 +35,7 @@ export async function addStickerToSet(
): Promise<StickerSet> { ): Promise<StickerSet> {
const res = await this.call({ const res = await this.call({
_: 'stickers.addStickerToSet', _: 'stickers.addStickerToSet',
stickerset: normalizeInputStickerSet(id), stickerset: normalizeInputStickerSet(setId),
sticker: { sticker: {
_: 'inputStickerSetItem', _: 'inputStickerSetItem',
document: await this._normalizeFileToDocument(sticker.file, params ?? {}), document: await this._normalizeFileToDocument(sticker.file, params ?? {}),

View file

@ -4,13 +4,13 @@ import { InputStickerSet, normalizeInputStickerSet, StickerSet } from '../../typ
/** /**
* Get a sticker pack and stickers inside of it. * Get a sticker pack and stickers inside of it.
* *
* @param id Sticker pack short name, dice emoji, `"emoji"` for animated emojis or input ID * @param setId Sticker pack short name, dice emoji, `"emoji"` for animated emojis or input ID
* @internal * @internal
*/ */
export async function getStickerSet(this: TelegramClient, id: InputStickerSet): Promise<StickerSet> { export async function getStickerSet(this: TelegramClient, setId: InputStickerSet): Promise<StickerSet> {
const res = await this.call({ const res = await this.call({
_: 'messages.getStickerSet', _: 'messages.getStickerSet',
stickerset: normalizeInputStickerSet(id), stickerset: normalizeInputStickerSet(setId),
hash: 0, hash: 0,
}) })

View file

@ -5,7 +5,7 @@ import { normalizeToInputChannel } from '../../utils'
/** /**
* Set group sticker set for a supergroup * Set group sticker set for a supergroup
* *
* @param id Sticker set short name or a TL object with input sticker set * @param setId Sticker set short name or a TL object with input sticker set
* @param thumb Sticker set thumbnail * @param thumb Sticker set thumbnail
* @param params * @param params
* @returns Modified sticker set * @returns Modified sticker set
@ -14,11 +14,11 @@ import { normalizeToInputChannel } from '../../utils'
export async function setChatStickerSet( export async function setChatStickerSet(
this: TelegramClient, this: TelegramClient,
chatId: InputPeerLike, chatId: InputPeerLike,
id: InputStickerSet, setId: InputStickerSet,
): Promise<void> { ): Promise<void> {
await this.call({ await this.call({
_: 'channels.setStickers', _: 'channels.setStickers',
channel: normalizeToInputChannel(await this.resolvePeer(chatId), chatId), channel: normalizeToInputChannel(await this.resolvePeer(chatId), chatId),
stickerset: normalizeInputStickerSet(id), stickerset: normalizeInputStickerSet(setId),
}) })
} }

View file

@ -1,4 +1,4 @@
/* eslint-disable max-depth */ /* eslint-disable max-depth,max-params */
import { assertNever, MtArgumentError, tl } from '@mtcute/core' import { assertNever, MtArgumentError, tl } from '@mtcute/core'
import { import {
AsyncLock, AsyncLock,

View file

@ -8,7 +8,7 @@ import { normalizeToInputPeer } from '../../utils/peer-utils'
// @available=both // @available=both
/** /**
* Get the `InputPeer` of a known peer id. * Get the `InputPeer` of a known peer id.
* Useful when an `InputPeer` is needed. * Useful when an `InputPeer` is needed in Raw API.
* *
* @param peerId The peer identifier that you want to extract the `InputPeer` from. * @param peerId The peer identifier that you want to extract the `InputPeer` from.
* @param force Whether to force re-fetch the peer from the server * @param force Whether to force re-fetch the peer from the server

View file

@ -9,19 +9,22 @@ import { InputFileLike, Photo } from '../../types'
* *
* You can also pass a file ID or an InputPhoto to re-use existing photo. * You can also pass a file ID or an InputPhoto to re-use existing photo.
* *
* @param type Media type (photo or video)
* @param media Input media file
* @param previewSec
* When `type = video`, timestamp in seconds which will be shown
* as a static preview.
* @internal * @internal
*/ */
export async function setProfilePhoto( export async function setProfilePhoto(
this: TelegramClient, this: TelegramClient,
type: 'photo' | 'video', params: {
media: InputFileLike | tl.TypeInputPhoto, /** Media type (photo or video) */
previewSec?: number, type: 'photo' | 'video'
/** Input media file */
media: InputFileLike | tl.TypeInputPhoto
/** When `type = video`, timestamp in seconds which will be shown as a static preview. */
previewSec?: number
},
): Promise<Photo> { ): Promise<Photo> {
const { type, previewSec } = params
let { media } = params
// try parsing media as file id or input photo // try parsing media as file id or input photo
if (tdFileId.isFileIdLike(media) || (typeof media === 'object' && tl.isAnyInputPhoto(media))) { if (tdFileId.isFileIdLike(media) || (typeof media === 'object' && tl.isAnyInputPhoto(media))) {
if (typeof media === 'string' && media.match(/^https?:\/\//)) { if (typeof media === 'string' && media.match(/^https?:\/\//)) {

View file

@ -1,2 +1 @@
export * from './sent-code' export * from './sent-code'
export * from './terms-of-service'

View file

@ -1,43 +0,0 @@
import { tl } from '@mtcute/core'
import { makeInspectable } from '../../utils'
import { MessageEntity } from '../messages'
/**
* Telegram's Terms of Service returned by {@link TelegramClient.signIn}
*/
export class TermsOfService {
/**
* Underlying raw TL object
*/
readonly tos: tl.help.TypeTermsOfService
constructor(obj: tl.help.TypeTermsOfService) {
this.tos = obj
}
/**
* Terms of Service identifier
*/
get id(): string {
return this.tos.id.data
}
/**
* Terms of Service text
*/
get text(): string {
return this.tos.text
}
private _entities?: MessageEntity[]
/**
* Terms of Service entities text
*/
get entities(): ReadonlyArray<MessageEntity> {
return (this._entities ??= this.tos.entities.map((it) => new MessageEntity(it, this.tos.text)))
}
}
makeInspectable(TermsOfService)

View file

@ -182,17 +182,17 @@ export class CallbackQuery {
return this.client.answerCallbackQuery(this.raw.queryId, params) return this.client.answerCallbackQuery(this.raw.queryId, params)
} }
/** // /**
* Edit the message that originated this callback query // * Edit the message that originated this callback query
*/ // */
async editMessage(params: Parameters<TelegramClient['editInlineMessage']>[1]): Promise<void> { // async editMessage(params: Parameters<TelegramClient['editInlineMessage']>[1]): Promise<void> {
// we can use editInlineMessage as a parameter since they share most of the parameters, // // we can use editInlineMessage as a parameter since they share most of the parameters,
// except the ones that won't apply to already sent message anyways. // // except the ones that won't apply to already sent message anyways.
if (this.raw._ === 'updateInlineBotCallbackQuery') { // if (this.raw._ === 'updateInlineBotCallbackQuery') {
return this.client.editInlineMessage(this.raw.msgId, params) // return this.client.editInlineMessage(this.raw.msgId, params)
} // }
await this.client.editMessage(getMarkedPeerId(this.raw.peer), this.raw.msgId, params) // await this.client.editMessage(getMarkedPeerId(this.raw.peer), this.raw.msgId, params)
} // }
} }
makeInspectable(CallbackQuery) makeInspectable(CallbackQuery)

View file

@ -218,7 +218,7 @@ export class Conversation {
message = this._lastMessage ?? 0 message = this._lastMessage ?? 0
} }
return this.client.readHistory(this._inputPeer, message, clearMentions) return this.client.readHistory(this._inputPeer, { maxId: message, clearMentions })
} }
/** /**

View file

@ -786,17 +786,14 @@ export class Message {
* @param revoke Whether to "revoke" (i.e. delete for both sides). Only used for chats and private chats. * @param revoke Whether to "revoke" (i.e. delete for both sides). Only used for chats and private chats.
*/ */
delete(revoke = false): Promise<void> { delete(revoke = false): Promise<void> {
return this.client.deleteMessages(this.chat.inputPeer, this.id, revoke) return this.client.deleteMessages(this.chat.inputPeer, this.id, { revoke })
} }
/** /**
* Pin this message. * Pin this message.
*
* @param notify Whether to send a notification (only for legacy groups and supergroups)
* @param bothSides Whether to pin for both sides (only for private chats)
*/ */
pin(notify = false, bothSides = false): Promise<void> { pin(params?: Parameters<TelegramClient['pinMessage']>[2]): Promise<void> {
return this.client.pinMessage(this.chat.inputPeer, this.id, notify, bothSides) return this.client.pinMessage(this.chat.inputPeer, this.id, params)
} }
/** /**
@ -806,34 +803,34 @@ export class Message {
return this.client.pinMessage(this.chat.inputPeer, this.id) return this.client.pinMessage(this.chat.inputPeer, this.id)
} }
/** // /**
* Edit this message's text and/or reply markup // * Edit this message's text and/or reply markup
* // *
* @link TelegramClient.editMessage // * @link TelegramClient.editMessage
*/ // */
edit(params: Parameters<TelegramClient['editMessage']>[2]): Promise<Message> { // edit(params: Parameters<TelegramClient['editMessage']>[2]): Promise<Message> {
return this.client.editMessage(this.chat.inputPeer, this.id, params) // return this.client.editMessage(this.chat.inputPeer, this.id, params)
} // }
/** // /**
* Edit message text and optionally reply markup. // * Edit message text and optionally reply markup.
* // *
* Convenience method that just wraps {@link edit}, // * Convenience method that just wraps {@link edit},
* passing positional `text` as object field. // * passing positional `text` as object field.
* // *
* @param text New message text // * @param text New message text
* @param params? Additional parameters // * @param params? Additional parameters
* @link TelegramClient.editMessage // * @link TelegramClient.editMessage
*/ // */
editText( // editText(
text: string | FormattedString<string>, // text: string | FormattedString<string>,
params?: Omit<Parameters<TelegramClient['editMessage']>[2], 'text'>, // params?: Omit<Parameters<TelegramClient['editMessage']>[2], 'text'>,
): Promise<Message> { // ): Promise<Message> {
return this.edit({ // return this.edit({
text, // text,
...(params || {}), // ...(params || {}),
}) // })
} // }
/** /**
* Forward this message to some chat * Forward this message to some chat
@ -842,13 +839,20 @@ export class Message {
* @param params * @param params
* @returns Forwarded message * @returns Forwarded message
*/ */
forwardTo(peer: InputPeerLike, params?: Parameters<TelegramClient['forwardMessages']>[3]): Promise<Message> { forwardTo(
return this.client.forwardMessages(peer, this.chat.inputPeer, this.id, params) peer: InputPeerLike,
params?: Omit<Parameters<TelegramClient['forwardMessages']>[0], 'messages' | 'toChatId' | 'fromChatId'>,
): Promise<Message> {
return this.client.forwardMessages({
toChatId: peer,
fromChatId: this.chat.inputPeer,
messages: this.id,
...params,
})
} }
/** /**
* Send this message as a copy (i.e. send the same message, * Send this message as a copy (i.e. send the same message, but do not forward it).
* but do not forward it).
* *
* Note that if the message contains a webpage, * Note that if the message contains a webpage,
* it will be copied simply as a text message, * it will be copied simply as a text message,
@ -858,7 +862,10 @@ export class Message {
* @param toChatId Target chat ID * @param toChatId Target chat ID
* @param params Copy parameters * @param params Copy parameters
*/ */
sendCopy(toChatId: InputPeerLike, params?: Parameters<TelegramClient['sendCopy']>[3]): Promise<Message> { sendCopy(
toChatId: InputPeerLike,
params?: Omit<Parameters<TelegramClient['sendCopy']>[0], 'fromChatId' | 'message' | 'toChatId'>,
): Promise<Message> {
if (!params) params = {} if (!params) params = {}
if (this.raw._ === 'messageService') { if (this.raw._ === 'messageService') {
@ -912,7 +919,7 @@ export class Message {
* @param clearMentions Whether to also clear mentions * @param clearMentions Whether to also clear mentions
*/ */
async read(clearMentions = false): Promise<void> { async read(clearMentions = false): Promise<void> {
return this.client.readHistory(this.chat.inputPeer, this.raw.id, clearMentions) return this.client.readHistory(this.chat.inputPeer, { maxId: this.raw.id, clearMentions })
} }
/** /**
@ -922,7 +929,12 @@ export class Message {
* @param big Whether to use a big reaction * @param big Whether to use a big reaction
*/ */
async react(emoji: string | null, big?: boolean): Promise<Message> { async react(emoji: string | null, big?: boolean): Promise<Message> {
return this.client.sendReaction(this.chat.inputPeer, this.raw.id, emoji, big) return this.client.sendReaction({
chatId: this.chat.inputPeer,
message: this.raw.id,
emoji,
big,
})
} }
async getCustomEmojis(): Promise<Sticker[]> { async getCustomEmojis(): Promise<Sticker[]> {

View file

@ -614,7 +614,7 @@ export class Chat {
* Only applicable to legacy groups, ignored for supergroups and channels * Only applicable to legacy groups, ignored for supergroups and channels
*/ */
async addMembers(users: MaybeArray<InputPeerLike>, forwardCount?: number): Promise<void> { async addMembers(users: MaybeArray<InputPeerLike>, forwardCount?: number): Promise<void> {
return this.client.addChatMembers(this.inputPeer, users, forwardCount) return this.client.addChatMembers(this.inputPeer, users, { forwardCount })
} }
/** /**
@ -638,7 +638,7 @@ export class Chat {
* @param clearMentions Whether to also clear all mentions in the chat * @param clearMentions Whether to also clear all mentions in the chat
*/ */
async readHistory(message = 0, clearMentions = false): Promise<void> { async readHistory(message = 0, clearMentions = false): Promise<void> {
return this.client.readHistory(this.inputPeer, message, clearMentions) return this.client.readHistory(this.inputPeer, { maxId: message, clearMentions })
} }
/** /**

View file

@ -74,9 +74,9 @@ export class BotChatJoinRequestUpdate {
/** /**
* Approve or deny the request. * Approve or deny the request.
*/ */
hide(action: Parameters<TelegramClient['hideJoinRequest']>[2]): Promise<void> { // hide(action: Parameters<TelegramClient['hideJoinRequest']>[1]['action']): Promise<void> {
return this.client.hideJoinRequest(this.chat.inputPeer, this.user.inputPeer, action) // return this.client.hideJoinRequest(this.chat.inputPeer, { action, user: this.user.inputPeer })
} // }
} }
makeInspectable(BotChatJoinRequestUpdate) makeInspectable(BotChatJoinRequestUpdate)

View file

@ -55,19 +55,19 @@ export class ChatJoinRequestUpdate {
/** /**
* Approve or deny the last requested user * Approve or deny the last requested user
*/ */
hideLast(action: Parameters<TelegramClient['hideJoinRequest']>[2]): Promise<void> { // hideLast(action: Parameters<TelegramClient['hideJoinRequest']>[1]['action']): Promise<void> {
return this.client.hideJoinRequest(this.chatId, this.raw.recentRequesters[0], action) // return this.client.hideJoinRequest(this.chatId, { user: this.raw.recentRequesters[0], action })
} // }
/** /**
* Approve or deny all recent requests * Approve or deny all recent requests
* (the ones available in {@link recentRequesters}) * (the ones available in {@link recentRequesters})
*/ */
async hideAllRecent(action: Parameters<TelegramClient['hideJoinRequest']>[2]): Promise<void> { // async hideAllRecent(action: Parameters<TelegramClient['hideJoinRequest']>[1]['action']): Promise<void> {
for (const id of this.raw.recentRequesters) { // for (const id of this.raw.recentRequesters) {
await this.client.hideJoinRequest(this.chatId, id, action) // await this.client.hideJoinRequest(this.chatId, { user: id, action })
} // }
} // }
} }
makeInspectable(ChatJoinRequestUpdate) makeInspectable(ChatJoinRequestUpdate)

View file

@ -1,4 +1,4 @@
import { MtArgumentError, tl } from '@mtcute/core' import { tl } from '@mtcute/core'
import { TelegramClient } from '../../client' import { TelegramClient } from '../../client'
import { makeInspectable } from '../../utils' import { makeInspectable } from '../../utils'
@ -78,13 +78,13 @@ export class ChosenInlineResult {
return encodeInlineMessageId(this.raw.msgId) return encodeInlineMessageId(this.raw.msgId)
} }
async editMessage(params: Parameters<TelegramClient['editInlineMessage']>[1]): Promise<void> { // async editMessage(params: Parameters<TelegramClient['editInlineMessage']>[1]): Promise<void> {
if (!this.raw.msgId) { // if (!this.raw.msgId) {
throw new MtArgumentError('No message ID, make sure you have included reply markup!') // throw new MtArgumentError('No message ID, make sure you have included reply markup!')
} // }
return this.client.editInlineMessage(this.raw.msgId, params) // return this.client.editInlineMessage(this.raw.msgId, params)
} // }
} }
makeInspectable(ChosenInlineResult) makeInspectable(ChosenInlineResult)

View file

@ -75,7 +75,7 @@ export class PreCheckoutQuery {
* Reject the query * Reject the query
*/ */
reject(error = ''): Promise<void> { reject(error = ''): Promise<void> {
return this.client.answerPreCheckoutQuery(this.queryId, error) return this.client.answerPreCheckoutQuery(this.queryId, { error })
} }
} }