diff --git a/.eslintrc.js b/.eslintrc.js index 3d8dd189..ce04e617 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -36,6 +36,7 @@ module.exports = { '@typescript-eslint/no-empty-function': 'off', '@typescript-eslint/no-this-alias': 'off', 'prefer-rest-params': 'off', + 'no-prototype-builtins': 'off', '@typescript-eslint/ban-types': 'off', '@typescript-eslint/adjacent-overload-signatures': 'off', '@typescript-eslint/no-namespace': 'off', diff --git a/packages/client/package.json b/packages/client/package.json index e02360b6..e171d85a 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -12,12 +12,13 @@ "build": "tsc" }, "dependencies": { + "@types/long": "^4.0.1", "@types/node": "^15.12.1", - "@mtcute/tl": "~1.131.0", + "@mtcute/tl": "~134.0", "@mtcute/core": "^1.0.0", "@mtcute/file-id": "^1.0.0", "eager-async-pool": "^1.0.0", "file-type": "^16.2.0", - "big-integer": "1.6.48" + "long": "^4.0.0" } } diff --git a/packages/client/scripts/generate-client.js b/packages/client/scripts/generate-client.js index da42643e..4dea7e84 100644 --- a/packages/client/scripts/generate-client.js +++ b/packages/client/scripts/generate-client.js @@ -2,8 +2,6 @@ const ts = require('typescript') const path = require('path') const fs = require('fs') const prettier = require('prettier') -// not the best way but who cares lol -const { createWriter } = require('../../tl/scripts/common') const updates = require('./generate-updates') const targetDir = path.join(__dirname, '../src') @@ -130,7 +128,7 @@ async function addSingleMethod(state, fileName) { if (!isExported && !isPrivate) { throwError( - isExported, + stmt, fileName, 'Public methods MUST be exported.' ) @@ -228,6 +226,14 @@ async function addSingleMethod(state, fileName) { } } } else if (stmt.kind === ts.SyntaxKind.InterfaceDeclaration) { + if (isCopy) { + state.copy.push({ + from: relPath, + code: stmt.getText() + }) + continue + } + if (!checkForFlag(stmt, '@extension')) continue const isExported = (stmt.modifiers || []).find( (mod) => mod.kind === 92 /* ExportKeyword */ @@ -260,7 +266,7 @@ async function addSingleMethod(state, fileName) { } async function main() { - const output = createWriter('../src/client.ts', __dirname) + const output = fs.createWriteStream(path.join(__dirname, '../src/client.ts')) const state = { imports: {}, fields: [], @@ -282,23 +288,22 @@ async function main() { output.write( '/* THIS FILE WAS AUTO-GENERATED */\n' + "import { BaseTelegramClient } from '@mtcute/core'\n" + - "import { tl } from '@mtcute/tl'" + "import { tl } from '@mtcute/tl'\n" ) Object.entries(state.imports).forEach(([module, items]) => { items = [...items] - output.write(`import { ${items.sort().join(', ')} } from '${module}'`) + output.write(`import { ${items.sort().join(', ')} } from '${module}'\n`) }) - output.write() + output.write('\n') state.copy.forEach(({ from, code }) => { output.write(`// from ${from}\n${code}\n`) }) output.write( - '\nexport interface TelegramClient extends BaseTelegramClient {' + '\nexport interface TelegramClient extends BaseTelegramClient {\n' ) - output.tab() output.write(`/** * Register a raw update handler @@ -306,14 +311,14 @@ async function main() { * @param name Event name * @param handler Raw update handler */ - on(name: 'raw_update', handler: ((upd: tl.TypeUpdate | tl.TypeMessage, users: UsersIndex, chats: ChatsIndex) => void)): this + on(name: 'raw_update', handler: ((upd: tl.TypeUpdate | tl.TypeMessage, peers: PeersIndex) => void)): this /** * Register a parsed update handler * * @param name Event name * @param handler Raw update handler */ - on(name: 'update', handler: ((upd: ParsedUpdate) => void)): this`) + on(name: 'update', handler: ((upd: ParsedUpdate) => void)): this\n`) updates.types.forEach((type) => { output.write(`/** @@ -322,7 +327,7 @@ async function main() { * @param name Event name * @param handler ${updates.toSentence(type, 'full')} */ -on(name: '${type.typeName}', handler: ((upd: ${type.updateType}) => void)): this`) +on(name: '${type.typeName}', handler: ((upd: ${type.updateType}) => void)): this\n`) }) @@ -441,10 +446,10 @@ on(name: '${type.typeName}', handler: ((upd: ${type.updateType}) => void)): this if (!isPrivate && !hasOverloads) { if (!comment.match(/\/\*\*?\s*\*\//)) // empty comment, no need to write it - output.write(comment) + output.write(comment + '\n') output.write( - `${name}${generics}(${parameters})${returnType}` + `${name}${generics}(${parameters})${returnType}\n` ) } @@ -456,27 +461,22 @@ on(name: '${type.typeName}', handler: ((upd: ${type.updateType}) => void)): this } } ) - output.untab() - output.write('}') - - output.write( - '/** @internal */\nexport class TelegramClient extends BaseTelegramClient {' - ) - output.tab() - - state.fields.forEach(({ code }) => output.write('protected ' + code)) - - output.write('constructor(opts: BaseTelegramClient.Options) {') - output.tab() - output.write('super(opts)') - state.init.forEach((code) => { - output.write(code) - }) - output.untab() output.write('}\n') - classContents.forEach((line) => output.write(line)) - output.untab() + output.write( + '/** @internal */\nexport class TelegramClient extends BaseTelegramClient {\n' + ) + + state.fields.forEach(({ code }) => output.write(`protected ${code}\n`)) + + output.write('constructor(opts: BaseTelegramClient.Options) {\n') + output.write('super(opts)\n') + state.init.forEach((code) => { + output.write(code + '\n') + }) + output.write('}\n') + + classContents.forEach((line) => output.write(line + '\n')) output.write('}') // format the resulting file with prettier diff --git a/packages/client/scripts/generate-updates.js b/packages/client/scripts/generate-updates.js index 53ea2fea..11c21524 100644 --- a/packages/client/scripts/generate-updates.js +++ b/packages/client/scripts/generate-updates.js @@ -1,11 +1,24 @@ const fs = require('fs') const path = require('path') const prettier = require('prettier') -const { - snakeToCamel, - camelToPascal, - camelToSnake, -} = require('../../tl/scripts/common') + +const snakeToCamel = (s) => { + return s.replace(/(? { + return $1.substr(1).toUpperCase() + }) +} + +const camelToPascal = (s) => + s[0].toUpperCase() + s.substr(1) + +const camelToSnake = (s) => { + return s.replace( + /(?<=[a-zA-Z0-9])([A-Z0-9]+(?=[A-Z]|$)|[A-Z0-9])/g, + ($1) => { + return '_' + $1.toLowerCase() + } + ) +} function parseUpdateTypes() { const lines = fs diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index 7f5bc95e..87d7ce1a 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -145,14 +145,31 @@ import { getInstalledStickers } from './methods/stickers/get-installed-stickers' import { getStickerSet } from './methods/stickers/get-sticker-set' import { moveStickerInSet } from './methods/stickers/move-sticker-in-set' import { setStickerSetThumb } from './methods/stickers/set-sticker-set-thumb' +import { ConditionVariable } from '@mtcute/core/src/utils/condition-variable' +import { + AsyncLock, + Deque, + MaybeArray, + MaybeAsync, + SessionConnection, + SortedLinkedList, +} from '@mtcute/core' +import { RpsMeter } from './utils/rps-meter' import { _dispatchUpdate, _fetchUpdatesState, _handleUpdate, _keepAliveAction, _loadStorage, + _onStop, _saveStorage, + _updatesLoop, catchUp, + enableRps, + getCurrentRpsIncoming, + getCurrentRpsProcessing, + startUpdatesLoop, + stopUpdatesLoop, } from './methods/updates' import { blockUser } from './methods/users/block-user' import { deleteProfilePhotos } from './methods/users/delete-profile-photos' @@ -181,7 +198,6 @@ import { ChatMember, ChatMemberUpdate, ChatPreview, - ChatsIndex, ChosenInlineResult, Conversation, DeleteMessageUpdate, @@ -203,6 +219,7 @@ import { ParsedUpdate, PartialExcept, PartialOnly, + PeersIndex, Photo, Poll, PollUpdate, @@ -219,17 +236,27 @@ import { User, UserStatusUpdate, UserTypingUpdate, - UsersIndex, } from './types' -import { - AsyncLock, - MaybeArray, - MaybeAsync, - TelegramConnection, -} from '@mtcute/core' import { tdFileId } from '@mtcute/file-id' import { Logger } from '@mtcute/core/src/utils/logger' +// from methods/updates.ts +interface PendingUpdateContainer { + upd: tl.TypeUpdates + seqStart: number + seqEnd: number +} +// from methods/updates.ts +interface PendingUpdate { + update: tl.TypeUpdate + channelId?: number + pts?: number + ptsBefore?: number + qtsBefore?: number + timeout?: number + peers?: PeersIndex +} + export interface TelegramClient extends BaseTelegramClient { /** * Register a raw update handler @@ -241,8 +268,7 @@ export interface TelegramClient extends BaseTelegramClient { name: 'raw_update', handler: ( upd: tl.TypeUpdate | tl.TypeMessage, - users: UsersIndex, - chats: ChatsIndex + peers: PeersIndex ) => void ): this /** @@ -3268,12 +3294,58 @@ export interface TelegramClient extends BaseTelegramClient { progressCallback?: (uploaded: number, total: number) => void } ): Promise + /** + * Enable RPS meter. + * Only available in NodeJS v10.7.0 and newer + * + * > **Note**: This may have negative impact on performance + * + * @param size Sampling size + * @param time Window time + */ + enableRps(size?: number, time?: number): void + /** + * Get current average incoming RPS + * + * Incoming RPS is calculated based on + * incoming update containers. Normally, + * they should be around the same, except + * rare situations when processing rps + * may peak. + * + */ + getCurrentRpsIncoming(): number + /** + * Get current average processing RPS + * + * Processing RPS is calculated based on + * dispatched updates. Normally, + * they should be around the same, except + * rare situations when processing rps + * may peak. + * + */ + getCurrentRpsProcessing(): number + /** + * **ADVANCED** + * + * Manually start updates loop. + * Usually done automatically inside {@link start} + */ + startUpdatesLoop(): void + /** + * **ADVANCED** + * + * Manually stop updates loop. + * Usually done automatically when stopping the client with {@link close} + */ + stopUpdatesLoop(): void _handleUpdate(update: tl.TypeUpdates, noDispatch?: boolean): void /** * Catch up with the server by loading missed updates. * */ - catchUp(): Promise + catchUp(): void /** * Block a user * @@ -3413,8 +3485,12 @@ export interface TelegramClient extends BaseTelegramClient { * Useful when an `InputPeer` is needed. * * @param peerId The peer identifier that you want to extract the `InputPeer` from. + * @param force (default: `false`) Whether to force re-fetch the peer from the server */ - resolvePeer(peerId: InputPeerLike): Promise + resolvePeer( + peerId: InputPeerLike, + force?: boolean + ): Promise /** * Change user status to offline or online * @@ -3483,11 +3559,21 @@ export class TelegramClient extends BaseTelegramClient { protected _selfUsername: string | null protected _pendingConversations: Record protected _hasConversations: boolean - protected _downloadConnections: Record - protected _connectionsForInline: Record + protected _downloadConnections: Record + protected _connectionsForInline: Record protected _parseModes: Record protected _defaultParseMode: string | null + protected _updatesLoopActive: boolean + protected _updatesLoopCv: ConditionVariable + protected _pendingUpdateContainers: SortedLinkedList + protected _pendingPtsUpdates: SortedLinkedList + protected _pendingPtsUpdatesPostponed: SortedLinkedList + protected _pendingQtsUpdates: SortedLinkedList + protected _pendingQtsUpdatesPostponed: SortedLinkedList + protected _pendingUnorderedUpdates: Deque protected _updLock: AsyncLock + protected _rpsIncoming?: RpsMeter + protected _rpsProcessing?: RpsMeter protected _pts?: number protected _qts?: number protected _date?: number @@ -3506,12 +3592,33 @@ export class TelegramClient extends BaseTelegramClient { this._userId = null this._isBot = false this._selfUsername = null + this.log.prefix = '[USER N/A] ' this._pendingConversations = {} this._hasConversations = false this._downloadConnections = {} this._connectionsForInline = {} this._parseModes = {} this._defaultParseMode = null + this._updatesLoopActive = false + this._updatesLoopCv = new ConditionVariable() + + this._pendingUpdateContainers = new SortedLinkedList( + (a, b) => a.seqStart - b.seqStart + ) + this._pendingPtsUpdates = new SortedLinkedList( + (a, b) => a.ptsBefore! - b.ptsBefore! + ) + this._pendingPtsUpdatesPostponed = new SortedLinkedList( + (a, b) => a.ptsBefore! - b.ptsBefore! + ) + this._pendingQtsUpdates = new SortedLinkedList( + (a, b) => a.qtsBefore! - b.qtsBefore! + ) + this._pendingQtsUpdatesPostponed = new SortedLinkedList( + (a, b) => a.qtsBefore! - b.qtsBefore! + ) + this._pendingUnorderedUpdates = new Deque() + this._updLock = new AsyncLock() // we dont need to initialize state fields since // they are always loaded either from the server, or from storage. @@ -3527,7 +3634,6 @@ export class TelegramClient extends BaseTelegramClient { this._updsLog = this.log.create('updates') } - acceptTos = acceptTos checkPassword = checkPassword getPasswordHint = getPasswordHint @@ -3667,12 +3773,19 @@ export class TelegramClient extends BaseTelegramClient { getStickerSet = getStickerSet moveStickerInSet = moveStickerInSet setStickerSetThumb = setStickerSetThumb + enableRps = enableRps + getCurrentRpsIncoming = getCurrentRpsIncoming + getCurrentRpsProcessing = getCurrentRpsProcessing protected _fetchUpdatesState = _fetchUpdatesState protected _loadStorage = _loadStorage + startUpdatesLoop = startUpdatesLoop + stopUpdatesLoop = stopUpdatesLoop + protected _onStop = _onStop protected _saveStorage = _saveStorage protected _dispatchUpdate = _dispatchUpdate _handleUpdate = _handleUpdate catchUp = catchUp + protected _updatesLoop = _updatesLoop protected _keepAliveAction = _keepAliveAction blockUser = blockUser deleteProfilePhotos = deleteProfilePhotos diff --git a/packages/client/src/methods/_imports.ts b/packages/client/src/methods/_imports.ts index e1938138..9a8d0243 100644 --- a/packages/client/src/methods/_imports.ts +++ b/packages/client/src/methods/_imports.ts @@ -32,8 +32,7 @@ import { Photo, ChatEvent, ChatInviteLink, - UsersIndex, - ChatsIndex, + PeersIndex, GameHighScore, ArrayWithTotal, BotCommands, @@ -60,7 +59,7 @@ import { import { MaybeArray, MaybeAsync, - TelegramConnection, + SessionConnection, AsyncLock, } from '@mtcute/core' diff --git a/packages/client/src/methods/auth/_initialize.ts b/packages/client/src/methods/auth/_initialize.ts index 52b092e8..0d3ad4cd 100644 --- a/packages/client/src/methods/auth/_initialize.ts +++ b/packages/client/src/methods/auth/_initialize.ts @@ -1,4 +1,5 @@ import { TelegramClient } from '../../client' +import { tl } from '@mtcute/tl' // @extension interface AuthState { @@ -17,4 +18,5 @@ function _initializeAuthState(this: TelegramClient) { this._userId = null this._isBot = false this._selfUsername = null + this.log.prefix = '[USER N/A] ' } diff --git a/packages/client/src/methods/auth/check-password.ts b/packages/client/src/methods/auth/check-password.ts index e0f4b7b9..9b41ffd8 100644 --- a/packages/client/src/methods/auth/check-password.ts +++ b/packages/client/src/methods/auth/check-password.ts @@ -37,11 +37,17 @@ export async function checkPassword( 'user' ) + this.log.prefix = `[USER ${this._userId}] ` this._userId = res.user.id this._isBot = false this._selfChanged = true + this._selfUsername = res.user.username ?? null await this._fetchUpdatesState() await this._saveStorage() + // telegram ignores invokeWithoutUpdates for auth methods + if (this._disableUpdates) this.primaryConnection._resetSession() + else this.startUpdatesLoop() + return new User(this, res.user) } diff --git a/packages/client/src/methods/auth/sign-in-bot.ts b/packages/client/src/methods/auth/sign-in-bot.ts index 57b03a22..4d70a967 100644 --- a/packages/client/src/methods/auth/sign-in-bot.ts +++ b/packages/client/src/methods/auth/sign-in-bot.ts @@ -33,6 +33,7 @@ export async function signInBot( 'user' ) + this.log.prefix = `[USER ${this._userId}] ` this._userId = res.user.id this._isBot = true this._selfUsername = res.user.username! @@ -40,5 +41,9 @@ export async function signInBot( await this._fetchUpdatesState() await this._saveStorage() + // telegram ignores invokeWithoutUpdates for auth methods + if (this._disableUpdates) this.primaryConnection._resetSession() + else this.startUpdatesLoop() + return new User(this, res.user) } diff --git a/packages/client/src/methods/auth/sign-in.ts b/packages/client/src/methods/auth/sign-in.ts index e74510ec..8e672167 100644 --- a/packages/client/src/methods/auth/sign-in.ts +++ b/packages/client/src/methods/auth/sign-in.ts @@ -41,6 +41,7 @@ export async function signIn( assertTypeIs('signIn (@ auth.signIn -> user)', res.user, 'user') + this.log.prefix = `[USER ${this._userId}] ` this._userId = res.user.id this._isBot = false this._selfChanged = true @@ -48,5 +49,9 @@ export async function signIn( await this._fetchUpdatesState() await this._saveStorage() + // telegram ignores invokeWithoutUpdates for auth methods + if (this._disableUpdates) this.primaryConnection._resetSession() + else this.startUpdatesLoop() + return new User(this, res.user) } diff --git a/packages/client/src/methods/auth/sign-up.ts b/packages/client/src/methods/auth/sign-up.ts index f8800bac..24437544 100644 --- a/packages/client/src/methods/auth/sign-up.ts +++ b/packages/client/src/methods/auth/sign-up.ts @@ -32,11 +32,16 @@ export async function signUp( assertTypeIs('signUp (@ auth.signUp)', res, 'auth.authorization') assertTypeIs('signUp (@ auth.signUp -> user)', res.user, 'user') + this.log.prefix = `[USER ${this._userId}] ` this._userId = res.user.id this._isBot = false this._selfChanged = true await this._fetchUpdatesState() await this._saveStorage() + // telegram ignores invokeWithoutUpdates for auth methods + if (this._disableUpdates) this.primaryConnection._resetSession() + else this.startUpdatesLoop() + return new User(this, res.user) } diff --git a/packages/client/src/methods/auth/start.ts b/packages/client/src/methods/auth/start.ts index cdbd2cae..25b6650d 100644 --- a/packages/client/src/methods/auth/start.ts +++ b/packages/client/src/methods/auth/start.ts @@ -148,15 +148,28 @@ export async function start( // user is already authorized + this.log.prefix = `[USER ${me.id}] ` + this.log.info( + 'Logged in as %s (ID: %s, username: %s, bot: %s)', + me.displayName, + me.id, + me.username, + me.isBot + ) + if (!this._disableUpdates) { this._catchUpChannels = !!params.catchUp - if (params.catchUp) { - await this.catchUp() - } else { + if (!params.catchUp) { // otherwise we will catch up as soon as we receive a new update await this._fetchUpdatesState() } + + this.startUpdatesLoop() + + if (params.catchUp) { + this.catchUp() + } } return me @@ -165,9 +178,7 @@ export async function start( } if (!params.phone && !params.botToken) - throw new MtArgumentError( - 'Neither phone nor bot token were provided' - ) + throw new MtArgumentError('Neither phone nor bot token were provided') let phone = params.phone ? await resolveMaybeDynamic(params.phone) : null if (phone) { @@ -249,9 +260,7 @@ export async function start( result = await this.checkPassword(password) } catch (e) { if (typeof params.password !== 'function') { - throw new MtArgumentError( - 'Provided password was invalid' - ) + throw new MtArgumentError('Provided password was invalid') } if (e instanceof PasswordHashInvalidError) { diff --git a/packages/client/src/methods/bots/get-game-high-scores.ts b/packages/client/src/methods/bots/get-game-high-scores.ts index f39b2b9b..05715de7 100644 --- a/packages/client/src/methods/bots/get-game-high-scores.ts +++ b/packages/client/src/methods/bots/get-game-high-scores.ts @@ -3,12 +3,10 @@ import { InputPeerLike, MtInvalidPeerTypeError, GameHighScore, + PeersIndex, } from '../../types' import { tl } from '@mtcute/tl' -import { - createUsersChatsIndex, - normalizeToInputUser, -} from '../../utils/peer-utils' +import { normalizeToInputUser } from '../../utils/peer-utils' /** * Get high scores of a game @@ -43,9 +41,9 @@ export async function getGameHighScores( userId: user, }) - const { users } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) - return res.scores.map((score) => new GameHighScore(this, score, users)) + return res.scores.map((score) => new GameHighScore(this, score, peers)) } /** @@ -81,7 +79,7 @@ export async function getInlineGameHighScores( { connection } ) - const { users } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) - return res.scores.map((score) => new GameHighScore(this, score, users)) + return res.scores.map((score) => new GameHighScore(this, score, peers)) } diff --git a/packages/client/src/methods/chats/create-channel.ts b/packages/client/src/methods/chats/create-channel.ts index ce30c6ae..1ebf131c 100644 --- a/packages/client/src/methods/chats/create-channel.ts +++ b/packages/client/src/methods/chats/create-channel.ts @@ -1,5 +1,5 @@ import { TelegramClient } from '../../client' -import { Chat, MtTypeAssertionError } from '../../types' +import { Chat } from '../../types' import { assertIsUpdatesGroup } from '../../utils/updates-utils' /** diff --git a/packages/client/src/methods/chats/create-group.ts b/packages/client/src/methods/chats/create-group.ts index 394ba633..8bd6aa19 100644 --- a/packages/client/src/methods/chats/create-group.ts +++ b/packages/client/src/methods/chats/create-group.ts @@ -1,8 +1,7 @@ import { TelegramClient } from '../../client' import { MaybeArray } from '@mtcute/core' -import { Chat, InputPeerLike, MtTypeAssertionError } from '../../types' +import { Chat, InputPeerLike } from '../../types' import { normalizeToInputUser } from '../../utils/peer-utils' -import { tl } from '@mtcute/tl' import { assertIsUpdatesGroup } from '../../utils/updates-utils' /** diff --git a/packages/client/src/methods/chats/create-supergroup.ts b/packages/client/src/methods/chats/create-supergroup.ts index 09b33dee..de227158 100644 --- a/packages/client/src/methods/chats/create-supergroup.ts +++ b/packages/client/src/methods/chats/create-supergroup.ts @@ -1,5 +1,5 @@ import { TelegramClient } from '../../client' -import { Chat, MtTypeAssertionError } from '../../types' +import { Chat } from '../../types' import { assertIsUpdatesGroup } from '../../utils/updates-utils' /** diff --git a/packages/client/src/methods/chats/get-chat-event-log.ts b/packages/client/src/methods/chats/get-chat-event-log.ts index dff7ea73..dc18c550 100644 --- a/packages/client/src/methods/chats/get-chat-event-log.ts +++ b/packages/client/src/methods/chats/get-chat-event-log.ts @@ -2,16 +2,15 @@ import { TelegramClient } from '../../client' import { InputPeerLike, MtInvalidPeerTypeError, - ChatEvent, + ChatEvent, PeersIndex, } from '../../types' import { tl } from '@mtcute/tl' import { MaybeArray } from '@mtcute/core' -import bigInt from 'big-integer' import { - createUsersChatsIndex, normalizeToInputChannel, normalizeToInputUser, } from '../../utils/peer-utils' +import Long from 'long' /** * Get chat event log ("Recent actions" in official @@ -89,8 +88,8 @@ export async function* getChatEventLog( if (!channel) throw new MtInvalidPeerTypeError(chatId, 'channel') let current = 0 - let maxId = params.maxId ?? bigInt.zero - const minId = params.minId ?? bigInt.zero + let maxId = params.maxId ?? Long.ZERO + const minId = params.minId ?? Long.ZERO const query = params.query ?? '' const total = params.limit || Infinity @@ -216,12 +215,12 @@ export async function* getChatEventLog( if (!res.events.length) break - const { users, chats } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) const last = res.events[res.events.length - 1] maxId = last.id for (const evt of res.events) { - const parsed = new ChatEvent(this, evt, users, chats) + const parsed = new ChatEvent(this, evt, peers) if ( localFilter && diff --git a/packages/client/src/methods/chats/get-chat-member.ts b/packages/client/src/methods/chats/get-chat-member.ts index 8bb82910..2507e87a 100644 --- a/packages/client/src/methods/chats/get-chat-member.ts +++ b/packages/client/src/methods/chats/get-chat-member.ts @@ -1,7 +1,6 @@ import { TelegramClient } from '../../client' -import { InputPeerLike, MtInvalidPeerTypeError } from '../../types' +import { InputPeerLike, MtInvalidPeerTypeError, PeersIndex } from '../../types' import { - createUsersChatsIndex, isInputPeerChannel, isInputPeerChat, isInputPeerUser, @@ -48,15 +47,15 @@ export async function getChatMember( ? [] : res.fullChat.participants.participants - const { users } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) for (const m of members) { if ( (user._ === 'inputPeerSelf' && - (users[m.userId] as tl.RawUser).self) || + (peers.user(m.userId) as tl.RawUser).self) || (user._ === 'inputPeerUser' && m.userId === user.userId) ) { - return new ChatMember(this, m, users) + return new ChatMember(this, m, peers) } } @@ -68,8 +67,8 @@ export async function getChatMember( participant: user, }) - const { users } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) - return new ChatMember(this, res.participant, users) + return new ChatMember(this, res.participant, peers) } else throw new MtInvalidPeerTypeError(chatId, 'chat or channel') } diff --git a/packages/client/src/methods/chats/get-chat-members.ts b/packages/client/src/methods/chats/get-chat-members.ts index 331b4921..d93be48e 100644 --- a/packages/client/src/methods/chats/get-chat-members.ts +++ b/packages/client/src/methods/chats/get-chat-members.ts @@ -2,10 +2,10 @@ import { ChatMember, InputPeerLike, MtInvalidPeerTypeError, + PeersIndex, } from '../../types' import { TelegramClient } from '../../client' import { - createUsersChatsIndex, isInputPeerChannel, isInputPeerChat, normalizeToInputChannel, @@ -13,6 +13,7 @@ import { import { assertTypeIs } from '../../utils/type-assertion' import { tl } from '@mtcute/tl' import { ArrayWithTotal } from '../../types' +import Long from 'long' /** * Get a chunk of members of some chat. @@ -94,9 +95,11 @@ export async function getChatMembers( if (params.offset) members = members.slice(params.offset) if (params.limit) members = members.slice(0, params.limit) - const { users } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) - const ret = members.map((m) => new ChatMember(this, m, users)) as ArrayWithTotal + const ret = members.map( + (m) => new ChatMember(this, m, peers) + ) as ArrayWithTotal ret.total = ret.length return ret @@ -140,7 +143,7 @@ export async function getChatMembers( filter, offset: params.offset ?? 0, limit: params.limit ?? 200, - hash: 0, + hash: Long.ZERO, }) assertTypeIs( @@ -149,9 +152,11 @@ export async function getChatMembers( 'channels.channelParticipants' ) - const { users } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) - const ret = res.participants.map((i) => new ChatMember(this, i, users)) as ArrayWithTotal + const ret = res.participants.map( + (i) => new ChatMember(this, i, peers) + ) as ArrayWithTotal ret.total = res.count return ret } diff --git a/packages/client/src/methods/chats/get-nearby-chats.ts b/packages/client/src/methods/chats/get-nearby-chats.ts index de8d5236..bdd95059 100644 --- a/packages/client/src/methods/chats/get-nearby-chats.ts +++ b/packages/client/src/methods/chats/get-nearby-chats.ts @@ -1,5 +1,5 @@ import { TelegramClient } from '../../client' -import { Chat, MtTypeAssertionError } from '../../types' +import { Chat } from '../../types' import { assertTypeIs } from '../../utils/type-assertion' import { getMarkedPeerId } from '@mtcute/core' import { tl } from 'packages/tl' diff --git a/packages/client/src/methods/chats/join-chat.ts b/packages/client/src/methods/chats/join-chat.ts index 8d8032f6..7aeda12b 100644 --- a/packages/client/src/methods/chats/join-chat.ts +++ b/packages/client/src/methods/chats/join-chat.ts @@ -1,10 +1,5 @@ import { TelegramClient } from '../../client' -import { - Chat, - InputPeerLike, - MtNotFoundError, - MtTypeAssertionError, -} from '../../types' +import { Chat, InputPeerLike, MtNotFoundError } from '../../types' import { INVITE_LINK_REGEX, normalizeToInputChannel, diff --git a/packages/client/src/methods/chats/set-chat-default-permissions.ts b/packages/client/src/methods/chats/set-chat-default-permissions.ts index 0ca4d710..f73880ff 100644 --- a/packages/client/src/methods/chats/set-chat-default-permissions.ts +++ b/packages/client/src/methods/chats/set-chat-default-permissions.ts @@ -1,5 +1,5 @@ import { TelegramClient } from '../../client' -import { Chat, InputPeerLike, MtTypeAssertionError } from '../../types' +import { Chat, InputPeerLike } from '../../types' import { tl } from '@mtcute/tl' import { assertIsUpdatesGroup } from '../../utils/updates-utils' diff --git a/packages/client/src/methods/contacts/add-contact.ts b/packages/client/src/methods/contacts/add-contact.ts index 4f082d04..ca6a343f 100644 --- a/packages/client/src/methods/contacts/add-contact.ts +++ b/packages/client/src/methods/contacts/add-contact.ts @@ -2,11 +2,9 @@ import { TelegramClient } from '../../client' import { InputPeerLike, MtInvalidPeerTypeError, - MtTypeAssertionError, User, } from '../../types' import { normalizeToInputUser } from '../../utils/peer-utils' -import { tl } from '@mtcute/tl' import { assertIsUpdatesGroup } from '../../utils/updates-utils' /** diff --git a/packages/client/src/methods/contacts/delete-contacts.ts b/packages/client/src/methods/contacts/delete-contacts.ts index dbc1f004..c27718a2 100644 --- a/packages/client/src/methods/contacts/delete-contacts.ts +++ b/packages/client/src/methods/contacts/delete-contacts.ts @@ -3,11 +3,9 @@ import { MaybeArray } from '@mtcute/core' import { InputPeerLike, MtInvalidPeerTypeError, - MtTypeAssertionError, User, } from '../../types' import { normalizeToInputUser } from '../../utils/peer-utils' -import { tl } from '@mtcute/tl' import { assertIsUpdatesGroup } from '../../utils/updates-utils' /** diff --git a/packages/client/src/methods/contacts/get-contacts.ts b/packages/client/src/methods/contacts/get-contacts.ts index 4dc2fde1..8263500b 100644 --- a/packages/client/src/methods/contacts/get-contacts.ts +++ b/packages/client/src/methods/contacts/get-contacts.ts @@ -1,7 +1,7 @@ import { TelegramClient } from '../../client' import { User } from '../../types' import { assertTypeIs } from '../../utils/type-assertion' -import { tl } from '@mtcute/tl' +import Long from 'long' /** * Get list of contacts from your Telegram contacts list. @@ -10,7 +10,7 @@ import { tl } from '@mtcute/tl' export async function getContacts(this: TelegramClient): Promise { const res = await this.call({ _: 'contacts.getContacts', - hash: 0, + hash: Long.ZERO, }) assertTypeIs('getContacts', res, 'contacts.contacts') diff --git a/packages/client/src/methods/contacts/import-contacts.ts b/packages/client/src/methods/contacts/import-contacts.ts index 81bb2603..a4d2c975 100644 --- a/packages/client/src/methods/contacts/import-contacts.ts +++ b/packages/client/src/methods/contacts/import-contacts.ts @@ -1,7 +1,7 @@ import { TelegramClient } from '../../client' import { tl } from '@mtcute/tl' import { PartialOnly } from '@mtcute/core' -import bigInt from 'big-integer' +import Long from 'long' /** * Import contacts to your Telegram contacts list. @@ -13,11 +13,11 @@ export async function importContacts( this: TelegramClient, contacts: PartialOnly, 'clientId'>[] ): Promise { - let seq = bigInt.zero + let seq = Long.ZERO const contactsNorm: tl.RawInputPhoneContact[] = contacts.map((input) => ({ _: 'inputPhoneContact', - clientId: (seq = seq.plus(1)), + clientId: (seq = seq.add(1)), ...input, })) diff --git a/packages/client/src/methods/dialogs/get-dialogs.ts b/packages/client/src/methods/dialogs/get-dialogs.ts index e2328b0b..202fd6db 100644 --- a/packages/client/src/methods/dialogs/get-dialogs.ts +++ b/packages/client/src/methods/dialogs/get-dialogs.ts @@ -2,12 +2,10 @@ import { TelegramClient } from '../../client' import { Dialog, MtArgumentError, - MtTypeAssertionError, } from '../../types' import { normalizeDate } from '../../utils/misc-utils' -import { createUsersChatsIndex } from '../../utils/peer-utils' import { tl } from '@mtcute/tl' -import { getMarkedPeerId } from '@mtcute/core' +import Long from 'long' /** * Iterate over dialogs. @@ -241,7 +239,7 @@ export async function* getDialogs( offsetPeer, limit: chunkSize, - hash: 0, + hash: Long.ZERO, }) ) if (!dialogs.length) return diff --git a/packages/client/src/methods/dialogs/parse-dialogs.ts b/packages/client/src/methods/dialogs/parse-dialogs.ts index 5e98e62e..8916a1c1 100644 --- a/packages/client/src/methods/dialogs/parse-dialogs.ts +++ b/packages/client/src/methods/dialogs/parse-dialogs.ts @@ -1,7 +1,6 @@ import { TelegramClient } from '../../client' import { tl } from '@mtcute/tl' -import { Dialog, MtTypeAssertionError } from '../../types' -import { createUsersChatsIndex } from '../../utils/peer-utils' +import { Dialog, MtTypeAssertionError, PeersIndex } from '../../types' import { getMarkedPeerId } from '@mtcute/core' /** @internal */ @@ -16,7 +15,7 @@ export function _parseDialogs( 'messages.dialogsNotModified' ) - const { users, chats } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) const messages: Record = {} res.messages.forEach((msg) => { @@ -27,7 +26,5 @@ export function _parseDialogs( return res.dialogs .filter((it) => it._ === 'dialog') - .map( - (it) => new Dialog(this, it as tl.RawDialog, users, chats, messages) - ) + .map((it) => new Dialog(this, it as tl.RawDialog, peers, messages)) } diff --git a/packages/client/src/methods/files/_initialize.ts b/packages/client/src/methods/files/_initialize.ts index 7d2a90f8..f01ea4b1 100644 --- a/packages/client/src/methods/files/_initialize.ts +++ b/packages/client/src/methods/files/_initialize.ts @@ -1,10 +1,10 @@ -import { TelegramConnection } from '@mtcute/core' +import { SessionConnection } from '@mtcute/core' import { TelegramClient } from '../../client' // @extension interface FilesExtension { - _downloadConnections: Record + _downloadConnections: Record } // @initialize diff --git a/packages/client/src/methods/files/normalize-input-media.ts b/packages/client/src/methods/files/normalize-input-media.ts index 5e33a143..72c069df 100644 --- a/packages/client/src/methods/files/normalize-input-media.ts +++ b/packages/client/src/methods/files/normalize-input-media.ts @@ -2,7 +2,6 @@ import { TelegramClient } from '../../client' import { InputMediaLike, isUploadedFile, - MtArgumentError, UploadFileLike, } from '../../types' import { tl } from '@mtcute/tl' @@ -14,9 +13,9 @@ import { } from '@mtcute/file-id' import { extractFileName } from '../../utils/file-utils' import { assertTypeIs } from '../../utils/type-assertion' -import bigInt from 'big-integer' import { normalizeDate } from '../../utils/misc-utils' import { encodeWaveform } from '../../utils/voice-utils' +import Long from 'long' /** * Normalize an {@link InputMediaLike} to `InputMedia`, @@ -182,7 +181,7 @@ export async function _normalizeInputMedia( poll: { _: 'poll', closed: media.closed, - id: bigInt.zero, + id: Long.ZERO, publicVoters: media.public, multipleChoice: media.multiple, quiz: media.type === 'quiz', diff --git a/packages/client/src/methods/files/upload-file.ts b/packages/client/src/methods/files/upload-file.ts index 20566ed4..0f227e33 100644 --- a/packages/client/src/methods/files/upload-file.ts +++ b/packages/client/src/methods/files/upload-file.ts @@ -7,11 +7,11 @@ import { import type { ReadStream } from 'fs' import { Readable } from 'stream' import { determinePartSize, isProbablyPlainText } from '../../utils/file-utils' -import { randomUlong } from '../../utils/misc-utils' import { fromBuffer } from 'file-type' import { tl } from '@mtcute/tl' import { MtArgumentError, UploadFileLike, UploadedFile } from '../../types' import { TelegramClient } from '../../client' +import { randomLong } from '@mtcute/core' let fs: any = null let path: any = null @@ -200,7 +200,7 @@ export async function uploadFile( const hash = this._crypto.createMd5() const partCount = ~~((fileSize + partSize - 1) / partSize) - this._baseLog.debug( + this.log.debug( 'uploading %d bytes file in %d chunks, each %d bytes', fileSize, partCount, @@ -209,7 +209,7 @@ export async function uploadFile( // why is the file id generated by the client? // isn't the server supposed to generate it and handle collisions? - const fileId = randomUlong() + const fileId = randomLong() let pos = 0 for (let idx = 0; idx < partCount; idx++) { diff --git a/packages/client/src/methods/invite-links/edit-invite-link.ts b/packages/client/src/methods/invite-links/edit-invite-link.ts index fc411a7d..e2aec52a 100644 --- a/packages/client/src/methods/invite-links/edit-invite-link.ts +++ b/packages/client/src/methods/invite-links/edit-invite-link.ts @@ -1,6 +1,5 @@ import { TelegramClient } from '../../client' -import { ChatInviteLink, InputPeerLike } from '../../types' -import { createUsersChatsIndex } from '../../utils/peer-utils' +import { ChatInviteLink, InputPeerLike, PeersIndex } from '../../types' import { normalizeDate } from '../../utils/misc-utils' /** @@ -43,7 +42,7 @@ export async function editInviteLink( usageLimit: params.usageLimit, }) - const { users } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) - return new ChatInviteLink(this, res.invite, users) + return new ChatInviteLink(this, res.invite, peers) } diff --git a/packages/client/src/methods/invite-links/get-invite-link-members.ts b/packages/client/src/methods/invite-links/get-invite-link-members.ts index dd9bac74..dca39a31 100644 --- a/packages/client/src/methods/invite-links/get-invite-link-members.ts +++ b/packages/client/src/methods/invite-links/get-invite-link-members.ts @@ -1,6 +1,5 @@ import { TelegramClient } from '../../client' -import { ChatInviteLink, InputPeerLike, User } from '../../types' -import { createUsersChatsIndex } from '../../utils/peer-utils' +import { ChatInviteLink, InputPeerLike, PeersIndex, User } from '../../types' import { tl } from '@mtcute/tl' /** @@ -40,18 +39,18 @@ export async function* getInviteLinkMembers( if (!res.importers.length) break - const { users } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) const last = res.importers[res.importers.length - 1] offsetDate = last.date offsetUser = { _: 'inputUser', userId: last.userId, - accessHash: (users[last.userId] as tl.RawUser).accessHash!, + accessHash: (peers.user(last.userId) as tl.RawUser).accessHash!, } for (const it of res.importers) { - const user = new User(this, users[it.userId]) + const user = new User(this, peers.user(it.userId)) yield { user, diff --git a/packages/client/src/methods/invite-links/get-invite-link.ts b/packages/client/src/methods/invite-links/get-invite-link.ts index 4881af9a..2839abf3 100644 --- a/packages/client/src/methods/invite-links/get-invite-link.ts +++ b/packages/client/src/methods/invite-links/get-invite-link.ts @@ -1,6 +1,5 @@ import { TelegramClient } from '../../client' -import { ChatInviteLink, InputPeerLike } from '../../types' -import { createUsersChatsIndex } from '../../utils/peer-utils' +import { ChatInviteLink, InputPeerLike, PeersIndex } from '../../types' /** * Get detailed information about an invite link @@ -20,7 +19,7 @@ export async function getInviteLink( link, }) - const { users } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) - return new ChatInviteLink(this, res.invite, users) + return new ChatInviteLink(this, res.invite, peers) } diff --git a/packages/client/src/methods/invite-links/get-invite-links.ts b/packages/client/src/methods/invite-links/get-invite-links.ts index a16eee02..f64cc71a 100644 --- a/packages/client/src/methods/invite-links/get-invite-links.ts +++ b/packages/client/src/methods/invite-links/get-invite-links.ts @@ -3,11 +3,9 @@ import { ChatInviteLink, InputPeerLike, MtInvalidPeerTypeError, + PeersIndex, } from '../../types' -import { - createUsersChatsIndex, - normalizeToInputUser, -} from '../../utils/peer-utils' +import { normalizeToInputUser } from '../../utils/peer-utils' import { tl } from '@mtcute/tl' /** @@ -74,14 +72,14 @@ export async function* getInviteLinks( if (!res.invites.length) break - const { users } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) const last = res.invites[res.invites.length - 1] offsetDate = last.date offsetLink = last.link for (const it of res.invites) { - yield new ChatInviteLink(this, it, users) + yield new ChatInviteLink(this, it, peers) } current += res.invites.length diff --git a/packages/client/src/methods/invite-links/get-primary-invite-link.ts b/packages/client/src/methods/invite-links/get-primary-invite-link.ts index 9b15b33d..c4326fc1 100644 --- a/packages/client/src/methods/invite-links/get-primary-invite-link.ts +++ b/packages/client/src/methods/invite-links/get-primary-invite-link.ts @@ -2,9 +2,8 @@ import { TelegramClient } from '../../client' import { ChatInviteLink, InputPeerLike, - MtTypeAssertionError, + MtTypeAssertionError, PeersIndex, } from '../../types' -import { createUsersChatsIndex } from '../../utils/peer-utils' /** * Get primary invite link of a chat @@ -31,7 +30,7 @@ export async function getPrimaryInviteLink( 'false' ) - const { users } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) - return new ChatInviteLink(this, res.invites[0], users) + return new ChatInviteLink(this, res.invites[0], peers) } diff --git a/packages/client/src/methods/invite-links/revoke-invite-link.ts b/packages/client/src/methods/invite-links/revoke-invite-link.ts index 82422cec..a5c52f07 100644 --- a/packages/client/src/methods/invite-links/revoke-invite-link.ts +++ b/packages/client/src/methods/invite-links/revoke-invite-link.ts @@ -1,6 +1,5 @@ import { TelegramClient } from '../../client' -import { ChatInviteLink, InputPeerLike } from '../../types' -import { createUsersChatsIndex } from '../../utils/peer-utils' +import { ChatInviteLink, InputPeerLike, PeersIndex } from '../../types' /** * Revoke an invite link. @@ -25,12 +24,12 @@ export async function revokeInviteLink( revoked: true, }) - const { users } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) const invite = res._ === 'messages.exportedChatInviteReplaced' ? res.newInvite : res.invite - return new ChatInviteLink(this, invite, users) + return new ChatInviteLink(this, invite, peers) } diff --git a/packages/client/src/methods/messages/close-poll.ts b/packages/client/src/methods/messages/close-poll.ts index 0a4d17ed..b9bb2643 100644 --- a/packages/client/src/methods/messages/close-poll.ts +++ b/packages/client/src/methods/messages/close-poll.ts @@ -1,9 +1,8 @@ import { TelegramClient } from '../../client' -import { InputPeerLike, MtTypeAssertionError, Poll } from '../../types' -import { createUsersChatsIndex } from '../../utils/peer-utils' -import bigInt from 'big-integer' +import { InputPeerLike, MtTypeAssertionError, PeersIndex, Poll } from '../../types' import { assertTypeIs } from '../../utils/type-assertion' import { assertIsUpdatesGroup } from '../../utils/updates-utils' +import Long from 'long' /** * Close a poll sent by you. @@ -28,7 +27,7 @@ export async function closePoll( _: 'inputMediaPoll', poll: { _: 'poll', - id: bigInt.zero, + id: Long.ZERO, closed: true, question: '', answers: [], @@ -54,7 +53,7 @@ export async function closePoll( ) } - const { users } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) - return new Poll(this, upd.poll, users, upd.results) + return new Poll(this, upd.poll, peers, upd.results) } diff --git a/packages/client/src/methods/messages/delete-scheduled-messages.ts b/packages/client/src/methods/messages/delete-scheduled-messages.ts index a9060083..540760be 100644 --- a/packages/client/src/methods/messages/delete-scheduled-messages.ts +++ b/packages/client/src/methods/messages/delete-scheduled-messages.ts @@ -1,11 +1,6 @@ import { TelegramClient } from '../../client' import { InputPeerLike } from '../../types' import { MaybeArray } from '@mtcute/core' -import { - isInputPeerChannel, - normalizeToInputChannel, -} from '../../utils/peer-utils' -import { createDummyUpdate } from '../../utils/updates-utils' /** * Delete scheduled messages. diff --git a/packages/client/src/methods/messages/find-in-update.ts b/packages/client/src/methods/messages/find-in-update.ts index 93a5dca5..65b3b9d6 100644 --- a/packages/client/src/methods/messages/find-in-update.ts +++ b/packages/client/src/methods/messages/find-in-update.ts @@ -1,7 +1,6 @@ import { TelegramClient } from '../../client' import { tl } from '@mtcute/tl' -import { Message, MtTypeAssertionError } from '../../types' -import { createUsersChatsIndex } from '../../utils/peer-utils' +import { Message, MtTypeAssertionError, PeersIndex } from '../../types' import { assertIsUpdatesGroup } from '../../utils/updates-utils' /** @internal */ @@ -24,13 +23,12 @@ export function _findMessageInUpdate( u._ === 'updateNewChannelMessage' || u._ === 'updateNewScheduledMessage')) ) { - const { users, chats } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) return new Message( this, u.message, - users, - chats, + peers, u._ === 'updateNewScheduledMessage' ) } diff --git a/packages/client/src/methods/messages/forward-messages.ts b/packages/client/src/methods/messages/forward-messages.ts index 513f1fb3..598d42bc 100644 --- a/packages/client/src/methods/messages/forward-messages.ts +++ b/packages/client/src/methods/messages/forward-messages.ts @@ -5,12 +5,11 @@ import { InputPeerLike, Message, MtArgumentError, - MtTypeAssertionError, + PeersIndex, } from '../../types' -import { MaybeArray } from '@mtcute/core' +import { MaybeArray, randomLong } from '@mtcute/core' import { tl } from '@mtcute/tl' -import { createUsersChatsIndex } from '../../utils/peer-utils' -import { normalizeDate, randomUlong } from '../../utils/misc-utils' +import { normalizeDate } from '../../utils/misc-utils' import { assertIsUpdatesGroup } from '../../utils/updates-utils' /** @@ -234,7 +233,7 @@ export async function forwardMessages( silent: params.silent, scheduleDate: normalizeDate(params.schedule), randomId: [...Array((messages as number[]).length)].map(() => - randomUlong() + randomLong() ), }) @@ -242,7 +241,7 @@ export async function forwardMessages( this._handleUpdate(res, true) - const { users, chats } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) const forwarded: Message[] = [] res.updates.forEach((upd) => { @@ -250,7 +249,14 @@ export async function forwardMessages( case 'updateNewMessage': case 'updateNewChannelMessage': case 'updateNewScheduledMessage': - forwarded.push(new Message(this, upd.message, users, chats)) + forwarded.push( + new Message( + this, + upd.message, + peers, + upd._ === 'updateNewScheduledMessage' + ) + ) break } }) diff --git a/packages/client/src/methods/messages/get-discussion-message.ts b/packages/client/src/methods/messages/get-discussion-message.ts index bc141edf..331e756c 100644 --- a/packages/client/src/methods/messages/get-discussion-message.ts +++ b/packages/client/src/methods/messages/get-discussion-message.ts @@ -1,7 +1,6 @@ import { TelegramClient } from '../../client' -import { InputPeerLike, Message } from '../../types' +import { InputPeerLike, Message, PeersIndex } from '../../types' import { tl } from '@mtcute/tl' -import { createUsersChatsIndex } from '../../utils/peer-utils' /** @internal */ export async function _getDiscussionMessage( @@ -71,7 +70,7 @@ export async function getDiscussionMessage( return null const msg = res.messages[0] - const { users, chats } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) - return new Message(this, msg, users, chats) + return new Message(this, msg, peers) } diff --git a/packages/client/src/methods/messages/get-history.ts b/packages/client/src/methods/messages/get-history.ts index 8e005039..e41c3027 100644 --- a/packages/client/src/methods/messages/get-history.ts +++ b/packages/client/src/methods/messages/get-history.ts @@ -1,7 +1,7 @@ import { TelegramClient } from '../../client' -import { InputPeerLike, Message, MtTypeAssertionError } from '../../types' -import { createUsersChatsIndex } from '../../utils/peer-utils' +import { InputPeerLike, Message, MtTypeAssertionError, PeersIndex } from '../../types' import { normalizeDate } from '../../utils/misc-utils' +import Long from 'long' /** * Retrieve a chunk of the chat history. @@ -70,7 +70,7 @@ export async function getHistory( limit, maxId: 0, minId: 0, - hash: 0, + hash: Long.ZERO, }) if (res._ === 'messages.messagesNotModified') @@ -80,11 +80,11 @@ export async function getHistory( res._ ) - const { users, chats } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) const msgs = res.messages .filter((msg) => msg._ !== 'messageEmpty') - .map((msg) => new Message(this, msg, users, chats)) + .map((msg) => new Message(this, msg, peers)) if (params.reverse) msgs.reverse() diff --git a/packages/client/src/methods/messages/get-messages-unsafe.ts b/packages/client/src/methods/messages/get-messages-unsafe.ts index ba2d4ef6..c53cdcf2 100644 --- a/packages/client/src/methods/messages/get-messages-unsafe.ts +++ b/packages/client/src/methods/messages/get-messages-unsafe.ts @@ -1,10 +1,7 @@ import { TelegramClient } from '../../client' import { MaybeArray } from '@mtcute/core' -import { - createUsersChatsIndex, -} from '../../utils/peer-utils' import { tl } from '@mtcute/tl' -import { Message, MtTypeAssertionError } from '../../types' +import { Message, MtTypeAssertionError, PeersIndex } from '../../types' /** * Get a single message from PM or legacy group by its ID. @@ -73,14 +70,13 @@ export async function getMessagesUnsafe( res._ ) - const { users, chats } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) - const ret = res.messages - .map((msg) => { - if (msg._ === 'messageEmpty') return null + const ret = res.messages.map((msg) => { + if (msg._ === 'messageEmpty') return null - return new Message(this, msg, users, chats) - }) + return new Message(this, msg, peers) + }) return isSingle ? ret[0] : ret } diff --git a/packages/client/src/methods/messages/get-messages.ts b/packages/client/src/methods/messages/get-messages.ts index a8c738d4..0d0a871c 100644 --- a/packages/client/src/methods/messages/get-messages.ts +++ b/packages/client/src/methods/messages/get-messages.ts @@ -1,12 +1,11 @@ import { TelegramClient } from '../../client' import { MaybeArray } from '@mtcute/core' import { - createUsersChatsIndex, isInputPeerChannel, normalizeToInputChannel, } from '../../utils/peer-utils' import { tl } from '@mtcute/tl' -import { Message, InputPeerLike, MtTypeAssertionError } from '../../types' +import { Message, InputPeerLike, MtTypeAssertionError, PeersIndex } from '../../types' /** * Get a single message in chat by its ID @@ -84,7 +83,7 @@ export async function getMessages( res._ ) - const { users, chats } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) const ret = res.messages.map((msg) => { if (msg._ === 'messageEmpty') return null @@ -109,7 +108,7 @@ export async function getMessages( } } - return new Message(this, msg, users, chats) + return new Message(this, msg, peers) }) return isSingle ? ret[0] : ret diff --git a/packages/client/src/methods/messages/get-scheduled-messages.ts b/packages/client/src/methods/messages/get-scheduled-messages.ts index d89f2649..c34e4314 100644 --- a/packages/client/src/methods/messages/get-scheduled-messages.ts +++ b/packages/client/src/methods/messages/get-scheduled-messages.ts @@ -1,12 +1,11 @@ import { TelegramClient } from '../../client' import { MaybeArray } from '@mtcute/core' import { - createUsersChatsIndex, - isInputPeerChannel, - normalizeToInputChannel, -} from '../../utils/peer-utils' -import { tl } from '@mtcute/tl' -import { Message, InputPeerLike, MtTypeAssertionError } from '../../types' + Message, + InputPeerLike, + MtTypeAssertionError, + PeersIndex, +} from '../../types' /** * Get a single scheduled message in chat by its ID @@ -60,12 +59,12 @@ export async function getScheduledMessages( res._ ) - const { users, chats } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) const ret = res.messages.map((msg) => { if (msg._ === 'messageEmpty') return null - return new Message(this, msg, users, chats) + return new Message(this, msg, peers, true) }) return isSingle ? ret[0] : ret diff --git a/packages/client/src/methods/messages/normalize-inline.ts b/packages/client/src/methods/messages/normalize-inline.ts index a1334e98..2b73ab97 100644 --- a/packages/client/src/methods/messages/normalize-inline.ts +++ b/packages/client/src/methods/messages/normalize-inline.ts @@ -1,11 +1,11 @@ import { TelegramClient } from '../../client' import { tl } from '@mtcute/tl' -import { TelegramConnection } from '@mtcute/core' +import { SessionConnection } from '@mtcute/core' import { parseInlineMessageId } from '../../utils/inline-utils' // @extension interface InlineExtension { - _connectionsForInline: Record + _connectionsForInline: Record } // @initialize @@ -17,7 +17,7 @@ function _initializeInline(this: TelegramClient) { export async function _normalizeInline( this: TelegramClient, id: string | tl.TypeInputBotInlineMessageID -): Promise<[tl.TypeInputBotInlineMessageID, TelegramConnection]> { +): Promise<[tl.TypeInputBotInlineMessageID, SessionConnection]> { if (typeof id === 'string') { id = parseInlineMessageId(id) } diff --git a/packages/client/src/methods/messages/search-global.ts b/packages/client/src/methods/messages/search-global.ts index 1307152c..96fc9e08 100644 --- a/packages/client/src/methods/messages/search-global.ts +++ b/packages/client/src/methods/messages/search-global.ts @@ -1,7 +1,6 @@ import { TelegramClient } from '../../client' -import { Message, MtTypeAssertionError } from '../../types' +import { Message, MtTypeAssertionError, PeersIndex } from '../../types' import { tl } from '@mtcute/tl' -import { createUsersChatsIndex } from '../../utils/peer-utils' import { SearchFilters } from '../../types' /** @@ -77,11 +76,11 @@ export async function* searchGlobal( res._ ) - const { users, chats } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) const msgs = res.messages .filter((msg) => msg._ !== 'messageEmpty') - .map((msg) => new Message(this, msg, users, chats)) + .map((msg) => new Message(this, msg, peers)) if (!msgs.length) break diff --git a/packages/client/src/methods/messages/search-messages.ts b/packages/client/src/methods/messages/search-messages.ts index 6670854d..ea05a1b2 100644 --- a/packages/client/src/methods/messages/search-messages.ts +++ b/packages/client/src/methods/messages/search-messages.ts @@ -1,8 +1,8 @@ import { TelegramClient } from '../../client' -import { InputPeerLike, Message, MtTypeAssertionError } from '../../types' +import { InputPeerLike, Message, MtTypeAssertionError, PeersIndex } from '../../types' import { tl } from '@mtcute/tl' -import { createUsersChatsIndex } from '../../utils/peer-utils' import { SearchFilters } from '../../types' +import Long from 'long' /** * Search for messages inside a specific chat @@ -88,7 +88,7 @@ export async function* searchMessages( minId: 0, maxId: 0, fromId: fromUser, - hash: 0, + hash: Long.ZERO, }) if (res._ === 'messages.messagesNotModified') @@ -98,11 +98,11 @@ export async function* searchMessages( res._ ) - const { users, chats } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) const msgs = res.messages .filter((msg) => msg._ !== 'messageEmpty') - .map((msg) => new Message(this, msg, users, chats)) + .map((msg) => new Message(this, msg, peers)) if (!msgs.length) break diff --git a/packages/client/src/methods/messages/send-media-group.ts b/packages/client/src/methods/messages/send-media-group.ts index 0c29fd4f..0e336e35 100644 --- a/packages/client/src/methods/messages/send-media-group.ts +++ b/packages/client/src/methods/messages/send-media-group.ts @@ -3,18 +3,17 @@ import { BotKeyboard, InputFileLike, InputMediaLike, InputPeerLike, - Message, MtArgumentError, + Message, MtArgumentError, PeersIndex, ReplyMarkup, } from '../../types' import { normalizeDate, normalizeMessageId, - randomUlong, } from '../../utils/misc-utils' import { tl } from '@mtcute/tl' import { assertIsUpdatesGroup } from '../../utils/updates-utils' -import { createUsersChatsIndex } from '../../utils/peer-utils' import { MessageNotFoundError } from '@mtcute/tl/errors' +import { randomLong } from '@mtcute/core' /** * Send a group of media. @@ -165,7 +164,7 @@ export async function sendMediaGroup( multiMedia.push({ _: 'inputSingleMedia', - randomId: randomUlong(), + randomId: randomLong(), media: inputMedia, message, entities, @@ -178,7 +177,7 @@ export async function sendMediaGroup( multiMedia, silent: params.silent, replyToMsgId: replyTo, - randomId: randomUlong(), + randomId: randomLong(), scheduleDate: normalizeDate(params.schedule), replyMarkup, clearDraft: params.clearDraft, @@ -187,7 +186,7 @@ export async function sendMediaGroup( assertIsUpdatesGroup('_findMessageInUpdate', res) this._handleUpdate(res, true) - const { users, chats } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) const msgs = res.updates .filter( @@ -201,8 +200,7 @@ export async function sendMediaGroup( new Message( this, (u as any).message, - users, - chats, + peers, u._ === 'updateNewScheduledMessage' ) ) diff --git a/packages/client/src/methods/messages/send-media.ts b/packages/client/src/methods/messages/send-media.ts index 0940592e..22e384a6 100644 --- a/packages/client/src/methods/messages/send-media.ts +++ b/packages/client/src/methods/messages/send-media.ts @@ -1,18 +1,17 @@ import { TelegramClient } from '../../client' import { - BotKeyboard, FormattedString, + BotKeyboard, + FormattedString, InputMediaLike, InputPeerLike, - Message, MtArgumentError, + Message, + MtArgumentError, ReplyMarkup, } from '../../types' -import { - normalizeDate, - normalizeMessageId, - randomUlong, -} from '../../utils/misc-utils' +import { normalizeDate, normalizeMessageId } from '../../utils/misc-utils' import { tl } from '@mtcute/tl' import { MessageNotFoundError } from '@mtcute/tl/errors' +import { randomLong } from '@mtcute/core' /** * Send a single media (a photo or a document-based media) @@ -157,8 +156,7 @@ export async function sendMedia( const msg = await this.getMessages(peer, replyTo) - if (!msg) - throw new MessageNotFoundError() + if (!msg) throw new MessageNotFoundError() } const res = await this.call({ @@ -167,7 +165,7 @@ export async function sendMedia( media: inputMedia, silent: params.silent, replyToMsgId: replyTo, - randomId: randomUlong(), + randomId: randomLong(), scheduleDate: normalizeDate(params.schedule), replyMarkup, message, diff --git a/packages/client/src/methods/messages/send-scheduled.ts b/packages/client/src/methods/messages/send-scheduled.ts index ae37cc3f..274b4463 100644 --- a/packages/client/src/methods/messages/send-scheduled.ts +++ b/packages/client/src/methods/messages/send-scheduled.ts @@ -1,8 +1,7 @@ -import { InputPeerLike, Message } from '../../types' +import { InputPeerLike, Message, PeersIndex } from '../../types' import { MaybeArray } from '@mtcute/core' import { TelegramClient } from '../../client' import { assertIsUpdatesGroup } from '../../utils/updates-utils' -import { createUsersChatsIndex } from '../../utils/peer-utils' /** * Send s previously scheduled message. @@ -56,7 +55,7 @@ export async function sendScheduled( assertIsUpdatesGroup('sendScheduled', res) this._handleUpdate(res, true) - const { users, chats } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) const msgs = res.updates .filter( @@ -69,8 +68,7 @@ export async function sendScheduled( new Message( this, (u as any).message, - users, - chats + peers ) ) diff --git a/packages/client/src/methods/messages/send-text.ts b/packages/client/src/methods/messages/send-text.ts index 78c205bb..da2df0ca 100644 --- a/packages/client/src/methods/messages/send-text.ts +++ b/packages/client/src/methods/messages/send-text.ts @@ -4,19 +4,16 @@ import { inputPeerToPeer } from '../../utils/peer-utils' import { normalizeDate, normalizeMessageId, - randomUlong, } from '../../utils/misc-utils' import { InputPeerLike, Message, BotKeyboard, ReplyMarkup, - UsersIndex, MtTypeAssertionError, - ChatsIndex, - MtArgumentError, FormattedString, + MtArgumentError, FormattedString, PeersIndex, } from '../../types' -import { getMarkedPeerId, MessageNotFoundError } from '@mtcute/core' +import { getMarkedPeerId, MessageNotFoundError, randomLong } from '@mtcute/core' import { createDummyUpdate } from '../../utils/updates-utils' /** @@ -143,7 +140,7 @@ export async function sendText( noWebpage: params.disableWebPreview, silent: params.silent, replyToMsgId: replyTo, - randomId: randomUlong(), + randomId: randomLong(), scheduleDate: normalizeDate(params.schedule), replyMarkup, message, @@ -170,8 +167,7 @@ export async function sendText( this._date = res.date this._handleUpdate(createDummyUpdate(res.pts, res.ptsCount)) - const users: UsersIndex = {} - const chats: ChatsIndex = {} + const peers = new PeersIndex() const fetchPeer = async ( peer: tl.TypePeer | tl.TypeInputPeer @@ -206,13 +202,13 @@ export async function sendText( switch (cached._) { case 'user': - users[cached.id] = cached + peers.users[cached.id] = cached break case 'chat': case 'chatForbidden': case 'channel': case 'channelForbidden': - chats[cached.id] = cached + peers.chats[cached.id] = cached break default: throw new MtTypeAssertionError( @@ -226,7 +222,7 @@ export async function sendText( await fetchPeer(peer) await fetchPeer(msg.fromId!) - const ret = new Message(this, msg, users, chats) + const ret = new Message(this, msg, peers) this._pushConversationMessage(ret) return ret } diff --git a/packages/client/src/methods/messages/send-vote.ts b/packages/client/src/methods/messages/send-vote.ts index 41565ba9..5330de59 100644 --- a/packages/client/src/methods/messages/send-vote.ts +++ b/packages/client/src/methods/messages/send-vote.ts @@ -3,10 +3,10 @@ import { InputPeerLike, MtArgumentError, MtTypeAssertionError, + PeersIndex, Poll, } from '../../types' import { MaybeArray, MessageNotFoundError } from '@mtcute/core' -import { createUsersChatsIndex } from '../../utils/peer-utils' import { assertTypeIs } from '../../utils/type-assertion' import { assertIsUpdatesGroup } from '../../utils/updates-utils' @@ -74,7 +74,7 @@ export async function sendVote( ) } - const { users } = createUsersChatsIndex(res) + const peers = PeersIndex.from(res) - return new Poll(this, upd.poll, users, upd.results) + return new Poll(this, upd.poll, peers, upd.results) } diff --git a/packages/client/src/methods/stickers/add-sticker-to-set.ts b/packages/client/src/methods/stickers/add-sticker-to-set.ts index c6511c16..daa6eb6a 100644 --- a/packages/client/src/methods/stickers/add-sticker-to-set.ts +++ b/packages/client/src/methods/stickers/add-sticker-to-set.ts @@ -1,5 +1,5 @@ import { TelegramClient } from '../../client' -import { InputFileLike, InputStickerSetItem, StickerSet } from '../../types' +import { InputStickerSetItem, StickerSet } from '../../types' import { tl } from '@mtcute/tl' const MASK_POS = { diff --git a/packages/client/src/methods/stickers/get-installed-stickers.ts b/packages/client/src/methods/stickers/get-installed-stickers.ts index 5daebdb0..c643a59b 100644 --- a/packages/client/src/methods/stickers/get-installed-stickers.ts +++ b/packages/client/src/methods/stickers/get-installed-stickers.ts @@ -1,6 +1,7 @@ import { TelegramClient } from '../../client' import { StickerSet } from '../../types' import { assertTypeIs } from '../../utils/type-assertion' +import Long from 'long' /** * Get a list of all installed sticker packs @@ -17,7 +18,7 @@ export async function getInstalledStickers( ): Promise { const res = await this.call({ _: 'messages.getAllStickers', - hash: 0, + hash: Long.ZERO, }) assertTypeIs('getInstalledStickers', res, 'messages.allStickers') diff --git a/packages/client/src/methods/updates.ts b/packages/client/src/methods/updates.ts index 155a2c76..c0d8fd02 100644 --- a/packages/client/src/methods/updates.ts +++ b/packages/client/src/methods/updates.ts @@ -1,27 +1,68 @@ import { tl } from '@mtcute/tl' import { TelegramClient } from '../client' -import { - createUsersChatsIndex, - normalizeToInputChannel, -} from '../utils/peer-utils' +import { normalizeToInputChannel } from '../utils/peer-utils' import { extractChannelIdFromUpdate } from '../utils/misc-utils' import { AsyncLock, getBarePeerId, getMarkedPeerId, markedPeerIdToBare, - MAX_CHANNEL_ID, + toggleChannelIdMark, } from '@mtcute/core' -import { isDummyUpdate } from '../utils/updates-utils' -import { ChatsIndex, UsersIndex } from '../types' +import { MtArgumentError, PeersIndex } from '../types' import { _parseUpdate } from '../utils/parse-update' import { Logger } from '@mtcute/core/src/utils/logger' +// @copy +import { ConditionVariable } from '@mtcute/core/src/utils/condition-variable' +// @copy +import { Deque, SortedLinkedList } from '@mtcute/core' +// @copy +import { RpsMeter } from '../utils/rps-meter' // code in this file is very bad, thanks to Telegram's awesome updates mechanism +// you will notice that stuff related to postponed updates +// (quote, "It may be useful to wait up to 0.5 seconds in this situation +// and abort the sync in case a new update arrives, that fills the gap") +// +// that is because i've tested a bit on a high-load bot, and logs have shown +// that out of >11k updates received with a gap, only 82 were filled in the +// span of 0.7 (not 0.5!) seconds. +// thus, i don't think it is worth the added complexity + +// @copy +interface PendingUpdateContainer { + upd: tl.TypeUpdates + seqStart: number + seqEnd: number +} + +// @copy +interface PendingUpdate { + update: tl.TypeUpdate + channelId?: number + pts?: number + ptsBefore?: number + qtsBefore?: number + timeout?: number + peers?: PeersIndex +} + // @extension interface UpdatesState { + _updatesLoopActive: boolean + _updatesLoopCv: ConditionVariable + + _pendingUpdateContainers: SortedLinkedList + _pendingPtsUpdates: SortedLinkedList + _pendingPtsUpdatesPostponed: SortedLinkedList + _pendingQtsUpdates: SortedLinkedList + _pendingQtsUpdatesPostponed: SortedLinkedList + _pendingUnorderedUpdates: Deque + _updLock: AsyncLock + _rpsIncoming?: RpsMeter + _rpsProcessing?: RpsMeter // accessing storage every time might be expensive, // so store everything here, and load & save @@ -51,6 +92,26 @@ interface UpdatesState { // @initialize function _initializeUpdates(this: TelegramClient) { + this._updatesLoopActive = false + this._updatesLoopCv = new ConditionVariable() + + this._pendingUpdateContainers = new SortedLinkedList( + (a, b) => a.seqStart - b.seqStart + ) + this._pendingPtsUpdates = new SortedLinkedList( + (a, b) => a.ptsBefore! - b.ptsBefore! + ) + this._pendingPtsUpdatesPostponed = new SortedLinkedList( + (a, b) => a.ptsBefore! - b.ptsBefore! + ) + this._pendingQtsUpdates = new SortedLinkedList( + (a, b) => a.qtsBefore! - b.qtsBefore! + ) + this._pendingQtsUpdatesPostponed = new SortedLinkedList( + (a, b) => a.qtsBefore! - b.qtsBefore! + ) + this._pendingUnorderedUpdates = new Deque() + this._updLock = new AsyncLock() // we dont need to initialize state fields since // they are always loaded either from the server, or from storage. @@ -67,6 +128,63 @@ function _initializeUpdates(this: TelegramClient) { this._updsLog = this.log.create('updates') } +/** + * Enable RPS meter. + * Only available in NodeJS v10.7.0 and newer + * + * > **Note**: This may have negative impact on performance + * + * @param size Sampling size + * @param time Window time + * @internal + */ +export function enableRps( + this: TelegramClient, + size?: number, + time?: number +): void { + this._rpsIncoming = new RpsMeter(size, time) + this._rpsProcessing = new RpsMeter(size, time) +} + +/** + * Get current average incoming RPS + * + * Incoming RPS is calculated based on + * incoming update containers. Normally, + * they should be around the same, except + * rare situations when processing rps + * may peak. + * + * @internal + */ +export function getCurrentRpsIncoming(this: TelegramClient): number { + if (!this._rpsIncoming) + throw new MtArgumentError( + 'RPS meter is not enabled, use .enableRps() first' + ) + return this._rpsIncoming.getRps() +} + +/** + * Get current average processing RPS + * + * Processing RPS is calculated based on + * dispatched updates. Normally, + * they should be around the same, except + * rare situations when processing rps + * may peak. + * + * @internal + */ +export function getCurrentRpsProcessing(this: TelegramClient): number { + if (!this._rpsProcessing) + throw new MtArgumentError( + 'RPS meter is not enabled, use .enableRps() first' + ) + return this._rpsProcessing.getRps() +} + /** * Fetch updates state from the server. * Meant to be used right after authorization, @@ -74,11 +192,23 @@ function _initializeUpdates(this: TelegramClient) { * @internal */ export async function _fetchUpdatesState(this: TelegramClient): Promise { - let state = await this.call({ _: 'updates.getState' }) + await this._updLock.acquire() - // for some unknown fucking reason getState may return old qts - // call getDifference to get actual values :shrug: - loop: for (;;) { + this._updsLog.debug('fetching initial state') + + try { + let state = await this.call({ _: 'updates.getState' }) + + this._updsLog.debug( + 'updates.getState returned state: pts=%d, qts=%d, date=%d, seq=%d', + state.pts, + state.qts, + state.date, + state.seq + ) + + // for some unknown fucking reason getState may return old qts + // call getDifference to get actual values :shrug: const diff = await this.call({ _: 'updates.getDifference', pts: state.pts, @@ -88,7 +218,7 @@ export async function _fetchUpdatesState(this: TelegramClient): Promise { switch (diff._) { case 'updates.differenceEmpty': - break loop + break case 'updates.differenceTooLong': // shouldn't happen, but who knows? ;(state as tl.Mutable).pts = diff.pts break @@ -97,22 +227,25 @@ export async function _fetchUpdatesState(this: TelegramClient): Promise { break default: state = diff.state - break loop } + + this._qts = state.qts + this._pts = state.pts + this._date = state.date + this._seq = state.seq + + this._updsLog.debug( + 'loaded initial state: pts=%d, qts=%d, date=%d, seq=%d', + state.pts, + state.qts, + state.date, + state.seq + ) + } catch (e) { + this._updsLog.error('failed to fetch updates state: %s', e) } - this._qts = state.qts - this._pts = state.pts - this._date = state.date - this._seq = state.seq - - this._updsLog.debug( - 'loaded initial state: pts=%d, qts=%d, date=%d, seq=%d', - state.pts, - state.qts, - state.date, - state.seq - ) + this._updLock.release() } /** @@ -127,6 +260,13 @@ export async function _loadStorage(this: TelegramClient): Promise { this._qts = this._oldQts = state[1] this._date = this._oldDate = state[2] this._seq = this._oldSeq = state[3] + this._updsLog.debug( + 'loaded stored state: pts=%d, qts=%d, date=%d, seq=%d', + state[0], + state[1], + state[2], + state[3] + ) } // if no state, don't bother initializing properties // since that means that there is no authorization, @@ -139,6 +279,39 @@ export async function _loadStorage(this: TelegramClient): Promise { } } +/** + * **ADVANCED** + * + * Manually start updates loop. + * Usually done automatically inside {@link start} + * @internal + */ +export function startUpdatesLoop(this: TelegramClient): void { + if (this._updatesLoopActive) return + + this._updatesLoopActive = true + this._updatesLoop().catch((err) => this._emitError(err)) +} + +/** + * **ADVANCED** + * + * Manually stop updates loop. + * Usually done automatically when stopping the client with {@link close} + * @internal + */ +export function stopUpdatesLoop(this: TelegramClient): void { + if (!this._updatesLoopActive) return + + this._updatesLoopActive = false + this._updatesLoopCv.notify() +} + +/** @internal */ +export function _onStop(this: TelegramClient): void { + this.stopUpdatesLoop() +} + /** * @internal */ @@ -199,144 +372,136 @@ export async function _saveStorage( export function _dispatchUpdate( this: TelegramClient, update: tl.TypeUpdate | tl.TypeMessage, - users: UsersIndex, - chats: ChatsIndex + peers: PeersIndex ): void { - this.emit('raw_update', update, users, chats) + this.emit('raw_update', update, peers) - const parsed = _parseUpdate(this, update, users, chats) + const parsed = _parseUpdate(this, update, peers) if (parsed) { this.emit('update', parsed) this.emit(parsed.name, parsed.data) } } -interface NoDispatchIndex { - // channel id or 0 => msg id - msg: Record> - // channel id or 0 => pts - pts: Record> - qts: Record -} +// interface NoDispatchIndex { +// // channel id or 0 => msg id +// msg: Record> +// // channel id or 0 => pts +// pts: Record> +// qts: Record +// } // creating and using a no-dispatch index is pretty expensive, // but its not a big deal since it's actually rarely needed -function _createNoDispatchIndex( - updates?: tl.TypeUpdates | tl.TypeUpdate -): NoDispatchIndex | undefined { - if (!updates) return undefined - const ret: NoDispatchIndex = { - msg: {}, - pts: {}, - qts: {}, - } - - function addUpdate(upd: tl.TypeUpdate) { - const cid = extractChannelIdFromUpdate(upd) ?? 0 - const pts = 'pts' in upd ? upd.pts : undefined - - if (pts) { - if (!ret.pts[cid]) ret.pts[cid] = {} - ret.pts[cid][pts] = true - } - - const qts = 'qts' in upd ? upd.qts : undefined - if (qts) { - ret.qts[qts] = true - } - - switch (upd._) { - case 'updateNewMessage': - case 'updateNewChannelMessage': { - const cid = - upd.message.peerId?._ === 'peerChannel' - ? upd.message.peerId.channelId - : 0 - if (!ret.msg[cid]) ret.msg[cid] = {} - ret.msg[cid][upd.message.id] = true - break - } - } - } - - switch (updates._) { - case 'updates': - case 'updatesCombined': - updates.updates.forEach(addUpdate) - break - case 'updateShortMessage': - case 'updateShortChatMessage': - case 'updateShortSentMessage': - // these updates are only used for non-channel messages, so we use 0 - if (!ret.msg[0]) ret.msg[0] = {} - if (!ret.pts[0]) ret.pts[0] = {} - - ret.msg[0][updates.id] = true - ret.pts[0][updates.pts] = true - break - case 'updateShort': - addUpdate(updates.update) - break - case 'updatesTooLong': - break - default: - addUpdate(updates) - break - } - - return ret -} +// function _createNoDispatchIndex( +// updates?: tl.TypeUpdates | tl.TypeUpdate +// ): NoDispatchIndex | undefined { +// if (!updates) return undefined +// const ret: NoDispatchIndex = { +// msg: {}, +// pts: {}, +// qts: {}, +// } +// +// function addUpdate(upd: tl.TypeUpdate) { +// const cid = extractChannelIdFromUpdate(upd) ?? 0 +// const pts = 'pts' in upd ? upd.pts : undefined +// +// if (pts) { +// if (!ret.pts[cid]) ret.pts[cid] = {} +// ret.pts[cid][pts] = true +// } +// +// const qts = 'qts' in upd ? upd.qts : undefined +// if (qts) { +// ret.qts[qts] = true +// } +// +// switch (upd._) { +// case 'updateNewMessage': +// case 'updateNewChannelMessage': { +// const cid = +// upd.message.peerId?._ === 'peerChannel' +// ? upd.message.peerId.channelId +// : 0 +// if (!ret.msg[cid]) ret.msg[cid] = {} +// ret.msg[cid][upd.message.id] = true +// break +// } +// } +// } +// +// switch (updates._) { +// case 'updates': +// case 'updatesCombined': +// updates.updates.forEach(addUpdate) +// break +// case 'updateShortMessage': +// case 'updateShortChatMessage': +// case 'updateShortSentMessage': +// // these updates are only used for non-channel messages, so we use 0 +// if (!ret.msg[0]) ret.msg[0] = {} +// if (!ret.pts[0]) ret.pts[0] = {} +// +// ret.msg[0][updates.id] = true +// ret.pts[0][updates.pts] = true +// break +// case 'updateShort': +// addUpdate(updates.update) +// break +// case 'updatesTooLong': +// break +// default: +// addUpdate(updates) +// break +// } +// +// return ret +// } async function _replaceMinPeers( this: TelegramClient, - upd: tl.TypeUpdates + peers: PeersIndex ): Promise { - switch (upd._) { - case 'updates': - case 'updatesCombined': { - for (let i = 0; i < upd.users.length; i++) { - if ((upd.users[i] as any).min) { - const cached = await this.storage.getFullPeerById( - upd.users[i].id - ) - if (!cached) return false - upd.users[i] = cached as tl.TypeUser - } - } - - for (let i = 0; i < upd.chats.length; i++) { - const c = upd.chats[i] - if ((c as any).min) { - let id: number - switch (c._) { - case 'channel': - case 'channelForbidden': - id = MAX_CHANNEL_ID - c.id - break - default: - id = -c.id - } - - const cached = await this.storage.getFullPeerById(id) - if (!cached) return false - upd.chats[i] = cached as tl.TypeChat - } - } + for (const key in peers.users) { + const user = peers.users[key] as any + if (user.min) { + const cached = await this.storage.getFullPeerById(user.id) + if (!cached) return false + peers.users[key] = cached as tl.TypeUser } } + for (const key in peers.chats) { + const c = peers.chats[key] as any + + if (c.min) { + let id: number + switch (c._) { + case 'channel': + case 'channelForbidden': + id = toggleChannelIdMark(c.id) + break + default: + id = -c.id + } + + const cached = await this.storage.getFullPeerById(id) + if (!cached) return false + peers.chats[key] = cached as tl.TypeChat + } + } + + peers.hasMin = false + return true } async function _fetchPeersForShort( this: TelegramClient, upd: tl.TypeUpdate | tl.RawMessage | tl.RawMessageService -): Promise<{ - users: UsersIndex - chats: ChatsIndex -} | null> { - const users: UsersIndex = {} - const chats: ChatsIndex = {} +): Promise { + const peers = new PeersIndex() const fetchPeer = async (peer?: tl.TypePeer | number) => { if (!peer) return true @@ -351,9 +516,9 @@ async function _fetchPeersForShort( const cached = await this.storage.getFullPeerById(marked) if (!cached) return false if (marked > 0) { - users[bare] = cached as tl.TypeUser + peers.users[bare] = cached as tl.TypeUser } else { - chats[bare] = cached as tl.TypeChat + peers.chats[bare] = cached as tl.TypeChat } return true } @@ -372,7 +537,8 @@ async function _fetchPeersForShort( : upd.message if (msg._ === 'messageEmpty') return null - // ref: https://github.com/tdlib/td/blob/e1ebf743988edfcf4400cd5d33a664ff941dc13e/td/telegram/UpdatesManager.cpp#L412 + // ref: https://github.com/tdlib/td/blob/master/td/telegram/UpdatesManager.cpp + // (search by UpdatesManager::is_acceptable_update) if (!(await fetchPeer(msg.peerId))) return null if (!(await fetchPeer(msg.fromId))) return null if (msg.replyTo && !(await fetchPeer(msg.replyTo.replyToPeerId))) @@ -423,7 +589,7 @@ async function _fetchPeersForShort( case 'messageActionChatMigrateTo': if ( !(await fetchPeer( - MAX_CHANNEL_ID - msg.action.channelId + toggleChannelIdMark(msg.action.channelId) )) ) return null @@ -449,21 +615,1346 @@ async function _fetchPeersForShort( } } - return { users, chats } + return peers } -async function _loadDifference( +function _isMessageEmpty(upd: any): boolean { + return upd._ === 'messageEmpty' || upd.message?._ === 'messageEmpty' +} + +// async function _loadDifference( +// this: TelegramClient, +// noDispatch?: NoDispatchIndex +// ): Promise { +// for (;;) { +// const diff = await this.call({ +// _: 'updates.getDifference', +// pts: this._pts!, +// date: this._date!, +// qts: this._qts!, +// }) +// +// switch (diff._) { +// case 'updates.differenceEmpty': +// this._updsLog.debug( +// 'updates.getDifference returned updates.differenceEmpty' +// ) +// return +// case 'updates.differenceTooLong': +// this._pts = diff.pts +// this._updsLog.debug( +// 'updates.getDifference returned updates.differenceTooLong' +// ) +// return +// } +// +// const state = +// diff._ === 'updates.difference' +// ? diff.state +// : diff.intermediateState +// +// this._updsLog.debug( +// 'updates.getDifference returned %d messages, %d updates. new pts: %d, qts: %d, seq: %d, final: %b', +// diff.newMessages.length, +// diff.otherUpdates.length, +// state.pts, +// state.qts, +// state.seq, +// diff._ === 'updates.difference' +// ) +// +// await this._cachePeersFrom(diff) +// +// const { users, chats } = createUsersChatsIndex(diff) +// +// diff.newMessages.forEach((message) => { +// this._updsLog.debug( +// 'processing message %d in %j (%s) from common diff', +// message.id, +// message.peerId, +// message._ +// ) +// +// if (noDispatch) { +// // in fact this field seems to only be used for PMs and legacy chats, +// // so `cid` will be 0 always, but that might change :shrug: +// const cid = +// message.peerId?._ === 'peerChannel' +// ? message.peerId.channelId +// : 0 +// if (noDispatch.msg[cid]?.[message.id]) return +// } +// if (message._ === 'messageEmpty') return +// +// this._dispatchUpdate(message, users, chats) +// }) +// +// for (const upd of diff.otherUpdates) { +// if (upd._ === 'updateChannelTooLong') { +// this._updsLog.debug( +// 'received updateChannelTooLong for channel %d (pts = %d), fetching diff', +// upd.channelId, +// upd.pts +// ) +// await _loadChannelDifference.call( +// this, +// upd.channelId, +// noDispatch, +// upd.pts +// ) +// continue +// } +// +// const cid = extractChannelIdFromUpdate(upd) +// const pts = 'pts' in upd ? upd.pts : undefined +// const ptsCount = 'ptsCount' in upd ? upd.ptsCount : undefined +// const qts = 'qts' in upd ? upd.qts : undefined +// +// this._updsLog.debug( +// 'processing %s from common diff, cid: %d, pts: %d, pts_count: %d, qts: %d', +// upd._, +// cid, +// pts, +// ptsCount, +// qts +// ) +// +// if (cid && pts !== undefined && ptsCount !== undefined) { +// // check that this pts is in fact the next one +// // we only need to check this for channels since for +// // common pts it is guaranteed by the server +// // (however i would not really trust telegram server lol) +// +// const ptsBefore = pts - ptsCount +// +// let localPts: number | null = null +// if (cid in this._cpts) localPts = this._cpts[cid] +// else if (this._catchUpChannels) { +// const saved = await this.storage.getChannelPts(cid) +// if (saved) { +// this._cpts[cid] = localPts = saved +// } +// } +// +// if (localPts) { +// if (localPts > ptsBefore) { +// this._updsLog.debug( +// 'ignoring %s (in channel %d) because already handled (by pts: exp %d, got %d)', +// upd._, +// cid, +// localPts, +// ptsBefore +// ) +// continue +// } +// if (localPts < ptsBefore) { +// this._updsLog.debug( +// 'fetching channel %d difference because gap detected (by pts: exp %d, got %d)', +// cid, +// localPts, +// ptsBefore +// ) +// await _loadChannelDifference.call( +// this, +// cid, +// noDispatch, +// ptsBefore +// ) +// continue +// } +// } +// +// this._cpts[cid] = pts +// this._cptsMod[cid] = pts +// } +// +// if (noDispatch) { +// if (pts && noDispatch.pts[cid ?? 0]?.[pts]) continue +// if (qts && noDispatch.qts[qts]) continue +// } +// +// if (_isMessageEmpty(upd)) { +// continue +// } +// +// this._dispatchUpdate(upd, users, chats) +// } +// +// this._pts = state.pts +// this._qts = state.qts +// this._seq = state.seq +// this._date = state.date +// +// if (diff._ === 'updates.difference') return +// } +// } + +// async function _loadChannelDifference( +// this: TelegramClient, +// channelId: number, +// noDispatch?: NoDispatchIndex, +// fallbackPts?: number +// ): Promise { +// let _pts: number | null | undefined = this._cpts[channelId] +// if (!_pts && this._catchUpChannels) { +// _pts = await this.storage.getChannelPts(channelId) +// } +// if (!_pts) _pts = fallbackPts +// +// if (!_pts) { +// this._updsLog.warn( +// 'getChannelDifference failed for channel %d: base pts not available', +// channelId +// ) +// return +// } +// +// let channel +// try { +// channel = normalizeToInputChannel( +// await this.resolvePeer(MAX_CHANNEL_ID - channelId) +// )! +// } catch (e) { +// this._updsLog.warn( +// 'getChannelDifference failed for channel %d: input peer not found', +// channelId +// ) +// return +// } +// +// // to make TS happy +// let pts = _pts +// let limit = this._isBot ? 100000 : 100 +// +// if (pts <= 0) { +// pts = 1 +// limit = 1 +// } +// +// for (;;) { +// const diff = await this.call({ +// _: 'updates.getChannelDifference', +// channel, +// pts, +// limit, +// filter: { _: 'channelMessagesFilterEmpty' }, +// }) +// +// if (diff._ === 'updates.channelDifferenceEmpty') { +// this._updsLog.debug( +// 'getChannelDifference (cid = %d) returned channelDifferenceEmpty', +// channelId +// ) +// break +// } +// +// await this._cachePeersFrom(diff) +// +// const { users, chats } = createUsersChatsIndex(diff) +// +// if (diff._ === 'updates.channelDifferenceTooLong') { +// if (diff.dialog._ === 'dialog') { +// pts = diff.dialog.pts! +// } +// +// this._updsLog.warn( +// 'getChannelDifference (cid = %d) returned channelDifferenceTooLong. new pts: %d, recent msgs: %d', +// channelId, +// pts, +// diff.messages.length +// ) +// +// diff.messages.forEach((message) => { +// this._updsLog.debug( +// 'processing message %d (%s) from diff for channel %d ', +// message.id, +// message._, +// channelId +// ) +// +// if (noDispatch && noDispatch.msg[channelId]?.[message.id]) +// return +// if (message._ === 'messageEmpty') return +// +// this._dispatchUpdate(message, users, chats) +// }) +// break +// } +// +// this._updsLog.debug( +// 'getChannelDifference (cid = %d) returned %d messages, %d updates. new pts: %d, final: %b', +// channelId, +// diff.newMessages.length, +// diff.otherUpdates.length, +// diff.pts, +// diff.final +// ) +// +// diff.newMessages.forEach((message) => { +// this._updsLog.debug( +// 'processing message %d (%s) from diff for channel %d ', +// message.id, +// message._, +// channelId +// ) +// +// if (noDispatch && noDispatch.msg[channelId]?.[message.id]) return +// if (message._ === 'messageEmpty') return +// +// this._dispatchUpdate(message, users, chats) +// }) +// +// diff.otherUpdates.forEach((upd) => { +// this._updsLog.debug( +// 'processing %s from diff for channel %d, pts: %d', +// upd._, +// channelId, +// (upd as any).pts +// ) +// +// if (noDispatch) { +// const pts = 'pts' in upd ? upd.pts : undefined +// +// // we don't check for pts sequence here since the server +// // is expected to return them in a correct order +// // again, i would not trust Telegram server that much, +// // but checking pts here seems like an overkill +// if (pts && noDispatch.pts[channelId]?.[pts]) return +// } +// +// if (_isMessageEmpty(upd)) return +// +// this._dispatchUpdate(upd, users, chats) +// }) +// +// pts = diff.pts +// +// if (diff.final) break +// } +// +// this._cpts[channelId] = pts +// this._cptsMod[channelId] = pts +// } + +// +// function _processPostponedUpdate( +// client: TelegramClient, +// upd: PendingUpdate, +// channelId: number, +// peers: { +// users: UsersIndex +// chats: ChatsIndex +// } | null +// ): void { +// // remove the item from the array +// +// const arr = client['_pendingUpdates'][channelId] +// if (!arr) return // weird +// +// const idx = arr.indexOf(upd) +// if (idx === -1) return // weird +// +// arr.splice(idx, 1) +// +// if (!arr.length) delete client['_pendingUpdates'][channelId] +// +// const { pts, ptsBefore, noDispatch } = upd +// +// const log = client['_updsLog'] +// +// log.debug( +// 'processing postponed %s: cid = %d, pts = %d, pts_before = %d - acquiring lock', +// upd.update._, +// channelId, +// pts, +// ptsBefore +// ) +// +// // since this is called from a timeout, we need to wrap the thing with a lock +// client['_updLock'] +// .acquire() +// .then(async () => { +// log.debug( +// 'processing postponed %s: cid = %d, pts = %d, pts_before = %d - lock acquired', +// upd.update._, +// channelId, +// pts, +// ptsBefore +// ) +// +// let localPts: number | null = null +// if (channelId === 0) localPts = client['_pts']! +// else if (channelId in client['_cpts']) +// localPts = client['_cpts'][channelId] +// +// if (!localPts) { +// // should not happen +// log.warn( +// 'no local_pts for postponed updates in channel %d (pts = %d)', +// channelId, +// pts +// ) +// return +// } +// +// if (localPts > ptsBefore) { +// // "the update was already applied, and must be ignored" +// log.debug( +// 'ignoring postponed %s (cid = %d) because already applied (by pts: exp %d, got %d)', +// upd.update._, +// channelId, +// localPts, +// ptsBefore +// ) +// return +// } +// +// if (localPts < ptsBefore) { +// if (channelId) { +// log.debug( +// '(from postponed) fetching difference (cid = %d) because gap detected (by pts: exp %d, got %d)', +// channelId, +// localPts, +// ptsBefore +// ) +// +// // "there's an update gap that must be filled" +// await _loadChannelDifference.call( +// client, +// channelId, +// noDispatch +// ? _createNoDispatchIndex(upd.update) +// : undefined, +// ptsBefore +// ) +// } else { +// await _loadDifference.call( +// client, +// noDispatch +// ? _createNoDispatchIndex(upd.update) +// : undefined +// ) +// } +// return +// } +// +// log.debug( +// 'received (postponed) pts-ordered %s (cid = %d), new pts: %d', +// upd.update._, +// channelId, +// pts +// ) +// +// if (channelId) { +// client['_cpts'][channelId] = pts +// client['_cptsMod'][channelId] = pts +// } else { +// client['_pts'] = pts +// } +// +// if (!noDispatch) { +// if (!peers) { +// // this is a short update, let's fetch cached peers +// peers = await _fetchPeersForShort.call(client, upd.update) +// if (!peers) { +// log.debug( +// 'fetching difference because some peers were not available for short %s (pts = %d, cid = %d)', +// upd.update._, +// upd.pts, +// channelId +// ) +// // some peer is not cached. +// // need to re-fetch the thing, and cache them on the way +// return await _loadDifference.call(client) +// } +// } +// +// client['_dispatchUpdate'](upd.update, peers.users, peers.chats) +// } +// }) +// .catch((err) => client['_emitError'](err)) +// .then(() => client['_updLock'].release()) +// .then(() => client['_saveStorage']()) +// } +// +// async function _checkPostponedUpdates( +// this: TelegramClient, +// channelId: number, +// peers: { +// users: UsersIndex +// chats: ChatsIndex +// } | null +// ): Promise { +// const arr = this._pendingUpdates[channelId] +// if (!arr) return +// +// let localPts = channelId === 0 ? this._pts! : this._cpts[channelId] +// if (!localPts) { +// // should not happen +// this._updsLog.warn( +// 'no local_pts for postponed updates in channel %d', +// channelId +// ) +// return +// } +// +// for (let i = 0; i < arr.length; i++) { +// const upd = arr[i] +// // arr is always sorted by pts_before, so we can try applying the updates right away +// +// if (localPts === upd.ptsBefore) { +// // it's time for the moment you've been waiting for +// // ta. tadadatatatata. tadadatatatatatatatatatata. +// // preparing the krabby patty update +// // ahem. apply the update +// +// this._updsLog.debug( +// 'pts gap was filled, applying postponed %s (cid = %d, pts = %d)', +// upd.update._, +// channelId, +// upd.pts +// ) +// +// localPts = upd.pts +// clearTimeout(upd.timeout) +// +// // remove item from array +// arr.splice(i, 1) +// i -= 1 +// +// if (upd.noDispatch) continue +// +// if (!peers) { +// // this is a short update, let's fetch cached peers +// peers = await _fetchPeersForShort.call(this, upd.update) +// if (!peers) { +// this._updsLog.debug( +// 'fetching difference because some peers were not available for short %s (pts = %d, cid = %d)', +// upd.update._, +// upd.pts, +// channelId +// ) +// // some peer is not cached. +// // need to re-fetch the thing, and cache them on the way +// return await _loadDifference.call(this) +// } +// } +// +// this._dispatchUpdate(upd.update, peers.users, peers.chats) +// } +// } +// +// // update local pts +// if (channelId === 0) { +// this._pts = localPts +// } else { +// this._cpts[channelId] = localPts +// this._cptsMod[channelId] = localPts +// } +// } + +// async function _processSingleUpdate( +// this: TelegramClient, +// upd: tl.TypeUpdate, +// peers: { +// users: UsersIndex +// chats: ChatsIndex +// } | null, +// noDispatch?: boolean +// ): Promise { +// const channelId = extractChannelIdFromUpdate(upd) || 0 +// const pts = 'pts' in upd ? upd.pts : undefined +// const ptsCount = 'ptsCount' in upd ? upd.ptsCount : pts ? 0 : undefined +// const qts = 'qts' in upd ? upd.qts : undefined +// +// this._updsLog.debug( +// 'processing %s: cid = %d, pts = %d, pts_count = %d, qts = %d', +// upd._, +// channelId, +// pts, +// ptsCount, +// qts +// ) +// +// if (pts !== undefined && ptsCount !== undefined) { +// const ptsBefore = pts - ptsCount +// let localPts: number | null = null +// if (channelId === 0) localPts = this._pts! +// else if (channelId in this._cpts) localPts = this._cpts[channelId] +// else if (this._catchUpChannels) { +// // only load stored channel pts in case +// // the user has enabled catching up. +// // not loading stored pts effectively disables +// // catching up, but doesn't interfere with further +// // update gaps +// +// const saved = await this.storage.getChannelPts(channelId) +// if (saved) { +// this._cpts[channelId] = localPts = saved +// } +// } +// +// if (localPts) { +// if (localPts > ptsBefore) { +// // "the update was already applied, and must be ignored" +// this._updsLog.debug( +// 'ignoring %s (cid = %d) because already applied (by pts: exp %d, got %d)', +// upd._, +// channelId, +// localPts, +// ptsBefore +// ) +// return +// } +// if (localPts < ptsBefore) { +// this._updsLog.debug( +// 'fetching difference (cid = %d) because gap detected (by pts: exp %d, got %d)', +// channelId, +// localPts, +// pts +// ) +// if (channelId) { +// // "there's an update gap that must be filled" +// await _loadChannelDifference.call( +// this, +// channelId, +// noDispatch ? _createNoDispatchIndex(upd) : undefined, +// pts +// ) +// } else { +// await _loadDifference.call( +// this, +// noDispatch ? _createNoDispatchIndex(upd) : undefined +// ) +// } +// +// // this._updsLog.debug( +// // 'waiting 0.5s for %s (cid = %d) because gap detected (by pts: exp %d, got %d)', +// // upd._, +// // channelId, +// // localPts, +// // ptsBefore +// // ) +// // +// // const pending: PendingUpdate = { +// // update: upd, +// // pts, +// // ptsBefore, +// // timeout: 0, +// // noDispatch, +// // } +// // // because we can't access `pending` in its initializer +// // pending.timeout = setTimeout( +// // _processPostponedUpdate, +// // 500, +// // this, +// // pending, +// // channelId, +// // peers +// // ) +// // +// // if (!this._pendingUpdates[channelId]) +// // this._pendingUpdates[channelId] = [] +// // const arr = this._pendingUpdates[channelId] +// // arr.push(pending) +// // +// // if (arr.length > 1) { +// // // sort by pts_before +// // arr.sort((a, b) => a.ptsBefore - b.ptsBefore) +// // } +// +// return +// } +// } +// } +// +// if (qts !== undefined) { +// const qtsBefore = qts - 1 +// const qtsLocal = this._qts! +// // qts is only used for non-channel updates +// +// if (qtsLocal > qtsBefore) { +// // "the update was already applied, and must be ignored" +// this._updsLog.debug( +// 'ignoring %s because already applied (by qts: exp %d, got %d)', +// upd._, +// qtsLocal, +// qtsBefore +// ) +// return +// } +// if (qtsLocal < qtsBefore) { +// this._updsLog.debug( +// 'fetching difference because gap detected (by qts: exp %d, got %d)', +// qtsLocal, +// qtsBefore +// ) +// +// return await _loadDifference.call( +// this, +// noDispatch ? _createNoDispatchIndex(upd) : undefined +// ) +// } +// } +// +// // update local pts/qts +// if (pts) { +// this._updsLog.debug( +// 'received pts-ordered %s (cid = %d), new pts: %d', +// upd._, +// channelId, +// pts +// ) +// +// if (channelId) { +// this._cpts[channelId] = pts +// this._cptsMod[channelId] = pts +// } else { +// this._pts = pts +// } +// } +// +// if (qts) { +// this._updsLog.debug('received qts-ordered %s, new qts: %d', upd._, qts) +// +// this._qts = qts +// } +// +// if (isDummyUpdate(upd)) { +// // we needed to check pts/qts, so we couldn't return right away +// // await _checkPostponedUpdates.call(this, channelId, peers) +// return +// } +// +// // updates that are also used internally +// switch (upd._) { +// case 'updateDcOptions': +// if (!this._config) { +// this._config = await this.call({ _: 'help.getConfig' }) +// } else { +// ;(this._config as tl.Mutable).dcOptions = +// upd.dcOptions +// } +// break +// case 'updateConfig': +// this._config = await this.call({ _: 'help.getConfig' }) +// break +// case 'updateUserName': +// if (upd.userId === this._userId) { +// this._selfUsername = upd.username || null +// } +// break +// } +// +// // all checks passed, dispatch the update +// +// if (!noDispatch) { +// if (!peers) { +// // this is a short update, let's fetch cached peers +// peers = await _fetchPeersForShort.call(this, upd) +// if (!peers) { +// this._updsLog.debug( +// 'fetching difference because some peers were not available for short %s (pts = %d, cid = %d)', +// upd._, +// pts, +// channelId +// ) +// // some peer is not cached. +// // need to re-fetch the thing, and cache them on the way +// return await _loadDifference.call(this) +// } +// } +// +// this._dispatchUpdate(upd, peers.users, peers.chats) +// } +// +// // await _checkPostponedUpdates.call(this, channelId, peers) +// } + +/** + * @internal + */ +export function _handleUpdate( this: TelegramClient, - noDispatch?: NoDispatchIndex + update: tl.TypeUpdates, + noDispatch = false // fixme +): void { + this._updsLog.debug( + 'received %s, queueing for processing. containers queue size: %d', + update._, + this._pendingUpdateContainers.length + ) + this._rpsIncoming?.hit() + + switch (update._) { + case 'updatesTooLong': + case 'updateShortMessage': + case 'updateShortChatMessage': + case 'updateShort': + case 'updateShortSentMessage': + this._pendingUpdateContainers.add({ + upd: update, + seqStart: 0, + seqEnd: 0, + }) + break + case 'updates': + case 'updatesCombined': + this._pendingUpdateContainers.add({ + upd: update, + seqStart: + update._ === 'updatesCombined' + ? update.seqStart + : update.seq, + seqEnd: update.seq, + }) + break + } + + this._updatesLoopCv.notify() + + // we want to process updates in order, so we use a lock + // it is *very* important that the lock is released, otherwise + // the incoming updates will be stuck forever, eventually killing the process with OOM + // thus, we wrap everything in what basically is a try..finally + + // additionally, locking here blocks updates handling while we are + // loading difference inside update handler. + + // this._updLock + // .acquire() + // .then(async () => { + // // i tried my best to follow the documentation, but i still may have missed something. + // // feel free to contribute! + // // reference: https://core.telegram.org/api/updates + // // (though it is out of date: https://t.me/tdlibchat/20155) + // switch (update._) { + // case 'updatesTooLong': + // // "there are too many events pending to be pushed to the client", we need to fetch them manually + // this._updsLog.debug( + // 'received updatesTooLong, fetching difference' + // ) + // await _loadDifference.call( + // this, + // noDispatch ? _createNoDispatchIndex(update) : undefined + // ) + // break + // case 'updates': + // case 'updatesCombined': { + // const seqStart = + // update._ === 'updatesCombined' + // ? update.seqStart + // : update.seq + // if (seqStart !== 0) { + // // https://t.me/tdlibchat/5843 + // const nextLocalSeq = this._seq! + 1 + // + // this._updsLog.debug( + // 'received seq-ordered %s (seq_start = %d, seq_end = %d, size = %d)', + // update._, + // seqStart, + // update.seq, + // update.updates.length + // ) + // + // if (nextLocalSeq > seqStart) { + // this._updsLog.debug( + // 'ignoring updates group because already applied (by seq: exp %d, got %d)', + // nextLocalSeq, + // seqStart + // ) + // // "the updates were already applied, and must be ignored" + // return + // } + // if (nextLocalSeq < seqStart) { + // this._updsLog.debug( + // 'fetching difference because gap detected (by seq: exp %d, got %d)', + // nextLocalSeq, + // seqStart + // ) + // // "there's an updates gap that must be filled" + // // loading difference will also load any updates contained + // // in this update, so we discard it + // return await _loadDifference.call( + // this, + // noDispatch + // ? _createNoDispatchIndex(update) + // : undefined + // ) + // } + // } else { + // this._updsLog.debug( + // 'received %s (size = %d)', + // update._, + // update.updates.length + // ) + // } + // + // const hasMin = await this._cachePeersFrom(update) + // if (hasMin) { + // if (!(await _replaceMinPeers.call(this, update))) { + // this._updsLog.debug( + // 'fetching difference because some peers were min and not cached' + // ) + // // some min peer is not cached. + // // need to re-fetch the thing, and cache them on the way + // return await _loadDifference.call( + // this, + // noDispatch + // ? _createNoDispatchIndex(update) + // : undefined + // ) + // } + // } + // + // const peers = createUsersChatsIndex(update) + // + // // sort the updates by pts_before/qts because the server is too busy for that + // update.updates.sort((a, b) => { + // const ptsA = 'pts' in a ? a.pts : undefined + // const ptsCountA = + // 'ptsCount' in a ? a.ptsCount : ptsA ? 0 : undefined + // const qtsA = 'qts' in a ? a.qts : undefined + // + // const ptsB = 'pts' in b ? b.pts : undefined + // const ptsCountB = + // 'ptsCount' in b ? b.ptsCount : ptsB ? 0 : undefined + // const qtsB = 'qts' in b ? b.qts : undefined + // + // if (ptsA !== undefined && ptsB !== undefined) { + // // both updates are pts-ordered + // // v ptsBeforeA v ptsBeforeB + // return ptsA - ptsCountA! - (ptsB - ptsCountB!) + // } + // + // if (qtsA !== undefined && qtsB !== undefined) { + // // both updates are qts-ordered + // return qtsA - qtsB + // } + // + // return 0 // we don't care about order otherwise + // }) + // + // for (const upd of update.updates) { + // if (upd._ === 'updateChannelTooLong') { + // this._updsLog.debug( + // 'received updateChannelTooLong for channel %d (pts = %d), fetching diff', + // upd.channelId, + // upd.pts + // ) + // await _loadChannelDifference.call( + // this, + // upd.channelId, + // noDispatch + // ? _createNoDispatchIndex(update) + // : undefined, // noDispatchIndex, + // upd.pts + // ) + // continue + // } + // + // await _processSingleUpdate.call( + // this, + // upd, + // peers, + // noDispatch + // ) + // } + // + // if (update.seq !== 0 && update.seq > this._seq!) { + // // https://t.me/tdlibchat/5844 + // // we also need to check that update seq > this._seq in case + // // there was a gap that was filled inside _processSingleUpdate + // this._seq = update.seq + // this._date = update.date + // } + // break + // } + // case 'updateShort': { + // const upd = update.update + // + // this._updsLog.debug('received short %s', upd._) + // + // await _processSingleUpdate.call(this, upd, null, noDispatch) + // + // this._date = update.date + // + // break + // } + // case 'updateShortMessage': { + // this._updsLog.debug('received updateShortMessage') + // + // const message: tl.RawMessage = { + // _: 'message', + // out: update.out, + // mentioned: update.mentioned, + // mediaUnread: update.mediaUnread, + // silent: update.silent, + // id: update.id, + // fromId: { + // _: 'peerUser', + // userId: update.out ? this._userId! : update.userId, + // }, + // peerId: { + // _: 'peerUser', + // userId: update.userId, + // }, + // fwdFrom: update.fwdFrom, + // viaBotId: update.viaBotId, + // replyTo: update.replyTo, + // date: update.date, + // message: update.message, + // entities: update.entities, + // ttlPeriod: update.ttlPeriod, + // } + // + // const upd: tl.RawUpdateNewMessage = { + // _: 'updateNewMessage', + // message, + // pts: update.pts, + // ptsCount: update.ptsCount, + // } + // + // await _processSingleUpdate.call(this, upd, null, noDispatch) + // + // break + // } + // case 'updateShortChatMessage': { + // this._updsLog.debug('received updateShortChatMessage') + // + // const message: tl.RawMessage = { + // _: 'message', + // out: update.out, + // mentioned: update.mentioned, + // mediaUnread: update.mediaUnread, + // silent: update.silent, + // id: update.id, + // fromId: { + // _: 'peerUser', + // userId: update.fromId, + // }, + // peerId: { + // _: 'peerChat', + // chatId: update.chatId, + // }, + // fwdFrom: update.fwdFrom, + // viaBotId: update.viaBotId, + // replyTo: update.replyTo, + // date: update.date, + // message: update.message, + // entities: update.entities, + // ttlPeriod: update.ttlPeriod, + // } + // + // const upd: tl.RawUpdateNewMessage = { + // _: 'updateNewMessage', + // message, + // pts: update.pts, + // ptsCount: update.ptsCount, + // } + // + // await _processSingleUpdate.call(this, upd, null, noDispatch) + // + // break + // } + // case 'updateShortSentMessage': { + // this._updsLog.debug('received updateShortChatMessage') + // + // // only store the new pts and date values + // // we never need to dispatch this + // + // const ptsBefore = update.pts - update.ptsCount + // const localPts = this._pts! + // if (localPts > ptsBefore) + // // "the update was already applied, and must be ignored" + // return + // if (localPts < ptsBefore) + // // "there's an update gap that must be filled" + // return await _loadDifference.call( + // this, + // noDispatch + // ? _createNoDispatchIndex(update) + // : undefined + // ) + // + // this._date = update.date + // this._pts = update.pts + // break + // } + // } + // }) + // .catch((err) => this._emitError(err)) + // .then(() => this._updLock.release()) + // .then(() => this._saveStorage()) +} + +/** + * Catch up with the server by loading missed updates. + * + * @internal + */ +export function catchUp(this: TelegramClient): void { + // we also use a lock here so new updates are not processed + // while we are catching up with older ones + + this._updsLog.debug('catch up requested') + + this._catchUpChannels = true + this._handleUpdate({ _: 'updatesTooLong' }) + + // return this._updLock + // .acquire() + // .then(() => _loadDifference.call(this)) + // .catch((err) => this._emitError(err)) + // .then(() => this._updLock.release()) + // .then(() => this._saveStorage()) +} + +function _toPendingUpdate( + upd: tl.TypeUpdate, + peers?: PeersIndex +): PendingUpdate { + const channelId = extractChannelIdFromUpdate(upd) || 0 + const pts = 'pts' in upd ? upd.pts : undefined + const ptsCount = 'ptsCount' in upd ? upd.ptsCount : pts ? 0 : undefined + const qts = 'qts' in upd ? upd.qts : undefined + + return { + update: upd, + channelId, + pts, + ptsBefore: pts ? pts - ptsCount! : undefined, + qtsBefore: qts ? qts - 1 : undefined, + peers, + } +} + +function _messageToUpdate(message: tl.TypeMessage): tl.TypeUpdate { + switch (message.peerId!._) { + case 'peerUser': + case 'peerChat': + return { + _: 'updateNewMessage', + message, + pts: 0, + ptsCount: 0, + } + case 'peerChannel': + return { + _: 'updateNewChannelMessage', + message, + pts: 0, + ptsCount: 0, + } + } +} + +// function _checkPts(local: number, remote: number): number { +// if (remote === 0x7ffffffff /* INT32_MAX */) { +// return 0 +// } +// +// const diff = remote - local +// // diff > 0 - there is a gap +// // diff < 0 - already applied +// // diff = 0 - ok +// +// if (diff < -399999) { +// // pts can only go up or drop cardinally +// return 0 +// } +// +// return diff +// } +// todo: pts/qts drop + +async function _fetchChannelDifference( + this: TelegramClient, + channelId: number, + fallbackPts?: number, + force = false ): Promise { + let _pts: number | null | undefined = this._cpts[channelId] + if (!_pts && this._catchUpChannels) { + _pts = await this.storage.getChannelPts(channelId) + } + if (!_pts) _pts = fallbackPts + + if (!_pts) { + this._updsLog.warn( + 'fetchChannelDifference failed for channel %d: base pts not available', + channelId + ) + return + } + + let channel + try { + channel = normalizeToInputChannel( + await this.resolvePeer(toggleChannelIdMark(channelId)) + )! + } catch (e) { + this._updsLog.warn( + 'fetchChannelDifference failed for channel %d: input peer not found', + channelId + ) + return + } + + // to make TS happy + let pts = _pts + let limit = this._isBot ? 100000 : 100 + + if (pts <= 0) { + pts = 1 + limit = 1 + } + + let isFirst = true + for (;;) { - const diff = await this.call({ - _: 'updates.getDifference', - pts: this._pts!, - date: this._date!, - qts: this._qts!, + const diff = await this.call( + { + _: 'updates.getChannelDifference', + force, + channel, + pts, + limit, + filter: { _: 'channelMessagesFilterEmpty' }, + } + // { flush: !isFirst } + ) + isFirst = false + + if (diff._ === 'updates.channelDifferenceEmpty') { + this._updsLog.debug( + 'getChannelDifference (cid = %d) returned channelDifferenceEmpty', + channelId + ) + break + } + + await this._cachePeersFrom(diff) + + const peers = PeersIndex.from(diff) + + if (diff._ === 'updates.channelDifferenceTooLong') { + if (diff.dialog._ === 'dialog') { + pts = diff.dialog.pts! + } + + this._updsLog.warn( + 'getChannelDifference (cid = %d) returned channelDifferenceTooLong. new pts: %d, recent msgs: %d', + channelId, + pts, + diff.messages.length + ) + + diff.messages.forEach((message) => { + this._updsLog.debug( + 'processing message %d (%s) from TooLong diff for channel %d', + message.id, + message._, + channelId + ) + + if (message._ === 'messageEmpty') return + + this._pendingUnorderedUpdates.pushBack( + _toPendingUpdate(_messageToUpdate(message), peers) + ) + }) + break + } + + this._updsLog.debug( + 'getChannelDifference (cid = %d) returned %d messages, %d updates. new pts: %d, final: %b', + channelId, + diff.newMessages.length, + diff.otherUpdates.length, + diff.pts, + diff.final + ) + + diff.newMessages.forEach((message) => { + this._updsLog.debug( + 'processing message %d (%s) from diff for channel %d', + message.id, + message._, + channelId + ) + + if (message._ === 'messageEmpty') return + + this._pendingUnorderedUpdates.pushBack( + _toPendingUpdate(_messageToUpdate(message), peers) + ) }) + diff.otherUpdates.forEach((upd) => { + const parsed = _toPendingUpdate(upd, peers) + + this._updsLog.debug( + 'processing %s from diff for channel %d, pts_before: %d, pts: %d', + upd._, + channelId, + parsed.ptsBefore, + parsed.pts + ) + + if (_isMessageEmpty(upd)) return + + this._pendingUnorderedUpdates.pushBack(parsed) + }) + + pts = diff.pts + + if (diff.final) break + } + + this._cpts[channelId] = pts + this._cptsMod[channelId] = pts +} + +function _fetchChannelDifferenceLater( + this: TelegramClient, + requestedDiff: Record>, + channelId: number, + fallbackPts?: number, + force = false +): void { + if (!requestedDiff[channelId]) + requestedDiff[channelId] = _fetchChannelDifference + .call(this, channelId, fallbackPts, force) + .catch((err) => { + this._updsLog.warn( + 'error fetching difference for %d: %s', + channelId, + err + ) + }) + .then(() => { + delete requestedDiff[channelId] + }) +} + +async function _fetchDifference( + this: TelegramClient, + requestedDiff: Record> +): Promise { + let isFirst = true + for (;;) { + const diff = await this.call( + { + _: 'updates.getDifference', + pts: this._pts!, + date: this._date!, + qts: this._qts!, + } + // { flush: !isFirst } + ) + isFirst = false + switch (diff._) { case 'updates.differenceEmpty': this._updsLog.debug( @@ -495,350 +1986,208 @@ async function _loadDifference( await this._cachePeersFrom(diff) - const { users, chats } = createUsersChatsIndex(diff) + const peers = PeersIndex.from(diff) diff.newMessages.forEach((message) => { - if (noDispatch) { - // in fact this field seems to only be used for PMs and legacy chats, - // so `cid` will be 0 always, but that might change :shrug: - const cid = - message.peerId?._ === 'peerChannel' - ? message.peerId.channelId - : 0 - if (noDispatch.msg[cid]?.[message.id]) return - } + this._updsLog.debug( + 'processing message %d in %j (%s) from common diff', + message.id, + message.peerId, + message._ + ) - this._dispatchUpdate(message, users, chats) + if (message._ === 'messageEmpty') return + + // pts does not need to be checked for them + this._pendingUnorderedUpdates.pushBack( + _toPendingUpdate(_messageToUpdate(message), peers) + ) }) - for (const upd of diff.otherUpdates) { + diff.otherUpdates.forEach((upd) => { if (upd._ === 'updateChannelTooLong') { - await _loadChannelDifference.call( - this, + this._updsLog.debug( + 'received updateChannelTooLong for channel %d in common diff (pts = %d), fetching diff', upd.channelId, - noDispatch, upd.pts ) - continue + + _fetchChannelDifferenceLater.call( + this, + requestedDiff, + upd.channelId, + upd.pts + ) + + return } - const cid = extractChannelIdFromUpdate(upd) - const pts = 'pts' in upd ? upd.pts : undefined - const ptsCount = 'ptsCount' in upd ? upd.ptsCount : undefined - const qts = 'qts' in upd ? upd.qts : undefined + if (_isMessageEmpty(upd)) return - if (cid && pts !== undefined && ptsCount !== undefined) { - // check that this pts is in fact the next one - // we only need to check this for channels since for - // common pts it is guaranteed by the server - // (however i would not really trust telegram server lol) - let nextLocalPts: number | null = null - if (cid in this._cpts) nextLocalPts = this._cpts[cid] + ptsCount - else if (this._catchUpChannels) { - const saved = await this.storage.getChannelPts(cid) - if (saved) { - this._cpts[cid] = saved - nextLocalPts = saved + ptsCount - } - } + const parsed = _toPendingUpdate(upd, peers) - if (nextLocalPts) { - if (nextLocalPts > pts) { - this._updsLog.debug( - 'ignoring %s (in channel %d) because already handled (by pts: exp %d, got %d)', - upd._, - cid, - nextLocalPts, - pts - ) - continue - } - if (nextLocalPts < pts) { - this._updsLog.debug( - 'fetching channel %d difference because gap detected (by pts: exp %d, got %d)', - cid, - nextLocalPts, - pts - ) - await _loadChannelDifference.call( - this, - cid, - noDispatch, - pts - ) - continue - } - } - - this._cpts[cid] = pts - this._cptsMod[cid] = pts + if (parsed.channelId && parsed.ptsBefore) { + // we need to check pts for these updates, put into pts queue + this._pendingPtsUpdates.add(parsed) + } else { + // the updates are in order already, we can treat them as unordered + this._pendingUnorderedUpdates.pushBack(parsed) } - if (noDispatch) { - if (pts && noDispatch.pts[cid ?? 0]?.[pts]) continue - if (qts && noDispatch.qts[qts]) continue - } - - this._dispatchUpdate(upd, users, chats) - } + this._updsLog.debug( + 'received %s from common diff, cid: %d, pts_before: %d, pts: %d, qts_before: %d', + upd._, + parsed.channelId, + parsed.ptsBefore, + parsed.pts, + parsed.qtsBefore + ) + }) this._pts = state.pts this._qts = state.qts this._seq = state.seq this._date = state.date - if (diff._ === 'updates.difference') return - } -} - -async function _loadChannelDifference( - this: TelegramClient, - channelId: number, - noDispatch?: NoDispatchIndex, - fallbackPts?: number -): Promise { - let channel - try { - channel = normalizeToInputChannel( - await this.resolvePeer(MAX_CHANNEL_ID - channelId) - )! - } catch (e) { - this._updsLog.warn( - 'getChannelDifference failed for channel %d: input peer not found', - channelId - ) - return - } - - let _pts: number | null | undefined = this._cpts[channelId] - if (!_pts && this._catchUpChannels) { - _pts = await this.storage.getChannelPts(channelId) - } - if (!_pts) _pts = fallbackPts - - if (!_pts) return - - // to make TS happy - let pts = _pts - - for (;;) { - const diff = await this.call({ - _: 'updates.getChannelDifference', - channel, - pts, - limit: this._isBot ? 1000 : 100, - filter: { _: 'channelMessagesFilterEmpty' }, - }) - - if (diff._ === 'updates.channelDifferenceEmpty') { - this._updsLog.debug( - 'getChannelDifference (cid = %d) returned channelDifferenceEmpty', - channelId - ) - break - } - - await this._cachePeersFrom(diff) - - const { users, chats } = createUsersChatsIndex(diff) - - if (diff._ === 'updates.channelDifferenceTooLong') { - if (diff.dialog._ === 'dialog') { - pts = diff.dialog.pts! - } - - this._updsLog.warn( - 'getChannelDifference (cid = %d) returned channelDifferenceTooLong. new pts: %d, recent msgs: %d', - channelId, - pts, - diff.messages.length - ) - - diff.messages.forEach((message) => { - if (noDispatch && noDispatch.msg[channelId]?.[message.id]) - return - if (message._ === 'messageEmpty') return - - this._dispatchUpdate(message, users, chats) - }) - break - } - - this._updsLog.warn( - 'getChannelDifference (cid = %d) returned %d messages, %d updates. new pts: %d, final: %b', - channelId, - diff.newMessages.length, - diff.otherUpdates.length, - pts, - diff.final - ) - - diff.newMessages.forEach((message) => { - if (noDispatch && noDispatch.msg[channelId]?.[message.id]) return - if (message._ === 'messageEmpty') return - - this._dispatchUpdate(message, users, chats) - }) - - diff.otherUpdates.forEach((upd) => { - if (noDispatch) { - const pts = 'pts' in upd ? upd.pts : undefined - - // we don't check for pts sequence here since the server - // is expected to return them in a correct order - // again, i would not trust Telegram server that much, - // but checking pts here seems like an overkill - if (pts && noDispatch.pts[channelId]?.[pts]) return - } - - if ( - upd._ === 'updateNewChannelMessage' && - upd.message._ === 'messageEmpty' - ) - return - - this._dispatchUpdate(upd, users, chats) - }) - - pts = diff.pts - - if (diff.final) break - } - - this._cpts[channelId] = pts - this._cptsMod[channelId] = pts -} - -async function _processSingleUpdate( - this: TelegramClient, - upd: tl.TypeUpdate, - peers: { - users: UsersIndex - chats: ChatsIndex - } | null, - noDispatch?: boolean -): Promise { - const channelId = extractChannelIdFromUpdate(upd) - const pts = 'pts' in upd ? upd.pts : undefined - const ptsCount = 'ptsCount' in upd ? upd.ptsCount : undefined - const qts = 'qts' in upd ? upd.qts : undefined - - if (pts !== undefined && ptsCount !== undefined) { - let nextLocalPts: number | null = null - if (channelId === undefined) nextLocalPts = this._pts! + ptsCount - else if (channelId in this._cpts) - nextLocalPts = this._cpts[channelId] + ptsCount - else if (this._catchUpChannels) { - // only load stored channel pts in case - // the user has enabled catching up. - // not loading stored pts effectively disables - // catching up, but doesn't interfere with further - // update gaps - - const saved = await this.storage.getChannelPts(channelId) - if (saved) { - this._cpts[channelId] = saved - nextLocalPts = saved + ptsCount - } - } - - if (nextLocalPts) { - if (nextLocalPts > pts) { - // "the update was already applied, and must be ignored" - this._updsLog.debug( - 'ignoring %s (cid = %d) because already applied (by pts: exp %d, got %d)', - upd._, - channelId, - nextLocalPts, - pts - ) - return - } - if (nextLocalPts < pts) { - this._updsLog.debug( - 'fetching difference (cid = %d) because gap detected (by pts: exp %d, got %d)', - channelId, - nextLocalPts, - pts - ) - if (channelId) { - // "there's an update gap that must be filled" - await _loadChannelDifference.call( - this, - channelId, - noDispatch ? _createNoDispatchIndex(upd) : undefined, - pts - ) - } else { - await _loadDifference.call( - this, - noDispatch ? _createNoDispatchIndex(upd) : undefined - ) - } - return - } - } - } - - if (qts !== undefined) { - // qts is only used for non-channel updates - const nextLocalQts = this._qts! + 1 - - if (nextLocalQts > qts) { - // "the update was already applied, and must be ignored" - this._updsLog.debug( - 'ignoring %s because already applied (by qts: exp %d, got %d)', - upd._, - nextLocalQts, - qts - ) + if (diff._ === 'updates.difference') { return } - if (nextLocalQts < qts) { + } +} + +function _fetchDifferenceLater( + this: TelegramClient, + requestedDiff: Record> +): void { + if (!requestedDiff[0]) + requestedDiff[0] = _fetchDifference + .call(this, requestedDiff) + .catch((err) => { + this._updsLog.warn('error fetching common difference: %s', err) + }) + .then(() => { + delete requestedDiff[0] + }) +} + +async function _onUpdate( + this: TelegramClient, + pending: PendingUpdate, + requestedDiff: Record>, + postponed = false, + unordered = false +): Promise { + const upd = pending.update + + // check for min peers, try to replace them + // it is important to do this before updating pts + if (pending.peers && pending.peers.hasMin) { + if (!(await _replaceMinPeers.call(this, pending.peers))) { this._updsLog.debug( - 'fetching difference because gap detected (by qts: exp %d, got %d)', + 'fetching difference because some peers were min and not cached for %s (pts = %d, cid = %d)', upd._, - nextLocalQts, - qts + pending.pts, + pending.channelId ) - return await _loadDifference.call( - this, - noDispatch ? _createNoDispatchIndex(upd) : undefined - ) + if (pending.channelId) { + _fetchChannelDifferenceLater.call( + this, + requestedDiff, + pending.channelId + ) + } else { + _fetchDifferenceLater.call(this, requestedDiff) + } + return } } - // update local pts/qts - if (pts) { - this._updsLog.debug( - 'received pts-ordered %s (cid = %d), new pts: %d', - upd._, - channelId, - pts - ) + if (!pending.peers) { + // this is a short update, we need to fetch the peers + const peers = await _fetchPeersForShort.call(this, upd) + if (!peers) { + this._updsLog.debug( + 'fetching difference because some peers were not available for short %s (pts = %d, cid = %d)', + upd._, + pending.pts, + pending.channelId + ) - if (channelId) { - this._cpts[channelId] = pts - this._cptsMod[channelId] = pts - } else { - this._pts = pts + if (pending.channelId) { + _fetchChannelDifferenceLater.call( + this, + requestedDiff, + pending.channelId + ) + } else { + _fetchDifferenceLater.call(this, requestedDiff) + } + return + } + pending.peers = peers + } + + // apply new pts/qts, if applicable + if (!unordered) { + // because unordered may contain pts/qts values when received from diff + + if (pending.pts) { + const localPts = pending.channelId + ? this._cpts[pending.channelId] + : this._pts + + if (localPts && pending.ptsBefore !== localPts) + this._updsLog.warn( + 'pts_before does not match local_pts for %s (cid = %d, pts_before = %d, pts = %d, local_pts = %d)', + upd._, + pending.channelId, + pending.ptsBefore, + pending.pts, + localPts + ) + + this._updsLog.debug( + 'applying new pts (cid = %d) because received %s: %d -> %d (before: %d, count: %d) (postponed = %s)', + pending.channelId, + upd._, + localPts, + pending.pts, + pending.ptsBefore, + pending.pts - pending.ptsBefore!, + postponed + ) + + if (pending.channelId) { + this._cpts[pending.channelId] = pending.pts! + this._cptsMod[pending.channelId] = pending.pts! + } else { + this._pts = pending.pts + } + } + + if (pending.qtsBefore) { + this._updsLog.debug( + 'applying new qts because received %s: %d -> %d (postponed = %s)', + upd._, + this._qts, + pending.qtsBefore + 1, + postponed + ) + + this._qts = pending.qtsBefore + 1 } } - if (qts) { - this._updsLog.debug('received qts-ordered %s, new qts: %d', upd._, qts) + if (_isMessageEmpty(upd)) return - this._qts = qts - } - - if (isDummyUpdate(upd) || noDispatch) { - // we needed to check pts/qts, so we couldn't return right away - return - } + this._rpsProcessing?.hit() // updates that are also used internally switch (upd._) { + case 'dummyUpdate': + // we just needed to apply new pts values + return case 'updateDcOptions': if (!this._config) { this._config = await this.call({ _: 'help.getConfig' }) @@ -857,274 +2206,582 @@ async function _processSingleUpdate( break } - // all checks passed, dispatch the update + // dispatch the update + this._updsLog.debug('dispatching %s (postponed = %s)', upd._, postponed) + this._dispatchUpdate(upd, pending.peers) +} - if (!noDispatch) { - if (!peers) { - // this is a short update, let's fetch cached peers - peers = await _fetchPeersForShort.call(this, upd) - if (!peers) { - this._updsLog.debug( - 'fetching difference because some peers were not available for short %s (pts = %d, cid = %d)', - upd._, - pts, - channelId - ) - // some peer is not cached. - // need to re-fetch the thing, and cache them on the way - return await _loadDifference.call(this) - } +// todo: updateChannelTooLong with catchUpChannels disabled should not trigger getDifference (?) +// todo: when min peer or similar use pts_before as base pts for channels + +/** @internal */ +export async function _updatesLoop(this: TelegramClient): Promise { + const log = this._updsLog + + log.debug('updates loop started, state available? %b', this._pts) + + try { + if (!this._pts) { + await this._fetchUpdatesState() } - this._dispatchUpdate(upd, peers.users, peers.chats) - } -} + while (this._updatesLoopActive) { + if ( + !( + this._pendingUpdateContainers.length || + this._pendingPtsUpdates.length || + this._pendingQtsUpdates.length || + this._pendingUnorderedUpdates.length + ) + ) { + await this._updatesLoopCv.wait() + } + if (!this._updatesLoopActive) break -/** - * @internal - */ -export function _handleUpdate( - this: TelegramClient, - update: tl.TypeUpdates, - noDispatch = false -): void { - // just in case, check that updates state is available - if (this._pts === undefined) { - this._updsLog.warn( - 'received an update before updates state is available' - ) - return - } + log.debug( + 'updates loop tick. pending containers: %d, pts: %d, pts_postponed: %d, qts: %d, qts_postponed: %d, unordered: %d', + this._pendingUpdateContainers.length, + this._pendingPtsUpdates.length, + this._pendingPtsUpdatesPostponed.length, + this._pendingQtsUpdates.length, + this._pendingQtsUpdatesPostponed.length, + this._pendingUnorderedUpdates.length + ) - // we want to process updates in order, so we use a lock - // it is *very* important that the lock is released, otherwise - // the incoming updates will be stuck forever, eventually killing the process with OOM - // thus, we wrap everything in what basically is a try..finally + const requestedDiff: Record> = {} - // additionally, locking here blocks updates handling while we are - // loading difference inside update handler. + // first process pending containers + while (this._pendingUpdateContainers.length) { + const { + upd, + seqStart, + seqEnd, + } = this._pendingUpdateContainers.popFront()! - this._updLock - .acquire() - .then(async () => { - // i tried my best to follow the documentation, but i still may have missed something. - // feel free to contribute! - // reference: https://core.telegram.org/api/updates - // (though it is out of date: https://t.me/tdlibchat/20155) - switch (update._) { - case 'updatesTooLong': - // "there are too many events pending to be pushed to the client", we need to fetch them manually - await _loadDifference.call( - this, - noDispatch ? _createNoDispatchIndex(update) : undefined - ) - break - case 'updates': - case 'updatesCombined': { - const seqStart = - update._ === 'updatesCombined' - ? update.seqStart - : update.seq - if (seqStart !== 0) { - // https://t.me/tdlibchat/5843 - const nextLocalSeq = this._seq! + 1 - - this._updsLog.debug( - 'received %s (seq_start = %d, seq_end = %d)', - update._, - seqStart, - update.seq + switch (upd._) { + case 'updatesTooLong': + log.debug( + 'received updatesTooLong, fetching difference' ) - - if (nextLocalSeq > seqStart) { - this._updsLog.debug( - 'ignoring updates group because already applied (by seq: exp %d, got %d)', - nextLocalSeq, - seqStart + _fetchDifferenceLater.call(this, requestedDiff) + break + case 'updatesCombined': + case 'updates': { + if (seqStart !== 0) { + // https://t.me/tdlibchat/5843 + const nextLocalSeq = this._seq! + 1 + log.debug( + 'received seq-ordered %s (seq_start = %d, seq_end = %d, size = %d)', + upd._, + seqStart, + seqEnd, + upd.updates.length ) - // "the updates were already applied, and must be ignored" - return - } - if (nextLocalSeq < seqStart) { - this._updsLog.debug( - 'fetching difference because gap detected (by seq: exp %d, got %d)', - nextLocalSeq, - seqStart - ) - // "there's an updates gap that must be filled" - // loading difference will also load any updates contained - // in this update, so we discard it - return await _loadDifference.call(this) - } - } - const hasMin = await this._cachePeersFrom(update) - if (hasMin) { - if (!(await _replaceMinPeers.call(this, update))) { - this._updsLog.debug( - 'fetching difference because some peers were min and not cached' - ) - // some min peer is not cached. - // need to re-fetch the thing, and cache them on the way - return await _loadDifference.call(this) - } - } + if (nextLocalSeq > seqStart) { + log.debug( + 'ignoring updates group because already applied (by seq: exp %d, got %d)', + nextLocalSeq, + seqStart + ) + // "the updates were already applied, and must be ignored" + continue + } - const peers = createUsersChatsIndex(update) - - for (const upd of update.updates) { - if (upd._ === 'updateChannelTooLong') { - await _loadChannelDifference.call( - this, - upd.channelId, - undefined, // noDispatchIndex, - upd.pts + if (nextLocalSeq < seqStart) { + log.debug( + 'fetching difference because gap detected (by seq: exp %d, got %d)', + nextLocalSeq, + seqStart + ) + // "there's an updates gap that must be filled" + _fetchDifferenceLater.call(this, requestedDiff) + } + } else { + log.debug( + 'received %s (size = %d)', + upd._, + upd.updates.length ) - continue } - await _processSingleUpdate.call( - this, - upd, - peers, - noDispatch - ) + await this._cachePeersFrom(upd) + // if (hasMin) { + // if (!( + // await _replaceMinPeers.call(this, upd) + // )) { + // log.debug( + // 'fetching difference because some peers were min and not cached' + // ) + // // some min peer is not cached. + // // need to re-fetch the thing, and cache them on the way + // await _fetchDifference.call(this) + // } + // } + + const peers = PeersIndex.from(upd) + + for (const update of upd.updates) { + if (update._ === 'updateChannelTooLong') { + log.debug( + 'received updateChannelTooLong for channel %d (pts = %d) in container, fetching diff', + update.channelId, + update.pts + ) + _fetchChannelDifferenceLater.call( + this, + requestedDiff, + update.channelId, + update.pts + ) + continue + } + + const parsed = _toPendingUpdate(update, peers) + + if (parsed.ptsBefore !== undefined) { + this._pendingPtsUpdates.add(parsed) + } else if (parsed.qtsBefore !== undefined) { + this._pendingQtsUpdates.add(parsed) + } else { + this._pendingUnorderedUpdates.pushBack(parsed) + } + } + + if (seqEnd !== 0 && seqEnd > this._seq!) { + this._seq = seqEnd + this._date = upd.date + } + + break } + case 'updateShort': { + log.debug('received short %s', upd._) - if (update.seq !== 0 && update.seq > this._seq!) { - // https://t.me/tdlibchat/5844 - // we also need to check that update seq > this._seq in case - // there was a gap that was filled inside _processSingleUpdate - this._seq = update.seq - this._date = update.date + const parsed = _toPendingUpdate(upd.update) + + if (parsed.ptsBefore !== undefined) { + this._pendingPtsUpdates.add(parsed) + } else if (parsed.qtsBefore !== undefined) { + this._pendingQtsUpdates.add(parsed) + } else { + this._pendingUnorderedUpdates.pushBack(parsed) + } + + break } - break - } - case 'updateShort': { - const upd = update.update + case 'updateShortMessage': { + log.debug('received updateShortMessage') - await _processSingleUpdate.call(this, upd, null, noDispatch) + const message: tl.RawMessage = { + _: 'message', + out: upd.out, + mentioned: upd.mentioned, + mediaUnread: upd.mediaUnread, + silent: upd.silent, + id: upd.id, + fromId: { + _: 'peerUser', + userId: upd.out ? this._userId! : upd.userId, + }, + peerId: { + _: 'peerUser', + userId: upd.userId, + }, + fwdFrom: upd.fwdFrom, + viaBotId: upd.viaBotId, + replyTo: upd.replyTo, + date: upd.date, + message: upd.message, + entities: upd.entities, + ttlPeriod: upd.ttlPeriod, + } - this._date = update.date + const update: tl.RawUpdateNewMessage = { + _: 'updateNewMessage', + message, + pts: upd.pts, + ptsCount: upd.ptsCount, + } - break - } - case 'updateShortMessage': { - const message: tl.RawMessage = { - _: 'message', - out: update.out, - mentioned: update.mentioned, - mediaUnread: update.mediaUnread, - silent: update.silent, - id: update.id, - fromId: { - _: 'peerUser', - userId: update.out ? this._userId! : update.userId, - }, - peerId: { - _: 'peerUser', - userId: update.userId, - }, - fwdFrom: update.fwdFrom, - viaBotId: update.viaBotId, - replyTo: update.replyTo, - date: update.date, - message: update.message, - entities: update.entities, - ttlPeriod: update.ttlPeriod, + this._pendingPtsUpdates.add({ + update, + ptsBefore: upd.pts - upd.ptsCount, + pts: upd.pts, + }) + + break } + case 'updateShortChatMessage': { + log.debug('received updateShortChatMessage') - const upd: tl.RawUpdateNewMessage = { - _: 'updateNewMessage', - message, - pts: update.pts, - ptsCount: update.ptsCount, + const message: tl.RawMessage = { + _: 'message', + out: upd.out, + mentioned: upd.mentioned, + mediaUnread: upd.mediaUnread, + silent: upd.silent, + id: upd.id, + fromId: { + _: 'peerUser', + userId: upd.fromId, + }, + peerId: { + _: 'peerChat', + chatId: upd.chatId, + }, + fwdFrom: upd.fwdFrom, + viaBotId: upd.viaBotId, + replyTo: upd.replyTo, + date: upd.date, + message: upd.message, + entities: upd.entities, + ttlPeriod: upd.ttlPeriod, + } + + const update: tl.RawUpdateNewMessage = { + _: 'updateNewMessage', + message, + pts: upd.pts, + ptsCount: upd.ptsCount, + } + + this._pendingPtsUpdates.add({ + update, + ptsBefore: upd.pts - upd.ptsCount, + pts: upd.pts, + }) + + break } - - await _processSingleUpdate.call(this, upd, null, noDispatch) - - break - } - case 'updateShortChatMessage': { - const message: tl.RawMessage = { - _: 'message', - out: update.out, - mentioned: update.mentioned, - mediaUnread: update.mediaUnread, - silent: update.silent, - id: update.id, - fromId: { - _: 'peerUser', - userId: update.fromId, - }, - peerId: { - _: 'peerChat', - chatId: update.chatId, - }, - fwdFrom: update.fwdFrom, - viaBotId: update.viaBotId, - replyTo: update.replyTo, - date: update.date, - message: update.message, - entities: update.entities, - ttlPeriod: update.ttlPeriod, + case 'updateShortSentMessage': { + // should not happen + log.warn('received updateShortSentMessage') + break } - - const upd: tl.RawUpdateNewMessage = { - _: 'updateNewMessage', - message, - pts: update.pts, - ptsCount: update.ptsCount, - } - - await _processSingleUpdate.call(this, upd, null, noDispatch) - - break - } - case 'updateShortSentMessage': { - // only store the new pts and date values - // we never need to dispatch this - - const nextLocalPts = this._pts! + update.ptsCount - if (nextLocalPts > update.pts) - // "the update was already applied, and must be ignored" - return - if (nextLocalPts < update.pts) - // "there's an update gap that must be filled" - return await _loadDifference.call(this) - - this._date = update.date - this._pts = update.pts - break } } - }) - .catch((err) => this._emitError(err)) - .then(() => this._updLock.release()) - .then(() => this._saveStorage()) -} -/** - * Catch up with the server by loading missed updates. - * - * @internal - */ -export function catchUp(this: TelegramClient): Promise { - // we also use a lock here so new updates are not processed - // while we are catching up with older ones + // process pts-ordered updates + while (this._pendingPtsUpdates.length) { + const pending = this._pendingPtsUpdates.popFront()! + const upd = pending.update - this._catchUpChannels = true + // check pts - return this._updLock - .acquire() - .then(() => _loadDifference.call(this)) - .catch((err) => this._emitError(err)) - .then(() => this._updLock.release()) - .then(() => this._saveStorage()) + let localPts: number | null = null + if (!pending.channelId) localPts = this._pts! + else if (pending.channelId in this._cpts) + localPts = this._cpts[pending.channelId] + else if (this._catchUpChannels) { + // only load stored channel pts in case + // the user has enabled catching up. + // not loading stored pts effectively disables + // catching up, but doesn't interfere with further + // update gaps (i.e. first update received is considered + // to be the base state) + + const saved = await this.storage.getChannelPts( + pending.channelId + ) + if (saved) { + this._cpts[pending.channelId] = localPts = saved + } + } + + if (localPts) { + if (localPts > pending.ptsBefore!) { + // "the update was already applied, and must be ignored" + log.debug( + 'ignoring %s (cid = %d) because already applied (by pts: exp %d, got %d)', + upd._, + pending.channelId, + localPts, + pending.ptsBefore + ) + continue + } + if (localPts < pending.ptsBefore!) { + // "there's an update gap that must be filled" + // if the gap is less than 3, put the update into postponed queue + // otherwise, call getDifference + if (pending.ptsBefore! - localPts < 3) { + log.debug( + 'postponing %s for 0.5s (cid = %d) because small gap detected (by pts: exp %d, got %d)', + upd._, + pending.channelId, + localPts, + pending.ptsBefore + ) + pending.timeout = Date.now() + 700 + this._pendingPtsUpdatesPostponed.add(pending) + } else { + log.debug( + 'fetching difference after %s (cid = %d) because pts gap detected (by pts: exp %d, got %d)', + upd._, + pending.channelId, + localPts, + pending.ptsBefore + ) + if (pending.channelId) { + _fetchChannelDifferenceLater.call( + this, + requestedDiff, + pending.channelId + ) + } else { + _fetchDifferenceLater.call(this, requestedDiff) + } + } + continue + } + } + + await _onUpdate.call(this, pending, requestedDiff) + } + + // process postponed pts-ordered updates + for ( + let item = this._pendingPtsUpdatesPostponed._first; + item; + item = item.n + ) { + // awesome fucking iteration because i'm so fucking tired and wanna kms + const pending = item.v + const upd = pending.update + + let localPts + if (!pending.channelId) localPts = this._pts! + else if (pending.channelId in this._cpts) + localPts = this._cpts[pending.channelId] + + // channel pts from storage will be available because we loaded it earlier + if (!localPts) { + log.warn( + 'local pts not available for postponed %s (cid = %d), skipping', + upd._, + pending.channelId + ) + continue + } + + // check the pts to see if the gap was filled + if (localPts > pending.ptsBefore!) { + // "the update was already applied, and must be ignored" + log.debug( + 'ignoring postponed %s (cid = %d) because already applied (by pts: exp %d, got %d)', + upd._, + pending.channelId, + localPts, + pending.ptsBefore + ) + this._pendingPtsUpdatesPostponed._remove(item) + continue + } + if (localPts < pending.ptsBefore!) { + // "there's an update gap that must be filled" + // if the timeout has not expired yet, keep the update in the queue + // otherwise, fetch diff + const now = Date.now() + if (now < pending.timeout!) { + log.debug( + 'postponed %s (cid = %d) is still waiting (%dms left) (current pts %d, need %d)', + upd._, + pending.channelId, + pending.timeout! - now, + localPts, + pending.ptsBefore + ) + } else { + log.debug( + "gap for postponed %s (cid = %d) wasn't filled, fetching diff (current pts %d, need %d)", + upd._, + pending.channelId, + localPts, + pending.ptsBefore + ) + this._pendingPtsUpdatesPostponed._remove(item) + if (pending.channelId) { + _fetchChannelDifferenceLater.call( + this, + requestedDiff, + pending.channelId + ) + } else { + _fetchDifferenceLater.call(this, requestedDiff) + } + } + continue + } + + await _onUpdate.call(this, pending, requestedDiff, true) + this._pendingPtsUpdatesPostponed._remove(item) + } + + // process qts-ordered updates + while (this._pendingQtsUpdates.length) { + const pending = this._pendingQtsUpdates.popFront()! + const upd = pending.update + + // check qts + + if (this._qts! > pending.qtsBefore!) { + // "the update was already applied, and must be ignored" + log.debug( + 'ignoring %s because already applied (by qts: exp %d, got %d)', + upd._, + this._qts!, + pending.qtsBefore + ) + continue + } + if (this._qts! < pending.qtsBefore!) { + // "there's an update gap that must be filled" + // if the gap is less than 3, put the update into postponed queue + // otherwise, call getDifference + // + if (pending.qtsBefore! - this._qts! < 3) { + log.debug( + 'postponing %s for 0.5s because small gap detected (by qts: exp %d, got %d)', + upd._, + this._qts!, + pending.qtsBefore + ) + pending.timeout = Date.now() + 700 + this._pendingQtsUpdatesPostponed.add(pending) + } else { + log.debug( + 'fetching difference after %s because qts gap detected (by qts: exp %d, got %d)', + upd._, + this._qts!, + pending.qtsBefore + ) + _fetchDifferenceLater.call(this, requestedDiff) + } + continue + } + + await _onUpdate.call(this, pending, requestedDiff) + } + + // process postponed qts-ordered updates + for ( + let item = this._pendingQtsUpdatesPostponed._first; + item; + item = item.n + ) { + // awesome fucking iteration because i'm so fucking tired and wanna kms + const pending = item.v + const upd = pending.update + + // check the pts to see if the gap was filled + if (this._qts! > pending.qtsBefore!) { + // "the update was already applied, and must be ignored" + log.debug( + 'ignoring postponed %s because already applied (by qts: exp %d, got %d)', + upd._, + this._qts!, + pending.qtsBefore + ) + continue + } + if (this._qts! < pending.qtsBefore!) { + // "there's an update gap that must be filled" + // if the timeout has not expired yet, keep the update in the queue + // otherwise, fetch diff + const now = Date.now() + if (now < pending.timeout!) { + log.debug( + 'postponed %s is still waiting (%dms left) (current qts %d, need %d)', + upd._, + pending.timeout! - now, + this._qts!, + pending.qtsBefore + ) + } else { + log.debug( + "gap for postponed %s wasn't filled, fetching diff (current qts %d, need %d)", + upd._, + this._qts!, + pending.qtsBefore + ) + this._pendingQtsUpdatesPostponed._remove(item) + _fetchDifferenceLater.call(this, requestedDiff) + } + continue + } + + // gap was filled, and the update can be applied + await _onUpdate.call(this, pending, requestedDiff, true) + this._pendingQtsUpdatesPostponed._remove(item) + } + + // wait for all pending diffs to load + let pendingDiffs = Object.values(requestedDiff) + while (pendingDiffs.length) { + log.debug( + 'waiting for %d pending diffs before processing unordered: %j', + pendingDiffs.length, + Object.keys(requestedDiff) // fixme + ) + + // this.primaryConnection._flushSendQueue() // fixme + await Promise.all(pendingDiffs) + + // diff results may as well contain new diffs to be requested + pendingDiffs = Object.values(requestedDiff) + log.debug( + 'pending diffs awaited, new diffs requested: %d (%j)', + pendingDiffs.length, + Object.keys(requestedDiff) // fixme + ) + } + + // process unordered updates (or updates received from diff) + while (this._pendingUnorderedUpdates.length) { + const pending = this._pendingUnorderedUpdates.popFront()! + + await _onUpdate.call(this, pending, requestedDiff, false, true) + } + + // onUpdate may also call getDiff in some cases, so we also need to check + // diff may also contain new updates, which will be processed in the next tick, + // but we don't want to postpone diff fetching + pendingDiffs = Object.values(requestedDiff) + while (pendingDiffs.length) { + log.debug( + 'waiting for %d pending diffs after processing unordered: %j', + pendingDiffs.length, + Object.keys(requestedDiff) // fixme + ) + + // fixme + // this.primaryConnection._flushSendQueue() + await Promise.all(pendingDiffs) + + // diff results may as well contain new diffs to be requested + pendingDiffs = Object.values(requestedDiff) + log.debug( + 'pending diffs awaited, new diffs requested: %d (%j)', + pendingDiffs.length, + Object.keys(requestedDiff) // fixme + ) + } + + // save new update state + await this._saveStorage() + } + + log.debug('updates loop stopped') + } catch (e) { + log.error('updates loop encountered error, restarting: %s', e) + this._updatesLoop().catch((err) => this._emitError(err)) + } } /** @internal */ export function _keepAliveAction(this: TelegramClient): void { this._updsLog.debug('no updates for >15 minutes, catching up') - this.catchUp().catch((err) => this._emitError(err)) + this._handleUpdate({ _: 'updatesTooLong' }) + // this.catchUp().catch((err) => this._emitError(err)) } diff --git a/packages/client/src/methods/users/get-common-chats.ts b/packages/client/src/methods/users/get-common-chats.ts index 3299db43..5746722e 100644 --- a/packages/client/src/methods/users/get-common-chats.ts +++ b/packages/client/src/methods/users/get-common-chats.ts @@ -2,6 +2,7 @@ import { InputPeerLike, MtInvalidPeerTypeError } from '../../types' import { TelegramClient } from '../../client' import { Chat } from '../../types' import { normalizeToInputUser } from '../../utils/peer-utils' +import Long from 'long' /** * Get a list of common chats you have with a given user diff --git a/packages/client/src/methods/users/get-me.ts b/packages/client/src/methods/users/get-me.ts index 297e72dd..2f003455 100644 --- a/packages/client/src/methods/users/get-me.ts +++ b/packages/client/src/methods/users/get-me.ts @@ -15,9 +15,20 @@ export function getMe(this: TelegramClient): Promise { _: 'inputUserSelf', }, ], - }).then(([user]) => { + }).then(async ([user]) => { assertTypeIs('getMe (@ users.getUsers)', user, 'user') + if (this._userId !== user.id) { + // there is such possibility, e.g. when + // using a string session without `self`, + // or logging out and re-logging in + // we need to update the fields accordingly, + // and force-save the session + this._userId = user.id + this._isBot = !!user.bot + await this._saveStorage() + } + this._selfUsername = user.username ?? null return new User(this, user) diff --git a/packages/client/src/methods/users/get-my-username.ts b/packages/client/src/methods/users/get-my-username.ts index bd69a945..430f5a72 100644 --- a/packages/client/src/methods/users/get-my-username.ts +++ b/packages/client/src/methods/users/get-my-username.ts @@ -1,6 +1,4 @@ import { TelegramClient } from '../../client' -import { User } from '../../types' -import { assertTypeIs } from '../../utils/type-assertion' /** * Get currently authorized user's username. diff --git a/packages/client/src/methods/users/get-profile-photos.ts b/packages/client/src/methods/users/get-profile-photos.ts index d85164b5..c29c58fa 100644 --- a/packages/client/src/methods/users/get-profile-photos.ts +++ b/packages/client/src/methods/users/get-profile-photos.ts @@ -1,8 +1,8 @@ import { TelegramClient } from '../../client' import { InputPeerLike, MtInvalidPeerTypeError, Photo } from '../../types' import { normalizeToInputUser } from '../../utils/peer-utils' -import bigInt from 'big-integer' import { tl } from '@mtcute/tl' +import Long from 'long' /** * Get a list of profile pictures of a user @@ -40,7 +40,7 @@ export async function getProfilePhotos( userId: peer, offset: params.offset ?? 0, limit: params.limit ?? 100, - maxId: bigInt.zero, + maxId: Long.ZERO, }) return res.photos.map((it) => new Photo(this, it as tl.RawPhoto)) diff --git a/packages/client/src/methods/users/get-users.ts b/packages/client/src/methods/users/get-users.ts index e0b007a1..9a98b35c 100644 --- a/packages/client/src/methods/users/get-users.ts +++ b/packages/client/src/methods/users/get-users.ts @@ -1,7 +1,6 @@ import { InputPeerLike, User } from '../../types' import { TelegramClient } from '../../client' import { MaybeArray } from '@mtcute/core' -import { tl } from '@mtcute/tl' import { normalizeToInputUser } from '../../utils/peer-utils' /** diff --git a/packages/client/src/methods/users/iter-profile-photos.ts b/packages/client/src/methods/users/iter-profile-photos.ts index 443ea059..b2babf34 100644 --- a/packages/client/src/methods/users/iter-profile-photos.ts +++ b/packages/client/src/methods/users/iter-profile-photos.ts @@ -2,7 +2,7 @@ import { TelegramClient } from '../../client' import { InputPeerLike, MtInvalidPeerTypeError, Photo } from '../../types' import { normalizeToInputUser } from '../../utils/peer-utils' import { tl } from '@mtcute/tl' -import bigInt from 'big-integer' +import Long from 'long' /** * Iterate over profile photos @@ -54,7 +54,7 @@ export async function* iterProfilePhotos( const limit = Math.min(params.chunkSize || 100, total) - const maxId = params.maxId || bigInt.zero + const maxId = params.maxId || Long.ZERO for (;;) { const res = await this.call({ diff --git a/packages/client/src/methods/users/resolve-peer.ts b/packages/client/src/methods/users/resolve-peer.ts index a031d35e..89158d3b 100644 --- a/packages/client/src/methods/users/resolve-peer.ts +++ b/packages/client/src/methods/users/resolve-peer.ts @@ -1,9 +1,9 @@ import { tl } from '@mtcute/tl' import { TelegramClient } from '../../client' import { InputPeerLike, MtNotFoundError } from '../../types' -import { getBasicPeerType, getMarkedPeerId, MAX_CHANNEL_ID } from '@mtcute/core' -import bigInt from 'big-integer' +import { getBasicPeerType, getMarkedPeerId, toggleChannelIdMark } from '@mtcute/core' import { normalizeToInputPeer } from '../../utils/peer-utils' +import Long from 'long' import { assertTypeIs } from '../../utils/type-assertion' /** @@ -11,11 +11,13 @@ import { assertTypeIs } from '../../utils/type-assertion' * Useful when an `InputPeer` is needed. * * @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 * @internal */ export async function resolvePeer( this: TelegramClient, - peerId: InputPeerLike + peerId: InputPeerLike, + force = false ): Promise { // for convenience we also accept tl objects directly if (typeof peerId === 'object') { @@ -26,7 +28,7 @@ export async function resolvePeer( } } - if (typeof peerId === 'number') { + if (typeof peerId === 'number' && !force) { const fromStorage = await this.storage.getPeerById(peerId) if (fromStorage) return fromStorage } @@ -42,7 +44,7 @@ export async function resolvePeer( const res = await this.call({ _: 'contacts.getContacts', - hash: 0, + hash: Long.ZERO, }) assertTypeIs('contacts.getContacts', res, 'contacts.contacts') @@ -62,8 +64,10 @@ export async function resolvePeer( ) } else { // username - const fromStorage = await this.storage.getPeerByUsername(peerId) - if (fromStorage) return fromStorage + if (!force) { + const fromStorage = await this.storage.getPeerByUsername(peerId) + if (fromStorage) return fromStorage + } const res = await this.call({ _: 'contacts.resolveUsername', @@ -122,7 +126,7 @@ export async function resolvePeer( { _: 'inputUser', userId: peerId, - accessHash: bigInt.zero, + accessHash: Long.ZERO, }, ], }) @@ -159,14 +163,15 @@ export async function resolvePeer( // break } case 'channel': { - const id = MAX_CHANNEL_ID - peerId + const id = toggleChannelIdMark(peerId as number) + const res = await this.call({ _: 'channels.getChannels', id: [ { _: 'inputChannel', - channelId: MAX_CHANNEL_ID - peerId, - accessHash: bigInt.zero, + channelId: id, + accessHash: Long.ZERO, }, ], }) diff --git a/packages/client/src/types/bots/callback-query.ts b/packages/client/src/types/bots/callback-query.ts index 0d5084fd..775eb632 100644 --- a/packages/client/src/types/bots/callback-query.ts +++ b/packages/client/src/types/bots/callback-query.ts @@ -5,7 +5,7 @@ import { Message } from '../messages' import { MtArgumentError } from '../errors' import { BasicPeerType, getBasicPeerType, getMarkedPeerId } from '@mtcute/core' import { encodeInlineMessageId } from '../../utils/inline-utils' -import { User, UsersIndex } from '../peers' +import { User, PeersIndex } from '../peers' import { MessageNotFoundError } from '@mtcute/core' /** @@ -13,22 +13,13 @@ import { MessageNotFoundError } from '@mtcute/core' * of an inline keyboard. */ export class CallbackQuery { - readonly client: TelegramClient - readonly raw: - | tl.RawUpdateBotCallbackQuery - | tl.RawUpdateInlineBotCallbackQuery - - readonly _users: UsersIndex - constructor( - client: TelegramClient, - raw: tl.RawUpdateBotCallbackQuery | tl.RawUpdateInlineBotCallbackQuery, - users: UsersIndex - ) { - this.client = client - this.raw = raw - this._users = users - } + readonly client: TelegramClient, + readonly raw: + | tl.RawUpdateBotCallbackQuery + | tl.RawUpdateInlineBotCallbackQuery, + readonly _peers: PeersIndex + ) {} /** * ID of this callback query @@ -43,7 +34,10 @@ export class CallbackQuery { */ get user(): User { if (!this._user) { - this._user = new User(this.client, this._users[this.raw.userId]) + this._user = new User( + this.client, + this._peers.user(this.raw.userId) + ) } return this._user diff --git a/packages/client/src/types/bots/game-high-score.ts b/packages/client/src/types/bots/game-high-score.ts index e66236d6..e5081123 100644 --- a/packages/client/src/types/bots/game-high-score.ts +++ b/packages/client/src/types/bots/game-high-score.ts @@ -1,26 +1,17 @@ import { makeInspectable } from '../utils' import { TelegramClient } from '../../client' import { tl } from '@mtcute/tl' -import { User, UsersIndex } from '../peers' +import { PeersIndex, User } from '../peers' /** * Game high score */ export class GameHighScore { - readonly client: TelegramClient - readonly raw: tl.RawHighScore - - readonly _users: UsersIndex - constructor( - client: TelegramClient, - raw: tl.RawHighScore, - users: UsersIndex - ) { - this.client = client - this.raw = raw - this._users = users - } + readonly client: TelegramClient, + readonly raw: tl.RawHighScore, + readonly _peers: PeersIndex + ) {} private _user?: User /** @@ -28,7 +19,10 @@ export class GameHighScore { */ get user(): User { if (!this._user) { - this._user = new User(this.client, this._users[this.raw.userId]) + this._user = new User( + this.client, + this._peers.user(this.raw.userId) + ) } return this._user diff --git a/packages/client/src/types/bots/inline-query.ts b/packages/client/src/types/bots/inline-query.ts index de0f8769..4dc96f59 100644 --- a/packages/client/src/types/bots/inline-query.ts +++ b/packages/client/src/types/bots/inline-query.ts @@ -1,6 +1,6 @@ import { makeInspectable } from '../utils' import { tl } from '@mtcute/tl' -import { PeerType, User, UsersIndex } from '../peers' +import { PeersIndex, PeerType, User } from '../peers' import { TelegramClient } from '../../client' import { Location } from '../media' import { InputInlineResult } from './input' @@ -14,20 +14,11 @@ const PEER_TYPE_MAP: Record = { } export class InlineQuery { - readonly client: TelegramClient - readonly raw: tl.RawUpdateBotInlineQuery - - /** Map of users in this message. Mainly for internal use */ - readonly _users: UsersIndex - constructor( - client: TelegramClient, - raw: tl.RawUpdateBotInlineQuery, - users: UsersIndex + readonly client: TelegramClient, + readonly raw: tl.RawUpdateBotInlineQuery, + readonly _peers: PeersIndex ) { - this.client = client - this.raw = raw - this._users = users } /** @@ -43,7 +34,7 @@ export class InlineQuery { */ get user(): User { if (!this._user) { - this._user = new User(this.client, this._users[this.raw.userId]) + this._user = new User(this.client, this._peers.user(this.raw.userId)) } return this._user diff --git a/packages/client/src/types/conversation.ts b/packages/client/src/types/conversation.ts index 7f577ecc..89b4b462 100644 --- a/packages/client/src/types/conversation.ts +++ b/packages/client/src/types/conversation.ts @@ -1,4 +1,4 @@ -import { AsyncLock, getMarkedPeerId, MaybeAsync } from '@mtcute/core' +import { AsyncLock, Deque, getMarkedPeerId, MaybeAsync } from '@mtcute/core' import { ControllablePromise, createControllablePromise, @@ -12,7 +12,6 @@ import { FormattedString } from './parser' import { Message } from './messages' import { tl } from '@mtcute/tl' import { TimeoutError } from '@mtcute/tl/errors' -import { Queue } from '../utils/queue' interface QueuedHandler { promise: ControllablePromise @@ -39,12 +38,12 @@ export class Conversation { private _lastMessage!: number private _lastReceivedMessage!: number - private _queuedNewMessage = new Queue>() - private _pendingNewMessages = new Queue() + private _queuedNewMessage = new Deque>() + private _pendingNewMessages = new Deque() private _lock = new AsyncLock() private _pendingEditMessage: Record> = {} - private _recentEdits = new Queue(10) + private _recentEdits = new Deque(10) private _pendingRead: Record> = {} @@ -276,7 +275,7 @@ export class Conversation { }, timeout) } - this._queuedNewMessage.push({ + this._queuedNewMessage.pushBack({ promise, check: filter, timeout: timer, @@ -476,12 +475,12 @@ export class Conversation { private _onNewMessage(msg: Message) { if (msg.chat.id !== this._chatId) return - if (this._queuedNewMessage.empty()) { - this._pendingNewMessages.push(msg) + if (!this._queuedNewMessage.length) { + this._pendingNewMessages.pushBack(msg) return } - const it = this._queuedNewMessage.peek()! + const it = this._queuedNewMessage.peekFront()! // order does matter for new messages this._lock.acquire().then(async () => { @@ -489,7 +488,7 @@ export class Conversation { if (!it.check || (await it.check(msg))) { if (it.timeout) clearTimeout(it.timeout) it.promise.resolve(msg) - this._queuedNewMessage.pop() + this._queuedNewMessage.popFront() } } catch (e) { this.client['_emitError'](e) @@ -507,7 +506,7 @@ export class Conversation { const it = this._pendingEditMessage[msg.id] if (!it && !fromRecent) { - this._recentEdits.push(msg) + this._recentEdits.pushBack(msg) return } @@ -536,21 +535,21 @@ export class Conversation { } private _processPendingNewMessages() { - if (this._pendingNewMessages.empty()) return + if (!this._pendingNewMessages.length) return let it - while ((it = this._pendingNewMessages.pop())) { + while ((it = this._pendingNewMessages.popFront())) { this._onNewMessage(it) } } private _processRecentEdits() { - if (this._recentEdits.empty()) return + if (!this._recentEdits.length) return - let it = this._recentEdits.first - do { - if (!it) break - this._onEditMessage(it.v, true) - } while ((it = it.n)) + const iter = this._recentEdits.iter() + let it + while (!(it = iter.next()).done) { + this._onEditMessage(it.value, true) + } } } diff --git a/packages/client/src/types/files/file-location.ts b/packages/client/src/types/files/file-location.ts index b7934e40..679a7e2e 100644 --- a/packages/client/src/types/files/file-location.ts +++ b/packages/client/src/types/files/file-location.ts @@ -10,43 +10,22 @@ import { makeInspectable } from '../utils' * including ones that are embedded directly into the entity. */ export class FileLocation { - /** - * Client that was used to create this object - */ - readonly client: TelegramClient - - /** - * Location of the file. - * - * Either a TL object declaring remote file location, - * a Buffer containing actual file content (for stripped thumbnails and vector previews), - * or a function that will return either of those. - * - * When a function is passed, it will be lazily resolved the - * first time downloading the file. - */ - readonly location: - | tl.TypeInputFileLocation - | tl.TypeInputWebFileLocation - | Buffer - | (() => - | tl.TypeInputFileLocation - | tl.TypeInputWebFileLocation - | Buffer) - - /** - * File size in bytes, when available - */ - readonly fileSize?: number - - /** - * DC ID of the file, when available - */ - readonly dcId?: number - constructor( - client: TelegramClient, - location: + /** + * Client that was used to create this object + */ + readonly client: TelegramClient, + /** + * Location of the file. + * + * Either a TL object declaring remote file location, + * a Buffer containing actual file content (for stripped thumbnails and vector previews), + * or a function that will return either of those. + * + * When a function is passed, it will be lazily resolved the + * first time downloading the file. + */ + readonly location: | tl.TypeInputFileLocation | tl.TypeInputWebFileLocation | Buffer @@ -54,13 +33,15 @@ export class FileLocation { | tl.TypeInputFileLocation | tl.TypeInputWebFileLocation | Buffer), - fileSize?: number, - dcId?: number + /** + * File size in bytes, when available + */ + readonly fileSize?: number, + /** + * DC ID of the file, when available + */ + readonly dcId?: number ) { - this.client = client - this.location = location - this.fileSize = fileSize - this.dcId = dcId } /** diff --git a/packages/client/src/types/files/web-document.ts b/packages/client/src/types/files/web-document.ts index 526586b3..f88bb155 100644 --- a/packages/client/src/types/files/web-document.ts +++ b/packages/client/src/types/files/web-document.ts @@ -21,9 +21,7 @@ const STUB_LOCATION = () => { * > To be sure, check `isDownloadable` property. */ export class WebDocument extends FileLocation { - readonly raw: tl.TypeWebDocument - - constructor(client: TelegramClient, raw: tl.TypeWebDocument) { + constructor(client: TelegramClient, readonly raw: tl.TypeWebDocument) { super( client, raw._ === 'webDocument' diff --git a/packages/client/src/types/media/audio.ts b/packages/client/src/types/media/audio.ts index 27cc46d9..48221b94 100644 --- a/packages/client/src/types/media/audio.ts +++ b/packages/client/src/types/media/audio.ts @@ -10,8 +10,6 @@ import { tdFileId } from '@mtcute/file-id' export class Audio extends RawDocument { readonly type = 'audio' as const - readonly attr: tl.RawDocumentAttributeAudio - protected _fileIdType(): tdFileId.FileType { return tdFileId.FileType.Audio } @@ -19,10 +17,9 @@ export class Audio extends RawDocument { constructor( client: TelegramClient, doc: tl.RawDocument, - attr: tl.RawDocumentAttributeAudio + readonly attr: tl.RawDocumentAttributeAudio ) { super(client, doc) - this.attr = attr } /** diff --git a/packages/client/src/types/media/contact.ts b/packages/client/src/types/media/contact.ts index d0099064..cea2e4fa 100644 --- a/packages/client/src/types/media/contact.ts +++ b/packages/client/src/types/media/contact.ts @@ -7,11 +7,7 @@ import { makeInspectable } from '../utils' export class Contact { readonly type = 'contact' as const - readonly obj: tl.RawMessageMediaContact - - constructor(obj: tl.RawMessageMediaContact) { - this.obj = obj - } + constructor(readonly obj: tl.RawMessageMediaContact) {} /** * Contact's phone number diff --git a/packages/client/src/types/media/dice.ts b/packages/client/src/types/media/dice.ts index e7cae761..d8c63466 100644 --- a/packages/client/src/types/media/dice.ts +++ b/packages/client/src/types/media/dice.ts @@ -7,8 +7,6 @@ import { makeInspectable } from '../utils' export class Dice { readonly type = 'dice' as const - readonly obj: tl.RawMessageMediaDice - /** * A simple 6-sided dice. * @@ -138,9 +136,7 @@ export class Dice { */ static readonly TYPE_SLOTS = '🎰' - constructor(obj: tl.RawMessageMediaDice) { - this.obj = obj - } + constructor(readonly obj: tl.RawMessageMediaDice) {} /** * An emoji which was originally sent. diff --git a/packages/client/src/types/media/document.ts b/packages/client/src/types/media/document.ts index 5aa7a4fd..cd7ffb28 100644 --- a/packages/client/src/types/media/document.ts +++ b/packages/client/src/types/media/document.ts @@ -11,25 +11,20 @@ import { tdFileId as td, toFileId, toUniqueFileId } from '@mtcute/file-id' * This also includes audios, videos, voices etc. */ export class RawDocument extends FileLocation { - /** - * Raw TL object with the document itself - */ - readonly doc: tl.RawDocument - - constructor(client: TelegramClient, doc: tl.RawDocument) { + constructor(client: TelegramClient, readonly raw: tl.RawDocument) { super( client, { _: 'inputDocumentFileLocation', - id: doc.id, - fileReference: doc.fileReference, - accessHash: doc.accessHash, + id: raw.id, + fileReference: raw.fileReference, + accessHash: raw.accessHash, thumbSize: '', }, - doc.size, - doc.dcId + raw.size, + raw.dcId ) - this.doc = doc + this.raw = raw } private _fileName?: string | null @@ -39,7 +34,7 @@ export class RawDocument extends FileLocation { */ get fileName(): string | null { if (this._fileName === undefined) { - const attr = this.doc.attributes.find( + const attr = this.raw.attributes.find( (it) => it._ === 'documentAttributeFilename' ) this._fileName = attr @@ -54,14 +49,14 @@ export class RawDocument extends FileLocation { * File MIME type, as defined by the sender. */ get mimeType(): string { - return this.doc.mimeType + return this.raw.mimeType } /** * Date the document was sent */ get date(): Date { - return new Date(this.doc.date * 1000) + return new Date(this.raw.date * 1000) } private _thumbnails?: Thumbnail[] @@ -72,9 +67,9 @@ export class RawDocument extends FileLocation { */ get thumbnails(): ReadonlyArray { if (!this._thumbnails) { - this._thumbnails = this.doc.thumbs - ? this.doc.thumbs.map( - (sz) => new Thumbnail(this.client, this.doc, sz) + this._thumbnails = this.raw.thumbs + ? this.raw.thumbs.map( + (sz) => new Thumbnail(this.client, this.raw, sz) ) : [] } @@ -102,9 +97,9 @@ export class RawDocument extends FileLocation { get inputDocument(): tl.TypeInputDocument { return { _: 'inputDocument', - id: this.doc.id, - accessHash: this.doc.accessHash, - fileReference: this.doc.fileReference, + id: this.raw.id, + accessHash: this.raw.accessHash, + fileReference: this.raw.fileReference, } } @@ -133,12 +128,12 @@ export class RawDocument extends FileLocation { if (!this._fileId) { this._fileId = toFileId({ type: this._fileIdType(), - dcId: this.doc.dcId, - fileReference: this.doc.fileReference, + dcId: this.raw.dcId, + fileReference: this.raw.fileReference, location: { _: 'common', - id: this.doc.id, - accessHash: this.doc.accessHash, + id: this.raw.id, + accessHash: this.raw.accessHash, }, }) } @@ -154,7 +149,7 @@ export class RawDocument extends FileLocation { if (!this._uniqueFileId) { this._uniqueFileId = toUniqueFileId(td.FileType.Document, { _: 'common', - id: this.doc.id, + id: this.raw.id, }) } diff --git a/packages/client/src/types/media/game.ts b/packages/client/src/types/media/game.ts index 8e3948a0..43363a53 100644 --- a/packages/client/src/types/media/game.ts +++ b/packages/client/src/types/media/game.ts @@ -7,13 +7,7 @@ import { makeInspectable } from '../utils' export class Game { readonly type = 'game' as const - readonly game: tl.RawGame - readonly client: TelegramClient - - constructor(client: TelegramClient, game: tl.RawGame) { - this.client = client - this.game = game - } + constructor(readonly client: TelegramClient, readonly game: tl.RawGame) {} /** * Unique identifier of the game. diff --git a/packages/client/src/types/media/invoice.ts b/packages/client/src/types/media/invoice.ts index 0db4d06a..581a66b5 100644 --- a/packages/client/src/types/media/invoice.ts +++ b/packages/client/src/types/media/invoice.ts @@ -10,13 +10,10 @@ import { MtArgumentError } from '../errors' export class Invoice { readonly type = 'invoice' as const - readonly client: TelegramClient - readonly raw: tl.RawMessageMediaInvoice - - constructor(client: TelegramClient, raw: tl.RawMessageMediaInvoice) { - this.client = client - this.raw = raw - } + constructor( + readonly client: TelegramClient, + readonly raw: tl.RawMessageMediaInvoice + ) {} /** * Whether the shipping address was requested diff --git a/packages/client/src/types/media/location.ts b/packages/client/src/types/media/location.ts index e7e9da3c..0f9a909d 100644 --- a/packages/client/src/types/media/location.ts +++ b/packages/client/src/types/media/location.ts @@ -7,13 +7,10 @@ import { TelegramClient } from '../../client' * A point on the map */ export class RawLocation { - readonly client: TelegramClient - readonly geo: tl.RawGeoPoint - - constructor(client: TelegramClient, geo: tl.RawGeoPoint) { - this.client = client - this.geo = geo - } + constructor( + readonly client: TelegramClient, + readonly geo: tl.RawGeoPoint + ) {} /** * Geo point latitude @@ -112,11 +109,8 @@ export class Location extends RawLocation { export class LiveLocation extends RawLocation { readonly type = 'live_location' as const - readonly live: tl.RawMessageMediaGeoLive - - constructor(client: TelegramClient, live: tl.RawMessageMediaGeoLive) { + constructor(client: TelegramClient, readonly live: tl.RawMessageMediaGeoLive) { super(client, live.geo as tl.RawGeoPoint) - this.live = live } /** diff --git a/packages/client/src/types/media/poll.ts b/packages/client/src/types/media/poll.ts index d3eb73ad..775fd57c 100644 --- a/packages/client/src/types/media/poll.ts +++ b/packages/client/src/types/media/poll.ts @@ -2,8 +2,8 @@ import { makeInspectable } from '../utils' import { tl } from '@mtcute/tl' import { TelegramClient } from '../../client' import { MessageEntity } from '../messages' -import bigInt from 'big-integer' -import { UsersIndex } from '../peers' +import { PeersIndex } from '../peers' +import Long from 'long' export namespace Poll { export interface PollAnswer { @@ -40,23 +40,12 @@ export namespace Poll { export class Poll { readonly type = 'poll' as const - readonly client: TelegramClient - readonly raw: tl.TypePoll - readonly results?: tl.TypePollResults - - readonly _users: UsersIndex - constructor( - client: TelegramClient, - raw: tl.TypePoll, - users: UsersIndex, - results?: tl.TypePollResults - ) { - this.client = client - this.raw = raw - this._users = users - this.results = results - } + readonly client: TelegramClient, + readonly raw: tl.TypePoll, + readonly _peers: PeersIndex, + readonly results?: tl.TypePollResults + ) {} /** * Unique identifier of the poll @@ -151,6 +140,7 @@ export class Poll { } private _entities?: MessageEntity[] + /** * Format entities for {@link solution}, only available * in case you have already answered @@ -206,7 +196,7 @@ export class Poll { poll: { _: 'poll', closed: false, - id: bigInt.zero, + id: Long.ZERO, publicVoters: this.raw.publicVoters, multipleChoice: this.raw.multipleChoice, question: this.raw.question, diff --git a/packages/client/src/types/media/sticker.ts b/packages/client/src/types/media/sticker.ts index 51c68ff8..56d8dd38 100644 --- a/packages/client/src/types/media/sticker.ts +++ b/packages/client/src/types/media/sticker.ts @@ -42,9 +42,6 @@ const MASK_POS = ['forehead', 'eyes', 'mouth', 'chin'] as const export class Sticker extends RawDocument { readonly type = 'sticker' as const - readonly attr: tl.RawDocumentAttributeSticker - readonly attrSize?: tl.RawDocumentAttributeImageSize - protected _fileIdType(): tdFileId.FileType { return tdFileId.FileType.Sticker } @@ -52,12 +49,10 @@ export class Sticker extends RawDocument { constructor( client: TelegramClient, doc: tl.RawDocument, - attr: tl.RawDocumentAttributeSticker, - attrSize?: tl.RawDocumentAttributeImageSize + readonly attr: tl.RawDocumentAttributeSticker, + readonly attrSize?: tl.RawDocumentAttributeImageSize ) { super(client, doc) - this.attr = attr - this.attrSize = attrSize } /** @@ -177,7 +172,7 @@ export class Sticker extends RawDocument { const set = await this.getStickerSet() if (!set) return '' - return set.stickers.find((it) => it.sticker.doc.id.eq(this.doc.id))! + return set.stickers.find((it) => it.sticker.raw.id.eq(this.raw.id))! .emoji } } diff --git a/packages/client/src/types/media/thumbnail.ts b/packages/client/src/types/media/thumbnail.ts index ad405915..375cd1be 100644 --- a/packages/client/src/types/media/thumbnail.ts +++ b/packages/client/src/types/media/thumbnail.ts @@ -10,7 +10,7 @@ import { MtArgumentError, MtTypeAssertionError } from '../errors' import { assertTypeIs } from '../../utils/type-assertion' import { makeInspectable } from '../utils' import { tdFileId as td, toFileId, toUniqueFileId } from '@mtcute/file-id' -import bigInt from 'big-integer' +import Long from 'long' /** * One size of some thumbnail @@ -171,8 +171,8 @@ export class Thumbnail extends FileLocation { fileReference: null, location: { _: 'photo', - id: bigInt.zero, - accessHash: bigInt.zero, + id: Long.ZERO, + accessHash: Long.ZERO, source: { _: 'stickerSetThumbnailVersion', id: this._media.id, @@ -226,7 +226,7 @@ export class Thumbnail extends FileLocation { if (this._media._ === 'stickerSet') { this._uniqueFileId = toUniqueFileId(td.FileType.Thumbnail, { _: 'photo', - id: bigInt.zero, + id: Long.ZERO, source: { _: 'stickerSetThumbnailVersion', id: this._media.id, diff --git a/packages/client/src/types/media/venue.ts b/packages/client/src/types/media/venue.ts index ef4a895c..d61854f6 100644 --- a/packages/client/src/types/media/venue.ts +++ b/packages/client/src/types/media/venue.ts @@ -31,13 +31,10 @@ export namespace Venue { export class Venue { readonly type = 'venue' as const - readonly client: TelegramClient - readonly raw: tl.RawMessageMediaVenue - - constructor(client: TelegramClient, raw: tl.RawMessageMediaVenue) { - this.client = client - this.raw = raw - } + constructor( + readonly client: TelegramClient, + readonly raw: tl.RawMessageMediaVenue + ) {} private _location?: Location /** diff --git a/packages/client/src/types/media/video.ts b/packages/client/src/types/media/video.ts index d8eca2d7..edafe311 100644 --- a/packages/client/src/types/media/video.ts +++ b/packages/client/src/types/media/video.ts @@ -12,10 +12,6 @@ import { tdFileId } from '@mtcute/file-id' export class Video extends RawDocument { readonly type = 'video' as const - readonly attr: - | tl.RawDocumentAttributeVideo - | tl.RawDocumentAttributeImageSize - protected _fileIdType(): tdFileId.FileType { return this.isRound ? tdFileId.FileType.VideoNote @@ -27,10 +23,11 @@ export class Video extends RawDocument { constructor( client: TelegramClient, doc: tl.RawDocument, - attr: tl.RawDocumentAttributeVideo | tl.RawDocumentAttributeImageSize + readonly attr: + | tl.RawDocumentAttributeVideo + | tl.RawDocumentAttributeImageSize ) { super(client, doc) - this.attr = attr } /** @@ -65,7 +62,7 @@ export class Video extends RawDocument { if (!this._isAnimation) { this._isAnimation = this.attr._ === 'documentAttributeImageSize' || - this.doc.attributes.some( + this.raw.attributes.some( (it) => it._ === 'documentAttributeAnimated' ) } diff --git a/packages/client/src/types/media/voice.ts b/packages/client/src/types/media/voice.ts index 46d5cd35..62bddf24 100644 --- a/packages/client/src/types/media/voice.ts +++ b/packages/client/src/types/media/voice.ts @@ -11,8 +11,6 @@ import { decodeWaveform } from '../../utils/voice-utils' export class Voice extends RawDocument { readonly type = 'voice' as const - readonly attr: tl.RawDocumentAttributeAudio - protected _fileIdType(): tdFileId.FileType { return tdFileId.FileType.VoiceNote } @@ -20,10 +18,9 @@ export class Voice extends RawDocument { constructor( client: TelegramClient, doc: tl.RawDocument, - attr: tl.RawDocumentAttributeAudio + readonly attr: tl.RawDocumentAttributeAudio ) { super(client, doc) - this.attr = attr } /** diff --git a/packages/client/src/types/media/web-page.ts b/packages/client/src/types/media/web-page.ts index 2787b668..5b089581 100644 --- a/packages/client/src/types/media/web-page.ts +++ b/packages/client/src/types/media/web-page.ts @@ -19,13 +19,7 @@ import { MtArgumentError } from '../errors' export class WebPage { readonly type = 'web_page' as const - readonly client: TelegramClient - readonly raw: tl.RawWebPage - - constructor(client: TelegramClient, raw: tl.RawWebPage) { - this.client = client - this.raw = raw - } + constructor(readonly client: TelegramClient, readonly raw: tl.RawWebPage) {} /** * Unique ID of the preview diff --git a/packages/client/src/types/messages/dialog.ts b/packages/client/src/types/messages/dialog.ts index c87dd24d..b6e87cea 100644 --- a/packages/client/src/types/messages/dialog.ts +++ b/packages/client/src/types/messages/dialog.ts @@ -1,6 +1,6 @@ import { TelegramClient } from '../../client' import { tl } from '@mtcute/tl' -import { Chat, ChatsIndex, UsersIndex } from '../peers' +import { Chat, PeersIndex } from '../peers' import { Message } from './message' import { DraftMessage } from './draft-message' import { makeInspectable } from '../utils' @@ -13,31 +13,12 @@ import { getMarkedPeerId, MessageNotFoundError } from '@mtcute/core' * in Telegram's main window. */ export class Dialog { - readonly client: TelegramClient - readonly raw: tl.RawDialog - - /** Map of users in this object. Mainly for internal use */ - readonly _users: UsersIndex - - /** Map of chats in this object. Mainly for internal use */ - readonly _chats: ChatsIndex - - /** Map of messages in this object. Mainly for internal use */ - readonly _messages: Record - constructor( - client: TelegramClient, - raw: tl.RawDialog, - users: UsersIndex, - chats: ChatsIndex, - messages: Record - ) { - this.client = client - this.raw = raw - this._users = users - this._chats = chats - this._messages = messages - } + readonly client: TelegramClient, + readonly raw: tl.RawDialog, + readonly _peers: PeersIndex, + readonly _messages: Record + ) {} /** * Find pinned dialogs from a list of dialogs @@ -172,12 +153,12 @@ export class Dialog { switch (peer._) { case 'peerChannel': case 'peerChat': - chat = this._chats[ + chat = this._peers.chat( peer._ === 'peerChannel' ? peer.channelId : peer.chatId - ] + ) break default: - chat = this._users[peer.userId] + chat = this._peers.user(peer.userId) break } @@ -200,8 +181,7 @@ export class Dialog { this._lastMessage = new Message( this.client, this._messages[cid], - this._users, - this._chats + this._peers ) } else { throw new MessageNotFoundError() diff --git a/packages/client/src/types/messages/draft-message.ts b/packages/client/src/types/messages/draft-message.ts index cea3afec..99b363ed 100644 --- a/packages/client/src/types/messages/draft-message.ts +++ b/packages/client/src/types/messages/draft-message.ts @@ -10,19 +10,11 @@ import { makeInspectable } from '../utils' import { InputMediaWithCaption } from '../media' export class DraftMessage { - readonly client: TelegramClient - readonly raw: tl.RawDraftMessage - - private _chatId: InputPeerLike - constructor( - client: TelegramClient, - raw: tl.RawDraftMessage, - chatId: InputPeerLike + readonly client: TelegramClient, + readonly raw: tl.RawDraftMessage, + readonly _chatId: InputPeerLike ) { - this.client = client - this.raw = raw - this._chatId = chatId } /** diff --git a/packages/client/src/types/messages/message-media.ts b/packages/client/src/types/messages/message-media.ts index 2e0f9673..b321bae2 100644 --- a/packages/client/src/types/messages/message-media.ts +++ b/packages/client/src/types/messages/message-media.ts @@ -69,7 +69,7 @@ export function _messageMediaFromTl( case 'messageMediaVenue': return new Venue(this.client, m) case 'messageMediaPoll': - return new Poll(this.client, m.poll, this._users, m.results) + return new Poll(this.client, m.poll, this._peers, m.results) case 'messageMediaInvoice': return new Invoice(this.client, m) default: diff --git a/packages/client/src/types/messages/message.ts b/packages/client/src/types/messages/message.ts index 25b694c3..40bdc84d 100644 --- a/packages/client/src/types/messages/message.ts +++ b/packages/client/src/types/messages/message.ts @@ -1,7 +1,7 @@ -import { User, Chat, InputPeerLike, UsersIndex, ChatsIndex } from '../peers' +import { User, Chat, InputPeerLike, PeersIndex } from '../peers' import { tl } from '@mtcute/tl' import { BotKeyboard, ReplyMarkup } from '../bots' -import { getMarkedPeerId, MAX_CHANNEL_ID } from '@mtcute/core' +import { getMarkedPeerId, toggleChannelIdMark } from '@mtcute/core' import { MtArgumentError, MtTypeAssertionError, @@ -96,44 +96,31 @@ export namespace Message { * A Telegram message. */ export class Message { - /** Telegram client that received this message */ - readonly client: TelegramClient - /** * Raw TL object. */ readonly raw: tl.RawMessage | tl.RawMessageService - /** Map of users in this message. Mainly for internal use */ - readonly _users: UsersIndex - /** Map of chats in this message. Mainly for internal use */ - readonly _chats: ChatsIndex - constructor( - client: TelegramClient, + readonly client: TelegramClient, raw: tl.TypeMessage, - users: UsersIndex, - chats: ChatsIndex, - isScheduled = false + readonly _peers: PeersIndex, + /** + * Whether the message is scheduled. + * If it is, then its {@link date} is set to future. + */ + readonly isScheduled = false ) { if (raw._ === 'messageEmpty') - throw new MtTypeAssertionError('Message#ctor', 'not messageEmpty', 'messageEmpty') - - this.client = client - this._users = users - this._chats = chats + throw new MtTypeAssertionError( + 'Message#ctor', + 'not messageEmpty', + 'messageEmpty' + ) this.raw = raw - - this.isScheduled = isScheduled } - /** - * Whether the message is scheduled. - * If it is, then its {@link date} is set to future. - */ - readonly isScheduled: boolean - /** Unique message identifier inside this chat */ get id(): number { return this.raw.id @@ -197,7 +184,7 @@ export class Message { if (this.raw.peerId._ === 'peerUser') { this._sender = new User( this.client, - this._users[this.raw.peerId.userId] + this._peers.user(this.raw.peerId.userId) ) } else { // anon admin, return the chat @@ -208,13 +195,13 @@ export class Message { case 'peerChannel': // forwarded channel post this._sender = new Chat( this.client, - this._chats[from.channelId] + this._peers.chat(from.channelId) ) break case 'peerUser': this._sender = new User( this.client, - this._users[from.userId] + this._peers.user(from.userId) ) break default: @@ -239,8 +226,7 @@ export class Message { this._chat = Chat._parseFromMessage( this.client, this.raw, - this._users, - this._chats + this._peers ) } @@ -274,13 +260,13 @@ export class Message { case 'peerChannel': sender = new Chat( this.client, - this._chats[fwd.fromId.channelId] + this._peers.chat(fwd.fromId.channelId) ) break case 'peerUser': sender = new User( this.client, - this._users[fwd.fromId.userId] + this._peers.user(fwd.fromId.userId) ) break default: @@ -329,7 +315,7 @@ export class Message { if (r.comments) { const o = (obj as unknown) as Message.MessageCommentsInfo - o.discussion = r.channelId! + o.discussion = getMarkedPeerId(r.channelId!, 'channel') o.repliers = r.recentRepliers?.map((it) => getMarkedPeerId(it)) ?? [] } @@ -375,7 +361,7 @@ export class Message { } else { this._viaBot = new User( this.client, - this._users[this.raw.viaBotId] + this._peers.user(this.raw.viaBotId) ) } } @@ -515,7 +501,7 @@ export class Message { if (this.chat.username) { return `https://t.me/${this.chat.username}/${this.id}` } else { - return `https://t.me/c/${MAX_CHANNEL_ID - this.chat.id}/${ + return `https://t.me/c/${toggleChannelIdMark(this.chat.id)}/${ this.id }` } @@ -547,8 +533,7 @@ export class Message { * this method will also return `null`. */ getReplyTo(): Promise { - if (!this.replyToMessageId) - return Promise.resolve(null) + if (!this.replyToMessageId) return Promise.resolve(null) if (this.raw.peerId._ === 'peerChannel') return this.client.getMessages(this.chat.inputPeer, this.id, true) @@ -795,7 +780,7 @@ export class Message { * passing positional `text` as object field. * * @param text New message text - * @param params Additional parameters + * @param params? Additional parameters * @link TelegramClient.editMessage */ editText( @@ -819,7 +804,12 @@ export class Message { peer: InputPeerLike, params?: Parameters[3] ): Promise { - return this.client.forwardMessages(peer, this.chat.inputPeer, this.id, params) + return this.client.forwardMessages( + peer, + this.chat.inputPeer, + this.id, + params + ) } /** @@ -885,7 +875,10 @@ export class Message { * message. */ async getDiscussionMessage(): Promise { - return this.client.getDiscussionMessage(this.chat.inputPeer, this.raw.id) + return this.client.getDiscussionMessage( + this.chat.inputPeer, + this.raw.id + ) } /** diff --git a/packages/client/src/types/messages/search-filters.ts b/packages/client/src/types/messages/search-filters.ts index f0cd3b03..867b6e6f 100644 --- a/packages/client/src/types/messages/search-filters.ts +++ b/packages/client/src/types/messages/search-filters.ts @@ -1,5 +1,3 @@ -import { tl } from '@mtcute/tl' - /** * Search filters to be used in {@link TelegramClient.searchMessages} * and {@link TelegramClient.searchGlobal}. diff --git a/packages/client/src/types/misc/sticker-set.ts b/packages/client/src/types/misc/sticker-set.ts index 66eab9a1..26b93c58 100644 --- a/packages/client/src/types/misc/sticker-set.ts +++ b/packages/client/src/types/misc/sticker-set.ts @@ -32,7 +32,6 @@ export namespace StickerSet { * A stickerset (aka sticker pack) */ export class StickerSet { - readonly client: TelegramClient readonly brief: tl.RawStickerSet readonly full?: tl.messages.RawStickerSet @@ -42,10 +41,9 @@ export class StickerSet { readonly isFull: boolean constructor( - client: TelegramClient, + readonly client: TelegramClient, raw: tl.RawStickerSet | tl.messages.RawStickerSet ) { - this.client = client if (raw._ === 'messages.stickerSet') { this.full = raw this.brief = raw.set diff --git a/packages/client/src/types/misc/takeout-session.ts b/packages/client/src/types/misc/takeout-session.ts index 481d9bd9..e5729055 100644 --- a/packages/client/src/types/misc/takeout-session.ts +++ b/packages/client/src/types/misc/takeout-session.ts @@ -6,15 +6,15 @@ import { makeInspectable } from '../utils' * Account takeout session */ export class TakeoutSession { - private client: TelegramClient - /** * Takeout session id */ readonly id: tl.Long - constructor(client: TelegramClient, session: tl.account.RawTakeout) { - this.client = client + constructor( + readonly client: TelegramClient, + session: tl.account.RawTakeout + ) { this.id = session.id } diff --git a/packages/client/src/types/peers/chat-event.ts b/packages/client/src/types/peers/chat-event.ts index b077f13e..4d1e19e3 100644 --- a/packages/client/src/types/peers/chat-event.ts +++ b/packages/client/src/types/peers/chat-event.ts @@ -8,7 +8,9 @@ import { Message } from '../messages' import { ChatPermissions } from './chat-permissions' import { ChatLocation } from './chat-location' import { ChatInviteLink } from './chat-invite-link' -import { ChatsIndex, UsersIndex } from './index' +import { PeersIndex } from './index' +import { toggleChannelIdMark } from '../../../../core' + export namespace ChatEvent { /** A user has joined the group (in the case of big groups, info of the user that has joined isn't shown) */ @@ -371,8 +373,7 @@ function _actionFromTl( message: new Message( this.client, e.message, - this._users, - this._chats + this._peers ), } case 'channelAdminLogEventActionEditMessage': @@ -381,14 +382,12 @@ function _actionFromTl( old: new Message( this.client, e.prevMessage, - this._users, - this._chats + this._peers ), new: new Message( this.client, e.newMessage, - this._users, - this._chats + this._peers ), } case 'channelAdminLogEventActionDeleteMessage': @@ -397,8 +396,7 @@ function _actionFromTl( message: new Message( this.client, e.message, - this._users, - this._chats + this._peers ), } case 'channelAdminLogEventActionParticipantLeave': @@ -406,7 +404,7 @@ function _actionFromTl( case 'channelAdminLogEventActionParticipantInvite': return { type: 'user_invited', - member: new ChatMember(this.client, e.participant, this._users), + member: new ChatMember(this.client, e.participant, this._peers), } case 'channelAdminLogEventActionParticipantToggleBan': return { @@ -414,9 +412,9 @@ function _actionFromTl( old: new ChatMember( this.client, e.prevParticipant, - this._users + this._peers ), - new: new ChatMember(this.client, e.newParticipant, this._users), + new: new ChatMember(this.client, e.newParticipant, this._peers), } case 'channelAdminLogEventActionParticipantToggleAdmin': return { @@ -424,9 +422,9 @@ function _actionFromTl( old: new ChatMember( this.client, e.prevParticipant, - this._users + this._peers ), - new: new ChatMember(this.client, e.newParticipant, this._users), + new: new ChatMember(this.client, e.newParticipant, this._peers), } case 'channelAdminLogEventActionChangeStickerSet': return { @@ -452,15 +450,14 @@ function _actionFromTl( message: new Message( this.client, e.message, - this._users, - this._chats + this._peers ), } case 'channelAdminLogEventActionChangeLinkedChat': return { type: 'linked_chat_changed', - old: e.prevValue, - new: e.newValue, + old: toggleChannelIdMark(e.prevValue), + new: toggleChannelIdMark(e.newValue), } case 'channelAdminLogEventActionChangeLocation': return { @@ -503,23 +500,23 @@ function _actionFromTl( case 'channelAdminLogEventActionParticipantJoinByInvite': return { type: 'user_joined_invite', - link: new ChatInviteLink(this.client, e.invite, this._users), + link: new ChatInviteLink(this.client, e.invite, this._peers), } case 'channelAdminLogEventActionExportedInviteDelete': return { type: 'invite_deleted', - link: new ChatInviteLink(this.client, e.invite, this._users), + link: new ChatInviteLink(this.client, e.invite, this._peers), } case 'channelAdminLogEventActionExportedInviteRevoke': return { type: 'invite_revoked', - link: new ChatInviteLink(this.client, e.invite, this._users), + link: new ChatInviteLink(this.client, e.invite, this._peers), } case 'channelAdminLogEventActionExportedInviteEdit': return { type: 'invite_edited', - old: new ChatInviteLink(this.client, e.prevInvite, this._users), - new: new ChatInviteLink(this.client, e.newInvite, this._users), + old: new ChatInviteLink(this.client, e.prevInvite, this._peers), + new: new ChatInviteLink(this.client, e.newInvite, this._peers), } case 'channelAdminLogEventActionChangeHistoryTTL': return { @@ -533,22 +530,11 @@ function _actionFromTl( } export class ChatEvent { - readonly client: TelegramClient - readonly raw: tl.TypeChannelAdminLogEvent - - readonly _users: UsersIndex - readonly _chats: ChatsIndex - constructor( - client: TelegramClient, - raw: tl.TypeChannelAdminLogEvent, - users: UsersIndex, - chats: ChatsIndex + readonly client: TelegramClient, + readonly raw: tl.TypeChannelAdminLogEvent, + readonly _peers: PeersIndex, ) { - this.client = client - this.raw = raw - this._users = users - this._chats = chats } /** @@ -574,7 +560,7 @@ export class ChatEvent { */ get actor(): User { if (!this._actor) { - this._actor = new User(this.client, this._users[this.raw.userId]) + this._actor = new User(this.client, this._peers.user(this.raw.userId)) } return this._actor diff --git a/packages/client/src/types/peers/chat-invite-link.ts b/packages/client/src/types/peers/chat-invite-link.ts index 8be19053..1d2bcc1f 100644 --- a/packages/client/src/types/peers/chat-invite-link.ts +++ b/packages/client/src/types/peers/chat-invite-link.ts @@ -2,7 +2,7 @@ import { makeInspectable } from '../utils' import { TelegramClient } from '../../client' import { tl } from '@mtcute/tl' import { User } from './user' -import { UsersIndex } from './index' +import { PeersIndex } from './index' export namespace ChatInviteLink { export interface JoinedMember { @@ -15,19 +15,11 @@ export namespace ChatInviteLink { * An invite link */ export class ChatInviteLink { - readonly client: TelegramClient - readonly raw: tl.RawChatInviteExported - - readonly _users?: UsersIndex - constructor( - client: TelegramClient, - raw: tl.RawChatInviteExported, - users?: UsersIndex + readonly client: TelegramClient, + readonly raw: tl.RawChatInviteExported, + readonly _peers?: PeersIndex ) { - this.client = client - this.raw = raw - this._users = users } /** @@ -45,10 +37,13 @@ export class ChatInviteLink { * Creator of the invite link, if available */ get creator(): User | null { - if (!this._users) return null + if (!this._peers) return null if (!this._creator) { - this._creator = new User(this.client, this._users[this.raw.adminId]) + this._creator = new User( + this.client, + this._peers.user(this.raw.adminId) + ) } return this._creator diff --git a/packages/client/src/types/peers/chat-location.ts b/packages/client/src/types/peers/chat-location.ts index 576c8dac..a651f67f 100644 --- a/packages/client/src/types/peers/chat-location.ts +++ b/packages/client/src/types/peers/chat-location.ts @@ -7,13 +7,10 @@ import { makeInspectable } from '../utils' * Geolocation of a supergroup */ export class ChatLocation { - readonly client: TelegramClient - readonly raw: tl.RawChannelLocation - - constructor(client: TelegramClient, raw: tl.RawChannelLocation) { - this.client = client - this.raw = raw - } + constructor( + readonly client: TelegramClient, + readonly raw: tl.RawChannelLocation + ) {} private _location?: Location /** diff --git a/packages/client/src/types/peers/chat-member.ts b/packages/client/src/types/peers/chat-member.ts index 93c08906..e2df8c22 100644 --- a/packages/client/src/types/peers/chat-member.ts +++ b/packages/client/src/types/peers/chat-member.ts @@ -4,7 +4,7 @@ import { tl } from '@mtcute/tl' import { User } from './user' import { assertTypeIs } from '../../utils/type-assertion' import { ChatPermissions } from './chat-permissions' -import { UsersIndex } from './index' +import { PeersIndex } from './index' export namespace ChatMember { /** @@ -29,21 +29,11 @@ export namespace ChatMember { * Information about one chat member */ export class ChatMember { - readonly client: TelegramClient - readonly raw: tl.TypeChatParticipant | tl.TypeChannelParticipant - - /** Map of users in this object. Mainly for internal use */ - readonly _users: UsersIndex - constructor( - client: TelegramClient, - raw: tl.TypeChatParticipant | tl.TypeChannelParticipant, - users: UsersIndex - ) { - this.client = client - this.raw = raw - this._users = users - } + readonly client: TelegramClient, + readonly raw: tl.TypeChatParticipant | tl.TypeChannelParticipant, + readonly _peers: PeersIndex + ) {} private _user?: User /** @@ -59,15 +49,16 @@ export class ChatMember { this.raw.peer, 'peerUser' ) + this._user = new User( this.client, - this._users[this.raw.peer.userId] + this._peers.user(this.raw.peer.userId) ) break default: this._user = new User( this.client, - this._users[this.raw.userId] + this._peers.user(this.raw.userId) ) break } @@ -148,7 +139,7 @@ export class ChatMember { if ('inviterId' in this.raw && this.raw.inviterId) { this._invitedBy = new User( this.client, - this._users[this.raw.inviterId] + this._peers.user(this.raw.inviterId) ) } else { this._invitedBy = null @@ -169,7 +160,7 @@ export class ChatMember { if (this.raw._ === 'channelParticipantAdmin') { this._promotedBy = new User( this.client, - this._users[this.raw.promotedBy] + this._peers.user(this.raw.promotedBy) ) } else { this._promotedBy = null @@ -190,7 +181,7 @@ export class ChatMember { if (this.raw._ === 'channelParticipantBanned') { this._restrictedBy = new User( this.client, - this._users[this.raw.kickedBy] + this._peers.user(this.raw.kickedBy) ) } else { this._restrictedBy = null diff --git a/packages/client/src/types/peers/chat-photo.ts b/packages/client/src/types/peers/chat-photo.ts index 29b7b7a8..10679fc7 100644 --- a/packages/client/src/types/peers/chat-photo.ts +++ b/packages/client/src/types/peers/chat-photo.ts @@ -4,23 +4,19 @@ import { TelegramClient } from '../../client' import { makeInspectable } from '../utils' import { strippedPhotoToJpg } from '../../utils/file-utils' import { tdFileId, toFileId, toUniqueFileId } from '@mtcute/file-id' -import bigInt from 'big-integer' -import { MAX_CHANNEL_ID } from '@mtcute/core' import { MtArgumentError } from '../errors' +import Long from 'long' +import { toggleChannelIdMark } from '../../../../core' /** * A size of a chat photo */ export class ChatPhotoSize extends FileLocation { - readonly obj: tl.RawUserProfilePhoto | tl.RawChatPhoto - readonly peer: tl.TypeInputPeer - readonly big: boolean - constructor( - client: TelegramClient, - peer: tl.TypeInputPeer, - obj: tl.RawUserProfilePhoto | tl.RawChatPhoto, - big: boolean + readonly client: TelegramClient, + readonly peer: tl.TypeInputPeer, + readonly obj: tl.RawUserProfilePhoto | tl.RawChatPhoto, + readonly big: boolean ) { super( client, @@ -57,10 +53,10 @@ export class ChatPhotoSize extends FileLocation { break case 'inputPeerChat': id = -peer.chatId - hash = bigInt.zero + hash = Long.ZERO break case 'inputPeerChannel': - id = MAX_CHANNEL_ID - peer.channelId + id = toggleChannelIdMark(peer.channelId) hash = peer.accessHash break default: @@ -75,11 +71,11 @@ export class ChatPhotoSize extends FileLocation { location: { _: 'photo', id: this.obj.photoId, - accessHash: bigInt.zero, + accessHash: Long.ZERO, source: { _: 'dialogPhoto', big: this.big, - id: bigInt(id), + id: id, accessHash: hash, }, }, diff --git a/packages/client/src/types/peers/chat-preview.ts b/packages/client/src/types/peers/chat-preview.ts index 3e14e4f8..f5497b4b 100644 --- a/packages/client/src/types/peers/chat-preview.ts +++ b/packages/client/src/types/peers/chat-preview.ts @@ -16,19 +16,15 @@ export namespace ChatPreview { } export class ChatPreview { - readonly client: TelegramClient - readonly invite: tl.RawChatInvite - /** - * Original invite link used to fetch - * this preview - */ - readonly link: string - - constructor(client: TelegramClient, raw: tl.RawChatInvite, link: string) { - this.client = client - this.invite = raw - this.link = link + constructor( + readonly client: TelegramClient, + readonly invite: tl.RawChatInvite, + /** + * Original invite link used to fetch this preview + */ + readonly link: string + ) { } /** diff --git a/packages/client/src/types/peers/chat.ts b/packages/client/src/types/peers/chat.ts index 37476c36..f65894f9 100644 --- a/packages/client/src/types/peers/chat.ts +++ b/packages/client/src/types/peers/chat.ts @@ -5,7 +5,7 @@ import { TelegramClient } from '../../client' import { getMarkedPeerId, MaybeArray } from '@mtcute/core' import { MtArgumentError, MtTypeAssertionError } from '../errors' import { makeInspectable } from '../utils' -import { ChatsIndex, InputPeerLike, User, UsersIndex } from './index' +import { InputPeerLike, PeersIndex, User } from './index' import { ChatLocation } from './chat-location' import { InputMediaLike } from '../media' import { FormattedString } from '../parser' @@ -33,9 +33,6 @@ export namespace Chat { * A chat. */ export class Chat { - /** Telegram client used for this chat */ - readonly client: TelegramClient - /** * Raw peer object that this {@link Chat} represents. */ @@ -46,15 +43,10 @@ export class Chat { | tl.RawChatForbidden | tl.RawChannelForbidden - /** - * Raw full peer object that this {@link Chat} represents. - */ - readonly fullPeer?: tl.TypeUserFull | tl.TypeChatFull - constructor( - client: TelegramClient, + readonly client: TelegramClient, peer: tl.TypeUser | tl.TypeChat, - fullPeer?: tl.TypeUserFull | tl.TypeChatFull + readonly fullPeer?: tl.TypeUserFull | tl.TypeChatFull ) { if (!peer) throw new MtArgumentError('peer is not available') @@ -478,27 +470,25 @@ export class Chat { static _parseFromMessage( client: TelegramClient, message: tl.RawMessage | tl.RawMessageService, - users: UsersIndex, - chats: ChatsIndex + peers: PeersIndex ): Chat { - return Chat._parseFromPeer(client, message.peerId, users, chats) + return Chat._parseFromPeer(client, message.peerId, peers) } /** @internal */ static _parseFromPeer( client: TelegramClient, peer: tl.TypePeer, - users: UsersIndex, - chats: ChatsIndex + peers: PeersIndex ): Chat { switch (peer._) { case 'peerUser': - return new Chat(client, users[peer.userId]) + return new Chat(client, peers.user(peer.userId)) case 'peerChat': - return new Chat(client, chats[peer.chatId]) + return new Chat(client, peers.chat(peer.chatId)) } - return new Chat(client, chats[peer.channelId]) + return new Chat(client, peers.chat(peer.channelId)) } /** @internal */ diff --git a/packages/client/src/types/peers/index.ts b/packages/client/src/types/peers/index.ts index 945c7454..d1727bb0 100644 --- a/packages/client/src/types/peers/index.ts +++ b/packages/client/src/types/peers/index.ts @@ -8,6 +8,7 @@ export * from './chat-member' export * from './chat-event' export * from './chat-invite-link' export * from './typing-status' +export * from './peers-index' /** * Peer types that have one-to-one relation to tl.Peer* types. @@ -23,11 +24,14 @@ export type PeerType = 'user' | 'bot' | 'group' | 'channel' | 'supergroup' /** * Type that can be used as an input peer * to most of the high-level methods. Can be: - * - `number`, representing peer's marked ID + * - `number`, representing peer's marked ID* * - `string`, representing peer's username (w/out preceding `@`) * - `string`, representing user's phone number (only for contacts) * - `"me"` and `"self"` which will be replaced with the current user/bot * - Raw TL object + * + * > Telegram has moved to int64 IDs. Though, Levin [has confirmed](https://t.me/tdlibchat/25075) + * > that new IDs *will* still fit into int53, meaning JS integers are fine. */ export type InputPeerLike = | string @@ -36,6 +40,3 @@ export type InputPeerLike = | tl.TypeInputPeer | tl.TypeInputUser | tl.TypeInputChannel - -export type UsersIndex = Record -export type ChatsIndex = Record diff --git a/packages/client/src/types/peers/peers-index.ts b/packages/client/src/types/peers/peers-index.ts new file mode 100644 index 00000000..c7424d98 --- /dev/null +++ b/packages/client/src/types/peers/peers-index.ts @@ -0,0 +1,48 @@ +import { tl } from '@mtcute/tl' +import { MtArgumentError } from '../errors' + +const ERROR_MSG = + 'Given peer is not available in this index. This is most likely an internal library error.' + +export class PeersIndex { + readonly users: Record = Object.create(null) + readonly chats: Record = Object.create(null) + + hasMin = false + + static from(obj: { + users?: tl.TypeUser[] + chats?: tl.TypeChat[] + }): PeersIndex { + const index = new PeersIndex() + + obj.users?.forEach((user) => { + index.users[user.id] = user + if ((user as any).min) index.hasMin = true + }) + obj.chats?.forEach((chat) => { + index.chats[chat.id] = chat + if ((chat as any).min) index.hasMin = true + }) + + return index + } + + user(id: number): tl.TypeUser { + const r = this.users[id] + if (!r) { + throw new MtArgumentError(ERROR_MSG) + } + + return r + } + + chat(id: number): tl.TypeChat { + const r = this.chats[id] + if (!r) { + throw new MtArgumentError(ERROR_MSG) + } + + return r + } +} diff --git a/packages/client/src/types/peers/user.ts b/packages/client/src/types/peers/user.ts index aedc4dc8..68a135f7 100644 --- a/packages/client/src/types/peers/user.ts +++ b/packages/client/src/types/peers/user.ts @@ -36,18 +36,14 @@ export namespace User { } export class User { - /** Client that this user belongs to */ - readonly client: TelegramClient - /** * Underlying raw TL object */ readonly raw: tl.RawUser - constructor(client: TelegramClient, user: tl.TypeUser) { + constructor(readonly client: TelegramClient, user: tl.TypeUser) { assertTypeIs('User#init', user, 'user') - this.client = client this.raw = user } @@ -163,11 +159,9 @@ export class User { } private _parsedStatus?: User.ParsedStatus + private _parseStatus() { - this._parsedStatus = User.parseStatus( - this.raw.status!, - this.raw.bot - ) + this._parsedStatus = User.parseStatus(this.raw.status!, this.raw.bot) } /** User's Last Seen & Online status */ @@ -228,9 +222,7 @@ export class User { */ get inputPeer(): tl.TypeInputPeer { if (!this.raw.accessHash) - throw new MtArgumentError( - "user's access hash is not available!" - ) + throw new MtArgumentError("user's access hash is not available!") return { _: 'inputPeerUser', @@ -294,7 +286,10 @@ export class User { * msg.replyText(`Hello, ${msg.sender.mention()`) * ``` */ - mention(text?: string | null, parseMode?: string | null): string | FormattedString { + mention( + text?: string | null, + parseMode?: string | null + ): string | FormattedString { if (text === undefined && this.username) { return `@${this.username}` } @@ -302,15 +297,18 @@ export class User { if (!text) text = this.displayName if (!parseMode) parseMode = this.client['_defaultParseMode'] - return new FormattedString(this.client.getParseMode(parseMode).unparse(text, [ - { - raw: undefined as any, - type: 'text_mention', - offset: 0, - length: text.length, - userId: this.id, - }, - ]), parseMode!) + return new FormattedString( + this.client.getParseMode(parseMode).unparse(text, [ + { + raw: undefined as any, + type: 'text_mention', + offset: 0, + length: text.length, + userId: this.raw.id, + }, + ]), + parseMode! + ) } /** @@ -343,28 +341,32 @@ export class User { * @param text Mention text * @param parseMode Parse mode to use when creating mention */ - permanentMention(text?: string | null, parseMode?: string | null): FormattedString { + permanentMention( + text?: string | null, + parseMode?: string | null + ): FormattedString { if (!this.raw.accessHash) - throw new MtArgumentError( - "user's access hash is not available!" - ) + throw new MtArgumentError("user's access hash is not available!") if (!text) text = this.displayName if (!parseMode) parseMode = this.client['_defaultParseMode'] // since we are just creating a link and not actual tg entity, // we can use this hack to create a valid link through our parse mode - return new FormattedString(this.client.getParseMode(parseMode).unparse(text, [ - { - raw: undefined as any, - type: 'text_link', - offset: 0, - length: text.length, - url: `tg://user?id=${ - this.id - }&hash=${this.raw.accessHash.toString(16)}`, - }, - ]), parseMode!) + return new FormattedString( + this.client.getParseMode(parseMode).unparse(text, [ + { + raw: undefined as any, + type: 'text_link', + offset: 0, + length: text.length, + url: `tg://user?id=${ + this.id + }&hash=${this.raw.accessHash.toString(16)}`, + }, + ]), + parseMode! + ) } /** @@ -392,6 +394,7 @@ export class User { ): ReturnType { return this.client.sendMedia(this.inputPeer, media, params) } + /** * Send a media group to this user. * diff --git a/packages/client/src/types/updates/bot-stopped.ts b/packages/client/src/types/updates/bot-stopped.ts index 3fc587e3..f055828f 100644 --- a/packages/client/src/types/updates/bot-stopped.ts +++ b/packages/client/src/types/updates/bot-stopped.ts @@ -1,7 +1,7 @@ import { TelegramClient } from '../../client' import { tl } from '@mtcute/tl' import { makeInspectable } from '../utils' -import { User, UsersIndex } from '../peers' +import { User, PeersIndex } from '../peers' /** * A user has stopped or restarted the bot. @@ -13,7 +13,7 @@ export class BotStoppedUpdate { constructor( readonly client: TelegramClient, readonly raw: tl.RawUpdateBotStopped, - readonly _users: UsersIndex + readonly _peers: PeersIndex ) {} /** @@ -31,7 +31,7 @@ export class BotStoppedUpdate { */ get user(): User { if (!this._user) { - this._user = new User(this.client, this._users[this.raw.userId]) + this._user = new User(this.client, this._peers.user(this.raw.userId)) } return this._user diff --git a/packages/client/src/types/updates/chat-member-update.ts b/packages/client/src/types/updates/chat-member-update.ts index e7e0d54d..934954dc 100644 --- a/packages/client/src/types/updates/chat-member-update.ts +++ b/packages/client/src/types/updates/chat-member-update.ts @@ -3,12 +3,12 @@ import { Chat, ChatInviteLink, ChatMember, - ChatsIndex, + PeersIndex, User, - UsersIndex, } from '../' import { TelegramClient } from '../../client' import { makeInspectable } from '../utils' +// todo: check case when restricted user joins chat export namespace ChatMemberUpdate { /** @@ -46,25 +46,11 @@ export namespace ChatMemberUpdate { * of a chat/channel member. */ export class ChatMemberUpdate { - readonly client: TelegramClient - - readonly raw: tl.RawUpdateChatParticipant | tl.RawUpdateChannelParticipant - - /** Map of users in this message. Mainly for internal use */ - readonly _users: UsersIndex - /** Map of chats in this message. Mainly for internal use */ - readonly _chats: ChatsIndex - constructor( - client: TelegramClient, - raw: tl.RawUpdateChatParticipant | tl.RawUpdateChannelParticipant, - users: UsersIndex, - chats: ChatsIndex + readonly client: TelegramClient, + readonly raw: tl.RawUpdateChatParticipant | tl.RawUpdateChannelParticipant, + readonly _peers: PeersIndex, ) { - this.client = client - this.raw = raw - this._users = users - this._chats = chats } /** @@ -182,7 +168,7 @@ export class ChatMemberUpdate { this.raw._ === 'updateChannelParticipant' ? this.raw.channelId : this.raw.chatId - this._chat = new Chat(this.client, this._chats[id]) + this._chat = new Chat(this.client, this._peers.chat(id)) } return this._chat @@ -196,7 +182,7 @@ export class ChatMemberUpdate { */ get actor(): User { if (!this._actor) { - this._actor = new User(this.client, this._users[this.raw.actorId]) + this._actor = new User(this.client, this._peers.user(this.raw.actorId)) } return this._actor @@ -208,7 +194,7 @@ export class ChatMemberUpdate { */ get user(): User { if (!this._user) { - this._user = new User(this.client, this._users[this.raw.userId]) + this._user = new User(this.client, this._peers.user(this.raw.userId)) } return this._user @@ -225,7 +211,7 @@ export class ChatMemberUpdate { this._oldMember = new ChatMember( this.client, this.raw.prevParticipant, - this._users + this._peers ) } @@ -243,7 +229,7 @@ export class ChatMemberUpdate { this._newMember = new ChatMember( this.client, this.raw.newParticipant, - this._users + this._peers ) } diff --git a/packages/client/src/types/updates/chosen-inline-result.ts b/packages/client/src/types/updates/chosen-inline-result.ts index 3198ee57..2343d15b 100644 --- a/packages/client/src/types/updates/chosen-inline-result.ts +++ b/packages/client/src/types/updates/chosen-inline-result.ts @@ -3,7 +3,7 @@ import { User, Location, MtArgumentError, - UsersIndex, + PeersIndex, } from '../' import { TelegramClient } from '../../client' import { encodeInlineMessageId } from '../../utils/inline-utils' @@ -16,19 +16,11 @@ import { makeInspectable } from '../utils' * > Inline feedback in [@BotFather](//t.me/botfather) */ export class ChosenInlineResult { - readonly client: TelegramClient - readonly raw: tl.RawUpdateBotInlineSend - - readonly _users: UsersIndex - constructor( - client: TelegramClient, - raw: tl.RawUpdateBotInlineSend, - users: UsersIndex + readonly client: TelegramClient, + readonly raw: tl.RawUpdateBotInlineSend, + readonly _peers: PeersIndex ) { - this.client = client - this.raw = raw - this._users = users } /** @@ -45,7 +37,7 @@ export class ChosenInlineResult { */ get user(): User { if (!this._user) { - this._user = new User(this.client, this._users[this.raw.userId]) + this._user = new User(this.client, this._peers.user(this.raw.userId)) } return this._user diff --git a/packages/client/src/types/updates/delete-message-update.ts b/packages/client/src/types/updates/delete-message-update.ts index 7c5346a4..9994099e 100644 --- a/packages/client/src/types/updates/delete-message-update.ts +++ b/packages/client/src/types/updates/delete-message-update.ts @@ -1,21 +1,16 @@ import { tl } from '@mtcute/tl' -import { MAX_CHANNEL_ID } from '@mtcute/core' import { TelegramClient } from '../../client' import { makeInspectable } from '../utils' +import { toggleChannelIdMark } from '../../../../core' /** * One or more messages were deleted */ export class DeleteMessageUpdate { - readonly client: TelegramClient - readonly raw: tl.RawUpdateDeleteMessages | tl.RawUpdateDeleteChannelMessages - constructor( - client: TelegramClient, - raw: tl.RawUpdateDeleteMessages | tl.RawUpdateDeleteChannelMessages + readonly client: TelegramClient, + readonly raw: tl.RawUpdateDeleteMessages | tl.RawUpdateDeleteChannelMessages ) { - this.client = client - this.raw = raw } /** @@ -30,7 +25,7 @@ export class DeleteMessageUpdate { */ get channelId(): number | null { return this.raw._ === 'updateDeleteChannelMessages' - ? MAX_CHANNEL_ID - this.raw.channelId + ? toggleChannelIdMark(this.raw.channelId) : null } } diff --git a/packages/client/src/types/updates/history-read-update.ts b/packages/client/src/types/updates/history-read-update.ts index 011a007e..4931c752 100644 --- a/packages/client/src/types/updates/history-read-update.ts +++ b/packages/client/src/types/updates/history-read-update.ts @@ -1,6 +1,6 @@ import { tl } from '@mtcute/tl' import { TelegramClient } from '../../client' -import { getMarkedPeerId, MAX_CHANNEL_ID } from '@mtcute/core' +import { getMarkedPeerId, toggleChannelIdMark } from '@mtcute/core' import { makeInspectable } from '../utils' export class HistoryReadUpdate { @@ -58,7 +58,7 @@ export class HistoryReadUpdate { case 'updateReadChannelInbox': case 'updateReadChannelDiscussionOutbox': case 'updateReadChannelDiscussionInbox': - return MAX_CHANNEL_ID - this.raw.channelId + return toggleChannelIdMark(this.raw.channelId) } } diff --git a/packages/client/src/types/updates/poll-update.ts b/packages/client/src/types/updates/poll-update.ts index 344ce00b..9a242c3e 100644 --- a/packages/client/src/types/updates/poll-update.ts +++ b/packages/client/src/types/updates/poll-update.ts @@ -1,4 +1,4 @@ -import { Poll, UsersIndex } from '../' +import { PeersIndex, Poll } from '../' import { TelegramClient } from '../../client' import { tl } from '@mtcute/tl' import { makeInspectable } from '../utils' @@ -11,19 +11,11 @@ import { makeInspectable } from '../utils' * polls which were sent by this bot */ export class PollUpdate { - readonly client: TelegramClient - readonly raw: tl.RawUpdateMessagePoll - - readonly _users: UsersIndex - constructor( - client: TelegramClient, - raw: tl.RawUpdateMessagePoll, - users: UsersIndex + readonly client: TelegramClient, + readonly raw: tl.RawUpdateMessagePoll, + readonly _peers: PeersIndex ) { - this.client = client - this.raw = raw - this._users = users } /** @@ -72,7 +64,7 @@ export class PollUpdate { this._poll = new Poll( this.client, poll, - this._users, + this._peers, this.raw.results ) } diff --git a/packages/client/src/types/updates/poll-vote.ts b/packages/client/src/types/updates/poll-vote.ts index 9cd02c87..917701fa 100644 --- a/packages/client/src/types/updates/poll-vote.ts +++ b/packages/client/src/types/updates/poll-vote.ts @@ -1,4 +1,4 @@ -import { MtUnsupportedError, User, UsersIndex } from '../' +import { MtUnsupportedError, User, PeersIndex } from '../' import { TelegramClient } from '../../client' import { tl } from '@mtcute/tl' import { makeInspectable } from '../utils' @@ -10,19 +10,11 @@ import { makeInspectable } from '../utils' * that were sent by this bot. */ export class PollVoteUpdate { - readonly client: TelegramClient - readonly raw: tl.RawUpdateMessagePollVote - - readonly _users: UsersIndex - constructor( - client: TelegramClient, - raw: tl.RawUpdateMessagePollVote, - users: UsersIndex + readonly client: TelegramClient, + readonly raw: tl.RawUpdateMessagePollVote, + readonly _peers: PeersIndex ) { - this.client = client - this.raw = raw - this._users = users } /** @@ -38,7 +30,7 @@ export class PollVoteUpdate { */ get user(): User { if (!this._user) { - this._user = new User(this.client, this._users[this.raw.userId]) + this._user = new User(this.client, this._peers.user(this.raw.userId)) } return this._user diff --git a/packages/client/src/types/updates/user-status-update.ts b/packages/client/src/types/updates/user-status-update.ts index 9af65a4d..896e52af 100644 --- a/packages/client/src/types/updates/user-status-update.ts +++ b/packages/client/src/types/updates/user-status-update.ts @@ -7,13 +7,10 @@ import { makeInspectable } from '../utils' * User status has changed */ export class UserStatusUpdate { - readonly client: TelegramClient - readonly raw: tl.RawUpdateUserStatus - - constructor(client: TelegramClient, raw: tl.RawUpdateUserStatus) { - this.client = client - this.raw = raw - } + constructor( + readonly client: TelegramClient, + readonly raw: tl.RawUpdateUserStatus + ) {} /** * ID of the user whose status has updated @@ -23,6 +20,7 @@ export class UserStatusUpdate { } private _parsedStatus?: User.ParsedStatus + private _parseStatus() { this._parsedStatus = User.parseStatus(this.raw.status) } diff --git a/packages/client/src/types/updates/user-typing-update.ts b/packages/client/src/types/updates/user-typing-update.ts index 52c5d260..6cc62aee 100644 --- a/packages/client/src/types/updates/user-typing-update.ts +++ b/packages/client/src/types/updates/user-typing-update.ts @@ -7,7 +7,7 @@ import { } from '../' import { TelegramClient } from '../../client' import { tl } from '@mtcute/tl' -import { getBarePeerId, MAX_CHANNEL_ID } from '@mtcute/core' +import { getBarePeerId, toggleChannelIdMark } from '@mtcute/core' import { makeInspectable } from '../utils' /** @@ -16,24 +16,23 @@ import { makeInspectable } from '../utils' * This update is valid for 6 seconds. */ export class UserTypingUpdate { - readonly client: TelegramClient - readonly raw: - | tl.RawUpdateUserTyping - | tl.RawUpdateChatUserTyping - | tl.RawUpdateChannelUserTyping - - constructor(client: TelegramClient, raw: UserTypingUpdate['raw']) { - this.client = client - this.raw = raw + constructor( + readonly client: TelegramClient, + readonly raw: + | tl.RawUpdateUserTyping + | tl.RawUpdateChatUserTyping + | tl.RawUpdateChannelUserTyping + ) { } /** * ID of the user whose typing status changed */ get userId(): number { - return this.raw._ === 'updateUserTyping' + return (this.raw._ === 'updateUserTyping' ? this.raw.userId : getBarePeerId(this.raw.fromId) + ) } /** @@ -49,7 +48,7 @@ export class UserTypingUpdate { case 'updateChatUserTyping': return -this.raw.chatId case 'updateChannelUserTyping': - return MAX_CHANNEL_ID - this.raw.channelId + return toggleChannelIdMark(this.raw.channelId) } } diff --git a/packages/client/src/utils/inline-utils.ts b/packages/client/src/utils/inline-utils.ts index 42bc6200..e7e0e0cb 100644 --- a/packages/client/src/utils/inline-utils.ts +++ b/packages/client/src/utils/inline-utils.ts @@ -2,32 +2,52 @@ import { tl } from '@mtcute/tl' import { encodeUrlSafeBase64, parseUrlSafeBase64, - BinaryReader, - BinaryWriter, + TlBinaryReader, + TlBinaryWriter, } from '@mtcute/core' export function parseInlineMessageId( id: string -): tl.RawInputBotInlineMessageID { +): tl.TypeInputBotInlineMessageID { const buf = parseUrlSafeBase64(id) - const reader = new BinaryReader(buf) + const reader = TlBinaryReader.manual(buf) + + if (buf.length === 20) { + return { + _: 'inputBotInlineMessageID', + dcId: reader.int(), + id: reader.long(), + accessHash: reader.long(), + } + } return { - _: 'inputBotInlineMessageID', - dcId: reader.int32(), - id: reader.long(), - accessHash: reader.long(), + _: 'inputBotInlineMessageID64', + dcId: reader.int(), + ownerId: reader.long(), + id: reader.int(), + accessHash: reader.long() } } export function encodeInlineMessageId( - id: tl.RawInputBotInlineMessageID + id: tl.TypeInputBotInlineMessageID ): string { - const writer = BinaryWriter.alloc(20) // int32, int64, int64 - - writer.int32(id.dcId) - writer.long(id.id) - writer.long(id.accessHash) + let writer: TlBinaryWriter + switch (id._) { + case 'inputBotInlineMessageID': + writer = TlBinaryWriter.manualAlloc(20) + writer.int(id.dcId) + writer.long(id.id) + writer.long(id.accessHash) + break + case 'inputBotInlineMessageID64': + writer = TlBinaryWriter.manualAlloc(24) + writer.int(id.dcId) + writer.long(id.ownerId) + writer.int(id.id) + writer.long(id.accessHash) + } return encodeUrlSafeBase64(writer.result()) } diff --git a/packages/client/src/utils/iter-utils.ts b/packages/client/src/utils/iter-utils.ts deleted file mode 100644 index 139bc612..00000000 --- a/packages/client/src/utils/iter-utils.ts +++ /dev/null @@ -1,54 +0,0 @@ -export function* chunks(iter: Iterable, size = 100): Iterable { - let buf: T[] = [] - - for (const item of iter) { - buf.push(item) - if (buf.length === size) { - yield buf - buf = [] - } - } - - if (buf.length) yield buf -} - -export function zip( - iter1: Iterable, - iter2: Iterable -): Iterable<[T1, T2]> -export function zip( - iter1: Iterable, - iter2: Iterable, - iter3: Iterable -): Iterable<[T1, T2, T3]> -export function zip(...iters: Iterable[]): Iterable -export function* zip(...iters: Iterable[]): Iterable { - const iterables = iters.map((iter) => iter[Symbol.iterator]()) - - for (;;) { - const row = [] - for (const iter of iterables) { - const res = iter.next() - if (res.done) return - row.push(res.value) - } - - yield row - } -} - -export async function groupByAsync( - items: Iterable, - keyer: (item: T) => Promise -): Promise> { - const ret: Record = {} as any - - for (const item of items) { - const key = await keyer(item) - if (!ret[key]) ret[key] = [] - - ret[key].push(item) - } - - return ret -} diff --git a/packages/client/src/utils/misc-utils.ts b/packages/client/src/utils/misc-utils.ts index 4b2e2be0..e41e2df9 100644 --- a/packages/client/src/utils/misc-utils.ts +++ b/packages/client/src/utils/misc-utils.ts @@ -1,10 +1,6 @@ import { MaybeDynamic, Message, MtClientError } from '../types' -import { BigInteger } from 'big-integer' -import { randomBytes, bufferToBigInt } from '@mtcute/core' import { tl } from '@mtcute/tl' -export const EMPTY_BUFFER = Buffer.alloc(0) - export function normalizePhoneNumber(phone: string): string { phone = phone.trim().replace(/[+()\s-]/g, '') if (!phone.match(/^\d+$/)) throw new MtClientError('Invalid phone number') @@ -16,10 +12,6 @@ export async function resolveMaybeDynamic(val: MaybeDynamic): Promise { return val instanceof Function ? await val() : await val } -export function randomUlong(): BigInteger { - return bufferToBigInt(randomBytes(8)) -} - export function extractChannelIdFromUpdate( upd: tl.TypeUpdate ): number | undefined { diff --git a/packages/client/src/utils/parse-update.ts b/packages/client/src/utils/parse-update.ts index 09ce0d8c..4f60217b 100644 --- a/packages/client/src/utils/parse-update.ts +++ b/packages/client/src/utils/parse-update.ts @@ -4,16 +4,14 @@ import { BotStoppedUpdate, CallbackQuery, ChatMemberUpdate, - ChatsIndex, ChosenInlineResult, DeleteMessageUpdate, HistoryReadUpdate, InlineQuery, Message, - ParsedUpdate, + ParsedUpdate, PeersIndex, PollUpdate, PollVoteUpdate, - UsersIndex, UserStatusUpdate, UserTypingUpdate, } from '../types' @@ -21,22 +19,19 @@ import { type ParserFunction = ( client: TelegramClient, upd: tl.TypeUpdate | tl.TypeMessage, - users: UsersIndex, - chats: ChatsIndex + peers: PeersIndex, ) => any type UpdateParser = [ParsedUpdate['name'], ParserFunction] const baseMessageParser: ParserFunction = ( client: TelegramClient, upd, - users, - chats + peers ) => new Message( client, tl.isAnyMessage(upd) ? upd : (upd as any).message, - users, - chats, + peers, upd._ === 'updateNewScheduledMessage' ) @@ -44,12 +39,12 @@ const newMessageParser: UpdateParser = ['new_message', baseMessageParser] const editMessageParser: UpdateParser = ['edit_message', baseMessageParser] const chatMemberParser: UpdateParser = [ 'chat_member', - (client, upd, users, chats) => - new ChatMemberUpdate(client, upd as any, users, chats), + (client, upd, peers) => + new ChatMemberUpdate(client, upd as any, peers), ] const callbackQueryParser: UpdateParser = [ 'callback_query', - (client, upd, users) => new CallbackQuery(client, upd as any, users), + (client, upd, peers) => new CallbackQuery(client, upd as any, peers), ] const userTypingParser: UpdateParser = [ 'user_typing', @@ -79,22 +74,22 @@ const PARSERS: Partial< updateChannelParticipant: chatMemberParser, updateBotInlineQuery: [ 'inline_query', - (client, upd, users) => new InlineQuery(client, upd as any, users), + (client, upd, peers) => new InlineQuery(client, upd as any, peers), ], updateBotInlineSend: [ 'chosen_inline_result', - (client, upd, users) => - new ChosenInlineResult(client, upd as any, users), + (client, upd, peers) => + new ChosenInlineResult(client, upd as any, peers), ], updateBotCallbackQuery: callbackQueryParser, updateInlineBotCallbackQuery: callbackQueryParser, updateMessagePoll: [ 'poll', - (client, upd, users) => new PollUpdate(client, upd as any, users), + (client, upd, peers) => new PollUpdate(client, upd as any, peers), ], updateMessagePollVote: [ 'poll_vote', - (client, upd, users) => new PollVoteUpdate(client, upd as any, users), + (client, upd, peers) => new PollVoteUpdate(client, upd as any, peers), ], updateUserStatus: [ 'user_status', @@ -121,14 +116,13 @@ const PARSERS: Partial< export function _parseUpdate( client: TelegramClient, update: tl.TypeUpdate | tl.TypeMessage, - users: UsersIndex, - chats: ChatsIndex + peers: PeersIndex ): ParsedUpdate | null { const pair = PARSERS[update._] if (pair) { return { name: pair[0], - data: pair[1](client, update, users, chats), + data: pair[1](client, update, peers), } } else { return null diff --git a/packages/client/src/utils/peer-utils.ts b/packages/client/src/utils/peer-utils.ts index 56410ce3..1f1c76e4 100644 --- a/packages/client/src/utils/peer-utils.ts +++ b/packages/client/src/utils/peer-utils.ts @@ -1,6 +1,5 @@ import { tl } from '@mtcute/tl' -import bigInt from 'big-integer' -import { ChatsIndex, UsersIndex } from '../types' +import Long from 'long' export const INVITE_LINK_REGEX = /^(?:https?:\/\/)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)\/joinchat\/)([\w-]+)$/i @@ -164,7 +163,7 @@ export function inputPeerToPeer(inp: tl.TypeInputPeer): tl.TypePeer { export function peerToInputPeer( peer: tl.TypePeer, - accessHash = bigInt.zero + accessHash = Long.ZERO ): tl.TypeInputPeer { switch (peer._) { case 'peerUser': @@ -179,29 +178,3 @@ export function peerToInputPeer( return { _: 'inputPeerChat', chatId: peer.chatId } } } - -export function createUsersChatsIndex( - obj: { - users?: tl.TypeUser[] - chats?: tl.TypeChat[] - }, - second?: { - users?: tl.TypeUser[] - chats?: tl.TypeChat[] - } -): { - users: UsersIndex - chats: ChatsIndex -} { - const users: UsersIndex = {} - const chats: ChatsIndex = {} - obj.users?.forEach((e) => (users[e.id] = e)) - obj.chats?.forEach((e) => (chats[e.id] = e)) - - if (second) { - second.users?.forEach((e) => (users[e.id] = e)) - second.chats?.forEach((e) => (chats[e.id] = e)) - } - - return { users, chats } -} diff --git a/packages/client/src/utils/queue.ts b/packages/client/src/utils/queue.ts deleted file mode 100644 index 99f3eb05..00000000 --- a/packages/client/src/utils/queue.ts +++ /dev/null @@ -1,81 +0,0 @@ -interface OneWayLinkedListItem { - v: T - n?: OneWayLinkedListItem -} - -export class Queue { - first?: OneWayLinkedListItem - last?: OneWayLinkedListItem - - length = 0 - - constructor(readonly limit = 0) {} - - push(item: T): void { - const it: OneWayLinkedListItem = { v: item } - if (!this.first) { - this.first = this.last = it - } else { - this.last!.n = it - this.last = it - } - - this.length += 1 - - if (this.limit) { - while (this.first && this.length > this.limit) { - this.first = this.first.n - this.length -= 1 - } - } - } - - empty(): boolean { - return this.first === undefined - } - - peek(): T | undefined { - return this.first?.v - } - - pop(): T | undefined { - if (!this.first) return undefined - - const it = this.first - this.first = this.first.n - if (!this.first) this.last = undefined - - this.length -= 1 - return it.v - } - - removeBy(pred: (it: T) => boolean): void { - if (!this.first) return - - let prev: OneWayLinkedListItem | undefined = undefined - let it = this.first - while (it && !pred(it.v)) { - if (!it.n) return - - prev = it - it = it.n - } - - if (!it) return - - if (prev) { - prev.n = it.n - } else { - this.first = it.n - } - - if (!this.first) this.last = undefined - - this.length -= 1 - } - - clear(): void { - this.first = this.last = undefined - this.length = 0 - } -} diff --git a/packages/client/src/utils/rps-meter.ts b/packages/client/src/utils/rps-meter.ts new file mode 100644 index 00000000..48e2ebf8 --- /dev/null +++ b/packages/client/src/utils/rps-meter.ts @@ -0,0 +1,41 @@ +import { Deque } from '@mtcute/core' + +export class RpsMeter { + _hits: Deque + time: bigint + + constructor(readonly size = 500, time = 5000) { + if (typeof process === 'undefined' || !process.hrtime.bigint) + throw new Error('RPS meter is not supported on this platform') + + this._hits = new Deque(size) + this.time = BigInt(time) * BigInt(1e+6) + } + + hit(): void { + this._hits.pushBack(process.hrtime.bigint()) + } + + getRps(): number { + // calculate average load based on last `size` hits in the last `time` ms + if (!this._hits.length) return 0 // no hits at all + + const now = process.hrtime.bigint() + const window = now - this.time + // find the first hit within the last `time` ms + const iter = this._hits.iter() + let first = iter.next() + let idx = 0 + while (!first.done && first.value < window) { + first = iter.next() + idx += 1 + } + if (!first.value) return 0 // no recent hits + + // number of hits within the window + const hits = this._hits.length - idx + + // average load per second + return hits * 1e9 / Number(now - first.value) + } +} diff --git a/packages/client/src/utils/updates-utils.ts b/packages/client/src/utils/updates-utils.ts index 6cd6477f..8cc52aae 100644 --- a/packages/client/src/utils/updates-utils.ts +++ b/packages/client/src/utils/updates-utils.ts @@ -1,5 +1,6 @@ import { tl } from '@mtcute/tl' import { MtTypeAssertionError } from '../types' +import Long from 'long' // dummy updates which are used for methods that return messages.affectedHistory. // that is not an update, but it carries info about pts, and we need to handle it @@ -24,36 +25,15 @@ export function createDummyUpdate( users: [], updates: [ { - _: 'updatePinnedChannelMessages', + _: 'dummyUpdate', channelId, pts, ptsCount, - // since message id cant be negative, using negative 42 - // here makes it distinctive from real updates - messages: [-42], }, ], } } -/** @internal */ -export function isDummyUpdate(upd: tl.TypeUpdate): boolean { - return ( - upd._ === 'updatePinnedChannelMessages' && - upd.messages.length === 1 && - upd.messages[0] === -42 - ) -} - -/** @internal */ -export function isDummyUpdates(upd: tl.TypeUpdates): boolean { - return ( - upd._ === 'updates' && - upd.updates.length === 1 && - isDummyUpdate(upd.updates[0]) - ) -} - /** @internal */ export function assertIsUpdatesGroup( ctx: string, diff --git a/packages/core/package.json b/packages/core/package.json index c626d827..a63d7ee9 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -20,9 +20,10 @@ "dependencies": { "@types/node": "^15.12.1", "@types/events": "^3.0.0", - "@mtcute/tl": "~1.131.0", - "pako": "2.0.2", + "@mtcute/tl": "^134.0.0", + "@mtcute/tl-runtime": "^1.0.0", "big-integer": "1.6.48", + "long": "^4.0.0", "events": "3.2.0" }, "devDependencies": { diff --git a/packages/core/src/base-client.ts b/packages/core/src/base-client.ts index 05e5a5cf..dd4d5c00 100644 --- a/packages/core/src/base-client.ts +++ b/packages/core/src/base-client.ts @@ -3,13 +3,19 @@ import { CryptoProviderFactory, ICryptoProvider, defaultCryptoProviderFactory, -} from './utils/crypto' + sleep, + getAllPeersFrom, + encodeUrlSafeBase64, + parseUrlSafeBase64, + LogManager, + toggleChannelIdMark, +} from './utils' import { TransportFactory, defaultReconnectionStrategy, ReconnectionStrategy, defaultTransportFactory, - TelegramConnection, + SessionConnection, } from './network' import { PersistentConnectionParams } from './network/persistent-connection' import { @@ -29,16 +35,23 @@ import { SlowmodeWaitError, UserMigrateError, } from '@mtcute/tl/errors' -import { sleep } from './utils/misc-utils' import { addPublicKey } from './utils/crypto/keys' import { ITelegramStorage, MemoryStorage } from './storage' -import { getAllPeersFrom, MAX_CHANNEL_ID } from './utils/peer-utils' -import bigInt from 'big-integer' -import { BinaryWriter } from './utils/binary/binary-writer' -import { encodeUrlSafeBase64, parseUrlSafeBase64 } from './utils/buffer-utils' -import { BinaryReader } from './utils/binary/binary-reader' import EventEmitter from 'events' -import { LogManager } from './utils/logger' +import Long from 'long' +import { + ControllablePromise, + createControllablePromise, +} from './utils/controllable-promise' +import { + TlBinaryReader, + TlBinaryWriter, + TlReaderMap, + TlWriterMap, +} from '@mtcute/tl-runtime' + +import defaultReaderMap from '@mtcute/tl/binary/reader' +import defaultWriterMap from '@mtcute/tl/binary/writer' export namespace BaseTelegramClient { export interface Options { @@ -166,6 +179,20 @@ export namespace BaseTelegramClient { * **Does not** change the schema used. */ overrideLayer?: number + + /** + * **EXPERT USE ONLY** + * + * Override reader map used for the connection. + */ + readerMap?: TlReaderMap + + /** + * **EXPERT USE ONLY** + * + * Override writer map used for the connection. + */ + writerMap?: TlWriterMap } } @@ -233,6 +260,8 @@ export class BaseTelegramClient extends EventEmitter { private _niceStacks: boolean readonly _layer: number + readonly _readerMap: TlReaderMap + readonly _writerMap: TlWriterMap private _keepAliveInterval?: NodeJS.Timeout protected _lastUpdateTime = 0 @@ -241,20 +270,20 @@ export class BaseTelegramClient extends EventEmitter { protected _config?: tl.RawConfig protected _cdnConfig?: tl.RawCdnConfig - private _additionalConnections: TelegramConnection[] = [] + private _additionalConnections: SessionConnection[] = [] // not really connected, but rather "connect() was called" - private _connected = false + private _connected: ControllablePromise | boolean = false - private _onError?: (err: Error, connection?: TelegramConnection) => void + private _onError?: (err: Error, connection?: SessionConnection) => void /** - * The primary {@link TelegramConnection} that is used for + * The primary {@link SessionConnection} that is used for * most of the communication with Telegram. * * Methods for downloading/uploading files may create additional connections as needed. */ - primaryConnection!: TelegramConnection + primaryConnection!: SessionConnection private _importFrom?: string @@ -269,7 +298,6 @@ export class BaseTelegramClient extends EventEmitter { protected _handleUpdate(update: tl.TypeUpdates): void {} readonly log = new LogManager() - protected readonly _baseLog = this.log.create('base') constructor(opts: BaseTelegramClient.Options) { super() @@ -301,9 +329,11 @@ export class BaseTelegramClient extends EventEmitter { this._disableUpdates = opts.disableUpdates ?? false this._niceStacks = opts.niceStacks ?? true - this.storage.setup?.(this._baseLog) + this._layer = opts.overrideLayer ?? tl.LAYER + this._readerMap = opts.readerMap ?? defaultReaderMap + this._writerMap = opts.writerMap ?? defaultWriterMap - this._layer = opts.overrideLayer ?? tl.CURRENT_LAYER + this.storage.setup?.(this.log, this._readerMap, this._writerMap) let deviceModel = 'MTCute on ' if (typeof process !== 'undefined' && typeof require !== 'undefined') { @@ -337,6 +367,8 @@ export class BaseTelegramClient extends EventEmitter { } protected _keepAliveAction(): void { + if (this._disableUpdates) return + // telegram asks to fetch pending updates // if there are no updates for 15 minutes. // core does not have update handling, @@ -357,16 +389,23 @@ export class BaseTelegramClient extends EventEmitter { private _setupPrimaryConnection(): void { this._cleanupPrimaryConnection(true) - this.primaryConnection = new TelegramConnection({ - crypto: this._crypto, - initConnection: this._initConnectionParams, - transportFactory: this._transportFactory, - dc: this._primaryDc, - testMode: this._testMode, - reconnectionStrategy: this._reconnectionStrategy, - layer: this._layer, - }, this.log.create('connection')) - this.primaryConnection.on('usable', async (isReconnection: boolean) => { + this.primaryConnection = new SessionConnection( + { + crypto: this._crypto, + initConnection: this._initConnectionParams, + transportFactory: this._transportFactory, + dc: this._primaryDc, + testMode: this._testMode, + reconnectionStrategy: this._reconnectionStrategy, + layer: this._layer, + disableUpdates: this._disableUpdates, + readerMap: this._readerMap, + writerMap: this._writerMap, + }, + this.log.create('connection') + ) + + this.primaryConnection.on('usable', async () => { this._lastUpdateTime = Date.now() this._keepAliveInterval = setInterval(async () => { @@ -375,20 +414,6 @@ export class BaseTelegramClient extends EventEmitter { this._lastUpdateTime = Date.now() } }, 60_000) - - // on reconnection we need to call updates.getState so Telegram - // knows we still want the updates - if (isReconnection && !this._disableUpdates) { - setTimeout(async () => { - try { - await this.call({ _: 'updates.getState' }) - } catch (e) { - if (!(e instanceof RpcError)) { - this.primaryConnection.reconnect() - } - } - }, 0) - } }) this.primaryConnection.on('update', (update) => { this._lastUpdateTime = Date.now() @@ -413,27 +438,39 @@ export class BaseTelegramClient extends EventEmitter { * implicitly the first time you call {@link call}. */ async connect(): Promise { + if (this._connected) { + await this._connected + return + } + + this._connected = createControllablePromise() + await this._loadStorage() const primaryDc = await this.storage.getDefaultDc() if (primaryDc !== null) this._primaryDc = primaryDc - this._connected = true this._setupPrimaryConnection() - this.primaryConnection.authKey = await this.storage.getAuthKeyFor( - this._primaryDc.id + await this.primaryConnection.setupKeys( + await this.storage.getAuthKeyFor(this._primaryDc.id) ) - if (!this.primaryConnection.authKey && this._importFrom) { + if (!this.primaryConnection.getAuthKey() && this._importFrom) { const buf = parseUrlSafeBase64(this._importFrom) if (buf[0] !== 1) throw new Error(`Invalid session string (version = ${buf[0]})`) - const reader = new BinaryReader(buf, 1) + const reader = new TlBinaryReader(this._readerMap, buf, 1) - const flags = reader.int32() + const flags = reader.int() const hasSelf = flags & 1 + if (!(flags & 2) !== !this._testMode) { + throw new Error( + 'This session string is not for the current backend' + ) + } + const primaryDc = reader.object() if (primaryDc._ !== 'dcOption') { throw new Error( @@ -445,19 +482,22 @@ export class BaseTelegramClient extends EventEmitter { await this.storage.setDefaultDc(primaryDc) if (hasSelf) { - const selfId = reader.int32() + const selfId = reader.int53() const selfBot = reader.boolean() await this.storage.setSelf({ userId: selfId, isBot: selfBot }) } const key = reader.bytes() - this.primaryConnection.authKey = key + await this.primaryConnection.setupKeys(key) await this.storage.setAuthKeyFor(primaryDc.id, key) await this._saveStorage(true) } + this._connected.resolve() + this._connected = true + this.primaryConnection.connect() } @@ -470,10 +510,18 @@ export class BaseTelegramClient extends EventEmitter { }) } + /** + * Additional cleanup for subclasses. + * @protected + */ + protected _onClose(): void {} + /** * Close all connections and finalize the client. */ async close(): Promise { + await this._onClose() + this._cleanupPrimaryConnection(true) // close additional connections this._additionalConnections.forEach((conn) => conn.destroy()) @@ -486,9 +534,14 @@ export class BaseTelegramClient extends EventEmitter { * Utility function to find the DC by its ID. * * @param id Datacenter ID + * @param preferMedia Whether to prefer media-only DCs * @param cdn Whether the needed DC is a CDN DC */ - async getDcById(id: number, cdn = false): Promise { + async getDcById( + id: number, + preferMedia = false, + cdn = false + ): Promise { if (!this._config) { this._config = await this.call({ _: 'help.getConfig' }) } @@ -502,16 +555,45 @@ export class BaseTelegramClient extends EventEmitter { if (this._useIpv6) { // first try to find ipv6 dc - const found = this._config.dcOptions.find( - (it) => - it.id === id && it.cdn === cdn && it.ipv6 && !it.tcpoOnly - ) + + let found + if (preferMedia) { + found = this._config.dcOptions.find( + (it) => + it.id === id && + it.mediaOnly && + it.cdn === cdn && + it.ipv6 && + !it.tcpoOnly + ) + } + + if (!found) + found = this._config.dcOptions.find( + (it) => + it.id === id && + it.cdn === cdn && + it.ipv6 && + !it.tcpoOnly + ) + if (found) return found } - const found = this._config.dcOptions.find( - (it) => it.id === id && it.cdn === cdn && !it.tcpoOnly - ) + let found + if (preferMedia) { + found = this._config.dcOptions.find( + (it) => + it.id === id && + it.mediaOnly && + it.cdn === cdn && + !it.tcpoOnly + ) + } + if (!found) + found = this._config.dcOptions.find( + (it) => it.id === id && it.cdn === cdn && !it.tcpoOnly + ) if (found) return found throw new Error(`Could not find${cdn ? ' CDN' : ''} DC ${id}`) @@ -531,7 +613,7 @@ export class BaseTelegramClient extends EventEmitter { this._primaryDc = newDc await this.storage.setDefaultDc(newDc) await this._saveStorage() - this.primaryConnection.changeDc(newDc) + await this.primaryConnection.changeDc(newDc) } /** @@ -551,11 +633,11 @@ export class BaseTelegramClient extends EventEmitter { message: T, params?: { throwFlood?: boolean - connection?: TelegramConnection + connection?: SessionConnection timeout?: number } ): Promise { - if (!this._connected) { + if (this._connected !== true) { await this.connect() } @@ -573,13 +655,6 @@ export class BaseTelegramClient extends EventEmitter { } } - if (this._disableUpdates) { - message = { - _: 'invokeWithoutUpdates', - query: message, - } as any // who cares - } - const connection = params?.connection ?? this.primaryConnection let lastError: Error | null = null @@ -587,7 +662,7 @@ export class BaseTelegramClient extends EventEmitter { for (let i = 0; i < this._rpcRetryCount; i++) { try { - const res = await connection.sendForResult( + const res = await connection.sendRpc( message, stack, params?.timeout @@ -599,7 +674,7 @@ export class BaseTelegramClient extends EventEmitter { lastError = e if (e instanceof InternalError) { - this._baseLog.warn('Telegram is having internal issues: %s', e) + this.log.warn('Telegram is having internal issues: %s', e) if (e.message === 'WORKER_BUSY_TOO_LONG_RETRY') { // according to tdlib, "it is dangerous to resend query without timeout, so use 1" await sleep(1000) @@ -628,7 +703,7 @@ export class BaseTelegramClient extends EventEmitter { params?.throwFlood !== true && e.seconds <= this._floodSleepThreshold ) { - this._baseLog.info('Flood wait for %d seconds', e.seconds) + this.log.info('Flood wait for %d seconds', e.seconds) await sleep(e.seconds * 1000) continue } @@ -640,21 +715,21 @@ export class BaseTelegramClient extends EventEmitter { e.constructor === UserMigrateError || e.constructor === NetworkMigrateError ) { - this._baseLog.info('Migrate error, new dc = %d', e.newDc) + this.log.info('Migrate error, new dc = %d', e.newDc) await this.changeDc(e.newDc) continue } } else { if (e.constructor === AuthKeyUnregisteredError) { // we can try re-exporting auth from the primary connection - this._baseLog.warn('exported auth key error, re-exporting..') + this.log.warn('exported auth key error, re-exporting..') const auth = await this.call({ _: 'auth.exportAuthorization', dcId: connection.params.dc.id, }) - await connection.sendForResult({ + await connection.sendRpc({ _: 'auth.importAuthorization', id: auth.id, bytes: auth.bytes, @@ -691,39 +766,54 @@ export class BaseTelegramClient extends EventEmitter { */ async createAdditionalConnection( dcId: number, - cdn = false, - inactivityTimeout = 300_000 - ): Promise { - const dc = await this.getDcById(dcId, cdn) - const connection = new TelegramConnection({ - dc, - testMode: this._testMode, - crypto: this._crypto, - initConnection: this._initConnectionParams, - transportFactory: this._transportFactory, - reconnectionStrategy: this._reconnectionStrategy, - inactivityTimeout, - layer: this._layer, - }, this.log.create('connection')) + params?: { + // todo proper docs + // default = false + media?: boolean + // default = fa;se + cdn?: boolean + // default = 300_000 + inactivityTimeout?: number + // default = false + disableUpdates?: boolean + } + ): Promise { + const dc = await this.getDcById(dcId, params?.media, params?.cdn) + const connection = new SessionConnection( + { + dc, + testMode: this._testMode, + crypto: this._crypto, + initConnection: this._initConnectionParams, + transportFactory: this._transportFactory, + reconnectionStrategy: this._reconnectionStrategy, + inactivityTimeout: params?.inactivityTimeout ?? 300_000, + layer: this._layer, + disableUpdates: params?.disableUpdates, + readerMap: this._readerMap, + writerMap: this._writerMap, + }, + this.log.create('connection') + ) connection.on('error', (err) => this._emitError(err, connection)) - connection.authKey = await this.storage.getAuthKeyFor(dc.id) + await connection.setupKeys(await this.storage.getAuthKeyFor(dc.id)) connection.connect() - if (!connection.authKey) { - this._baseLog.info('exporting auth to DC %d', dcId) + if (!connection.getAuthKey()) { + this.log.info('exporting auth to DC %d', dcId) const auth = await this.call({ _: 'auth.exportAuthorization', dcId, }) - await connection.sendForResult({ + await connection.sendRpc({ _: 'auth.importAuthorization', id: auth.id, bytes: auth.bytes, }) // connection.authKey was already generated at this point - this.storage.setAuthKeyFor(dc.id, connection.authKey) + this.storage.setAuthKeyFor(dc.id, connection.getAuthKey()!) await this._saveStorage() } else { // in case the auth key is invalid @@ -752,7 +842,7 @@ export class BaseTelegramClient extends EventEmitter { * @param connection Connection created with {@link createAdditionalConnection} */ async destroyAdditionalConnection( - connection: TelegramConnection + connection: SessionConnection ): Promise { const idx = this._additionalConnections.indexOf(connection) if (idx === -1) return @@ -789,12 +879,12 @@ export class BaseTelegramClient extends EventEmitter { * this was connection-related error. */ onError( - handler: (err: Error, connection?: TelegramConnection) => void + handler: (err: Error, connection?: SessionConnection) => void ): void { this._onError = handler } - protected _emitError(err: Error, connection?: TelegramConnection): void { + protected _emitError(err: Error, connection?: SessionConnection): void { if (this._onError) { this._onError(err, connection) } else { @@ -817,6 +907,7 @@ export class BaseTelegramClient extends EventEmitter { // absolutely incredible min peer handling, courtesy of levlam. // see this thread: https://t.me/tdlibchat/15084 hadMin = true + this.log.debug('received min peer: %j', peer) continue } @@ -837,7 +928,7 @@ export class BaseTelegramClient extends EventEmitter { case 'chatForbidden': parsedPeers.push({ id: -peer.id, - accessHash: bigInt.zero, + accessHash: Long.ZERO, type: 'chat', full: peer, }) @@ -845,7 +936,7 @@ export class BaseTelegramClient extends EventEmitter { case 'channel': case 'channelForbidden': parsedPeers.push({ - id: MAX_CHANNEL_ID - peer.id, + id: toggleChannelIdMark(peer.id), accessHash: peer.accessHash!, username: peer._ === 'channel' @@ -861,7 +952,7 @@ export class BaseTelegramClient extends EventEmitter { await this.storage.updatePeers(parsedPeers) if (count > 0) { - this._baseLog.debug('cached %d peers, had min: %b', count, hadMin) + this.log.debug('cached %d peers', count) } return hadMin @@ -883,10 +974,10 @@ export class BaseTelegramClient extends EventEmitter { * > with [@BotFather](//t.me/botfather) */ async exportSession(): Promise { - if (!this.primaryConnection.authKey) + if (!this.primaryConnection.getAuthKey()) throw new Error('Auth key is not generated yet') - const writer = BinaryWriter.alloc(512) + const writer = TlBinaryWriter.alloc(this._writerMap, 512) const self = await this.storage.getSelf() @@ -897,18 +988,22 @@ export class BaseTelegramClient extends EventEmitter { flags |= 1 } + if (this._testMode) { + flags |= 2 + } + writer.buffer[0] = version writer.pos += 1 - writer.int32(flags) + writer.int(flags) writer.object(this._primaryDc) if (self) { - writer.int32(self.userId) + writer.int53(self.userId) writer.boolean(self.isBot) } - writer.bytes(this.primaryConnection.authKey) + writer.bytes(this.primaryConnection.getAuthKey()!) return encodeUrlSafeBase64(writer.result()) } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index eb17eb59..1239ff49 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -2,24 +2,10 @@ export * from './base-client' export * from './network' export * from './storage' export * from './types' +export * from './utils' export * from '@mtcute/tl' export * from '@mtcute/tl/errors' - -export * from './utils/crypto' -export * from './utils/peer-utils' -export * from './utils/tl-json' -export * from './utils/async-lock' -export * from './utils/lru-map' -export * from './utils/function-utils' -export { - encodeUrlSafeBase64, - parseUrlSafeBase64, - randomBytes, -} from './utils/buffer-utils' -export * from './utils/bigint-utils' - -export { BinaryReader } from './utils/binary/binary-reader' -export { BinaryWriter } from './utils/binary/binary-writer' +export * from '@mtcute/tl-runtime' export { defaultDcs } from './utils/default-dcs' diff --git a/packages/core/src/network/authorization.ts b/packages/core/src/network/authorization.ts index 95090b1e..2c70565a 100644 --- a/packages/core/src/network/authorization.ts +++ b/packages/core/src/network/authorization.ts @@ -1,24 +1,94 @@ -import { TelegramConnection } from './telegram-connection' -import { buffersEqual, randomBytes, xorBuffer } from '../utils/buffer-utils' -import { tl } from '@mtcute/tl' -import { BinaryReader } from '../utils/binary/binary-reader' import { - BinaryWriter, - SerializationCounter, -} from '../utils/binary/binary-writer' + buffersEqual, + randomBytes, + xorBuffer, + xorBufferInPlace, +} from '../utils/buffer-utils' +import { mtp } from '@mtcute/tl' +import { ICryptoProvider, bigIntToBuffer, bufferToBigInt } from '../utils' import { findKeyByFingerprints } from '../utils/crypto/keys' -import { ICryptoProvider } from '../utils/crypto' import bigInt from 'big-integer' import { generateKeyAndIvFromNonce } from '../utils/crypto/mtproto' +import { SessionConnection } from './session-connection' +import Long from 'long' import { - bigIntToBuffer, - bigIntTwo, - bufferToBigInt, -} from '../utils/bigint-utils' + TlBinaryReader, + TlBinaryWriter, + TlSerializationCounter, +} from '@mtcute/tl-runtime' +import { TlPublicKey } from '@mtcute/tl/binary/rsa-keys' // Heavily based on code from https://github.com/LonamiWebs/Telethon/blob/master/telethon/network/authenticator.py -const DH_SAFETY_RANGE = bigIntTwo.pow(2048 - 64) +const DH_SAFETY_RANGE = bigInt[2].pow(2048 - 64) + +async function rsaPad( + data: Buffer, + crypto: ICryptoProvider, + key: TlPublicKey +): Promise { + // since Summer 2021, they use "version of RSA with a variant of OAEP+ padding explained below" + + const keyModulus = bigInt(key.modulus, 16) + const keyExponent = bigInt(key.exponent, 16) + + if (data.length > 144) { + throw new Error('Failed to pad: too big data') + } + + data = Buffer.concat([data, randomBytes(192 - data.length)]) + + for (;;) { + const aesIv = Buffer.alloc(32) + + const aesKey = randomBytes(32) + + const dataWithHash = Buffer.concat([ + data, + await crypto.sha256(Buffer.concat([aesKey, data])), + ]) + // we only need to reverse the data + dataWithHash.slice(0, 192).reverse() + + const aes = crypto.createAesIge(aesKey, aesIv) + const encrypted = await aes.encrypt(dataWithHash) + const encryptedHash = await crypto.sha256(encrypted) + + xorBufferInPlace(aesKey, encryptedHash) + const decryptedData = Buffer.concat([aesKey, encrypted]) + + const decryptedDataBigint = bufferToBigInt(decryptedData) + if (decryptedDataBigint.geq(keyModulus)) { + continue + } + + const encryptedBigint = decryptedDataBigint.modPow( + keyExponent, + keyModulus + ) + return bigIntToBuffer(encryptedBigint, 256) + } +} + +async function rsaEncrypt( + data: Buffer, + crypto: ICryptoProvider, + key: TlPublicKey +): Promise { + const toEncrypt = Buffer.concat([ + await crypto.sha1(data), + data, + // sha1 is always 20 bytes, so we're left with 255 - 20 - x padding + randomBytes(235 - data.length), + ]) + + const encryptedBigInt = bufferToBigInt(toEncrypt).modPow( + bigInt(key.exponent, 16), + bigInt(key.modulus, 16) + ) + + return bigIntToBuffer(encryptedBigInt) +} /** * Execute authorization flow on `connection` using `crypto`. @@ -26,60 +96,71 @@ const DH_SAFETY_RANGE = bigIntTwo.pow(2048 - 64) * Returns tuple: [authKey, serverSalt, timeOffset] */ export async function doAuthorization( - connection: TelegramConnection, + connection: SessionConnection, crypto: ICryptoProvider -): Promise<[Buffer, Buffer, number]> { - function sendPlainMessage(message: tl.TlObject): Promise { - const length = SerializationCounter.countNeededBytes(message) - const writer = BinaryWriter.alloc(length + 20) // 20 bytes for mtproto header +): Promise<[Buffer, Long, number]> { + const session = connection['_session'] + const readerMap = session._readerMap + const writerMap = session._writerMap - const messageId = connection['_getMessageId']() + function sendPlainMessage(message: mtp.TlObject): Promise { + const length = TlSerializationCounter.countNeededBytes( + writerMap, + message + ) + const writer = TlBinaryWriter.alloc(writerMap, length + 20) // 20 bytes for mtproto header - writer.long(bigInt.zero) + const messageId = session.getMessageId() + + writer.long(Long.ZERO) writer.long(messageId) - writer.uint32(length) + writer.uint(length) writer.object(message) return connection.send(writer.result()) } + async function readNext(): Promise { + return TlBinaryReader.deserializeObject( + readerMap, + await connection.waitForNextMessage(), + 20 // skip mtproto header + ) + } + const log = connection.log.create('auth') const nonce = randomBytes(16) // Step 1: PQ request - log.debug( - '%s: starting PQ handshake, nonce = %h', - connection.params.dc.ipAddress, - nonce - ) - await sendPlainMessage({ _: 'mt_reqPqMulti', nonce }) - const resPq = BinaryReader.deserializeObject( - await connection.waitForNextMessage(), - 20 // skip mtproto header - ) + log.debug('starting PQ handshake, nonce = %h', nonce) + + await sendPlainMessage({ _: 'mt_req_pq_multi', nonce }) + const resPq = await readNext() if (resPq._ !== 'mt_resPQ') throw new Error('Step 1: answer was ' + resPq._) if (!buffersEqual(resPq.nonce, nonce)) throw new Error('Step 1: invalid nonce from server') - log.debug('%s: received PQ', connection.params.dc.ipAddress) + + const serverKeys = resPq.serverPublicKeyFingerprints.map((it) => + it.toUnsigned().toString(16) + ) + log.debug('received PQ, keys: %j', serverKeys) // Step 2: DH exchange - const publicKey = findKeyByFingerprints(resPq.serverPublicKeyFingerprints) + const publicKey = findKeyByFingerprints(serverKeys) if (!publicKey) throw new Error( 'Step 2: Could not find server public key with any of these fingerprints: ' + - resPq.serverPublicKeyFingerprints - .map((i) => i.toString(16)) - .join(', ') + serverKeys.join(', ') ) log.debug( - '%s: found server key, fp = %s', - connection.params.dc.ipAddress, - publicKey.fingerprint + 'found server key, fp = %s, old = %s', + publicKey.fingerprint, + publicKey.old ) const [p, q] = await crypto.factorizePQ(resPq.pq) - log.debug('%s: factorized PQ', connection.params.dc.ipAddress) + log.debug('factorized PQ: PQ = %h, P = %h, Q = %h', resPq.pq, p, q) const newNonce = randomBytes(32) @@ -87,7 +168,7 @@ export async function doAuthorization( if (connection.params.testMode) dcId += 10000 if (connection.params.dc.mediaOnly) dcId = -dcId - const pqInnerData = BinaryWriter.serializeObject({ + const _pqInnerData: mtp.RawMt_p_q_inner_data_dc = { _: 'mt_p_q_inner_data_dc', pq: resPq.pq, p, @@ -95,26 +176,31 @@ export async function doAuthorization( nonce, newNonce, serverNonce: resPq.serverNonce, - dc: dcId - } as tl.mtproto.RawP_q_inner_data_dc) - const encryptedData = await crypto.rsaEncrypt(pqInnerData, publicKey) - log.debug('%s: requesting DH params', connection.params.dc.ipAddress) + dc: dcId, + } + const pqInnerData = TlBinaryWriter.serializeObject(writerMap, _pqInnerData) + + const encryptedData = publicKey.old + ? await rsaEncrypt(pqInnerData, crypto, publicKey) + : await rsaPad(pqInnerData, crypto, publicKey) + + log.debug('requesting DH params') await sendPlainMessage({ - _: 'mt_reqDHParams', + _: 'mt_req_DH_params', nonce, serverNonce: resPq.serverNonce, p, q, - publicKeyFingerprint: bigInt(publicKey.fingerprint, 16), + publicKeyFingerprint: Long.fromString(publicKey.fingerprint, true, 16), encryptedData, }) - const serverDhParams = BinaryReader.deserializeObject( - await connection.waitForNextMessage(), - 20 // skip mtproto header - ) + const serverDhParams = await readNext() - if (!tl.mtproto.isAnyServer_DH_Params(serverDhParams)) + if (!mtp.isAnyServer_DH_Params(serverDhParams)) + throw new Error('Step 2.1: answer was ' + serverDhParams._) + + if (serverDhParams._ !== 'mt_server_DH_params_ok') throw new Error('Step 2.1: answer was ' + serverDhParams._) if (!buffersEqual(serverDhParams.nonce, nonce)) @@ -131,7 +217,7 @@ export async function doAuthorization( // throw new Error('Step 2: server DH failed') // } - log.debug('%s: server DH ok', connection.params.dc.ipAddress) + log.debug('server DH ok') if (serverDhParams.encryptedAnswer.length % 16 != 0) throw new Error('Step 2: AES block size is invalid') @@ -146,8 +232,12 @@ export async function doAuthorization( const plainTextAnswer = await ige.decrypt(serverDhParams.encryptedAnswer) const innerDataHash = plainTextAnswer.slice(0, 20) - const serverDhInnerReader = new BinaryReader(plainTextAnswer, 20) - const serverDhInner = serverDhInnerReader.object() as tl.TlObject + const serverDhInnerReader = new TlBinaryReader( + readerMap, + plainTextAnswer, + 20 + ) + const serverDhInner = serverDhInnerReader.object() as mtp.TlObject if ( !buffersEqual( @@ -174,7 +264,7 @@ export async function doAuthorization( const g = bigInt(serverDhInner.g) const gA = bufferToBigInt(serverDhInner.gA) - let retryId = bigInt.zero + let retryId = Long.ZERO const serverSalt = xorBuffer( newNonce.slice(0, 8), resPq.serverNonce.slice(0, 8) @@ -213,7 +303,7 @@ export async function doAuthorization( const gB_buf = bigIntToBuffer(gB, 0, false) // Step 4: send client DH - const clientDhInner: tl.mtproto.RawClient_DH_inner_data = { + const clientDhInner: mtp.RawMt_client_DH_inner_data = { _: 'mt_client_DH_inner_data', nonce, serverNonce: resPq.serverNonce, @@ -221,11 +311,12 @@ export async function doAuthorization( gB: gB_buf, } let innerLength = - SerializationCounter.countNeededBytes(clientDhInner) + 20 // for hash + TlSerializationCounter.countNeededBytes(writerMap, clientDhInner) + + 20 // for hash const innerPaddingLength = innerLength % 16 if (innerPaddingLength > 0) innerLength += 16 - innerPaddingLength - const clientDhInnerWriter = BinaryWriter.alloc(innerLength) + const clientDhInnerWriter = TlBinaryWriter.alloc(writerMap, innerLength) clientDhInnerWriter.pos = 20 clientDhInnerWriter.object(clientDhInner) const clientDhInnerHash = await crypto.sha1( @@ -234,25 +325,18 @@ export async function doAuthorization( clientDhInnerWriter.pos = 0 clientDhInnerWriter.raw(clientDhInnerHash) - log.debug( - '%s: sending client DH (timeOffset = %d)', - connection.params.dc.ipAddress, - timeOffset - ) + log.debug('sending client DH (timeOffset = %d)', timeOffset) const clientDhEncrypted = await ige.encrypt(clientDhInnerWriter.buffer) await sendPlainMessage({ - _: 'mt_setClientDHParams', + _: 'mt_set_client_DH_params', nonce, serverNonce: resPq.serverNonce, encryptedData: clientDhEncrypted, }) - const dhGen = BinaryReader.deserializeObject( - await connection.waitForNextMessage(), - 20 // skip mtproto header - ) - if (!tl.mtproto.isAnySet_client_DH_params_answer(dhGen)) + const dhGen = await readNext() + if (!mtp.isAnySet_client_DH_params_answer(dhGen)) throw new Error('Step 4: answer was ' + dhGen._) if (!buffersEqual(dhGen.nonce, nonce)) @@ -260,7 +344,7 @@ export async function doAuthorization( if (!buffersEqual(dhGen.serverNonce, resPq.serverNonce)) throw Error('Step 4: invalid server nonce from server') - log.debug('%s: DH result: %s', connection.params.dc.ipAddress, dhGen._) + log.debug('DH result: %s', dhGen._) if (dhGen._ === 'mt_dh_gen_fail') { // in theory i would be supposed to calculate newNonceHash, but why, we are failing anyway @@ -273,19 +357,24 @@ export async function doAuthorization( ) if (!buffersEqual(expectedHash.slice(4, 20), dhGen.newNonceHash2)) throw Error('Step 4: invalid retry nonce hash from server') - retryId = bufferToBigInt(authKeyAuxHash) + retryId = Long.fromBytesLE(authKeyAuxHash as any) continue } - // dhGen._ === 'mt_dh_gen_ok' + if (dhGen._ !== 'mt_dh_gen_ok') throw new Error() // unreachable + const expectedHash = await crypto.sha1( Buffer.concat([newNonce, Buffer.from([1]), authKeyAuxHash]) ) if (!buffersEqual(expectedHash.slice(4, 20), dhGen.newNonceHash1)) throw Error('Step 4: invalid nonce hash from server') - log.info('%s: authorization successful', connection.params.dc.ipAddress) + log.info('authorization successful') - return [authKey, serverSalt, timeOffset] + return [ + authKey, + new Long(serverSalt.readInt32LE(), serverSalt.readInt32LE(4)), + timeOffset, + ] } } diff --git a/packages/core/src/network/index.ts b/packages/core/src/network/index.ts index 0385d63e..2ad339e3 100644 --- a/packages/core/src/network/index.ts +++ b/packages/core/src/network/index.ts @@ -1,3 +1,3 @@ -export * from './telegram-connection' +export * from './session-connection' export * from './reconnection' export * from './transports' diff --git a/packages/core/src/network/mtproto-session.ts b/packages/core/src/network/mtproto-session.ts index b8522c7e..c1232035 100644 --- a/packages/core/src/network/mtproto-session.ts +++ b/packages/core/src/network/mtproto-session.ts @@ -1,20 +1,9 @@ -import { BigInteger } from 'big-integer' import { buffersEqual, randomBytes } from '../utils/buffer-utils' -import { ICryptoProvider } from '../utils/crypto' -import { tl } from '@mtcute/tl' +import { mtp, tl } from '@mtcute/tl' import { createAesIgeForMessage } from '../utils/crypto/mtproto' -import { - BinaryWriter, - SerializationCounter, -} from '../utils/binary/binary-writer' -import { BinaryReader } from '../utils/binary/binary-reader' -import { Logger } from '../utils/logger' - -export interface EncryptedMessage { - messageId: BigInteger - seqNo: number - content: tl.TlObject -} +import { ICryptoProvider, Logger, getRandomInt, randomLong } from '../utils' +import Long from 'long' +import { TlBinaryReader, TlBinaryWriter, TlReaderMap, TlSerializationCounter, TlWriterMap } from '@mtcute/tl-runtime' /** * Class encapsulating a single MTProto session. @@ -23,17 +12,25 @@ export interface EncryptedMessage { export class MtprotoSession { readonly _crypto: ICryptoProvider - _sessionId = randomBytes(8) + _sessionId = randomLong() _authKey?: Buffer _authKeyId?: Buffer _authKeyClientSalt?: Buffer _authKeyServerSalt?: Buffer - // default salt: [0x00]*8 - serverSalt: Buffer = Buffer.alloc(8) + _timeOffset = 0 + _lastMessageId = Long.ZERO + _seqNo = 0 - constructor(crypto: ICryptoProvider, readonly log: Logger) { + serverSalt = Long.ZERO + + constructor( + crypto: ICryptoProvider, + readonly log: Logger, + readonly _readerMap: TlReaderMap, + readonly _writerMap: TlWriterMap + ) { this._crypto = crypto } @@ -43,52 +40,58 @@ export class MtprotoSession { } /** Setup keys based on authKey */ - async setupKeys(authKey: Buffer): Promise { - this._authKey = authKey - this._authKeyClientSalt = authKey.slice(88, 120) - this._authKeyServerSalt = authKey.slice(96, 128) - this._authKeyId = (await this._crypto.sha1(this._authKey)).slice(-8) + async setupKeys(authKey?: Buffer | null): Promise { + if (authKey) { + this._authKey = authKey + this._authKeyClientSalt = authKey.slice(88, 120) + this._authKeyServerSalt = authKey.slice(96, 128) + this._authKeyId = (await this._crypto.sha1(this._authKey)).slice(-8) + } else { + this._authKey = undefined + this._authKeyClientSalt = undefined + this._authKeyServerSalt = undefined + this._authKeyId = undefined + } } /** Reset session by removing authKey and values derived from it */ reset(): void { + this._lastMessageId = Long.ZERO + this._seqNo = 0 + this._authKey = undefined this._authKeyClientSalt = undefined this._authKeyServerSalt = undefined this._authKeyId = undefined - this._sessionId = randomBytes(8) + this._sessionId = randomLong() // no need to reset server salt } + changeSessionId(): void { + this._sessionId = randomLong() + this._seqNo = 0 + } + /** Encrypt a single MTProto message using session's keys */ - async encryptMessage( - message: tl.TlObject | Buffer, - messageId: BigInteger, - seqNo: number - ): Promise { + async encryptMessage(message: Buffer): Promise { if (!this._authKey) throw new Error('Keys are not set up!') - const length = Buffer.isBuffer(message) - ? message.length - : SerializationCounter.countNeededBytes(message) let padding = - (32 /* header size */ + length + 12) /* min padding */ % 16 + (16 /* header size */ + message.length + 12) /* min padding */ % 16 padding = 12 + (padding ? 16 - padding : 0) - const encryptedWriter = BinaryWriter.alloc(32 + length + padding) - encryptedWriter.raw(this.serverSalt!) - encryptedWriter.raw(this._sessionId) - encryptedWriter.long(messageId) - encryptedWriter.int32(seqNo) - encryptedWriter.uint32(length) - if (Buffer.isBuffer(message)) encryptedWriter.raw(message) - else encryptedWriter.object(message as tl.TlObject) - encryptedWriter.raw(randomBytes(padding)) + const buf = Buffer.alloc(16 + message.length + padding) + + buf.writeInt32LE(this.serverSalt!.low) + buf.writeInt32LE(this.serverSalt!.high, 4) + buf.writeInt32LE(this._sessionId.low, 8) + buf.writeInt32LE(this._sessionId.high, 12) + message.copy(buf, 16) + randomBytes(padding).copy(buf, 16 + message.length) - const innerData = encryptedWriter.result() const messageKey = ( await this._crypto.sha256( - Buffer.concat([this._authKeyClientSalt!, innerData]) + Buffer.concat([this._authKeyClientSalt!, buf]) ) ).slice(8, 24) const ige = await createAesIgeForMessage( @@ -97,20 +100,26 @@ export class MtprotoSession { messageKey, true ) - const encryptedData = await ige.encrypt(innerData) + const encryptedData = await ige.encrypt(buf) return Buffer.concat([this._authKeyId!, messageKey, encryptedData]) } /** Decrypt a single MTProto message using session's keys */ - async decryptMessage(data: Buffer): Promise { + async decryptMessage( + data: Buffer, + callback: ( + msgId: tl.Long, + seqNo: number, + data: TlBinaryReader + ) => void + ): Promise { if (!this._authKey) throw new Error('Keys are not set up!') - const reader = new BinaryReader(data) + const authKeyId = data.slice(0, 8) + const messageKey = data.slice(8, 24) - const authKeyId = reader.raw(8) - const messageKey = reader.int128() - let encryptedData = reader.raw() + let encryptedData = data.slice(24) if (!buffersEqual(authKeyId, this._authKeyId!)) { this.log.warn( @@ -119,7 +128,7 @@ export class MtprotoSession { authKeyId, this._authKeyId ) - return null + return } const padSize = encryptedData.length % 16 @@ -142,6 +151,7 @@ export class MtprotoSession { Buffer.concat([this._authKeyServerSalt!, innerData]) ) ).slice(8, 24) + if (!buffersEqual(messageKey, expectedMessageKey)) { this.log.warn( '[%h] received message with invalid messageKey = %h (expected %h)', @@ -149,24 +159,24 @@ export class MtprotoSession { messageKey, expectedMessageKey ) - return null + return } - const innerReader = new BinaryReader(innerData) + const innerReader = new TlBinaryReader(this._readerMap, innerData) innerReader.seek(8) // skip salt - const sessionId = innerReader.raw(8) + const sessionId = innerReader.long() const messageId = innerReader.long(true) - if (!buffersEqual(sessionId, this._sessionId)) { + if (sessionId.neq(this._sessionId)) { this.log.warn( 'ignoring message with invalid sessionId = %h', sessionId ) - return null + return } - const seqNo = innerReader.uint32() - const length = innerReader.uint32() + const seqNo = innerReader.uint() + const length = innerReader.uint() if (length > innerData.length - 32 /* header size */) { this.log.warn( @@ -174,7 +184,7 @@ export class MtprotoSession { length, innerData.length - 32 ) - return null + return } if (length % 4 !== 0) { @@ -182,25 +192,68 @@ export class MtprotoSession { 'ignoring message with invalid length: %d is not a multiple of 4', length ) - return null + return } - const content = innerReader.object() - - const paddingSize = innerData.length - innerReader.pos + const paddingSize = innerData.length - length - 32 // header size if (paddingSize < 12 || paddingSize > 1024) { this.log.warn( 'ignoring message with invalid padding size: %d', paddingSize ) - return null + return } - return { - messageId, - seqNo, - content, + callback(messageId, seqNo, innerReader) + } + + getMessageId(): Long { + const timeTicks = Date.now() + const timeSec = Math.floor(timeTicks / 1000) + this._timeOffset + const timeMSec = timeTicks % 1000 + const random = getRandomInt(0xffff) + + let messageId = new Long((timeMSec << 21) | (random << 3) | 4, timeSec) + + if (this._lastMessageId.gt(messageId)) { + messageId = this._lastMessageId.add(4) } + + this._lastMessageId = messageId + + return messageId + } + + getSeqNo(isContentRelated = true): number { + let seqNo = this._seqNo * 2 + + if (isContentRelated) { + seqNo += 1 + this._seqNo += 1 + } + + return seqNo + } + + writeMessage( + writer: TlBinaryWriter, + content: tl.TlObject | mtp.TlObject | Buffer, + isContentRelated = true + ): Long { + const messageId = this.getMessageId() + const seqNo = this.getSeqNo(isContentRelated) + + const length = Buffer.isBuffer(content) + ? content.length + : TlSerializationCounter.countNeededBytes(writer.objectMap!, content) + + writer.long(messageId) + writer.int(seqNo) + writer.uint(length) + if (Buffer.isBuffer(content)) writer.raw(content) + else writer.object(content as tl.TlObject) + + return messageId } } diff --git a/packages/core/src/network/persistent-connection.ts b/packages/core/src/network/persistent-connection.ts index fdc5258d..0bcf796d 100644 --- a/packages/core/src/network/persistent-connection.ts +++ b/packages/core/src/network/persistent-connection.ts @@ -6,8 +6,7 @@ import { ControllablePromise, createControllablePromise, } from '../utils/controllable-promise' -import { ICryptoProvider } from '../utils/crypto' -import { Logger } from '../utils/logger' +import { ICryptoProvider, Logger } from '../utils' export interface PersistentConnectionParams { crypto: ICryptoProvider @@ -20,7 +19,7 @@ export interface PersistentConnectionParams { /** * Base class for persistent connections. - * Only used for {@link TelegramConnection} and used as a mean of code splitting. + * Only used for {@link PersistentConnection} and used as a mean of code splitting. */ export abstract class PersistentConnection extends EventEmitter { readonly params: PersistentConnectionParams @@ -41,8 +40,8 @@ export abstract class PersistentConnection extends EventEmitter { // waitForMessage private _pendingWaitForMessages: ControllablePromise[] = [] - protected _destroyed = false - protected _usable = false + _destroyed = false + _usable = false protected abstract onConnected(): void diff --git a/packages/core/src/network/session-connection.ts b/packages/core/src/network/session-connection.ts new file mode 100644 index 00000000..b1e14458 --- /dev/null +++ b/packages/core/src/network/session-connection.ts @@ -0,0 +1,1595 @@ +import { + PersistentConnection, + PersistentConnectionParams, +} from './persistent-connection' +import { mtp, tl } from '@mtcute/tl' +import { Logger, LongMap, randomLong, removeFromLongArray } from '../utils' +import { MtprotoSession } from './mtproto-session' +import { doAuthorization } from './authorization' +import { TransportError } from './transports' +import Long from 'long' +import { LruSet } from '../utils/lru-string-set' +import { + ControllablePromise, + createCancellablePromise, +} from '../utils/controllable-promise' +import { + createRpcErrorFromTl, + RpcError, + RpcTimeoutError, +} from '@mtcute/tl/errors' +import { gzipDeflate, gzipInflate } from '@mtcute/tl-runtime/src/platform/gzip' +import { SortedArray } from '../utils/sorted-array' +import { EarlyTimer } from '../utils/early-timer' +import { Deque } from '../utils' +import { TlBinaryReader, TlBinaryWriter, TlReaderMap, TlSerializationCounter, TlWriterMap } from '@mtcute/tl-runtime' + +export interface SessionConnectionParams extends PersistentConnectionParams { + initConnection: tl.RawInitConnectionRequest + inactivityTimeout?: number + niceStacks?: boolean + layer: number + disableUpdates?: boolean + + readerMap: TlReaderMap + writerMap: TlWriterMap +} + +interface PendingRpc { + method: string + data: Buffer + promise: ControllablePromise + stack?: string + gzipOverhead?: number + + sent?: boolean + msgId?: Long + seqNo?: number + containerId?: Long + acked?: boolean + initConn?: boolean + getState?: number + cancelled?: boolean + timeout?: number +} + +type PendingMessage = + | { + _: 'rpc' + rpc: PendingRpc + } + | { + _: 'container' + msgIds: Long[] + } + | { + _: 'state' + msgIds: Long[] + containerId: Long + } + | { + _: 'resend' + msgIds: Long[] + containerId: Long + } + | { + _: 'ping' + pingId: Long + containerId: Long + } + | { + _: 'destroy_session' + sessionId: Long + containerId: Long + } + | { + _: 'cancel' + msgId: Long + containerId: Long + } + | { + _: 'future_salts' + containerId: Long + } + +// destroy_session#e7512126 session_id:long +// todo +const DESTROY_SESSION_ID = Buffer.from('262151e7', 'hex') + +function makeNiceStack(error: RpcError, stack: string, method?: string) { + error.stack = `${error.constructor.name} (${error.code} ${error.text}): ${ + error.message + }\n at ${method}\n${stack.split('\n').slice(2).join('\n')}` +} + +let nextConnectionUid = 0 + +export class SessionConnection extends PersistentConnection { + readonly params!: SessionConnectionParams + + private _uid = nextConnectionUid++ + private _session: MtprotoSession + private _flushTimer = new EarlyTimer() + + /// internal state /// + + // recent msg ids + private _recentOutgoingMsgIds = new LruSet(1000, false, true) + private _recentIncomingMsgIds = new LruSet(1000, false, true) + + // queues + private _queuedRpc = new Deque() + private _queuedAcks: Long[] = [] + private _queuedStateReq: Long[] = [] + private _queuedResendReq: Long[] = [] + private _queuedCancelReq: Long[] = [] + private _queuedDestroySession: Long[] = [] + private _getStateSchedule = new SortedArray( + [], + (a, b) => a.getState! - b.getState! + ) + + // requests info + private _pendingMessages = new LongMap() + + private _initConnectionCalled = false + + private _lastPingRtt = NaN + private _lastPingTime = 0 + private _lastPingMsgId = Long.ZERO + private _lastSessionCreatedUid = Long.ZERO + + private _next429Timeout = 1000 + private _current429Timeout?: NodeJS.Timeout + + private _readerMap: TlReaderMap + private _writerMap: TlWriterMap + + constructor(params: SessionConnectionParams, log: Logger) { + super(params, log.create('conn')) + this._updateLogPrefix() + this._session = new MtprotoSession( + params.crypto, + log.create('session'), + params.readerMap, + params.writerMap + ) + this._flushTimer.onTimeout(this._flush.bind(this)) + + this._readerMap = params.readerMap + this._writerMap = params.writerMap + this._handleRawMessage = this._handleRawMessage.bind(this) + } + + private _updateLogPrefix() { + this.log.prefix = `[UID ${this._uid}, DC ${this.params.dc.id}] ` + } + + async changeDc(dc: tl.RawDcOption, authKey?: Buffer): Promise { + this.log.debug('dc changed (has_auth_key = %b) to: %j', authKey, dc) + this._updateLogPrefix() + + this._session.reset() + await this._session.setupKeys(authKey) + this.params.dc = dc + this.reconnect() + } + + setupKeys(authKey: Buffer | null): Promise { + return this._session.setupKeys(authKey) + } + + getAuthKey(): Buffer | undefined { + return this._session._authKey + } + + onTransportClose(): void { + super.onTransportClose() + this.emit('disconnect') + + this.reset() + } + + destroy(): void { + super.destroy() + this.reset(true) + } + + reset(forever = false): void { + this._initConnectionCalled = false + this._resetLastPing(true) + this._flushTimer.reset() + clearTimeout(this._current429Timeout!) + + if (forever) { + // reset all the queues, cancel all pending messages, etc + this._session.reset() + + for (const info of this._pendingMessages.values()) { + if (info._ === 'rpc') { + info.rpc.promise.reject(new Error('Connection destroyed')) + } + } + this._pendingMessages.clear() + + this._recentOutgoingMsgIds.clear() + this._recentIncomingMsgIds.clear() + + while (this._queuedRpc.length) { + const rpc = this._queuedRpc.popFront()! + + if (rpc.sent === false) { + rpc.promise.reject(new Error('Connection destroyed')) + } + } + + this._queuedAcks.length = 0 + this._queuedStateReq.length = 0 + this._queuedResendReq.length = 0 + this._getStateSchedule.clear() + this.removeAllListeners() + } + } + + protected async onConnected(): Promise { + if (this._session.authorized) { + this.onConnectionUsable() + } else { + this._authorize() + } + } + + protected onError(error: Error): void { + // https://core.telegram.org/mtproto/mtproto-_transports#_transport-errors + if (error instanceof TransportError) { + this.log.error('transport error %d', error.code) + + if (error.code === 404) { + this._session.reset() + this.emit('key-change', null) + this._authorize() + return + } + + if (error.code === 429) { + // all active queries must be resent + const timeout = this._next429Timeout + + this._next429Timeout = Math.min(this._next429Timeout * 2, 16000) + clearTimeout(this._current429Timeout!) + this._current429Timeout = setTimeout(() => { + this._current429Timeout = undefined + this._flushTimer.emitNow() + }, timeout) + + this.log.debug( + 'transport flood, waiting for %d ms before proceeding', + timeout + ) + + for (const msgId of this._pendingMessages.keys()) { + const info = this._pendingMessages.get(msgId)! + + if (info._ === 'container') { + this._pendingMessages.delete(msgId) + } else { + this._onMessageFailed(msgId, 'transport flood', true) + } + } + return + } + } + + this.emit('error', error) + } + + protected onConnectionUsable() { + super.onConnectionUsable() + + // just in case + this._flushTimer.emitBeforeNext(1000) + } + + private _authorize(): void { + doAuthorization(this, this.params.crypto) + .then(async ([authKey, serverSalt, timeOffset]) => { + await this._session.setupKeys(authKey) + this._session.serverSalt = serverSalt + this._session._timeOffset = timeOffset + + this.emit('key-change', authKey) + + this.onConnectionUsable() + }) + .catch((err) => { + this.log.error('Authorization error: %s', err.message) + this.onError(err) + this.reconnect() + }) + } + + protected async onMessage(data: Buffer): Promise { + if (!this._session.authorized) { + // if a message is received before authorization, + // either the server is misbehaving, + // or there was a problem with authorization. + this.log.warn('received message before authorization: %h', data) + return + } + + try { + await this._session.decryptMessage(data, this._handleRawMessage) + } catch (err) { + this.log.error('failed to decrypt message: %s\ndata: %h', err, data) + } + } + + private _handleRawMessage( + messageId: Long, + seqNo: number, + message: TlBinaryReader + ): void { + if (message.peekUint() === 0x3072cfa1) { + // gzip_packed + // we can't use message.gzip() because it may contain msg_container, + // so we parse it manually. + message.uint() + return this._handleRawMessage( + messageId, + seqNo, + new TlBinaryReader( + this._readerMap, + gzipInflate(message.bytes()) + ) + ) + } + + if (message.peekUint() === 0x73f1f8dc) { + // msg_container + message.uint() + const count = message.uint() + + for (let i = 0; i < count; i++) { + // msg_id:long seqno:int bytes:int + const msgId = message.long() + message.uint() // seqno + const length = message.uint() + + // container can't contain other containers, so we are safe + const start = message.pos + const obj = message.object() + + // ensure length + if (message.pos - start !== length) { + this.log.warn( + 'received message with invalid length in container (%d != %d)', + message.pos - start, + length + ) + } + + this._handleMessage(msgId, obj) + } + return + } + + // we are safe.. i guess + this._handleMessage(messageId, message.object()) + } + + private _handleMessage( + messageId: Long, + message: mtp.TlObject + ): void { + if (messageId.isEven()) { + this.log.warn( + 'warn: ignoring message with invalid messageId = %s (is even)', + messageId + ) + return + } + + this.log.verbose('received %s (msg_id: %s)', message._, messageId) + this._recentIncomingMsgIds.add(messageId) + + switch (message._) { + case 'mt_msgs_ack': + case 'mt_http_wait': + case 'mt_bad_msg_notification': + case 'mt_bad_server_salt': + case 'mt_msgs_all_info': + case 'mt_msgs_state_info': + case 'mt_msg_detailed_info': + case 'mt_msg_new_detailed_info': + break + default: + this._sendAck(messageId) + } + + switch (message._) { + case 'mt_rpc_result': + this._onRpcResult(message) + break + case 'mt_pong': + this._onPong(message) + break + case 'mt_bad_server_salt': + this._onBadServerSalt(message) + break + case 'mt_bad_msg_notification': + this._onBadMsgNotification(messageId, message) + break + case 'mt_msgs_ack': + message.msgIds.forEach((msgId) => this._onMessageAcked(msgId)) + break + case 'mt_new_session_created': + this._onNewSessionCreated(message) + break + case 'mt_msgs_all_info': + this._onMessagesInfo(message.msgIds, message.info) + break + case 'mt_msg_detailed_info': + this._onMessageInfo( + message.msgId, + message.status, + message.answerMsgId + ) + break + case 'mt_msg_new_detailed_info': + this._onMessageInfo(Long.ZERO, 0, message.answerMsgId) + break + case 'mt_msgs_state_info': + this._onMsgsStateInfo(message) + break + case 'mt_future_salts': + // todo + break + case 'mt_msgs_state_req': + case 'mt_msg_resend_req': + // tdlib doesnt handle them, so why should we? :upside_down_face: + this.log.warn( + 'received %s (msg_id = %l): %j', + message._, + messageId, + message + ) + break + default: + if (tl.isAnyUpdates(message)) { + if (this._usable && this.params.inactivityTimeout) + this._rescheduleInactivity() + + this.emit('update', message) + return + } + + this.log.warn('unknown message received: %j', message) + } + } + + private _onRpcResult({ result, reqMsgId }: mtp.RawMt_rpc_result): void { + if (this._usable && this.params.inactivityTimeout) + this._rescheduleInactivity() + + if (reqMsgId.isZero()) { + this.log.warn( + 'received rpc_result with %s with req_msg_id = 0', + result._ + ) + return + } + + const msg = this._pendingMessages.get(reqMsgId) + if (!msg) { + // check if the msg is one of the recent ones + if (this._recentOutgoingMsgIds.has(reqMsgId)) { + this.log.debug( + 'received rpc_result again for %l (contains %s)', + reqMsgId, + result._ + ) + } else { + this.log.warn( + 'received rpc_result for unknown message %l: %j', + reqMsgId, + result + ) + } + return + } + + if (msg._ !== 'rpc') { + this.log.error( + 'received rpc_result for %s request %l', + msg._, + reqMsgId + ) + return + } + const rpc = msg.rpc + + // initConnection call was definitely received and + // processed by the server, so we no longer need to use it + if (rpc.initConn) this._initConnectionCalled = true + + this.log.verbose('<<< (%s) %j', rpc.method, result) + + if (result._ === 'mt_rpc_error') { + const res = result as mtp.RawMt_rpc_error + this.log.debug( + 'received rpc_error [%d:%s] for %l (%s)', + res.errorCode, + res.errorMessage, + reqMsgId, + rpc.method + ) + + if (rpc.cancelled) return + + const error = createRpcErrorFromTl(res) + if (this.params.niceStacks !== false) { + makeNiceStack(error, rpc.stack!, rpc.method) + } + + rpc.promise.reject(error) + } else { + this.log.debug( + 'received rpc_result (%s) for request %l (%s)', + result._, + reqMsgId, + rpc.method + ) + + if (rpc.cancelled) return + + rpc.promise.resolve(result) + } + + this._onMessageAcked(reqMsgId) + this._pendingMessages.delete(reqMsgId) + } + + private _onMessageAcked(msgId: Long, inContainer = false): void { + const msg = this._pendingMessages.get(msgId) + + if (!msg) { + this.log.warn('received ack for unknown message %l', msgId) + return + } + + switch (msg._) { + case 'container': + this.log.debug( + 'received ack for container %l (size = %d)', + msgId, + msg.msgIds.length + ) + + msg.msgIds.forEach((msgId) => this._onMessageAcked(msgId, true)) + + // we no longer need info about the container + this._pendingMessages.delete(msgId) + break + case 'rpc': { + const rpc = msg.rpc + this.log.debug( + 'received ack for rpc query %l (%s, acked before = %s)', + msgId, + rpc.method, + rpc.acked + ) + + rpc.acked = true + + if ( + !inContainer && + rpc.containerId && + this._pendingMessages.has(rpc.containerId) + ) { + // ack all the messages in that container + this._onMessageAcked(rpc.containerId) + } + + // this message could also already be in some queue, + removeFromLongArray(this._queuedStateReq, msgId) + removeFromLongArray(this._queuedResendReq, msgId) + + // if resend/state was already requested, it will simply be ignored + + this._getStateSchedule.remove(rpc) + break + } + default: + if (!inContainer) { + this.log.warn( + 'received unexpected ack for %s query %l', + msg._, + msgId + ) + } + } + } + + private _onMessageFailed( + msgId: Long, + reason: string, + inContainer = false + ): void { + const msgInfo = this._pendingMessages.get(msgId) + if (!msgInfo) { + this.log.debug( + 'unknown message %l failed because of %s', + msgId, + reason + ) + return + } + + switch (msgInfo._) { + case 'container': + this.log.debug( + 'container %l (size = %d) failed because of %s', + msgId, + msgInfo.msgIds.length, + reason + ) + msgInfo.msgIds.forEach((msgId) => + this._onMessageFailed(msgId, reason, true) + ) + break + case 'ping': + this.log.debug( + 'ping (msg_id = %l) failed because of %s', + msgId, + reason + ) + // restart ping + this._resetLastPing(true) + break + case 'rpc': { + const rpc = msgInfo.rpc + this.log.debug( + 'rpc query %l (%s) failed because of %s', + msgId, + rpc.method, + reason + ) + + // since the query was rejected, we can let it reassign msg_id to avoid containers + this._pendingMessages.delete(msgId) + rpc.msgId = undefined + this._enqueueRpc(rpc, true) + + if ( + !inContainer && + rpc.containerId && + this._pendingMessages.has(rpc.containerId) + ) { + // fail all the messages in that container + this._onMessageFailed(rpc.containerId, reason) + } + + // this message could also already be in some queue, + removeFromLongArray(this._queuedStateReq, msgId) + removeFromLongArray(this._queuedResendReq, msgId) + + // if resend/state was already requested, it will simply be ignored + + this._getStateSchedule.remove(rpc) + + break + } + case 'resend': + this.log.debug( + 'resend request %l (size = %d) failed because of %s', + msgId, + msgInfo.msgIds.length, + reason + ) + this._queuedResendReq.splice(0, 0, ...msgInfo.msgIds) + this._flushTimer.emitWhenIdle() + break + case 'state': + this.log.debug( + 'state request %l (size = %d) failed because of %s', + msgId, + msgInfo.msgIds.length, + reason + ) + this._queuedStateReq.splice(0, 0, ...msgInfo.msgIds) + this._flushTimer.emitWhenIdle() + break + } + + this._pendingMessages.delete(msgId) + } + + private _resetLastPing(withTime = false): void { + if (withTime) this._lastPingTime = 0 + + if (!this._lastPingMsgId.isZero()) { + this._pendingMessages.delete(this._lastPingMsgId) + } + + this._lastPingMsgId = Long.ZERO + } + + private _registerOutgoingMsgId(msgId: Long): Long { + this._recentOutgoingMsgIds.add(msgId) + return msgId + } + + private _onPong({ msgId, pingId }: mtp.RawMt_pong): void { + const info = this._pendingMessages.get(msgId) + + if (!info) { + this.log.warn( + 'received pong to unknown ping (msg_id %l, ping_id %l)', + msgId, + pingId + ) + return + } + + if (info._ !== 'ping') { + this.log.warn( + 'received pong to %s query, not ping (msg_id %l, ping_id %l)', + info._, + msgId, + pingId + ) + return + } + + if (info.pingId.neq(pingId)) { + this.log.warn( + 'received pong to %l, but expected ping_id = %l (got %l)', + msgId, + info.pingId, + pingId + ) + } + + const rtt = Date.now() - this._lastPingTime + this._lastPingRtt = rtt + + if (info.containerId.neq(msgId)) { + this._onMessageAcked(info.containerId) + } + + this.log.debug( + 'received pong: msg_id %l, ping_id %l, rtt = %dms', + msgId, + pingId, + rtt + ) + this._resetLastPing() + } + + private _onBadServerSalt(msg: mtp.RawMt_bad_server_salt): void { + this._session.serverSalt = msg.newServerSalt + + this._onMessageFailed(msg.badMsgId, 'bad_server_salt') + } + + private _onBadMsgNotification( + msgId: Long, + msg: mtp.RawMt_bad_msg_notification + ): void { + switch (msg.errorCode) { + case 16: + case 17: + case 20: { + if (msg.errorCode !== 20) { + // msg_id is either too high or too low + // code 20 means msg_id is too old, + // we just need to resend the message + const serverTime = msgId.low >>> 0 + const timeOffset = + Math.floor(Date.now() / 1000) - serverTime + + this._session._timeOffset = timeOffset + this.log.debug( + 'server time: %d, corrected offset to %d', + serverTime, + timeOffset + ) + } + + this._onMessageFailed( + msg.badMsgId, + `bad_msg_notification ${msg.errorCode}` + ) + break + } + default: + // something went very wrong, we need to reset the session + this.log.error( + 'received bad_msg_notification for msg_id = %l, code = %d. session will be reset' + ) + this._resetSession() + break + } + } + + private _onNewSessionCreated({ + firstMsgId, + serverSalt, + uniqueId, + }: mtp.RawMt_new_session_created): void { + if (uniqueId.eq(this._lastSessionCreatedUid)) { + this.log.debug( + 'received new_session_created with the same uid = %l, ignoring', + uniqueId + ) + return + } + + if ( + !this._lastSessionCreatedUid.isZero() && + !this.params.disableUpdates + ) { + // force the client to fetch missed updates + // when _lastSessionCreatedUid == 0, the connection has + // just been established, and the client will fetch them anyways + this.emit('update', { _: 'updatesTooLong' }) + } + + this._session.serverSalt = serverSalt + + this.log.debug( + 'received new_session_created, uid = %l, first msg_id = %l', + uniqueId, + firstMsgId + ) + + for (const msgId of this._pendingMessages.keys()) { + const val = this._pendingMessages.get(msgId)! + + if (val._ === 'container') { + if (msgId.lt(firstMsgId)) { + // all messages in this container will be resent + // info about this container is no longer needed + this._pendingMessages.delete(msgId) + } + return + } + + const containerId = + val._ === 'rpc' ? val.rpc.containerId || msgId : val.containerId + + if (containerId.lt(firstMsgId)) { + this._onMessageFailed(msgId, 'new_session_created', true) + } + } + } + + private _onMessageInfo( + msgId: Long, + status: number, + answerMsgId: Long + ): void { + if (!msgId.isZero()) { + const info = this._pendingMessages.get(msgId) + if (!info) { + this.log.info( + 'received message info about unknown message %l', + msgId + ) + return + } + + switch (status & 7) { + case 1: + case 2: + case 3: + // message wasn't received by the server + this._onMessageFailed(msgId, `message info state ${status}`) + break + case 0: + if (!answerMsgId.isZero()) { + this.log.warn( + 'received message info with status = 0: msg_id = %l, status = %d, ans_id = %l', + msgId, + status, + answerMsgId + ) + return this._onMessageFailed( + msgId, + `message info state = 0, ans_id = 0` + ) + } + // fallthrough + case 4: + this._onMessageAcked(msgId) + break + } + } + + if ( + !answerMsgId.isZero() && + !this._recentIncomingMsgIds.has(answerMsgId) + ) { + this.log.debug( + 'received message info for %l, but answer (%l) was not received yet', + msgId, + answerMsgId + ) + this._queuedResendReq.push(answerMsgId) + this._flushTimer.emitWhenIdle() + return + } + + this.log.debug( + 'received message info for %l, and answer (%l) was already received', + msgId, + answerMsgId + ) + } + + private _onMessagesInfo(msgIds: Long[], info: Buffer): void { + if (msgIds.length !== info.length) { + this.log.warn( + 'messages state info was invalid: msg_ids.length !== info.length' + ) + } + + for (let i = 0; i < msgIds.length; i++) { + this._onMessageInfo(msgIds[i], info[i], Long.ZERO) + } + } + + private _onMsgsStateInfo(msg: mtp.RawMt_msgs_state_info): void { + const info = this._pendingMessages.get(msg.reqMsgId) + + if (!info) { + this.log.warn( + 'received msgs_state_info to unknown request %l', + msg.reqMsgId + ) + return + } + + if (info._ !== 'state') { + this.log.warn( + 'received msgs_state_info to %s query %l', + info._, + msg.reqMsgId + ) + return + } + + this._onMessagesInfo(info.msgIds, msg.info) + } + + private _enqueueRpc(rpc: PendingRpc, force?: boolean) { + // already queued or cancelled + if ((!force && !rpc.sent) || rpc.cancelled) return + + rpc.sent = false + rpc.containerId = undefined + this.log.debug( + 'enqueued %s for sending (msg_id = %s)', + rpc.method, + rpc.msgId || 'n/a' + ) + this._queuedRpc.pushBack(rpc) + + this._flushTimer.emitWhenIdle() + } + + _resetSession(): void { + this._queuedDestroySession.push(this._session._sessionId) + + this.reconnect() + this._session.changeSessionId() + this.log.debug('session reset, new sid = %l', this._session._sessionId) + + // once we receive new_session_created, all pending messages will be resent. + // clear getState/resend queues because they are not needed anymore + this._queuedStateReq.length = 0 + this._queuedResendReq.length = 0 + this._flushTimer.reset() + } + + private _sendAck(msgId: Long): void { + if (this._queuedAcks.length === 0) { + this._flushTimer.emitBeforeNext(30000) + } + + this._queuedAcks.push(msgId) + + if (this._queuedAcks.length >= 100) { + this._flushTimer.emitNow() + } + } + + sendRpc( + request: T, + stack?: string, + timeout?: number + ): Promise { + if (this._usable && this.params.inactivityTimeout) + this._rescheduleInactivity() + + if (!stack && this.params.niceStacks !== false) { + stack = new Error().stack + } + + const method = request._ + + let obj: tl.TlObject = request + let initConn = false + + if (this.params.disableUpdates) { + obj = { + _: 'invokeWithoutUpdates', + query: obj, + } + } + + if (!this._initConnectionCalled) { + // we will wrap every rpc call with initConnection + // until some of the requests wrapped with it is + // either acked or returns rpc_result + + this.log.debug( + 'wrapping %s with initConnection, layer: %d', + method, + this.params.layer + ) + obj = { + _: 'invokeWithLayer', + layer: this.params.layer, + query: { + ...this.params.initConnection, + query: obj, + }, + } + initConn = true + } + + this.log.verbose('>>> %j', obj) + + let content = TlBinaryWriter.serializeObject(this._writerMap,obj) + + if (content.length > 1044404) { + // if you send larger payloads, telegram will just close connection, + // and since we resend them, it will get resent after reconnection and + // that will be an endless loop of reconnections. we don't want that, + // and payloads this large are usually a sign of an error in the code. + throw new Error(`Payload is too big (${content.length} > 1044404)`) + } + + // gzip + let shouldGzip = content.length > 128 + if (content.length > 16384) { + // test compression ratio for the middle part + // if it is less than 0.9, then try to compress the whole request + + const middle = ~~((content.length - 1024) / 2) + const gzipped = gzipDeflate( + content.slice(middle, middle + 1024), + 0.9 + ) + + if (!gzipped) shouldGzip = false + } + + if (shouldGzip) { + const gzipped = gzipDeflate(content, 0.9) + + if (gzipped) { + this.log.debug( + 'gzipped %s (%db -> %db)', + method, + content.length, + gzipped.length + ) + + content = gzipped + } else { + shouldGzip = false + } + } + + const pending: PendingRpc = { + method, + promise: undefined as any, // because we need the object to make a promise + data: content, + stack, + // we will need to know size of gzip_packed overhead in _flush() + gzipOverhead: shouldGzip + ? 4 + TlSerializationCounter.countBytesOverhead(content.length) + : 0, + initConn, + + // setting them as well so jit can optimize stuff + sent: undefined, + getState: undefined, + msgId: undefined, + seqNo: undefined, + containerId: undefined, + acked: undefined, + cancelled: undefined, + timeout: undefined, + } + + const promise = createCancellablePromise( + this._cancelRpc.bind(this, pending) + ) + pending.promise = promise + + if (timeout) { + pending.timeout = setTimeout( + this._cancelRpc, + timeout, + pending, + true + ) + } + + this._enqueueRpc(pending, true) + + return promise + } + + private _cancelRpc(rpc: PendingRpc, onTimeout = false): void { + if (rpc.cancelled && !onTimeout) { + throw new Error('RPC was already cancelled') + } + + if (!onTimeout && rpc.timeout) { + clearTimeout(rpc.timeout) + } + + if (onTimeout) { + const error = new RpcTimeoutError() + if (this.params.niceStacks !== false) { + makeNiceStack(error, rpc.stack!, rpc.method) + } + + rpc.promise.reject(error) + } + + rpc.cancelled = true + if (rpc.msgId) { + this._queuedCancelReq.push(rpc.msgId) + this._flushTimer.emitWhenIdle() + } else { + // in case rpc wasn't sent yet (or had some error), + // we can simply remove it from queue + this._queuedRpc.remove(rpc) + } + } + + private _flush(): void { + if (!this._session.authorized || this._current429Timeout) { + // it will be flushed once connection is usable + return + } + + try { + this._doFlush() + } catch (e) { + this.log.error('flush error: %s', e.stack) + // should not happen unless there's a bug in the code + } + + // schedule next flush + // if there are more queued requests, flush immediately + // (they likely didn't fit into one message) + if ( + this._queuedRpc.length || + this._queuedAcks.length || + this._queuedStateReq.length || + this._queuedResendReq.length + ) { + this._flush() + } else { + this._flushTimer.emitBefore(this._lastPingTime + 60000) + } + } + + private _doFlush(): void { + this.log.debug( + 'flushing send queue. queued rpc: %d', + this._queuedRpc.length + ) + + // oh bloody hell mate + + // total size & count + let packetSize = 0 + let messageCount = 0 + // size & msg count that count towards container limit + // these will be added to total later + let containerMessageCount = 0 + let containerSize = 0 + + let ackRequest: Buffer | null = null + let ackMsgIds: Long[] | null = null + + let pingRequest: Buffer | null = null + let pingId: Long | null = null + let pingMsgId: Long | null = null + + let getStateRequest: Buffer | null = null + let getStateMsgId: Long | null = null + let getStateMsgIds: Long[] | null = null + + let resendRequest: Buffer | null = null + let resendMsgId: Long | null = null + let resendMsgIds: Long[] | null = null + + let cancelRpcs: Long[] | null = null + let destroySessions: Long[] | null = null + + const now = Date.now() + + if (this._queuedAcks.length) { + let acks = this._queuedAcks + if (acks.length > 8192) { + this._queuedAcks = acks.slice(8192) + acks = acks.slice(0, 8192) + } else { + this._queuedAcks = [] + } + + const obj: mtp.RawMt_msgs_ack = { + _: 'mt_msgs_ack', + msgIds: acks, + } + ackMsgIds = obj.msgIds + + ackRequest = TlBinaryWriter.serializeObject(this._writerMap, obj) + packetSize += ackRequest.length + 16 + messageCount += 1 + } + + const getStateTime = now + 1500 + + if (now - this._lastPingTime > 60000) { + if (!this._lastPingMsgId.isZero()) { + this.log.warn( + "didn't receive pong for previous ping (msg_id = %l)", + this._lastPingMsgId + ) + this._pendingMessages.delete(this._lastPingMsgId) + } + + pingId = randomLong() + const obj: mtp.RawMt_ping = { + _: 'mt_ping', + pingId, + } + + this._lastPingTime = Date.now() + + pingRequest = TlBinaryWriter.serializeObject(this._writerMap, obj) + containerSize += pingRequest.length + 16 + containerMessageCount += 1 + } + + { + if (this._queuedStateReq.length) { + let ids = this._queuedStateReq + if (ids.length > 8192) { + this._queuedStateReq = ids.slice(8192) + ids = ids.slice(0, 8192) + } else { + this._queuedStateReq = [] + } + getStateMsgIds = ids + } + + const idx = this._getStateSchedule.index( + { getState: now } as any, + true + ) + if (idx > 0) { + const toGetState = this._getStateSchedule.raw.splice(0, idx) + if (!getStateMsgIds) getStateMsgIds = [] + toGetState.forEach((it) => getStateMsgIds!.push(it.msgId!)) + } + + if (getStateMsgIds) { + const obj: mtp.RawMt_msgs_state_req = { + _: 'mt_msgs_state_req', + msgIds: getStateMsgIds, + } + + getStateRequest = TlBinaryWriter.serializeObject(this._writerMap, obj) + packetSize += getStateRequest.length + 16 + messageCount += 1 + } + } + + if (this._queuedResendReq.length) { + resendMsgIds = this._queuedResendReq + if (resendMsgIds.length > 8192) { + this._queuedResendReq = resendMsgIds.slice(8192) + resendMsgIds = resendMsgIds.slice(0, 8192) + } else { + this._queuedResendReq = [] + } + + const obj: mtp.RawMt_msg_resend_req = { + _: 'mt_msg_resend_req', + msgIds: resendMsgIds, + } + + resendRequest = TlBinaryWriter.serializeObject(this._writerMap, obj) + packetSize += resendRequest.length + 16 + messageCount += 1 + } + + if (this._queuedCancelReq.length) { + containerMessageCount += this._queuedCancelReq.length + containerSize += this._queuedCancelReq.length * 28 + cancelRpcs = this._queuedCancelReq + this._queuedCancelReq = [] + } + + if (this._queuedDestroySession.length) { + containerMessageCount += this._queuedCancelReq.length + containerSize += this._queuedCancelReq.length * 28 + destroySessions = this._queuedDestroySession + this._queuedDestroySession = [] + } + + let forceContainer = false + const rpcToSend: PendingRpc[] = [] + while ( + this._queuedRpc.length && + containerSize < 32768 && // 2^15 + containerMessageCount < 1020 + ) { + const msg = this._queuedRpc.popFront()! + if (msg.cancelled) continue + + // note: we don't check for <2^15 here + // this is not documented, but large requests + // (like upload.saveFilePart) *may* exceed that limit + + rpcToSend.push(msg) + containerSize += msg.data.length + 16 + if (msg.gzipOverhead) containerSize += msg.gzipOverhead + + // if message was already assigned a msg_id, + // we must wrap it in a container with a newer msg_id + if (msg.msgId) forceContainer = true + } + + packetSize += containerSize + messageCount += containerMessageCount + rpcToSend.length + + if (!messageCount) { + this.log.debug('flush failed: nothing to flush') + return + } + + const useContainer = forceContainer || messageCount > 1 + if (useContainer) packetSize += 24 // 8 (msg_container) + 16 (mtproto header) + + const writer = TlBinaryWriter.alloc(this._writerMap, packetSize) + if (useContainer) { + // leave bytes for mtproto header (we'll write it later, + // since we need seqno and msg_id to be larger than the content) + writer.pos += 16 + writer.uint(0x73f1f8dc) // msg_container + writer.uint(messageCount) + } + + const otherPendings: Exclude< + PendingMessage, + { _: 'rpc' | 'container' } + >[] = [] + + if (ackRequest) + this._registerOutgoingMsgId( + this._session.writeMessage(writer, ackRequest) + ) + + if (pingRequest) { + pingMsgId = this._registerOutgoingMsgId( + this._session.writeMessage(writer, pingRequest) + ) + this._lastPingMsgId = pingMsgId + const pingPending: PendingMessage = { + _: 'ping', + pingId: pingId!, + containerId: pingMsgId, + } + this._pendingMessages.set(pingMsgId, pingPending) + otherPendings.push(pingPending) + } + + if (getStateRequest) { + getStateMsgId = this._registerOutgoingMsgId( + this._session.writeMessage(writer, getStateRequest) + ) + const getStatePending: PendingMessage = { + _: 'state', + msgIds: getStateMsgIds!, + containerId: getStateMsgId, + } + this._pendingMessages.set(getStateMsgId, getStatePending) + otherPendings.push(getStatePending) + } + + if (resendRequest) { + resendMsgId = this._registerOutgoingMsgId( + this._session.writeMessage(writer, resendRequest) + ) + const resendPending: PendingMessage = { + _: 'resend', + msgIds: resendMsgIds!, + containerId: resendMsgId, + } + this._pendingMessages.set(resendMsgId, resendPending) + otherPendings.push(resendPending) + } + + if (cancelRpcs) { + cancelRpcs.forEach((msgId) => { + const cancelMsgId = this._registerOutgoingMsgId( + this._session.writeMessage(writer, { + _: 'mt_rpc_drop_answer', + reqMsgId: msgId, + }) + ) + + const pending: PendingMessage = { + _: 'cancel', + msgId, + containerId: cancelMsgId, + } + this._pendingMessages.set(cancelMsgId, pending) + otherPendings.push(pending) + }) + } + + if (destroySessions) { + destroySessions.forEach((sessionId) => { + const msgId = this._registerOutgoingMsgId( + this._session.writeMessage(writer, { + _: 'mt_destroy_session', + sessionId, + }) + ) + + const pending: PendingMessage = { + _: 'destroy_session', + sessionId, + containerId: msgId, + } + this._pendingMessages.set(msgId, pending) + otherPendings.push(pending) + }) + } + + for (let i = 0; i < rpcToSend.length; i++) { + const msg = rpcToSend[i] + // not using writeMessage here because we also need seqNo, and + // i dont want to also return seqNo there because that would mean + // extra object overhead + + if (!msg.msgId) { + const msgId = this._session.getMessageId() + const seqNo = this._session.getSeqNo() + + this.log.debug( + '%s: msg_id assigned %l, seqno: %d', + msg.method, + msgId, + seqNo + ) + + msg.msgId = msgId + msg.seqNo = seqNo + this._pendingMessages.set(msgId, { + _: 'rpc', + rpc: msg, + }) + } else { + this.log.debug( + '%s: msg_id already assigned, reusing %l, seqno: %d', + msg.method, + msg.msgId, + msg.seqNo + ) + } + + // (re-)schedule get_state if needed + if (msg.getState) { + this._getStateSchedule.remove(msg) + } + if (!msg.acked) { + msg.getState = getStateTime + this._getStateSchedule.insert(msg) + } + + writer.long(this._registerOutgoingMsgId(msg.msgId)) + writer.uint(msg.seqNo!) + if (msg.gzipOverhead) { + writer.uint(msg.data.length + msg.gzipOverhead) + writer.uint(0x3072cfa1) // gzip_packed#3072cfa1 + writer.bytes(msg.data) + } else { + writer.uint(msg.data.length) + writer.raw(msg.data) + } + + msg.sent = true + } + + if (useContainer) { + // we now need to assign the container msg_id and seqno + // we couldn't have assigned them earlier because mtproto + // requires them to be >= than the contained messages + + // writer.pos is expected to be packetSize + + const containerId = this._session.getMessageId() + writer.pos = 0 + writer.long(this._registerOutgoingMsgId(containerId)) + writer.uint(this._session.getSeqNo(false)) + writer.uint(packetSize - 16) + writer.pos = packetSize + + const msgIds = [] + + for (let i = 0; i < rpcToSend.length; i++) { + const msg = rpcToSend[i] + msg.containerId = containerId + msgIds.push(msg.msgId!) + } + + if (otherPendings.length) { + otherPendings.forEach((msg) => { + msgIds.push(msg.containerId) + msg.containerId = containerId + }) + } + + this._pendingMessages.set(containerId, { _: 'container', msgIds }) + } + + const result = writer.result() + // probably the easiest way lol + const rootMsgId = new Long(result.readInt32LE(), result.readInt32LE(4)) + + this.log.debug( + 'sending %d messages: size = %db, acks = %d (msg_id = %s), ping = %s (msg_id = %s), state_req = %s (msg_id = %s), resend = %s (msg_id = %s), rpc = %s, container = %s, root msg_id = %l', + messageCount, + packetSize, + ackMsgIds?.length || 'false', + ackMsgIds?.map((it) => it.toString()), + !!pingRequest, + pingMsgId, + getStateMsgIds?.map((it) => it.toString()) || 'false', + getStateMsgId, + resendMsgIds?.map((it) => it.toString()) || 'false', + resendMsgId, + rpcToSend.map((it) => it.method), + useContainer, + rootMsgId + ) + + this._session + .encryptMessage(result) + .then((enc) => this.send(enc)) + .catch((err) => { + this.log.error( + 'error while sending pending messages (root msg_id = %l): %s', + rootMsgId, + err.stack + ) + + // put acks in the front so they are the first to be sent + if (ackMsgIds) this._queuedAcks.splice(0, 0, ...ackMsgIds) + this._onMessageFailed(rootMsgId, 'unknown error') + }) + } +} diff --git a/packages/core/src/network/telegram-connection.ts b/packages/core/src/network/telegram-connection.ts deleted file mode 100644 index 5e1dd537..00000000 --- a/packages/core/src/network/telegram-connection.ts +++ /dev/null @@ -1,739 +0,0 @@ -import { - PersistentConnection, - PersistentConnectionParams, -} from './persistent-connection' -import { TransportError } from './transports' -import { tl } from '@mtcute/tl' -import { doAuthorization } from './authorization' -import { MtprotoSession } from './mtproto-session' -import { BinaryWriter } from '../utils/binary/binary-writer' -import bigInt, { BigInteger } from 'big-integer' -import { getRandomInt } from '../utils/misc-utils' -import { - ControllablePromise, - createControllablePromise, -} from '../utils/controllable-promise' -import { debounce } from '../utils/function-utils' -import { bufferToBigInt, ulongToLong } from '../utils/bigint-utils' -import { randomBytes } from '../utils/buffer-utils' -import { - BadRequestError, - createRpcErrorFromTl, - RpcError, - RpcTimeoutError, -} from '@mtcute/tl/errors' -import { LruStringSet } from '../utils/lru-string-set' -import { Logger } from '../utils/logger' - -function makeNiceStack(error: RpcError, stack: string, method?: string) { - error.stack = `${error.constructor.name} (${error.code} ${error.text}): ${ - error.message - }\n at ${method}\n${stack.split('\n').slice(2).join('\n')}` -} - -export interface TelegramConnectionParams extends PersistentConnectionParams { - initConnection: tl.RawInitConnectionRequest - inactivityTimeout?: number - niceStacks?: boolean - layer: number -} - -const messageHandlers: Record = { - // because keyof does not return private fields :shrug: - - mt_rpc_result: '_handleRpcResult', - mt_msg_container: '_handleContainer', - mt_pong: '_handlePong', - mt_bad_server_salt: '_handleBadServerSalt', - mt_bad_msg_notification: '_handleBadMsgNotification', - mt_msgs_ack: '_handleAcks', - mt_new_session_created: '_handleNewSessionCreated', - mt_msg_detailed_info: '_handleDetailedInfo', - mt_msg_new_detailed_info: '_handleNewDetailedInfo', - mt_future_salts: '_handleFutureSalts', - mt_msg_state_req: '_handleStateForgotten', - mt_msg_resend_req: '_handleStateForgotten', -} - -interface PendingMessage { - // name of the method which is being called - method: string - stack?: string - promise: ControllablePromise - message: Buffer - // timeout after which the call will be cancelled - cancel?: NodeJS.Timeout -} - -// TODO: error handling basically everywhere, most importantly (de-)serialization errors -// noinspection JSUnusedLocalSymbols -export class TelegramConnection extends PersistentConnection { - readonly params!: TelegramConnectionParams - - private readonly _mtproto: MtprotoSession - - private _timeOffset = 0 - private _lastMessageId = bigInt.zero - private _seqNo = 0 - - _initConnectionCalled = false - private _initConnectionPromise?: Promise - - // set of messages to be sent once there's a usable connection - private _sendOnceUsable: PendingMessage[] = [] - // currently active rpc calls. TODO: somehow handle resending? mtproto really does stink - private _pendingRpcCalls: Record = {} - - private _pendingAcks: BigInteger[] = [] - private _pingInterval: NodeJS.Timeout | null = null - private _pendingPing: BigInteger | null = null - private _pendingPingMsgId: BigInteger | null = null - - private _recentUpdateMsgIds = new LruStringSet(100) - - authKey: Buffer | null = null - - private _sendPendingAcks = debounce(async () => { - if (this._destroyed) return - if (!this._pendingAcks.length) return - if (!this._mtproto.authorized) return this._sendPendingAcks() // reschedule - - await this.sendEncryptedMessage({ - _: 'mt_msgs_ack', - msgIds: this._pendingAcks, - }) - this._pendingAcks = [] - }, 500) - - constructor(params: TelegramConnectionParams, log: Logger) { - super(params, log) - this._mtproto = new MtprotoSession(this.params.crypto, log.create('session')) - } - - onTransportClose(): void { - super.onTransportClose() - this.emit('disconnect') - - this.reset() - } - - destroy(): void { - super.destroy() - this.reset(true) - } - - reset(forever = false): void { - this._pendingAcks = [] - if (this._pingInterval) clearInterval(this._pingInterval) - this._pingInterval = null - this._pendingPing = null - this._lastMessageId = bigInt.zero - this._seqNo = 0 - this._initConnectionCalled = false - - if (forever) { - // if connection will be used later (i.e. resetting for reconnection), - // there's no need to cancel pending rpc calls - // (and even the opposite, we want them to be sent once connected) - ;[ - ...Object.values(this._pendingRpcCalls), - ...this._sendOnceUsable, - ].forEach((it) => - it.promise.reject(new Error('Connection destroyed')) - ) - this._mtproto.reset() - this.removeAllListeners() - } - } - - changeDc(dc: tl.RawDcOption): void { - this._mtproto.reset() - this.authKey = null - this.params.dc = dc - this.reconnect() - } - - protected onError(error: Error): void { - // https://core.telegram.org/mtproto/mtproto-_transports#_transport-errors - if (error instanceof TransportError) { - this.log.error('transport error %d (dc %d)', error.code, this.params.dc.id) - if (error.code === 404) { - this._mtproto.reset() - this.emit('key-change', null) - this._authorize() - return - } - } - this.emit('error', error) - } - - protected async onConnected(): Promise { - if (this.authKey) { - await this._mtproto.setupKeys(this.authKey) - - this.onConnectionUsable() - } else { - this._authorize() - } - } - - protected onConnectionUsable(): void { - super.onConnectionUsable() - - Object.entries(this._pendingRpcCalls).forEach(([id, it]) => - this._resend(it, id) - ) - - const sendOnceUsable = this._sendOnceUsable - // this way in case connection is still invalid (somehow??) messages aren't lost - this._sendOnceUsable = [] - sendOnceUsable.forEach((it) => this._resend(it)) - - this._pingInterval = setInterval(() => { - if (this._pendingPing === null) { - this._pendingPing = ulongToLong( - bufferToBigInt(randomBytes(8), 0, 8, true) - ) - this.sendEncryptedMessage( - { _: 'mt_ping', pingId: this._pendingPing }, - false - ) - .then((id) => { - this._pendingPingMsgId = id - }) - .catch((err) => this.emit('error', err)) - } else { - // connection is dead, thank you durov - this.reconnect() - } - }, 60_000) - } - - protected async onMessage(data: Buffer): Promise { - if (!this._mtproto) { - // if a message is received before authorization, - // either the server is misbehaving, - // or there was a problem with authorization. - this.log.warn('warn: received message before authorization') - return - } - - try { - const message = await this._mtproto.decryptMessage(data) - if (!message) return - - await this._handleMessage(message.content, message.messageId) - } catch (err) { - this.emit('error', err) - } - } - - private _sendAckLater(id: BigInteger) { - this._pendingAcks.push(id) - this._sendPendingAcks() - } - - private _resend(it: PendingMessage, id?: string): void { - this.log.debug('resending %s', it.method) - this._sendBufferForResult(it).catch(it.promise.reject) - if (it.cancel) clearTimeout(it.cancel) - if (id) delete this._pendingRpcCalls[id] - } - - private _authorize(): void { - doAuthorization(this, this.params.crypto) - .then(async ([authKey, serverSalt, timeOffset]) => { - await this._mtproto.setupKeys(authKey) - this._mtproto.serverSalt = serverSalt - this._timeOffset = timeOffset - this.authKey = authKey - - this.emit('key-change', authKey) - - this.onConnectionUsable() - }) - .catch((err) => { - this.log.error('Authorization error: %s', err.message) - this.onError(err) - this.reconnect() - }) - } - - private async _handleMessage( - message: tl.TlObject, - messageId: BigInteger - ): Promise { - if (messageId.isEven()) { - this.log.warn( - 'warn: ignoring message with invalid messageId = %s (is even)', - messageId - ) - return - } - - this._sendAckLater(messageId) - - if (message._ in messageHandlers) { - await (this as any)[messageHandlers[message._]](message, messageId) - return - } - - if (tl.isAnyUpdates(message)) { - if (this._usable && this.params.inactivityTimeout) - this._rescheduleInactivity() - - this._recentUpdateMsgIds.add(messageId.toString(16)) - this.emit('update', message) - return - } - - this.log.warn('unknown message received: %o', message) - } - - private _handleContainer(message: tl.mtproto.RawMsg_container): void { - message.messages.forEach((msg) => - this._handleMessage(msg.body, msg.msgId) - ) - } - - private _handleRpcResult(message: tl.mtproto.RawRpc_result): void { - if (this._usable && this.params.inactivityTimeout) - this._rescheduleInactivity() - - const reqMsgId = message.reqMsgId.toString(16) - const pending = this._pendingRpcCalls[reqMsgId] - if (!pending) { - this.log.warn('received rpc result for unknown message %s', reqMsgId) - return - } - this.log.request('<<< (%s) %j', pending.method, message.result) - - if (message.result._ === 'mt_rpc_error') { - const error = createRpcErrorFromTl(message.result) - if (this.params.niceStacks !== false) { - makeNiceStack(error, pending.stack!, pending.method) - } - pending.promise.reject(error) - } else { - if (pending.cancel) clearTimeout(pending.cancel) - pending.promise.resolve(message.result) - } - - delete this._pendingRpcCalls[reqMsgId] - } - - private _handlePong(message: tl.mtproto.RawPong): void { - const msgId = message.msgId.toString(16) - - this.log.debug('handling pong for %s (ping id %s)', msgId, message.pingId) - - if (this._pendingPing && message.pingId.eq(this._pendingPing)) { - this._pendingPing = null - this._pendingPingMsgId = null - return - } - - if (this._pendingRpcCalls[msgId]) { - const pending = this._pendingRpcCalls[msgId] - if (pending.cancel) clearTimeout(pending.cancel) - - pending.promise.resolve(message) - delete this._pendingRpcCalls[msgId] - } else { - this.log.warn('pong to unknown ping %o', message) - } - } - - private async _handleBadServerSalt( - message: tl.mtproto.RawBad_server_salt - ): Promise { - const badMsgId = message.badMsgId.toString(16) - - this.log.debug( - 'handling bad_server_salt for msg %s, new salt: %h', - badMsgId, - message.newServerSalt - ) - - this._mtproto.serverSalt = message.newServerSalt - - if (this._pendingRpcCalls[badMsgId]) { - this._resend(this._pendingRpcCalls[badMsgId], badMsgId) - } else if ( - this._pendingPingMsgId && - this._pendingPingMsgId.eq(message.badMsgId) - ) { - // acknowledge the ping was received. - // no need to re-send it - this._pendingPing = null - this._pendingPingMsgId = null - } else { - this.log.warn('bad_server_salt to unknown message %o', message) - } - } - - private async _handleBadMsgNotification( - message: tl.mtproto.RawBad_msg_notification, - messageId: BigInteger - ): Promise { - const badMsgId = message.badMsgId.toString(16) - - this.log.debug('handling bad_msg_notification, code: %d', message.errorCode) - - if (message.errorCode === 16 || message.errorCode === 17) { - // msg_id is either too high or too low - const serverTime = bigInt(messageId).shiftRight(32).toJSNumber() - this._timeOffset = Math.floor(Date.now() / 1000) - serverTime - } else if (message.errorCode === 32) { - // meg_seqno is too low, so, like telethon, __just pump it up by some "large" amount__ - this._seqNo += 64 - } else if (message.errorCode === 33) { - // does not seem to happen, but whatever - this._seqNo -= 16 - } else if (this._pendingRpcCalls[badMsgId]) { - const pending = this._pendingRpcCalls[badMsgId] - - const error = new BadRequestError( - 'BAD_REQUEST', - 'bad_msg_notification ' + message.errorCode - ) - if (this.params.niceStacks !== false) { - makeNiceStack(error, pending.stack!, pending.method) - } - pending.promise.reject(error) - delete this._pendingRpcCalls[badMsgId] - return - } - - if (this._pendingRpcCalls[badMsgId]) { - this._resend(this._pendingRpcCalls[badMsgId], badMsgId) - } else { - this.log.warn('bad_msg_notification to unknown message %s', badMsgId) - } - } - - private _handleAcks(message: tl.mtproto.RawMsgs_ack) { - // according to telethon, we should only care about acks to auth.logOut. - message.msgIds.forEach((idLong) => { - const id = idLong.toString(16) - if (this._pendingRpcCalls[id]?.method === 'auth.logOut') { - const pending = this._pendingRpcCalls[id] - if (pending.cancel) clearTimeout(pending.cancel) - - pending.promise.resolve(true) - delete this._pendingRpcCalls[id] - } - }) - } - - private _handleNewSessionCreated( - message: tl.mtproto.RawNew_session_created - ): void { - const firstMsgId = message.firstMsgId - const firstMsgIdStr = firstMsgId.toString(16) - - for (const [msgId, info] of Object.entries(this._pendingRpcCalls)) { - if ( - // almost always true. integers of the same string length - // can be compared alphanumerically, without the need to parse - // the integer from the string - (msgId.length === firstMsgIdStr.length && - msgId > firstMsgIdStr) || - firstMsgId.lt(bigInt(msgId, 16)) - ) { - this._resend(info, msgId) - } - } - - this.log.debug( - 'handling new_session_created (sid = %h), salt: %h', - this._mtproto._sessionId, - message.serverSalt - ) - this._mtproto.serverSalt = message.serverSalt - } - - private _handleDetailedInfo( - message: tl.mtproto.RawMsg_detailed_info - ): void { - this.log.debug( - 'handling msg_detailed_info (sid = %h), msgId = %s', - this._mtproto._sessionId, - message.answerMsgId - ) - - const msgId = message.msgId.toString(16) - if (!(msgId in this._pendingRpcCalls)) { - // received - this._sendAckLater(message.answerMsgId) - } else { - // not received, request the response - this.sendEncryptedMessage({ - _: 'mt_msg_resend_req', - msgIds: [message.answerMsgId], - }).catch(() => { - /* no-op */ - }) - } - } - - private _handleNewDetailedInfo( - message: tl.mtproto.RawMsg_new_detailed_info - ): void { - this.log.debug( - 'handling msg_new_detailed_info (sid = %h), msgId = %s', - this._mtproto._sessionId, - message.answerMsgId - ) - - const received = this._recentUpdateMsgIds.has( - message.answerMsgId.toString(16) - ) - if (received) { - this._sendAckLater(message.answerMsgId) - } else { - this.sendEncryptedMessage({ - _: 'mt_msg_resend_req', - msgIds: [message.answerMsgId], - }).catch(() => { - /* no-op */ - }) - } - } - - private _handleFutureSalts(message: tl.mtproto.RawFuture_salts): void { - // TODO actually handle these salts - this.log.debug( - 'handling future_salts (sid = %h), msgId = %s, %d salts', - this._mtproto._sessionId, - message.reqMsgId, - message.salts.length - ) - - const reqMsgId = message.reqMsgId.toString(16) - - if (reqMsgId in this._pendingRpcCalls) { - const pending = this._pendingRpcCalls[reqMsgId] - if (pending.cancel) clearTimeout(pending.cancel) - - pending.promise.resolve(message) - } - } - - private _handleStateForgotten( - message: tl.mtproto.RawMsgs_state_req | tl.mtproto.RawMsg_resend_req, - messageId: BigInteger - ): void { - const info = Buffer.alloc(message.msgIds.length) - for (let i = 0; i < info.length; i++) { - info[i] = 0x01 - } - - this.sendEncryptedMessage({ - _: 'mt_msgs_state_info', - reqMsgId: messageId, - info, - }).catch(() => { - /* no-op */ - }) - } - - async sendEncryptedMessage( - message: tl.TlObject | Buffer, - isContentRelated = true - ): Promise { - if (!this._mtproto.authorized) throw new Error('Keys are not set up!') - - const messageId = this._getMessageId() - const seqNo = this._getSeqNo(isContentRelated) - - const encrypted = await this._mtproto.encryptMessage( - message, - messageId, - seqNo - ) - await this.send(encrypted) - - return messageId - } - - async _sendBufferForResult(message: PendingMessage): Promise - async _sendBufferForResult( - method: string, - message: Buffer, - stack?: string, - timeout?: number - ): Promise - async _sendBufferForResult( - method: string | PendingMessage, - message?: Buffer, - stack?: string, - timeout?: number - ): Promise { - if ( - typeof method === 'string' && - this.params.niceStacks !== false && - !stack - ) { - stack = new Error().stack - } - - const content = typeof method === 'string' ? message! : method.message - if (content.length > 1044404) { - // if you send larger payloads, telegram will just close connection, - // and since we resend them, it will get resent after reconnection and - // that will be an endless loop of reconnections. we don't want that, - // and payloads this large are usually a sign of an error in the code. - const err = new Error( - `Payload is too big (${content.length} > 1044404)` - ) - if (typeof method === 'string') { - throw err - } else { - // shouldn't happen, but whatever - method.promise.reject(err) - } - } - - const promise = - typeof method === 'string' - ? createControllablePromise() - : method.promise - - const pending = - typeof method === 'string' - ? { method, promise, message: message!, stack } - : method - - if (!this._mtproto.authorized) { - this._sendOnceUsable.push(pending) - return promise - } - - const messageId = this._getMessageId() - const messageIdStr = messageId.toString(16) - const seqNo = this._getSeqNo(true) - - this._pendingRpcCalls[messageIdStr] = pending - - if (timeout) { - pending.cancel = setTimeout(() => { - const pending = this._pendingRpcCalls[messageIdStr] - if (pending) { - this.sendEncryptedMessage({ - _: 'mt_rpcDropAnswer', - reqMsgId: messageId, - }).catch(() => { - /* no-op */ - }) - - const error = new RpcTimeoutError(timeout) - if (this.params.niceStacks !== false) { - makeNiceStack(error, pending.stack!, pending.method) - } - - pending.promise.reject(error) - delete this._pendingRpcCalls[messageIdStr] - } - }, timeout) - } - - const encrypted = await this._mtproto.encryptMessage( - content, - messageId, - seqNo - ) - await this.send(encrypted) - - return promise - } - - async sendForResult( - message: T, - stack?: string, - timeout?: number - ): Promise { - if (this._usable && this.params.inactivityTimeout) - this._rescheduleInactivity() - - this.log.request('>>> %j', message) - - let obj: tl.TlObject = message - if (!this._initConnectionCalled) { - this.log.debug('wrapping %s with initConnection', message._) - obj = { - _: 'invokeWithLayer', - layer: this.params.layer, - query: { - ...this.params.initConnection, - query: message, - }, - } as tl.RawInvokeWithLayerRequest - this._initConnectionCalled = true - - // initConnection must be the first rpc call at all times - // to ensure that we store the promise for the initConnection call - // and wait until it resolves before sending any other calls. - - const ret = this._sendBufferForResult( - message._, - BinaryWriter.serializeObject(obj), - stack - ) - - this._initConnectionPromise = ret - .catch(() => {}) - .then(() => { - this._initConnectionPromise = undefined - }) - - return ret - } - - if (this._initConnectionPromise) { - await this._initConnectionPromise - } - - return this._sendBufferForResult( - message._, - BinaryWriter.serializeObject(obj), - stack, - timeout - ) - } - - private _getMessageId(): BigInteger { - const timeTicks = Date.now() - const timeSec = Math.floor(timeTicks / 1000) + this._timeOffset - const timeMSec = timeTicks % 1000 - const random = getRandomInt(0xffff) - - let messageId = bigInt(timeSec) - .shiftLeft(32) - .add( - bigInt(timeMSec) - .shiftLeft(21) - .or(random << 3) - .or(4) - ) - - if (this._lastMessageId.gt(messageId)) { - messageId = this._lastMessageId.plus(4) - } - - this._lastMessageId = messageId - - return messageId - } - - private _getSeqNo(isContentRelated = true): number { - let seqNo = this._seqNo * 2 - - if (isContentRelated) { - seqNo += 1 - this._seqNo += 1 - } - - return seqNo - } -} diff --git a/packages/core/src/network/transports/abstract.ts b/packages/core/src/network/transports/abstract.ts index 7c56bf39..ce4a79b5 100644 --- a/packages/core/src/network/transports/abstract.ts +++ b/packages/core/src/network/transports/abstract.ts @@ -1,8 +1,7 @@ import { tl } from '@mtcute/tl' -import { MaybeAsync } from '../../types/utils' -import { ICryptoProvider } from '../../utils/crypto' +import { MaybeAsync } from '../../types' +import { ICryptoProvider, Logger } from '../../utils' import EventEmitter from 'events' -import { Logger } from '../../utils/logger' export enum TransportState { /** diff --git a/packages/core/src/network/transports/tcp.ts b/packages/core/src/network/transports/tcp.ts index 168c8df9..f1411a2a 100644 --- a/packages/core/src/network/transports/tcp.ts +++ b/packages/core/src/network/transports/tcp.ts @@ -2,9 +2,8 @@ import { ITelegramTransport, IPacketCodec, TransportState } from './abstract' import { tl } from '@mtcute/tl' import { Socket, connect } from 'net' import EventEmitter from 'events' -import { ICryptoProvider } from '../../utils/crypto' +import { ICryptoProvider, Logger } from '../../utils' import { IntermediatePacketCodec } from './intermediate' -import { Logger } from '../../utils/logger' /** * Base for TCP transports. @@ -23,9 +22,18 @@ export abstract class BaseTcpTransport packetCodecInitialized = false + private _updateLogPrefix() { + if (this._currentDc) { + this.log.prefix = `[TCP:${this._currentDc.ipAddress}:${this._currentDc.port}] ` + } else { + this.log.prefix = '[TCP:disconnected] ' + } + } + setup(crypto: ICryptoProvider, log: Logger): void { this._crypto = crypto this.log = log.create('tcp') + this._updateLogPrefix() } state(): TransportState { @@ -50,6 +58,10 @@ export abstract class BaseTcpTransport this._state = TransportState.Connecting this._currentDc = dc + this._updateLogPrefix() + + this.log.debug('connecting to %j', dc) + this._socket = connect( dc.port, dc.ipAddress, @@ -63,7 +75,7 @@ export abstract class BaseTcpTransport close(): void { if (this._state === TransportState.Idle) return - this.log.debug('%s: close', this._currentDc!.ipAddress) + this.log.info('connection closed') this.emit('close') this._state = TransportState.Idle @@ -75,12 +87,12 @@ export abstract class BaseTcpTransport } async handleError(error: Error): Promise { - this.log.error('%s: error: %s', this._currentDc!.ipAddress, error.stack) + this.log.error('error: %s', error.stack) this.emit('error', error) } async handleConnect(): Promise { - this.log.debug('%s: connected', this._currentDc!.ipAddress) + this.log.info('connected') const initialMessage = await this._packetCodec.tag() if (initialMessage.length) { diff --git a/packages/core/src/network/transports/websocket.ts b/packages/core/src/network/transports/websocket.ts index c861829f..6f600905 100644 --- a/packages/core/src/network/transports/websocket.ts +++ b/packages/core/src/network/transports/websocket.ts @@ -1,12 +1,10 @@ import { ITelegramTransport, IPacketCodec, TransportState } from './abstract' import { tl } from '@mtcute/tl' import EventEmitter from 'events' -import { typedArrayToBuffer } from '../../utils/buffer-utils' -import { ICryptoProvider } from '../../utils/crypto' import type WebSocket from 'ws' import { IntermediatePacketCodec } from './intermediate' import { ObfuscatedPacketCodec } from './obfuscated' -import { Logger } from '../../utils/logger' +import { typedArrayToBuffer, Logger, ICryptoProvider } from '../../utils' let ws: { new (address: string, options?: string): WebSocket @@ -69,9 +67,17 @@ export abstract class BaseWebSocketTransport this.close = this.close.bind(this) } + private _updateLogPrefix() { + if (this._currentDc) { + this.log.prefix = `[WS:${this._subdomains[this._currentDc.id]}.${this._baseDomain}] ` + } else { + this.log.prefix = '[WS:disconnected] ' + } + } + setup(crypto: ICryptoProvider, log: Logger): void { this._crypto = crypto - this.log = log.create('tcp') + this.log = log.create('ws') } state(): TransportState { @@ -102,6 +108,9 @@ export abstract class BaseWebSocketTransport 'binary' ) + this._updateLogPrefix() + this.log.debug('connecting to %s (%j)', this._socket.url, dc) + this._socket.binaryType = 'arraybuffer' this._socket.addEventListener('message', (evt) => @@ -114,9 +123,9 @@ export abstract class BaseWebSocketTransport close(): void { if (this._state === TransportState.Idle) return - this.log.debug('%s: close', this._currentDc!.ipAddress) + this.log.info('close') - this.emit('close') + this.emit('connection closed') this._state = TransportState.Idle this._socket!.removeEventListener('close', this.close) this._socket!.close() @@ -126,12 +135,12 @@ export abstract class BaseWebSocketTransport } async handleError({ error }: { error: Error }): Promise { - this.log.error('%s: error: %s', this._currentDc!.ipAddress, error.stack) + this.log.error('error: %s', error.stack) this.emit('error', error) } async handleConnect(): Promise { - this.log.debug('%s: connected', this._currentDc!.ipAddress) + this.log.info('connected') const initialMessage = await this._packetCodec.tag() this._socket!.send(initialMessage) diff --git a/packages/core/src/network/transports/wrapped.ts b/packages/core/src/network/transports/wrapped.ts index 9f02d50f..27e08560 100644 --- a/packages/core/src/network/transports/wrapped.ts +++ b/packages/core/src/network/transports/wrapped.ts @@ -1,7 +1,6 @@ import EventEmitter from 'events' import { IPacketCodec } from './abstract' -import { ICryptoProvider } from '../../utils/crypto' -import { Logger } from '../../utils/logger' +import { ICryptoProvider, Logger } from '../../utils' export abstract class WrappedCodec extends EventEmitter { protected _crypto!: ICryptoProvider diff --git a/packages/core/src/storage/abstract.ts b/packages/core/src/storage/abstract.ts index 6e61c544..bf164078 100644 --- a/packages/core/src/storage/abstract.ts +++ b/packages/core/src/storage/abstract.ts @@ -1,6 +1,7 @@ import { BasicPeerType, MaybeAsync } from '../types' import { tl } from '@mtcute/tl' -import { Logger } from '../utils/logger' +import { Logger } from '../utils' +import { TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime' export namespace ITelegramStorage { export interface PeerInfo { @@ -35,8 +36,9 @@ export interface ITelegramStorage { /** * This method is called before any other. * For storages that use logging, logger instance. + * For storages that use binary storage, binary maps */ - setup?(log: Logger): void + setup?(log: Logger, readerMap: TlReaderMap, writerMap: TlWriterMap): void /** * Load session from some external storage. diff --git a/packages/core/src/storage/json.ts b/packages/core/src/storage/json.ts index 8e7ab611..3a9f8b0a 100644 --- a/packages/core/src/storage/json.ts +++ b/packages/core/src/storage/json.ts @@ -1,5 +1,6 @@ import { MemoryStorage } from './memory' import bigInt from 'big-integer' +import { longFromFastString, longToFastString } from '../utils' /** * Helper class that provides json serialization functions @@ -21,7 +22,7 @@ export class JsonMemoryStorage extends MemoryStorage { } if (key === 'accessHash') { - return bigInt(value, 32) + return longFromFastString(value) } return value @@ -41,7 +42,7 @@ export class JsonMemoryStorage extends MemoryStorage { .join('|') } if (key === 'accessHash') { - return bigInt(value).toString(32) + return longToFastString(value) } return value }) diff --git a/packages/core/src/storage/memory.ts b/packages/core/src/storage/memory.ts index 70c74438..3c0c611d 100644 --- a/packages/core/src/storage/memory.ts +++ b/packages/core/src/storage/memory.ts @@ -1,8 +1,7 @@ import { ITelegramStorage } from './abstract' import { MaybeAsync } from '../types' import { tl } from '@mtcute/tl' -import { MAX_CHANNEL_ID } from '../utils/peer-utils' -import { LruMap } from '../utils/lru-map' +import { LruMap, toggleChannelIdMark } from '../utils' const CURRENT_VERSION = 1 @@ -232,7 +231,7 @@ export class MemoryStorage implements ITelegramStorage /*, IStateStorage */ { case 'channel': return { _: 'inputPeerChannel', - channelId: MAX_CHANNEL_ID - peerInfo.id, + channelId: toggleChannelIdMark(peerInfo.id), accessHash: peerInfo.accessHash, } } diff --git a/packages/core/src/utils/async-lock.ts b/packages/core/src/utils/async-lock.ts index abe3e8d2..a3fb8399 100644 --- a/packages/core/src/utils/async-lock.ts +++ b/packages/core/src/utils/async-lock.ts @@ -1,20 +1,18 @@ +import { Deque } from './deque' + type LockInfo = [Promise, () => void] -interface OneWayLinkedList { - v: T - n?: OneWayLinkedList -} /** * Simple class implementing a semaphore like * behaviour. */ export class AsyncLock { - private _first?: OneWayLinkedList - private _last?: OneWayLinkedList + private _queue = new Deque() async acquire(): Promise { - while (this._first) { - await this._first.v[0] + let info + while ((info = this._queue.peekFront())) { + await info[0] } let unlock: () => void @@ -22,18 +20,24 @@ export class AsyncLock { unlock = resolve }) - if (this._last) { - this._last.n = { v: [prom, unlock!] } - this._last = this._last.n - } else { - this._first = this._last = { v: [prom, unlock!] } - } + this._queue.pushBack([prom, unlock!]) } release(): void { - if (!this._first) throw new Error('Nothing to release') - this._first.v[1]() - this._first = this._first.n - if (!this._first) this._last = undefined + if (!this._queue.length) + throw new Error('Nothing to release') + + this._queue.popFront()![1]() + } + + with(func: () => Promise): Promise { + let err: unknown = null + return this.acquire() + .then(() => func()) + .catch((e) => (err = e)) + .then(() => { + this.release() + if (err) throw err + }) } } diff --git a/packages/core/src/utils/bigint-utils.ts b/packages/core/src/utils/bigint-utils.ts index 9940110d..09c48770 100644 --- a/packages/core/src/utils/bigint-utils.ts +++ b/packages/core/src/utils/bigint-utils.ts @@ -1,41 +1,6 @@ import bigInt, { BigInteger } from 'big-integer' import { randomBytes } from './buffer-utils' -export const bigIntTwo = bigInt(2) -export const LONG_OVERFLOW = bigInt('8000000000000000', 16) -export const LONG_SIGN_DELTA = bigInt('10000000000000000', 16) - -export function ulongToLong(val: BigInteger): BigInteger { - if (val.gt(LONG_OVERFLOW)) return val.minus(LONG_SIGN_DELTA) - return val -} - -export function longToUlong(val: BigInteger): BigInteger { - if (val.isNegative()) return val.plus(LONG_SIGN_DELTA) - return val -} - -export function writeBigInt( - buffer: Buffer, - value: BigInteger, - length = 0, - offset = 0, - le = false -): void { - const array = value.toArray(256).value - if (length !== 0 && array.length > length) - throw new Error('Value out of bounds') - - if (length !== 0) { - // padding - while (array.length !== length) array.unshift(0) - } - - if (le) array.reverse() - - buffer.set(array, offset) -} - export function bigIntToBuffer( value: BigInteger, length = 0, diff --git a/packages/core/src/utils/binary/binary-writer.ts b/packages/core/src/utils/binary/binary-writer.ts deleted file mode 100644 index 2e5b76e1..00000000 --- a/packages/core/src/utils/binary/binary-writer.ts +++ /dev/null @@ -1,314 +0,0 @@ -import { BigInteger } from 'big-integer' -import { longToUlong, ulongToLong, writeBigInt } from '../bigint-utils' -import writerMap, { - ITlBinaryWriter, - TlBinaryWriterFunction, - TlWriterMap, -} from '@mtcute/tl/binary/writer' - -type SerializableObject = { - _: string - [key: string]: any -} - -const isNativeBigIntAvailable = - typeof BigInt !== 'undefined' && 'writeBigInt64LE' in Buffer.prototype - -function utf8ByteLength(string: string): number { - let codePoint - const length = string.length - let leadSurrogate = null - let bytes = 0 - - for (let i = 0; i < length; ++i) { - codePoint = string.charCodeAt(i) - - // is surrogate component - if (codePoint > 0xd7ff && codePoint < 0xe000) { - // last char was a lead - if (!leadSurrogate) { - // no lead yet - if (codePoint > 0xdbff) { - // unexpected trail - bytes += 3 - continue - } else if (i + 1 === length) { - // unpaired lead - bytes += 3 - continue - } - - // valid lead - leadSurrogate = codePoint - - continue - } - - // 2 leads in a row - if (codePoint < 0xdc00) { - bytes += 3 - leadSurrogate = codePoint - continue - } - - // valid surrogate pair - codePoint = - (((leadSurrogate - 0xd800) << 10) | (codePoint - 0xdc00)) + - 0x10000 - } else if (leadSurrogate) { - // valid bmp char, but last char was a lead - bytes += 3 - } - - leadSurrogate = null - - // encode utf8 - if (codePoint < 0x80) { - bytes += 1 - } else if (codePoint < 0x800) { - bytes += 2 - } else if (codePoint < 0x10000) { - bytes += 3 - } else if (codePoint < 0x110000) { - bytes += 4 - } else { - throw new Error('Invalid code point') - } - } - - return bytes -} - -// buffer package for the web detects size by writing the string to an array and checking size -// that is slow. -// see https://github.com/feross/buffer/blob/795bbb5bda1b39f1370ebd784bea6107b087e3a7/index.js#L527 -const utfLength = (Buffer.prototype as any)._isBuffer - ? utf8ByteLength - : Buffer.byteLength - -export class SerializationCounter implements ITlBinaryWriter { - count = 0 - _objectMap = writerMap - - static countNeededBytes( - obj: SerializableObject, - objectMap?: TlWriterMap - ): number { - const cnt = new SerializationCounter() - if (objectMap) cnt._objectMap = objectMap - cnt.object(obj) - return cnt.count - } - - boolean(): void { - this.count += 4 - } - - double(): void { - this.count += 8 - } - - float(): void { - this.count += 4 - } - - int128(): void { - this.count += 16 - } - - int256(): void { - this.count += 32 - } - - int32(): void { - this.count += 4 - } - - uint32(): void { - this.count += 4 - } - - long(): void { - this.count += 8 - } - - rawLong(): void { - this.count += 8 - } - - raw(val: Buffer): void { - this.count += val.length - } - - bytes(val: Buffer): void { - let padding - if (val.length <= 253) { - this.count += 1 - padding = (val.length + 1) % 4 - } else { - this.count += 4 - padding = val.length % 4 - } - - if (padding > 0) this.count += 4 - padding - this.count += val.length - } - - string(val: string): void { - const length = utfLength(val) - let padding - if (length <= 253) { - this.count += 1 - padding = (length + 1) % 4 - } else { - this.count += 4 - padding = length % 4 - } - - if (padding > 0) this.count += 4 - padding - this.count += length - } - - object(obj: SerializableObject, bare?: boolean): void { - if (!this._objectMap[obj._]) throw new Error(`Unknown object ${obj._}`) - this._objectMap[obj._].call(this, obj, bare) - } - - vector(fn: TlBinaryWriterFunction, items: unknown[], bare?: boolean): void { - this.count += bare ? 4 : 8 - items.forEach((it) => fn.call(this, it, bare)) - } -} - -export class BinaryWriter implements ITlBinaryWriter { - _objectMap = writerMap - buffer: Buffer - pos: number - - constructor(buffer: Buffer, start = 0) { - this.buffer = buffer - this.pos = start - } - - static alloc(size: number): BinaryWriter { - return new BinaryWriter(Buffer.allocUnsafe(size)) - } - - static serializeObject( - obj: SerializableObject, - knownSize = -1, - objectMap?: TlWriterMap - ): Buffer { - if (knownSize === -1) - knownSize = SerializationCounter.countNeededBytes(obj) - - const writer = BinaryWriter.alloc(knownSize) - if (objectMap) writer._objectMap = objectMap - - writer.object(obj) - return writer.buffer - } - - int32(val: number): void { - this.buffer.writeInt32LE(val, this.pos) - this.pos += 4 - } - - uint32(val: number): void { - this.buffer.writeUInt32LE(val, this.pos) - this.pos += 4 - } - - long(val: BigInteger): void { - if (isNativeBigIntAvailable) { - val = ulongToLong(val) - // if BigInt is supported, `BigInteger` is just a - // wrapper over native BigInt, stored in `value` - this.buffer.writeBigInt64LE((val as any).value, this.pos) - } else { - val = longToUlong(val) - writeBigInt(this.buffer, val, 8, this.pos, true) - } - - this.pos += 8 - } - - rawLong(val: Buffer): void { - val.copy(this.buffer, this.pos) - this.pos += 8 - } - - float(val: number): void { - this.buffer.writeFloatLE(val, this.pos) - this.pos += 4 - } - - double(val: number): void { - this.buffer.writeDoubleLE(val, this.pos) - this.pos += 8 - } - - boolean(val: boolean): void { - this.buffer.writeUInt32LE(val ? 0x997275b5 : 0xbc799737, this.pos) - this.pos += 4 - } - - raw(val: Buffer): void { - val.copy(this.buffer, this.pos) - this.pos += val.length - } - - int128(val: Buffer): void { - val.copy(this.buffer, this.pos) - this.pos += 16 - } - - int256(val: Buffer): void { - val.copy(this.buffer, this.pos) - this.pos += 32 - } - - bytes(val: Buffer): void { - const length = val.length - let padding - if (length <= 253) { - this.buffer[this.pos++] = val.length - padding = (length + 1) % 4 - } else { - this.buffer[this.pos++] = 254 - this.buffer[this.pos++] = val.length & 0xff - this.buffer[this.pos++] = (val.length >> 8) & 0xff - this.buffer[this.pos++] = (val.length >> 16) & 0xff - padding = length % 4 - } - - val.copy(this.buffer, this.pos) - this.pos += val.length - - if (padding > 0) { - padding = 4 - padding - - while (padding--) this.buffer[this.pos++] = 0 - } - } - - string(val: string): void { - this.bytes(Buffer.from(val, 'utf-8')) - } - - object(obj: SerializableObject, bare?: boolean): void { - if (!this._objectMap[obj._]) throw new Error(`Unknown object ${obj._}`) - this._objectMap[obj._].call(this, obj, bare) - } - - vector(fn: TlBinaryWriterFunction, val: unknown[], bare?: boolean): void { - if (!bare) this.uint32(0x1cb5c415) - this.uint32(val.length) - - val.forEach((it) => fn.call(this, it, bare)) - } - - result(): Buffer { - return this.buffer.slice(0, this.pos) - } -} diff --git a/packages/core/src/utils/condition-variable.ts b/packages/core/src/utils/condition-variable.ts new file mode 100644 index 00000000..7ffd49b3 --- /dev/null +++ b/packages/core/src/utils/condition-variable.ts @@ -0,0 +1,25 @@ +export class ConditionVariable { + private _notify?: () => void + private _timeout?: NodeJS.Timeout + + wait(timeout?: number): Promise { + const prom = new Promise((resolve) =>{ + this._notify = resolve + }) + + if (timeout) { + this._timeout = setTimeout(() => { + this._notify?.() + this._timeout = undefined + }, timeout) + } + + return prom + } + + notify(): void { + this._notify?.() + if (this._timeout) clearTimeout(this._timeout) + this._notify = undefined + } +} diff --git a/packages/core/src/utils/controllable-promise.ts b/packages/core/src/utils/controllable-promise.ts index ce48eedf..ce60d93a 100644 --- a/packages/core/src/utils/controllable-promise.ts +++ b/packages/core/src/utils/controllable-promise.ts @@ -1,12 +1,15 @@ -export type ControllablePromise = Promise & { +export type ControllablePromise = Promise & { resolve(val: T): void - reject(err?: E): void + reject(err?: unknown): void } -export function createControllablePromise< - T = any, - E = any ->(): ControllablePromise { +export type CancellablePromise = Promise & { + cancel(): void +} + +export class PromiseCancelledError extends Error {} + +export function createControllablePromise(): ControllablePromise { let _resolve: any let _reject: any const promise = new Promise((resolve, reject) => { @@ -17,3 +20,14 @@ export function createControllablePromise< ;(promise as ControllablePromise).reject = _reject return promise as ControllablePromise } + +export function createCancellablePromise( + onCancel: () => void +): ControllablePromise & CancellablePromise { + const promise = createControllablePromise() + ;((promise as unknown) as CancellablePromise).cancel = () => { + promise.reject(new PromiseCancelledError()) + onCancel() + } + return promise as ControllablePromise & CancellablePromise +} diff --git a/packages/core/src/utils/crypto/abstract.ts b/packages/core/src/utils/crypto/abstract.ts index f53788c4..79de17ba 100644 --- a/packages/core/src/utils/crypto/abstract.ts +++ b/packages/core/src/utils/crypto/abstract.ts @@ -1,9 +1,5 @@ import { MaybeAsync } from '../../types' -import { TlPublicKey } from '@mtcute/tl/binary/rsa-keys' import { AesModeOfOperationIge } from './common' -import { bigIntToBuffer, bufferToBigInt } from '../bigint-utils' -import bigInt from 'big-integer' -import { randomBytes } from '../buffer-utils' import { factorizePQSync } from './factorization' export interface IEncryptionScheme { @@ -26,7 +22,6 @@ export interface ICryptoProvider { salt: Buffer, iterations: number ): MaybeAsync - rsaEncrypt(data: Buffer, key: TlPublicKey): MaybeAsync hmacSha256(data: Buffer, key: Buffer): MaybeAsync // in telegram, iv is always either used only once, or is the same for all calls for the key @@ -50,22 +45,6 @@ export abstract class BaseCryptoProvider implements ICryptoProvider { initialize(): void {} - async rsaEncrypt(data: Buffer, key: TlPublicKey): Promise { - const toEncrypt = Buffer.concat([ - await this.sha1(data), - data, - // sha1 is always 20 bytes, so we're left with 255 - 20 - x padding - randomBytes(235 - data.length), - ]) - - const encryptedBigInt = bufferToBigInt(toEncrypt).modPow( - bigInt(key.exponent, 16), - bigInt(key.modulus, 16) - ) - - return bigIntToBuffer(encryptedBigInt) - } - abstract createAesCtr( key: Buffer, iv: Buffer, diff --git a/packages/core/src/utils/crypto/keys.ts b/packages/core/src/utils/crypto/keys.ts index c7ed8a0e..9540d550 100644 --- a/packages/core/src/utils/crypto/keys.ts +++ b/packages/core/src/utils/crypto/keys.ts @@ -1,9 +1,8 @@ -// This file was auto-generated. Do not edit. -import { BigInteger } from 'big-integer' import { parseAsn1, parsePemContents } from '../binary/asn1-parser' -import { BinaryWriter } from '../binary/binary-writer' +import { TlBinaryWriter } from '@mtcute/tl-runtime/src/writer' import { ICryptoProvider } from './abstract' import keysIndex, { TlPublicKey } from '@mtcute/tl/binary/rsa-keys' +import Long from 'long' export async function parsePublicKey( crypto: ICryptoProvider, @@ -15,7 +14,8 @@ export async function parsePublicKey( const exponent = asn1.children?.[1].value if (!modulus || !exponent) throw new Error('Invalid public key') - const writer = BinaryWriter.alloc(512) // they are actually smaller, about 270 bytes, but idc :D + const writer = TlBinaryWriter.manualAlloc(512) + // they are actually smaller, about 270 bytes, but idc :D writer.bytes(modulus) writer.bytes(exponent) @@ -41,11 +41,13 @@ export async function addPublicKey( } export function findKeyByFingerprints( - fingerprints: (string | BigInteger)[], + fingerprints: (string | Long)[], allowOld = false ): TlPublicKey | null { for (let fp of fingerprints) { - if (typeof fp !== 'string') fp = fp.toString(16) + if (typeof fp !== 'string') { + fp = fp.toUnsigned().toString(16) + } if (fp in keysIndex) { if (keysIndex[fp].old && !allowOld) continue return keysIndex[fp] diff --git a/packages/core/src/utils/deque.ts b/packages/core/src/utils/deque.ts new file mode 100644 index 00000000..c471c924 --- /dev/null +++ b/packages/core/src/utils/deque.ts @@ -0,0 +1,254 @@ +const MIN_INITIAL_CAPACITY = 8 + +// System.arraycopy from java +function arraycopy( + src: T[], + srcPos: number, + dest: T[], + destPos: number, + length: number +) { + for (let i = 0; i < length; i++) { + dest[destPos + i] = src[srcPos + i] + } +} + +/** + * Deque implementation. + * `undefined` values are not allowed + * + * Based on Java implementation + */ +export class Deque { + // another implementation variant would be to use + // blocks of fixed size instead of a single array + // to avoid copying stuff around + protected _elements: (T | undefined)[] + protected _head = 0 + protected _tail = 0 + protected _capacity: number + + constructor( + minCapacity = MIN_INITIAL_CAPACITY, + readonly maxLength = Infinity + ) { + let capacity = minCapacity + if (capacity >= MIN_INITIAL_CAPACITY) { + // Find the best power of two to hold elements. + // >= because array can't be full + capacity |= capacity >>> 1 + capacity |= capacity >>> 2 + capacity |= capacity >>> 4 + capacity |= capacity >>> 8 + capacity |= capacity >>> 16 + capacity += 1 + + if (capacity < 0) { + // too many + capacity >>>= 1 + } + } + + this._elements = new Array(capacity) + this._capacity = capacity + } + + protected _resize() { + const p = this._head + const n = this._capacity + const r = n - p // number of elements to the right of the head + + const newCapacity = n << 1 + if (newCapacity < 0) throw new Error('Deque is too big') + + const arr = new Array(newCapacity) + + // copy items to the new array + // copy head till the end of arr + arraycopy(this._elements, p, arr, 0, r) + // copy from start to tail + arraycopy(this._elements, 0, arr, r, p) + + this._elements = arr + this._head = 0 + this._tail = n + this._capacity = newCapacity + } + + pushBack(item: T): void { + if (item === undefined) throw new Error('item can not be undefined') + + this._elements[this._tail] = item + + if ( + (this._tail = (this._tail + 1) & (this._capacity - 1)) === + this._head + ) { + this._resize() + } + + if (this.length > this.maxLength) { + this.popFront() + } + } + + pushFront(item: T): void { + if (item === undefined) throw new Error('item can not be undefined') + + this._elements[ + (this._head = (this._head - 1) & (this._capacity - 1)) + ] = item + + if (this._head === this._tail) { + this._resize() + } + + if (this.length > this.maxLength) { + this.popBack() + } + } + + popFront(): T | undefined { + const h = this._head + const res = this._elements[h] + if (res === undefined) return undefined + + this._elements[h] = undefined + this._head = (h + 1) & (this._capacity - 1) + + return res + } + + popBack(): T | undefined { + const t = (this._tail - 1) & (this._capacity - 1) + const res = this._elements[t] + if (res === undefined) return undefined + + this._elements[t] = undefined + this._tail = t + + return res + } + + peekFront(): T | undefined { + return this._elements[this._head] + } + + peekBack(): T | undefined { + return this._elements[(this._tail - 1) & (this._capacity - 1)] + } + + get length(): number { + return (this._tail - this._head) & (this._capacity - 1) + } + + toArray(): T[] { + const sz = this.length + const arr = new Array(sz) + + if (this._head < this._tail) { + // copy as-is, head to tail + arraycopy(this._elements, this._head, arr, 0, sz) + } else { + const headPortion = this._capacity - this._head + + // copy from head to end + arraycopy(this._elements, this._head, arr, 0, headPortion) + // copy from start to tail + arraycopy(this._elements, 0, arr, headPortion, this._tail) + } + + return arr as T[] + } + + protected _delete(i: number): void { + const els = this._elements + const mask = this._capacity - 1 + const h = this._head + const t = this._tail + const front = (i - h) & mask + const back = (t - i) & mask + + if (front < back) { + if (h <= i) { + arraycopy(els, h, els, h + 1, front) + } else { + // wrap + arraycopy(els, 0, els, 1, i) + els[0] = els[mask] + arraycopy(els, h, els, h + 1, mask - h) + } + els[h] = undefined + this._head = (h + 1) & mask + } else { + if (i < t) { + // copy null tail as well + arraycopy(els, i + 1, els, i, back) + this._tail = t - 1 + } else { + // wrap + arraycopy(els, i + 1, els, i, mask - i) + els[mask] = els[0] + arraycopy(els, 1, els, 0, t) + this._tail = (t - 1) & mask + } + } + } + + remove(item: T): void { + const mask = this._capacity - 1 + let i = this._head + let val: T | undefined + while ((val = this._elements[i]) !== undefined) { + if (item === val) { + this._delete(i) + return + } + i = (i + 1) & mask + } + } + + removeBy(pred: (it: T) => boolean): void { + const mask = this._capacity - 1 + let i = this._head + let val: T | undefined + while ((val = this._elements[i]) !== undefined) { + if (pred(val)) { + this._delete(i) + return + } + i = (i + 1) & mask + } + } + + at(idx: number): T | undefined { + return this._elements[(this._head + idx) & (this._capacity - 1)] + } + + *iter(): IterableIterator { + const sz = this.length + + if (this._head < this._tail) { + // head to tail + for (let i = 0; i < sz; i++) { + yield this._elements[this._head + i]! + } + } else { + const headPortion = this._capacity - this._head + + // head to end + for (let i = 0; i < headPortion; i++) { + yield this._elements[this._head + i]! + } + // start to tail + for (let i = 0; i < this._tail; i++) { + yield this._elements[i]! + } + } + } + + clear(): void { + this._elements = new Array(this._capacity) + this._head = this._tail = 0 + } +} diff --git a/packages/core/src/utils/early-timer.ts b/packages/core/src/utils/early-timer.ts new file mode 100644 index 00000000..8102fbb4 --- /dev/null +++ b/packages/core/src/utils/early-timer.ts @@ -0,0 +1,84 @@ +/** + * Wrapper over JS timers that allows re-scheduling them + * to earlier time + */ +export class EarlyTimer { + private _timeout?: NodeJS.Timeout + private _immediate?: NodeJS.Immediate + private _timeoutTs?: number + + private _handler: () => void = () => {} + + constructor () { + this.emitNow = this.emitNow.bind(this) + } + + /** + * Emit the timer when the event loop is idle + * (basically `setImmediate()`) + */ + emitWhenIdle(): void { + if (this._immediate) return + + clearTimeout(this._timeout!) + this._timeoutTs = Date.now() + + if (typeof setImmediate !== 'undefined') { + this._immediate = setImmediate(this.emitNow) + } else { + this._timeout = setTimeout(this.emitNow, 0) + } + } + + /** + * Emit the timer before the next given milliseconds + * + * Shorthand for `emitBefore(Date.now() + ms)` + * + * @param ms Milliseconds to schedule for + */ + emitBeforeNext(ms: number): void { + return this.emitBefore(Date.now() + ms) + } + + /** + * Emit the timer before the given time + * + * @param ts Unix time in MS + */ + emitBefore(ts: number): void { + if (!this._timeoutTs || ts < this._timeoutTs) { + this.reset() + this._timeout = setTimeout(this.emitNow, ts - Date.now()) + this._timeoutTs = ts + } + } + + /** + * Emit the timer right now + */ + emitNow(): void { + this._handler() + this.reset() + } + + /** + * Cancel the timer + */ + reset(): void { + if (this._immediate) { + clearImmediate(this._immediate!) + this._immediate = undefined + } else { + clearTimeout(this._timeout!) + } + this._timeoutTs = undefined + } + + /** + * Set timeout handler + */ + onTimeout(handler: () => void): void { + this._handler = handler + } +} diff --git a/packages/core/src/utils/flood-control.ts b/packages/core/src/utils/flood-control.ts new file mode 100644 index 00000000..4aa307d2 --- /dev/null +++ b/packages/core/src/utils/flood-control.ts @@ -0,0 +1,79 @@ +// based on https://github.dev/tdlib/td/blob/master/tdutils/td/utils/FloodControlStrict.h + +interface FloodControlLimit { + duration: number + count: number + pos: number +} + +export class FloodControl { + private _wakeupAt = 1 + private _withoutUpdate = 0 + private _events: number[] = [] + // pair: duration, count + private _limits: FloodControlLimit[] = [] + + // no more than count in each duration + addLimit(duration: number, count: number): void { + this._limits.push({ duration, count, pos: 0 }) + this._withoutUpdate = 0 + } + + addEvent(now: number): void { + this._events.push(now) + if (this._withoutUpdate > 0) { + this._withoutUpdate -= 1 + } else { + this._update(now) + } + } + + clear(): void { + this._events.length = 0 + this._withoutUpdate = 0 + this._wakeupAt = 1 + this._limits.forEach((limit) => (limit.pos = 0)) + } + + get wakeupAt(): number { + return this._wakeupAt + } + + private _update(now: number): void { + let minPos = this._events.length + this._withoutUpdate = Infinity + + this._limits.forEach((limit) => { + if (limit.count < this._events.length - limit.pos) { + limit.pos = this._events.length - limit.count + } + + while ( + limit.pos < this._events.length && + this._events[limit.pos] + limit.duration < now + ) { + limit.pos += 1 + } + + if (limit.count + limit.pos < this._events.length) { + this._wakeupAt = Math.max( + this._wakeupAt, + this._events[limit.pos] + limit.duration + ) + this._withoutUpdate = 0 + } else { + this._withoutUpdate = Math.min( + this._withoutUpdate, + limit.count + limit.pos - this._events.length - 1 + ) + } + + minPos = Math.min(minPos, limit.pos) + }) + + if (minPos * 2 > this._events.length) { + this._limits.forEach((limit) => (limit.pos -= minPos)) + this._events.splice(0, minPos) + } + } +} diff --git a/packages/core/src/utils/function-utils.ts b/packages/core/src/utils/function-utils.ts index 382e8da2..f9c9d2b2 100644 --- a/packages/core/src/utils/function-utils.ts +++ b/packages/core/src/utils/function-utils.ts @@ -1,33 +1,3 @@ -/** - * Debounce a function with a given delay. - * Similar to lodash. - * - * Debouncing `F` for a delay of `D` means that - * `F` will be called after `D` passed since tha - * last time `F` was called. - * - * @param func Function to debounce - * @param delay Debounce delay - */ -export function debounce( - func: T, - delay: number -): () => void { - let timeout: NodeJS.Timeout | null - return function (this: any) { - const self = this - const args = arguments - const later = function (): void { - timeout = null - func.apply(self, args) - } - if (timeout) { - clearTimeout(timeout) - } - timeout = setTimeout(later, delay) - } as any -} - /** * Throttle a function with a given delay. * Similar to lodash. @@ -40,7 +10,10 @@ export function debounce( * @param func Function to throttle * @param delay Throttle delay */ -export function throttle(func: T, delay: number): T { +export function throttle( + func: T, + delay: number +): () => void { let timeout: NodeJS.Timeout | null return function (this: any) { diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts new file mode 100644 index 00000000..e318af9c --- /dev/null +++ b/packages/core/src/utils/index.ts @@ -0,0 +1,13 @@ +export * from './crypto' +export * from './logger' +export * from './peer-utils' +export * from './tl-json' +export * from './async-lock' +export * from './lru-map' +export * from './function-utils' +export * from './buffer-utils' +export * from './bigint-utils' +export * from './misc-utils' +export * from './long-utils' +export * from './linked-list' +export * from './deque' diff --git a/packages/core/src/utils/linked-list.ts b/packages/core/src/utils/linked-list.ts new file mode 100644 index 00000000..a7928e01 --- /dev/null +++ b/packages/core/src/utils/linked-list.ts @@ -0,0 +1,74 @@ +interface LinkedListItem { + v: T + n?: LinkedListItem + p?: LinkedListItem +} + +export class SortedLinkedList { + _first?: LinkedListItem + _last?: LinkedListItem + _size = 0 + + constructor(private comparator: (a: T, b: T) => number) { + } + + get length(): number { + return this._size + } + + _remove(item: LinkedListItem): void { + if (this._first === item) { + this._first = item.n + } + + if (this._last === item) { + this._last = item.p + } + + if (item.p) item.p.n = item.n + if (item.n) item.n.p = item.p + + this._size -= 1 + } + + popFront(): T | undefined { + if (!this._first) return undefined + + const it = this._first + this._first = this._first.n + if (!this._first) this._last = undefined + + this._size -= 1 + return it.v + } + + add(item: T): void { + const it: LinkedListItem = { v: item } + + if (!this._first) { + this._first = this._last = it + } else { + let cur: LinkedListItem | undefined = this._first + while (cur && this.comparator(cur.v, it.v) < 0) { + cur = cur.n + } + + if (!cur) { + // reached end, the item should be new last + this._last!.n = it + it.p = this._last + this._last = it + } else { + // `cur` is the first item greater or equal than the given + it.n = cur + it.p = cur.p + if (cur.p) cur.p.n = it + cur.p = it + + if (cur === this._first) this._first = it + } + } + + this._size += 1 + } +} diff --git a/packages/core/src/utils/logger.ts b/packages/core/src/utils/logger.ts index 39ad16d7..b8f6e133 100644 --- a/packages/core/src/utils/logger.ts +++ b/packages/core/src/utils/logger.ts @@ -21,7 +21,9 @@ const FORMATTER_RE = /%[a-zA-Z]/g export class Logger { private color: number - constructor(readonly mgr: LogManager, readonly tag: string) { + prefix = '' + + constructor(readonly mgr: LogManager, readonly tag: string, readonly parent: Logger = mgr) { let hash = 0 for (let i = 0; i < tag.length; i++) { @@ -32,24 +34,39 @@ export class Logger { this.color = Math.abs(hash) % 6 } + getPrefix(): string { + let s = '' + + let obj: Logger | undefined = this + while (obj) { + if (obj.prefix) s = obj.prefix + s + obj = obj.parent + } + + return s + } + log(level: number, fmt: string, ...args: any[]): void { if (level > this.mgr.level) return + if (!this.mgr['_filter'](this.tag)) return // custom formatters if ( fmt.indexOf('%h') > -1 || fmt.indexOf('%b') > -1 || - fmt.indexOf('%j') > -1 + fmt.indexOf('%j') > -1 || + fmt.indexOf('%l') > -1 ) { let idx = 0 fmt = fmt.replace(FORMATTER_RE, (m) => { - if (m === '%h' || m === '%b' || m === '%j') { + if (m === '%h' || m === '%b' || m === '%j' || m == '%l') { const val = args[idx] args.splice(idx, 1) if (m === '%h') return val.toString('hex') if (m === '%b') return !!val + '' if (m === '%j') return JSON.stringify(val) + if (m === '%l') return val + '' } idx++ @@ -58,14 +75,14 @@ export class Logger { }) } - this.mgr.handler(this.color, level, this.tag, fmt, args) + this.mgr.handler(this.color, level, this.tag, this.getPrefix() + fmt, args) } readonly error = this.log.bind(this, LogManager.ERROR) readonly warn = this.log.bind(this, LogManager.WARN) readonly info = this.log.bind(this, LogManager.INFO) readonly debug = this.log.bind(this, LogManager.DEBUG) - readonly request = this.log.bind(this, LogManager.REQUEST) + readonly verbose = this.log.bind(this, LogManager.VERBOSE) /** * Create a {@link Logger} with the given tag @@ -75,24 +92,30 @@ export class Logger { * @param tag Logger tag */ create(tag: string): Logger { - return this.mgr.create(tag) + return new Logger(this.mgr, tag, this) } } +const defaultFilter = () => true + /** - * Log manager. - * - * Does nothing by itself, but allows managing instance logs + * Log manager. A logger that allows managing child loggers */ -export class LogManager { +export class LogManager extends Logger { static OFF = 0 static ERROR = 1 static WARN = 2 static INFO = 3 static DEBUG = 4 - static REQUEST = 5 + static VERBOSE = 5 - private _cache: Record = {} + constructor () { + // workaround because we cant pass this to super + super(null as any, 'base') + ;(this as any).mgr = this + } + + private _filter: (tag: string) => boolean = defaultFilter level = defaultLogLevel handler = _defaultLoggingHandler @@ -107,9 +130,15 @@ export class LogManager { * @param tag Logger tag */ create(tag: string): Logger { - if (!(tag in this._cache)) { - this._cache[tag] = new Logger(this, tag) - } - return this._cache[tag] + return new Logger(this, tag) + } + + /** + * Filter logging by tags. + * + * @param cb + */ + filter(cb: ((tag: string) => boolean) | null): void { + this._filter = cb ?? defaultFilter } } diff --git a/packages/core/src/utils/long-utils.ts b/packages/core/src/utils/long-utils.ts new file mode 100644 index 00000000..2ba6fb90 --- /dev/null +++ b/packages/core/src/utils/long-utils.ts @@ -0,0 +1,250 @@ +import Long from 'long' +import { getRandomInt } from './misc-utils' + +export function randomLong(unsigned = false): Long { + const lo = getRandomInt(0xffffffff) + const hi = getRandomInt(0xffffffff) + + return new Long(lo, hi, unsigned) +} + +export function removeFromLongArray(arr: Long[], val: Long): boolean { + for (let i = 0; i < arr.length; i++) { + const v = arr[i] + // v === val for the case when the exact same object was passed + if (v === val || v.eq(val)) { + arr.splice(i, 1) + return true + } + } + + return false +} + +/** + * Serialize a Long (int64) to its fast string representation: + * `$high|$low`. + * + * This method is about 500* times faster than using `Long.toString()`, + * and very useful when the string will only ever be used internally + * (e.g. for keying a map) + * + * _\* benchmark result, YMMV_ + * + * @param val + */ +export function longToFastString(val: Long): string { + return `${val.low}|${val.high}` +} + +/** + * Parse a Long (int64) from its fast string representation: + * `$high|$low`. + * + * This method is about 2 times faster than using Long.fromString. + * + * @param val + * @param unsigned + */ +export function longFromFastString(val: string, unsigned = false): Long { + const parts = val.split('|') + + if (parts.length !== 2) throw new Error(`Invalid long fast string: ${val}`) + const low = parseInt(parts[0]) + const high = parseInt(parts[1]) + + if (isNaN(low) || isNaN(high)) + throw new Error(`Invalid long fast string: ${val}`) + + return new Long(low, high, unsigned) +} + + +/** + * Map with Longs as key. + * + * Uses fast string representation internally. + */ +export class LongMap { + private _map?: Map + private _obj?: any + + constructor(useObject = false) { + if (typeof Map === 'undefined' || useObject) { + this._obj = Object.create(null) + this.set = this._setForObj.bind(this) + this.has = this._hasForObj.bind(this) + this.get = this._getForObj.bind(this) + this.delete = this._deleteForObj.bind(this) + this.keys = this._keysForObj.bind(this) + this.values = this._valuesForObj.bind(this) + this.clear = this._clearForObj.bind(this) + this.size = this._sizeForObj.bind(this) + } else { + this._map = new Map() + this.set = this._setForMap.bind(this) + this.has = this._hasForMap.bind(this) + this.get = this._getForMap.bind(this) + this.delete = this._deleteForMap.bind(this) + this.keys = this._keysForMap.bind(this) + this.values = this._valuesForMap.bind(this) + this.clear = this._clearForMap.bind(this) + this.size = this._sizeForMap.bind(this) + } + } + + readonly set: (key: Long, value: V) => void + readonly has: (key: Long) => boolean + readonly get: (key: Long) => V | undefined + readonly delete: (key: Long) => void + readonly keys: (unsigned?: boolean) => IterableIterator + readonly values: () => IterableIterator + readonly clear: () => void + readonly size: () => number + + private _setForMap(key: Long, value: V): void { + this._map!.set(longToFastString(key), value) + } + + private _hasForMap(key: Long): boolean { + return this._map!.has(longToFastString(key)) + } + + private _getForMap(key: Long): V | undefined { + return this._map!.get(longToFastString(key)) + } + + private _deleteForMap(key: Long): void { + this._map!.delete(longToFastString(key)) + } + + private *_keysForMap(unsigned?: boolean): IterableIterator { + for (const v of this._map!.keys()) { + yield longFromFastString(v, unsigned) + } + } + + private _valuesForMap(): IterableIterator { + return this._map!.values() + } + + private _clearForMap(): void { + this._map!.clear() + } + + private _sizeForMap(): number { + return this._map!.size + } + + private _setForObj(key: Long, value: V): void { + this._obj![longToFastString(key)] = value + } + + private _hasForObj(key: Long): boolean { + return longToFastString(key) in this._obj! + } + + private _getForObj(key: Long): V | undefined { + return this._obj![longToFastString(key)] + } + + private _deleteForObj(key: Long): void { + delete this._obj![longToFastString(key)] + } + + private *_keysForObj(unsigned?: boolean): IterableIterator { + for (const v of Object.keys(this._obj)) { + yield longFromFastString(v, unsigned) + } + } + + private *_valuesForObj(): IterableIterator { + yield * (Object.values(this._obj!) as any) + } + + private _clearForObj(): void { + this._obj = {} + } + + private _sizeForObj(): number { + return Object.keys(this._obj).length + } +} + +/** + * Set for Longs. + * + * Uses fast string representation internally + */ +export class LongSet { + private _set?: Set + private _obj?: any + private _objSize?: number + + constructor(useObject = false) { + if (typeof Set === 'undefined' || useObject) { + this._obj = Object.create(null) + this._objSize = 0 + this.add = this._addForObj.bind(this) + this.delete = this._deleteForObj.bind(this) + this.has = this._hasForObj.bind(this) + this.clear = this._clearForObj.bind(this) + } else { + this._set = new Set() + this.add = this._addForSet.bind(this) + this.delete = this._deleteForSet.bind(this) + this.has = this._hasForSet.bind(this) + this.clear = this._clearForSet.bind(this) + } + } + + readonly add: (val: Long) => void + readonly delete: (val: Long) => void + readonly has: (val: Long) => boolean + readonly clear: () => void + + get size(): number { + return this._objSize ?? this._set!.size + } + + private _addForSet(val: Long) { + this._set!.add(longToFastString(val)) + } + + private _deleteForSet(val: Long) { + this._set!.delete(longToFastString(val)) + } + + private _hasForSet(val: Long) { + return this._set!.has(longToFastString(val)) + } + + private _clearForSet() { + this._set!.clear() + } + + private _addForObj(val: Long) { + const k = longToFastString(val) + if (k in this._obj!) return + + this._obj![k] = true + this._objSize! += 1 + } + + private _deleteForObj(val: Long) { + const k = longToFastString(val) + if (!(k in this._obj!)) return + + delete this._obj![k] + this._objSize! -= 1 + } + + private _hasForObj(val: Long) { + return longToFastString(val) in this._obj! + } + + private _clearForObj() { + this._obj = {} + this._objSize = 0 + } +} diff --git a/packages/core/src/utils/lru-map.ts b/packages/core/src/utils/lru-map.ts index 165e746d..bd21043f 100644 --- a/packages/core/src/utils/lru-map.ts +++ b/packages/core/src/utils/lru-map.ts @@ -1,3 +1,5 @@ +import { LongMap } from './long-utils' + interface TwoWayLinkedList { // k = key k: K @@ -17,18 +19,24 @@ interface TwoWayLinkedList { * * Uses two-way linked list internally to keep track of insertion/access order */ -export class LruMap { +export class LruMap { private _capacity: number private _first?: TwoWayLinkedList private _last?: TwoWayLinkedList private _size = 0 - constructor(capacity: number, useObject = false) { + constructor(capacity: number, useObject = false, forLong = false) { this._capacity = capacity - if (typeof Map === 'undefined' || useObject) { - const obj = {} as any + if (forLong) { + const map = new LongMap(useObject) + this._set = map.set.bind(map) as any + this._has = map.has.bind(map) as any + this._get = map.get.bind(map) as any + this._del = map.delete.bind(map) as any + } else if (typeof Map === 'undefined' || useObject) { + const obj = Object.create(null) this._set = (k, v) => (obj[k] = v) this._has = (k) => k in obj this._get = (k) => obj[k] diff --git a/packages/core/src/utils/lru-string-set.ts b/packages/core/src/utils/lru-string-set.ts index 0c4b3291..30039d5e 100644 --- a/packages/core/src/utils/lru-string-set.ts +++ b/packages/core/src/utils/lru-string-set.ts @@ -1,11 +1,13 @@ +import { LongSet } from './long-utils' + interface OneWayLinkedList { v: T n?: OneWayLinkedList } /** - * Simple class implementing LRU-like behaviour for a set - * of strings, falling back to objects when `Set` is not available. + * Simple class implementing LRU-like behaviour for a set, + * falling back to objects when `Set` is not available. * * Used to store recently received message IDs in {@link TelegramConnection} * @@ -13,67 +15,81 @@ interface OneWayLinkedList { * * @internal */ -export class LruStringSet { +export class LruSet { private _capacity: number - private _first?: OneWayLinkedList - private _last?: OneWayLinkedList + private _first?: OneWayLinkedList + private _last?: OneWayLinkedList - private _set?: Set - private _obj?: Record + private _set?: Set | LongSet + private _obj?: any private _objSize?: number - constructor(capacity: number, useObject = false) { + constructor(capacity: number, useObject = false, forLong = false) { this._capacity = capacity - if (typeof Set === 'undefined' || useObject) { - this._obj = {} + if (!forLong && (typeof Set === 'undefined' || useObject)) { + this._obj = Object.create(null) this._objSize = 0 this.add = this._addForObj.bind(this) this.has = this._hasForObj.bind(this) + this.clear = this._clearForObj.bind(this) } else { - this._set = new Set() + this._set = forLong ? new LongSet(useObject) : new Set() this.add = this._addForSet.bind(this) this.has = this._hasForSet.bind(this) + this.clear = this._clearForSet.bind(this) } } - add: (str: string) => void - has: (str: string) => boolean + readonly add: (val: T) => void + readonly has: (val: T) => boolean + readonly clear: () => void - private _addForSet(str: string) { - if (this._set!.has(str)) return + private _clearForSet() { + this._first = this._last = undefined + this._set!.clear() + } - if (!this._first) this._first = { v: str } + private _clearForObj() { + this._first = this._last = undefined + this._obj = {} + this._objSize = 0 + } + + private _addForSet(val: T) { + if (this._set!.has(val as any)) return + + if (!this._first) this._first = { v: val } if (!this._last) this._last = this._first else { - this._last.n = { v: str } + this._last.n = { v: val } this._last = this._last.n } - this._set!.add(str) + this._set!.add(val as any) if (this._set!.size > this._capacity && this._first) { // remove least recently used - this._set!.delete(this._first.v) + this._set!.delete(this._first.v as any) this._first = this._first.n } } - private _hasForSet(str: string) { - return this._set!.has(str) + private _hasForSet(val: T) { + return this._set!.has(val as any) } - private _addForObj(str: string) { - if (str in this._obj!) return + private _addForObj(val: T) { + if (val in this._obj!) return - if (!this._first) this._first = { v: str } + if (!this._first) this._first = { v: val } if (!this._last) this._last = this._first else { - this._last.n = { v: str } + this._last.n = { v: val } this._last = this._last.n } - this._obj![str] = true + this._obj![val] = true if (this._objSize === this._capacity) { // remove least recently used @@ -84,7 +100,7 @@ export class LruStringSet { } } - private _hasForObj(str: string) { - return str in this._obj! + private _hasForObj(val: T) { + return val in this._obj! } } diff --git a/packages/core/src/utils/peer-utils.ts b/packages/core/src/utils/peer-utils.ts index 4dedf2c7..9c8c2735 100644 --- a/packages/core/src/utils/peer-utils.ts +++ b/packages/core/src/utils/peer-utils.ts @@ -1,10 +1,25 @@ import { tl } from '@mtcute/tl' import { BasicPeerType } from '../types' -export const MIN_CHANNEL_ID = -1002147483647 -export const MAX_CHANNEL_ID = -1000000000000 -export const MIN_CHAT_ID = -2147483647 -export const MAX_USER_ID = 2147483647 +// src: https://github.com/tdlib/td/blob/master/td/telegram/DialogId.h +const ZERO_CHANNEL_ID = -1000000000000 +const ZERO_SECRET_CHAT_ID = -2000000000000 +const MAX_SECRET_CHAT_ID = -1997852516353 // ZERO_SECRET_CHAT_ID + 0x7fffffff +// src: https://github.com/tdlib/td/blob/master/td/telegram/ChatId.h +// const MAX_CHAT_ID = 999999999999 +const MIN_MARKED_CHAT_ID = -999999999999 // -MAX_CHAT_ID +// src: https://github.com/tdlib/td/blob/master/td/telegram/UserId.h +// MAX_USER_ID = (1ll << 40) - 1 +const MAX_USER_ID = 1099511627775 +// src: https://github.com/tdlib/td/blob/master/td/telegram/ChannelId.h +// the last (1 << 31) - 1 identifiers will be used for secret chat dialog identifiers +// MAX_CHANNEL_ID = 1000000000000ll - (1ll << 31) +// const MAX_CHANNEL_ID = 997852516352 +const MIN_MARKED_CHANNEL_ID = -1997852516352 // ZERO_CHANNEL_ID - MAX_CHANNEL_ID + +export function toggleChannelIdMark(id: number): number { + return ZERO_CHANNEL_ID - id +} /** * Get the bare (non-marked) ID from a {@link tl.TypePeer} @@ -20,15 +35,20 @@ export function getBarePeerId(peer: tl.TypePeer): number { } } +// src: https://github.com/tdlib/td/blob/master/td/telegram/DialogId.cpp + /** * Get the marked (non-bare) ID from a {@link tl.TypePeer} * * *Mark* is bot API compatible, which is: * - ID stays the same for users * - ID is negated for chats - * - ID is negated and `1e12` is subtracted for channels + * - ID is negated and `-1e12` is subtracted for channels */ -export function getMarkedPeerId(peerId: number, peerType: BasicPeerType): number +export function getMarkedPeerId( + peerId: number, + peerType: BasicPeerType +): number export function getMarkedPeerId(peer: tl.TypePeer | tl.TypeInputPeer): number export function getMarkedPeerId( peer: tl.TypePeer | tl.TypeInputPeer | number, @@ -41,7 +61,7 @@ export function getMarkedPeerId( case 'chat': return -peer case 'channel': - return MAX_CHANNEL_ID - peer + return ZERO_CHANNEL_ID - peer } throw new Error('Invalid peer type') } @@ -55,7 +75,7 @@ export function getMarkedPeerId( return -peer.chatId case 'peerChannel': case 'inputPeerChannel': - return MAX_CHANNEL_ID - peer.channelId + return ZERO_CHANNEL_ID - peer.channelId } throw new Error('Invalid peer') @@ -77,9 +97,24 @@ export function getBasicPeerType(peer: tl.TypePeer | number): BasicPeerType { } if (peer < 0) { - if (MIN_CHAT_ID <= peer) return 'chat' - if (MIN_CHANNEL_ID <= peer && peer < MAX_CHANNEL_ID) return 'channel' - } else if (0 < peer && peer <= MAX_USER_ID) { + if (MIN_MARKED_CHAT_ID <= peer) { + return 'chat' + } + + if ( + MIN_MARKED_CHANNEL_ID <= peer && + peer !== ZERO_CHANNEL_ID + ) + return 'channel' + + if ( + MAX_SECRET_CHAT_ID <= peer && + peer !== ZERO_SECRET_CHAT_ID + ) { + // return 'secret' + throw new Error('Unsupported') + } + } else if (peer > 0 && peer <= MAX_USER_ID) { return 'user' } @@ -88,13 +123,14 @@ export function getBasicPeerType(peer: tl.TypePeer | number): BasicPeerType { export function markedPeerIdToBare(peerId: number): number { const type = getBasicPeerType(peerId) + switch (type) { case 'user': return peerId case 'chat': return -peerId case 'channel': - return MAX_CHANNEL_ID - peerId + return toggleChannelIdMark(peerId) } throw new Error('Invalid marked peer id') diff --git a/packages/core/src/utils/platform/logging.ts b/packages/core/src/utils/platform/logging.ts index e8ebbdc3..5ffc1751 100644 --- a/packages/core/src/utils/platform/logging.ts +++ b/packages/core/src/utils/platform/logging.ts @@ -10,7 +10,7 @@ const LEVEL_NAMES = isTty '\x1b[33mWRN\x1b[0m', '\x1b[34mINF\x1b[0m', '\x1b[36mDBG\x1b[0m', - '\x1b[35mREQ\x1b[0m', + '\x1b[35mVRB\x1b[0m', ] : [ '', // OFF @@ -18,7 +18,7 @@ const LEVEL_NAMES = isTty 'WRN', 'INF', 'DBG', - 'REQ', + 'VRB', ] const TAG_COLORS = [6, 2, 3, 4, 5, 1].map((i) => `\x1b[3${i};1m`) diff --git a/packages/core/src/utils/platform/logging.web.ts b/packages/core/src/utils/platform/logging.web.ts index d3e6a42a..8d932fa8 100644 --- a/packages/core/src/utils/platform/logging.web.ts +++ b/packages/core/src/utils/platform/logging.web.ts @@ -5,7 +5,7 @@ const LEVEL_NAMES = [ 'WRN', 'INF', 'DBG', - 'REQ' + 'VRB' ] const COLORS = [ '', // OFF diff --git a/packages/core/src/utils/sorted-array.ts b/packages/core/src/utils/sorted-array.ts new file mode 100644 index 00000000..f3d68866 --- /dev/null +++ b/packages/core/src/utils/sorted-array.ts @@ -0,0 +1,88 @@ +type Comparator = (a: T, b: T) => number + +// Comparator is always called like: +// comparator(itemFromSortedArray, yourItem) + +export class SortedArray { + readonly raw: T[] + comparator: Comparator + + constructor(array: T[] = [], comparator: Comparator) { + this.raw = array.sort(comparator) + this.comparator = comparator + } + + get length(): number { + return this.raw.length + } + + insert(item: T | T[]): number { + if (Array.isArray(item)) { + let ind = -1 + item.forEach((it) => { + ind = this.insert(it) + }) + return ind + } + + if (this.raw.length === 0) { + this.raw.push(item) + return 0 + } + + // find insert position + let lo = 0 + let hi = this.raw.length + while (lo < hi) { + const mid = Math.floor((lo + hi) / 2) + if (this.comparator(this.raw[mid], item) > 0) { + hi = mid + } else { + lo = mid + 1 + } + } + + this.raw.splice(lo, 0, item) + return lo + } + + // closest: return the closest value (right-hand side) + // meaning that raw[idx - 1] <= item <= raw[idx] + index(item: T, closest = false): number { + let lo = 0 + let hi = this.raw.length + while (lo < hi) { + const mid = Math.floor((lo + hi) / 2) + const cmp = this.comparator(this.raw[mid], item) + if (cmp === 0) { + return mid + } + if (cmp > 0) { + hi = mid + } else { + lo = mid + 1 + } + } + return closest ? lo : -1 + } + + remove(item: T): void { + const idx = this.index(item) + if (idx === -1) return + + this.raw.splice(idx, 1) + } + + includes(item: T): boolean { + return this.index(item) !== -1 + } + + find(item: T): T | null { + const ind = this.index(item) + return ind === -1 ? null : this.raw[ind] + } + + clear(): void { + this.raw.length = 0 + } +} diff --git a/packages/dispatcher/package.json b/packages/dispatcher/package.json index ee98ea8a..19d3b63a 100644 --- a/packages/dispatcher/package.json +++ b/packages/dispatcher/package.json @@ -12,7 +12,7 @@ "build": "tsc" }, "dependencies": { - "@mtcute/tl": "~1.131.0", + "@mtcute/tl": "^134.0", "@mtcute/core": "^1.0.0", "@mtcute/client": "^1.0.0", "events": "^3.2.0" diff --git a/packages/dispatcher/src/dispatcher.ts b/packages/dispatcher/src/dispatcher.ts index b333366c..147b8133 100644 --- a/packages/dispatcher/src/dispatcher.ts +++ b/packages/dispatcher/src/dispatcher.ts @@ -1,13 +1,12 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { CallbackQuery, - ChatsIndex, InlineQuery, MaybeAsync, Message, MtArgumentError, TelegramClient, - UsersIndex, + PeersIndex, ChatMemberUpdate, ChosenInlineResult, PollUpdate, @@ -185,14 +184,13 @@ export class Dispatcher { */ dispatchRawUpdate( update: tl.TypeUpdate | tl.TypeMessage, - users: UsersIndex, - chats: ChatsIndex + peers: PeersIndex ): void { if (!this._client) return // order does not matter in the dispatcher, // so we can handle each update in its own task - this.dispatchRawUpdateNow(update, users, chats).catch((err) => + this.dispatchRawUpdateNow(update, peers).catch((err) => this._client!['_emitError'](err) ) } @@ -206,14 +204,12 @@ export class Dispatcher { * was fully processed by all the registered handlers, including children. * * @param update Update to process - * @param users Users map - * @param chats Chats map + * @param peers Peers map * @returns Whether the update was handled */ async dispatchRawUpdateNow( update: tl.TypeUpdate | tl.TypeMessage, - users: UsersIndex, - chats: ChatsIndex + peers: PeersIndex ): Promise { if (!this._client) return false @@ -230,13 +226,12 @@ export class Dispatcher { if ( !h.check || - (await h.check(this._client, update, users, chats)) + (await h.check(this._client, update, peers)) ) { result = await h.callback( this._client, update, - users, - chats + peers ) handled = true } else continue @@ -256,7 +251,7 @@ export class Dispatcher { } for (const child of this._children) { - handled ||= await child.dispatchRawUpdateNow(update, users, chats) + handled ||= await child.dispatchRawUpdateNow(update, peers) } return handled diff --git a/packages/dispatcher/src/filters.ts b/packages/dispatcher/src/filters.ts index 7b4b86c6..926bf8e2 100644 --- a/packages/dispatcher/src/filters.ts +++ b/packages/dispatcher/src/filters.ts @@ -31,6 +31,7 @@ import { } from '@mtcute/client' import { MaybeArray } from '@mtcute/core' import { UpdateState } from './state' +import { tl } from '@mtcute/tl' function extractText( obj: Message | InlineQuery | ChosenInlineResult | CallbackQuery @@ -135,14 +136,25 @@ export namespace filters { ? I : never - type ExtractBase< - Filter extends UpdateFilter - > = Filter extends UpdateFilter ? I : never + type ExtractBase = Filter extends UpdateFilter + ? I + : never - type ExtractMod< - Base, - Filter extends UpdateFilter - > = Filter extends UpdateFilter ? I : never + type ExtractMod = Filter extends UpdateFilter + ? I + : never + + type ExtractState = Filter extends UpdateFilter + ? I + : never + + type TupleKeys = Exclude + type WrapBase = { + [K in TupleKeys]: { base: ExtractBase } + } + type Values = T[keyof T] + type UnwrapBase = T extends { base: any } ? T["base"] : never + type ExtractBaseMany = UnwrapBase>>> /** * Invert a filter by applying a NOT logical operation: @@ -156,11 +168,11 @@ export namespace filters { * * @param fn Filter to negate */ - export function not( - fn: UpdateFilter - ): UpdateFilter> { - return (upd, client) => { - const res = fn(upd, client) + export function not( + fn: UpdateFilter + ): UpdateFilter, State> { + return (upd, state) => { + const res = fn(upd, state) if (typeof res === 'boolean') return !res @@ -181,22 +193,22 @@ export namespace filters { * @param fn1 First filter * @param fn2 Second filter */ - export function and( - fn1: UpdateFilter, - fn2: UpdateFilter - ): UpdateFilter { - return (upd, client) => { - const res1 = fn1(upd, client) + export function and( + fn1: UpdateFilter, + fn2: UpdateFilter + ): UpdateFilter { + return (upd, state) => { + const res1 = fn1(upd, state as any) if (typeof res1 === 'boolean') { if (!res1) return false - return fn2(upd, client) + return fn2(upd, state as any) } return res1.then((r1) => { if (!r1) return false - return fn2(upd, client) + return fn2(upd, state as any) }) } } @@ -218,22 +230,22 @@ export namespace filters { * @param fn1 First filter * @param fn2 Second filter */ - export function or( - fn1: UpdateFilter, - fn2: UpdateFilter - ): UpdateFilter { - return (upd, cilent) => { - const res1 = fn1(upd, cilent) + export function or( + fn1: UpdateFilter, + fn2: UpdateFilter + ): UpdateFilter { + return (upd, state) => { + const res1 = fn1(upd, state as any) if (typeof res1 === 'boolean') { if (res1) return true - return fn2(upd, cilent) + return fn2(upd, state as any) } return res1.then((r1) => { if (r1) return true - return fn2(upd, cilent) + return fn2(upd, state as any) }) } } @@ -251,26 +263,28 @@ export namespace filters { * > * > This method is less efficient than {@link and} * + * > **Note**: This method *currently* does not propagate state + * > type. This might be fixed in the future, but for now either + * > use {@link and} or add type manually. + * * @param fns Filters to combine */ export function every[]>( ...fns: Filters ): UpdateFilter< - UnionToIntersection>, - UnionToIntersection< - ExtractMod, Filters[number]> - > + ExtractBaseMany, + UnionToIntersection> > { if (fns.length === 2) return and(fns[0], fns[1]) - return (upd, client) => { + return (upd, state) => { let i = 0 const max = fns.length const next = (): MaybeAsync => { if (i === max) return true - const res = fns[i++](upd, client) + const res = fns[i++](upd, state) if (typeof res === 'boolean') { if (!res) return false @@ -299,24 +313,29 @@ export namespace filters { * > * > This method is less efficient than {@link or} * + * > **Note**: This method *currently* does not propagate state + * > type. This might be fixed in the future, but for now either + * > use {@link or} or add type manually. + * * @param fns Filters to combine */ - export function some[]>( + export function some[]>( ...fns: Filters ): UpdateFilter< - UnionToIntersection>, - ExtractMod, Filters[number]> + ExtractBaseMany, + ExtractMod, + ExtractState > { if (fns.length === 2) return or(fns[0], fns[1]) - return (upd, client) => { + return (upd, state) => { let i = 0 const max = fns.length const next = (): MaybeAsync => { if (i === max) return false - const res = fns[i++](upd, client) + const res = fns[i++](upd, state) if (typeof res === 'boolean') { if (res) return true @@ -409,7 +428,7 @@ export namespace filters { * For chat member updates, uses `user.id` */ export const userId = ( - id: MaybeArray + id: MaybeArray ): UpdateFilter< | Message | InlineQuery @@ -420,118 +439,121 @@ export namespace filters { | UserStatusUpdate | UserTypingUpdate > => { - if (Array.isArray(id)) { - const index: Record = {} - let matchSelf = false - id.forEach((id) => { - if (id === 'me' || id === 'self') { - matchSelf = true - } else { - index[id] = true - } - }) + // TODO + return () => false - return (upd) => { - const ctor = upd.constructor - - if (ctor === Message) { - const sender = (upd as Message).sender - return ( - (matchSelf && sender.isSelf) || - sender.id in index || - sender.username! in index - ) - } else { - if ( - ctor === UserStatusUpdate || - ctor === UserTypingUpdate - ) { - const id = (upd as UserStatusUpdate | UserTypingUpdate) - .userId - return ( - (matchSelf && id === upd.client['_userId']) || - id in index - ) - } else { - const user = (upd as Exclude< - typeof upd, - Message | UserStatusUpdate | UserTypingUpdate - >).user - - return ( - (matchSelf && user.isSelf) || - user.id in index || - user.username! in index - ) - } - } - } - } - - if (id === 'me' || id === 'self') { - return (upd) => { - const ctor = upd.constructor - - if (ctor === Message) { - return (upd as Message).sender.isSelf - } else if ( - ctor === UserStatusUpdate || - ctor === UserTypingUpdate - ) { - return ( - (upd as UserStatusUpdate | UserTypingUpdate).userId === - upd.client['_userId'] - ) - } else { - return (upd as Exclude< - typeof upd, - Message | UserStatusUpdate | UserTypingUpdate - >).user.isSelf - } - } - } - - if (typeof id === 'string') { - return (upd) => { - const ctor = upd.constructor - - if (ctor === Message) { - return (upd as Message).sender.username === id - } else if ( - ctor === UserStatusUpdate || - ctor === UserTypingUpdate - ) { - // username is not available - return false - } else { - return ( - (upd as Exclude< - typeof upd, - Message | UserStatusUpdate | UserTypingUpdate - >).user.username === id - ) - } - } - } - - return (upd) => { - const ctor = upd.constructor - - if (ctor === Message) { - return (upd as Message).sender.id === id - } else if (ctor === UserStatusUpdate || ctor === UserTypingUpdate) { - return ( - (upd as UserStatusUpdate | UserTypingUpdate).userId === id - ) - } else { - return ( - (upd as Exclude< - typeof upd, - Message | UserStatusUpdate | UserTypingUpdate - >).user.id === id - ) - } - } + // if (Array.isArray(id)) { + // const index: Record = {} + // let matchSelf = false + // id.forEach((id) => { + // if (id === 'me' || id === 'self') { + // matchSelf = true + // } else { + // index[id] = true + // } + // }) + // + // return (upd) => { + // const ctor = upd.constructor + // + // if (ctor === Message) { + // const sender = (upd as Message).sender + // return ( + // (matchSelf && sender.isSelf) || + // sender.id in index || + // sender.username! in index + // ) + // } else { + // if ( + // ctor === UserStatusUpdate || + // ctor === UserTypingUpdate + // ) { + // const id = (upd as UserStatusUpdate | UserTypingUpdate) + // .userId + // return ( + // (matchSelf && id === upd.client['_userId']) || + // id in index + // ) + // } else { + // const user = (upd as Exclude< + // typeof upd, + // Message | UserStatusUpdate | UserTypingUpdate + // >).user + // + // return ( + // (matchSelf && user.isSelf) || + // user.id in index || + // user.username! in index + // ) + // } + // } + // } + // } + // + // if (id === 'me' || id === 'self') { + // return (upd) => { + // const ctor = upd.constructor + // + // if (ctor === Message) { + // return (upd as Message).sender.isSelf + // } else if ( + // ctor === UserStatusUpdate || + // ctor === UserTypingUpdate + // ) { + // return ( + // (upd as UserStatusUpdate | UserTypingUpdate).userId === + // upd.client['_userId'] + // ) + // } else { + // return (upd as Exclude< + // typeof upd, + // Message | UserStatusUpdate | UserTypingUpdate + // >).user.isSelf + // } + // } + // } + // + // if (typeof id === 'string') { + // return (upd) => { + // const ctor = upd.constructor + // + // if (ctor === Message) { + // return (upd as Message).sender.username === id + // } else if ( + // ctor === UserStatusUpdate || + // ctor === UserTypingUpdate + // ) { + // // username is not available + // return false + // } else { + // return ( + // (upd as Exclude< + // typeof upd, + // Message | UserStatusUpdate | UserTypingUpdate + // >).user.username === id + // ) + // } + // } + // } + // + // return (upd) => { + // const ctor = upd.constructor + // + // if (ctor === Message) { + // return (upd as Message).sender.id === id + // } else if (ctor === UserStatusUpdate || ctor === UserTypingUpdate) { + // return ( + // (upd as UserStatusUpdate | UserTypingUpdate).userId === id + // ) + // } else { + // return ( + // (upd as Exclude< + // typeof upd, + // Message | UserStatusUpdate | UserTypingUpdate + // >).user.id === id + // ) + // } + // } } /** @@ -894,7 +916,10 @@ export namespace filters { return (obj) => { const txt = extractText(obj) - return txt != null && txt.toLowerCase().substring(0, str.length) === str + return ( + txt != null && + txt.toLowerCase().substring(0, str.length) === str + ) } } @@ -925,7 +950,10 @@ export namespace filters { return (obj) => { const txt = extractText(obj) - return txt != null && txt.toLowerCase().substring(0, str.length) === str + return ( + txt != null && + txt.toLowerCase().substring(0, str.length) === str + ) } } diff --git a/packages/dispatcher/src/handler.ts b/packages/dispatcher/src/handler.ts index 60d79f65..f95f4207 100644 --- a/packages/dispatcher/src/handler.ts +++ b/packages/dispatcher/src/handler.ts @@ -4,8 +4,7 @@ import { TelegramClient, InlineQuery, CallbackQuery, - UsersIndex, - ChatsIndex, + PeersIndex, ChatMemberUpdate, PollVoteUpdate, UserStatusUpdate, @@ -37,14 +36,12 @@ export type RawUpdateHandler = BaseUpdateHandler< ( client: TelegramClient, update: tl.TypeUpdate | tl.TypeMessage, - users: UsersIndex, - chats: ChatsIndex + peers: PeersIndex ) => MaybeAsync, ( client: TelegramClient, update: tl.TypeUpdate | tl.TypeMessage, - users: UsersIndex, - chats: ChatsIndex + peers: PeersIndex ) => MaybeAsync > diff --git a/packages/dispatcher/src/state/key.ts b/packages/dispatcher/src/state/key.ts index 8f561211..b2395d2a 100644 --- a/packages/dispatcher/src/state/key.ts +++ b/packages/dispatcher/src/state/key.ts @@ -1,5 +1,5 @@ import { CallbackQuery, Message } from '@mtcute/client' -import { MaybeAsync } from '@mtcute/core' +import { longToFastString, MaybeAsync } from '@mtcute/core' /** * Function that determines how the state key is derived. diff --git a/packages/dispatcher/src/wizard.ts b/packages/dispatcher/src/wizard.ts index 587ab2da..59b0c002 100644 --- a/packages/dispatcher/src/wizard.ts +++ b/packages/dispatcher/src/wizard.ts @@ -1,6 +1,5 @@ import { MaybeAsync, Message } from '@mtcute/client' import { Dispatcher } from './dispatcher' -import { NewMessageHandler } from './handler' import { UpdateState } from './state' import { filters } from './filters' diff --git a/packages/file-id/package.json b/packages/file-id/package.json index f819778d..ca9b656b 100644 --- a/packages/file-id/package.json +++ b/packages/file-id/package.json @@ -12,8 +12,12 @@ "build": "tsc" }, "dependencies": { - "@mtcute/tl": "~1.131.0", + "@mtcute/tl": "^134.0.0", + "@mtcute/tl-runtime": "^1.0.0", "@mtcute/core": "^1.0.0", - "big-integer": "1.6.48" + "long": "^4.0.0" + }, + "devDependencies": { + "@types/long": "^4.0.1" } } diff --git a/packages/file-id/src/convert.ts b/packages/file-id/src/convert.ts index 85176cff..e24cafe2 100644 --- a/packages/file-id/src/convert.ts +++ b/packages/file-id/src/convert.ts @@ -3,7 +3,7 @@ import { tdFileId, tdFileId as td } from './types' import { parseFileId } from './parse' import { getBasicPeerType, markedPeerIdToBare } from '@mtcute/core' import FileType = tdFileId.FileType -import bigInt from 'big-integer' +import Long from 'long' const EMPTY_BUFFER = Buffer.alloc(0) @@ -12,7 +12,7 @@ type FileId = td.RawFullRemoteFileLocation function dialogPhotoToInputPeer( dialog: td.RawPhotoSizeSourceDialogPhoto | td.RawPhotoSizeSourceDialogPhotoLegacy ): tl.TypeInputPeer { - const markedPeerId = dialog.id.toJSNumber() + const markedPeerId = dialog.id const peerType = getBasicPeerType(markedPeerId) const peerId = markedPeerIdToBare(markedPeerId) @@ -87,7 +87,7 @@ export function fileIdToInputFileLocation( fileReference: fileId.fileReference, id: loc.id, accessHash: loc.accessHash, - volumeId: bigInt.zero, + volumeId: Long.ZERO, localId: 0, secret: loc.source.secret, } diff --git a/packages/file-id/src/parse.ts b/packages/file-id/src/parse.ts index d9c0193e..771cbe3c 100644 --- a/packages/file-id/src/parse.ts +++ b/packages/file-id/src/parse.ts @@ -1,9 +1,10 @@ import { telegramRleDecode } from './utils' import { tdFileId as td } from './types' -import { BinaryReader, parseUrlSafeBase64 } from '@mtcute/core' +import { parseUrlSafeBase64 } from '@mtcute/core' +import { TlBinaryReader } from '@mtcute/tl-runtime' function parseWebFileLocation( - reader: BinaryReader + reader: TlBinaryReader ): td.RawWebRemoteFileLocation { return { _: 'web', @@ -12,8 +13,8 @@ function parseWebFileLocation( } } -function parsePhotoSizeSource(reader: BinaryReader): td.TypePhotoSizeSource { - const variant = reader.int32() +function parsePhotoSizeSource(reader: TlBinaryReader): td.TypePhotoSizeSource { + const variant = reader.int() switch (variant) { case 0 /* LEGACY */: return { @@ -21,7 +22,7 @@ function parsePhotoSizeSource(reader: BinaryReader): td.TypePhotoSizeSource { secret: reader.long(), } case 1 /* THUMBNAIL */: { - const fileType = reader.int32() + const fileType = reader.int() if (fileType < 0 || fileType >= td.FileType.Size) throw new td.UnsupportedError( `Unsupported file type: ${fileType} (${reader.data.toString( @@ -29,7 +30,7 @@ function parsePhotoSizeSource(reader: BinaryReader): td.TypePhotoSizeSource { )})` ) - const thumbnailType = reader.int32() + const thumbnailType = reader.int() if (thumbnailType < 0 || thumbnailType > 255) { throw new td.InvalidFileIdError( `Wrong thumbnail type: ${thumbnailType} (${reader.data.toString( @@ -49,7 +50,7 @@ function parsePhotoSizeSource(reader: BinaryReader): td.TypePhotoSizeSource { return { _: 'dialogPhoto', big: variant === 3, - id: reader.long(), + id: reader.int53(), accessHash: reader.long(), } case 4 /* STICKERSET_THUMBNAIL */: @@ -63,7 +64,7 @@ function parsePhotoSizeSource(reader: BinaryReader): td.TypePhotoSizeSource { _: 'fullLegacy', volumeId: reader.long(), secret: reader.long(), - localId: reader.int32() + localId: reader.int(), } if (res.localId < 0) { @@ -77,10 +78,10 @@ function parsePhotoSizeSource(reader: BinaryReader): td.TypePhotoSizeSource { const res: td.RawPhotoSizeSourceDialogPhotoLegacy = { _: 'dialogPhotoLegacy', big: variant === 7, - id: reader.long(), + id: reader.int53(), accessHash: reader.long(), volumeId: reader.long(), - localId: reader.int32() + localId: reader.int(), } if (res.localId < 0) { @@ -95,7 +96,7 @@ function parsePhotoSizeSource(reader: BinaryReader): td.TypePhotoSizeSource { id: reader.long(), accessHash: reader.long(), volumeId: reader.long(), - localId: reader.int32() + localId: reader.int(), } if (res.localId < 0) { @@ -109,7 +110,7 @@ function parsePhotoSizeSource(reader: BinaryReader): td.TypePhotoSizeSource { _: 'stickerSetThumbnailVersion', id: reader.long(), accessHash: reader.long(), - version: reader.int32() + version: reader.int(), } default: throw new td.UnsupportedError( @@ -121,11 +122,9 @@ function parsePhotoSizeSource(reader: BinaryReader): td.TypePhotoSizeSource { } function parsePhotoFileLocation( - reader: BinaryReader, + reader: TlBinaryReader, version: number ): td.RawPhotoRemoteFileLocation { - // todo: check how tdlib handles volume ids - const id = reader.long() const accessHash = reader.long() let source: td.TypePhotoSizeSource @@ -138,13 +137,13 @@ function parsePhotoFileLocation( if (version >= 22) { source = parsePhotoSizeSource(reader) - localId = reader.int32() + localId = reader.int() } else { source = { _: 'fullLegacy', secret: reader.long(), - localId: reader.int32(), - volumeId + localId: reader.int(), + volumeId, } } @@ -153,8 +152,8 @@ function parsePhotoFileLocation( source = { _: 'fullLegacy', secret: reader.long(), - localId: reader.int32(), - volumeId + localId: reader.int(), + volumeId, } break case 'fullLegacy': @@ -167,7 +166,7 @@ function parsePhotoFileLocation( accessHash: source.accessHash, big: source.big, localId, - volumeId + volumeId, } break case 'stickerSetThumbnail': @@ -176,11 +175,13 @@ function parsePhotoFileLocation( id: source.id, accessHash: source.accessHash, localId, - volumeId + volumeId, } break default: - throw new td.InvalidFileIdError('Invalid PhotoSizeSource in legacy PhotoRemoteFileLocation') + throw new td.InvalidFileIdError( + 'Invalid PhotoSizeSource in legacy PhotoRemoteFileLocation' + ) } } @@ -188,12 +189,12 @@ function parsePhotoFileLocation( _: 'photo', id, accessHash, - source + source, } } function parseCommonFileLocation( - reader: BinaryReader + reader: TlBinaryReader ): td.RawCommonRemoteFileLocation { return { _: 'common', @@ -215,9 +216,9 @@ function fromPersistentIdV23( binary = telegramRleDecode(binary) - const reader = new BinaryReader(binary) + const reader = TlBinaryReader.manual(binary) - let fileType = reader.int32() + let fileType = reader.int() const isWeb = !!(fileType & td.WEB_LOCATION_FLAG) const hasFileReference = !!(fileType & td.FILE_REFERENCE_FLAG) @@ -230,12 +231,12 @@ function fromPersistentIdV23( `Unsupported file type: ${fileType} (${binary.toString('base64')})` ) - const dcId = reader.int32() + const dcId = reader.int() let fileReference: Buffer | null = null if (hasFileReference) { fileReference = reader.bytes() - if (fileReference.length === 1 && fileReference[0] === 0x23 /* # */) { + if (fileReference!.length === 1 && fileReference![0] === 0x23 /* # */) { // "invalid file reference" // see https://github.com/tdlib/td/blob/ed291840d3a841bb5b49457c88c57e8467e4a5b0/td/telegram/files/FileLocation.h#L32 fileReference = null diff --git a/packages/file-id/src/serialize-unique.ts b/packages/file-id/src/serialize-unique.ts index 4b316d13..b4e2cff5 100644 --- a/packages/file-id/src/serialize-unique.ts +++ b/packages/file-id/src/serialize-unique.ts @@ -1,7 +1,8 @@ import { tdFileId, tdFileId as td } from './types' -import { encodeUrlSafeBase64, BinaryWriter } from '@mtcute/core' +import { encodeUrlSafeBase64 } from '@mtcute/core' import { telegramRleEncode } from './utils' import FileType = tdFileId.FileType +import { TlBinaryWriter } from '@mtcute/tl-runtime' type InputUniqueLocation = | Pick @@ -74,38 +75,38 @@ export function toUniqueFileId( } } - let writer: BinaryWriter + let writer: TlBinaryWriter switch (inputLocation._) { case 'photo': { const source = inputLocation.source switch (source._) { case 'legacy': { // tdlib does not implement this - writer = BinaryWriter.alloc(16) - writer.int32(type) - writer.int32(100) + writer = TlBinaryWriter.manualAlloc(16) + writer.int(type) + writer.int(100) writer.long(source.secret) break } case 'stickerSetThumbnail': { // tdlib does not implement this - writer = BinaryWriter.alloc(24) - writer.int32(type) - writer.int32(150) + writer = TlBinaryWriter.manualAlloc(24) + writer.int(type) + writer.int(150) writer.long(source.id) writer.long(source.accessHash) break } case 'dialogPhoto': { - writer = BinaryWriter.alloc(13) - writer.int32(type) + writer = TlBinaryWriter.manualAlloc(13) + writer.int(type) writer.long(inputLocation.id) writer.raw(Buffer.from([+source.big])) // it doesn't matter to which Dialog the photo belongs break } case 'thumbnail': { - writer = BinaryWriter.alloc(13) + writer = TlBinaryWriter.manualAlloc(13) let thumbType = source.thumbnailType.charCodeAt(0) if (thumbType === 97 /* 'a' */) { @@ -116,7 +117,7 @@ export function toUniqueFileId( thumbType += 5 } - writer.int32(type) + writer.int(type) writer.long(inputLocation.id) writer.raw(Buffer.from([thumbType])) break @@ -124,31 +125,31 @@ export function toUniqueFileId( case 'fullLegacy': case 'dialogPhotoLegacy': case 'stickerSetThumbnailLegacy': - writer = BinaryWriter.alloc(16) - writer.int32(type) + writer = TlBinaryWriter.manualAlloc(16) + writer.int(type) writer.long(source.volumeId) - writer.int32(source.localId) + writer.int(source.localId) break case 'stickerSetThumbnailVersion': - writer = BinaryWriter.alloc(17) - writer.int32(type) + writer = TlBinaryWriter.manualAlloc(17) + writer.int(type) writer.raw(Buffer.from([2])) writer.long(source.id) - writer.int32(source.version) + writer.int(source.version) break } break } case 'web': - writer = BinaryWriter.alloc( + writer = TlBinaryWriter.alloc({}, Buffer.byteLength(inputLocation.url, 'utf-8') + 8 ) - writer.int32(type) + writer.int(type) writer.string(inputLocation.url) break case 'common': - writer = BinaryWriter.alloc(12) - writer.int32(type) + writer = TlBinaryWriter.manualAlloc(12) + writer.int(type) writer.long(inputLocation.id) break default: diff --git a/packages/file-id/src/serialize.ts b/packages/file-id/src/serialize.ts index 1c29fd81..cba68af2 100644 --- a/packages/file-id/src/serialize.ts +++ b/packages/file-id/src/serialize.ts @@ -1,6 +1,7 @@ import { tdFileId as td } from './types' -import { encodeUrlSafeBase64, BinaryWriter } from '@mtcute/core' +import { encodeUrlSafeBase64 } from '@mtcute/core' import { telegramRleEncode } from './utils' +import { TlBinaryWriter } from '@mtcute/tl-runtime' const SUFFIX = Buffer.from([td.CURRENT_VERSION, td.PERSISTENT_ID_VERSION]) @@ -19,7 +20,8 @@ export function toFileId( if (loc._ === 'web') type |= td.WEB_LOCATION_FLAG if (location.fileReference) type |= td.FILE_REFERENCE_FLAG - const writer = BinaryWriter.alloc( + const writer = TlBinaryWriter.alloc( + {}, loc._ === 'web' ? // overhead of the web file id: // 8-16 bytes header, @@ -31,8 +33,8 @@ export function toFileId( 100 ) - writer.int32(type) - writer.int32(location.dcId) + writer.int(type) + writer.int(location.dcId) if (location.fileReference) { writer.bytes(location.fileReference) } @@ -49,49 +51,49 @@ export function toFileId( switch (loc.source._) { case 'legacy': - writer.int32(0) + writer.int(0) writer.long(loc.source.secret) break case 'thumbnail': - writer.int32(1) - writer.int32(loc.source.fileType) - writer.int32(loc.source.thumbnailType.charCodeAt(0)) + writer.int(1) + writer.int(loc.source.fileType) + writer.int(loc.source.thumbnailType.charCodeAt(0)) break case 'dialogPhoto': - writer.int32(loc.source.big ? 3 : 2) - writer.long(loc.source.id) + writer.int(loc.source.big ? 3 : 2) + writer.int53(loc.source.id) writer.long(loc.source.accessHash) break case 'stickerSetThumbnail': - writer.int32(4) + writer.int(4) writer.long(loc.source.id) writer.long(loc.source.accessHash) break case 'fullLegacy': - writer.int32(5) + writer.int(5) writer.long(loc.source.volumeId) writer.long(loc.source.secret) - writer.int32(loc.source.localId) + writer.int(loc.source.localId) break case 'dialogPhotoLegacy': - writer.int32(loc.source.big ? 7 : 6) - writer.long(loc.source.id) + writer.int(loc.source.big ? 7 : 6) + writer.int53(loc.source.id) writer.long(loc.source.accessHash) writer.long(loc.source.volumeId) - writer.int32(loc.source.localId) + writer.int(loc.source.localId) break case 'stickerSetThumbnailLegacy': - writer.int32(8) + writer.int(8) writer.long(loc.source.id) writer.long(loc.source.accessHash) writer.long(loc.source.volumeId) - writer.int32(loc.source.localId) + writer.int(loc.source.localId) break case 'stickerSetThumbnailVersion': - writer.int32(9) + writer.int(9) writer.long(loc.source.id) writer.long(loc.source.accessHash) - writer.int32(loc.source.version) + writer.int(loc.source.version) break } diff --git a/packages/file-id/src/types.ts b/packages/file-id/src/types.ts index e7dca4f7..0ae7c2fa 100644 --- a/packages/file-id/src/types.ts +++ b/packages/file-id/src/types.ts @@ -1,6 +1,4 @@ -import { BigInteger } from 'big-integer' - -type Long = BigInteger +import Long from 'long' export namespace tdFileId { export const PERSISTENT_ID_VERSION_OLD = 2 @@ -100,7 +98,7 @@ export namespace tdFileId { export interface RawPhotoSizeSourceDialogPhoto { readonly _: 'dialogPhoto' readonly big: boolean - readonly id: Long + readonly id: number readonly accessHash: Long } diff --git a/packages/html-parser/package.json b/packages/html-parser/package.json index 9b39d010..fc8ed4bf 100644 --- a/packages/html-parser/package.json +++ b/packages/html-parser/package.json @@ -13,11 +13,12 @@ "docs": "npx typedoc" }, "dependencies": { - "@mtcute/tl": "~1.131.0", + "@mtcute/tl": "^134.0.0", "htmlparser2": "^6.0.1", - "big-integer": "^1.6.48" + "long": "^4.0.0" }, "devDependencies": { - "@mtcute/client": "^1.0.0" + "@mtcute/client": "^1.0.0", + "@types/long": "^4.0.1" } } diff --git a/packages/html-parser/src/index.ts b/packages/html-parser/src/index.ts index 5ebf98ed..5e7e2266 100644 --- a/packages/html-parser/src/index.ts +++ b/packages/html-parser/src/index.ts @@ -5,7 +5,7 @@ import type { } from '@mtcute/client' import { tl } from '@mtcute/tl' import { Parser } from 'htmlparser2' -import bigInt from 'big-integer' +import Long from 'long' const MENTION_REGEX = /^tg:\/\/user\?id=(\d+)(?:&hash=(-?[0-9a-fA-F]+)(?:&|$)|&|$)/ @@ -173,7 +173,9 @@ export class HtmlMessageEntityParser implements IMessageEntityParser { const mention = MENTION_REGEX.exec(url) if (mention) { + const id = parseInt(mention[1]) const accessHash = mention[2] + if (accessHash) { entity = { _: 'inputMessageEntityMentionName', @@ -181,8 +183,8 @@ export class HtmlMessageEntityParser implements IMessageEntityParser { length: 0, userId: { _: 'inputUser', - userId: parseInt(mention[1]), - accessHash: bigInt(accessHash, 16), + userId: id, + accessHash: Long.fromString(accessHash, false, 16), }, } } else { @@ -190,7 +192,7 @@ export class HtmlMessageEntityParser implements IMessageEntityParser { _: 'messageEntityMentionName', offset: plainText.length, length: 0, - userId: parseInt(mention[1]), + userId: id, } } } else { diff --git a/packages/markdown-parser/package.json b/packages/markdown-parser/package.json index 9f9d28c7..72c7c50c 100644 --- a/packages/markdown-parser/package.json +++ b/packages/markdown-parser/package.json @@ -13,10 +13,11 @@ "docs": "npx typedoc" }, "dependencies": { - "@mtcute/tl": "~1.131.0", - "big-integer": "^1.6.48" + "@mtcute/tl": "^134.0.0", + "long": "^4.0.0" }, "devDependencies": { - "@mtcute/client": "^1.0.0" + "@mtcute/client": "^1.0.0", + "@types/long": "^4.0.1" } } diff --git a/packages/markdown-parser/src/index.ts b/packages/markdown-parser/src/index.ts index 50899290..a17a8682 100644 --- a/packages/markdown-parser/src/index.ts +++ b/packages/markdown-parser/src/index.ts @@ -1,7 +1,7 @@ import type { IMessageEntityParser, MessageEntity } from '@mtcute/client' import { tl } from '@mtcute/tl' -import bigInt from 'big-integer' import { FormattedString } from '@mtcute/client' +import Long from 'long' const MENTION_REGEX = /^tg:\/\/user\?id=(\d+)(?:&hash=(-?[0-9a-fA-F]+)(?:&|$)|&|$)/ @@ -156,7 +156,7 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser { ;(ent as tl.Mutable).userId = { _: 'inputUser', userId, - accessHash: bigInt(accessHash, 16), + accessHash: Long.fromString(accessHash, false,16), } } else { ;(ent as tl.Mutable)._ = diff --git a/packages/mtproxy/index.ts b/packages/mtproxy/index.ts index f6757743..ebe871d6 100644 --- a/packages/mtproxy/index.ts +++ b/packages/mtproxy/index.ts @@ -124,7 +124,7 @@ export class MtProxyTcpTransport extends BaseTcpTransport { ) } - this._packetCodec.setupCrypto?.(this._crypto) + this._packetCodec.setup?.(this._crypto, this.log) this._packetCodec.on('error', (err) => this.emit('error', err)) this._packetCodec.on('packet', (buf) => this.emit('message', buf)) } diff --git a/packages/mtproxy/package.json b/packages/mtproxy/package.json index 7071285a..7e42b2cf 100644 --- a/packages/mtproxy/package.json +++ b/packages/mtproxy/package.json @@ -13,7 +13,6 @@ }, "dependencies": { "@mtcute/core": "^1.0.0", - "@mtcute/tl": "~1.131.0", "big-integer": "1.6.48" } } diff --git a/packages/sqlite/index.ts b/packages/sqlite/index.ts index 737bdb9a..1493a4e4 100644 --- a/packages/sqlite/index.ts +++ b/packages/sqlite/index.ts @@ -1,62 +1,57 @@ // noinspection SqlResolve import { - BinaryReader, - BinaryWriter, ITelegramStorage, + longFromFastString, + longToFastString, LruMap, - MAX_CHANNEL_ID, + TlBinaryWriter, + toggleChannelIdMark, } from '@mtcute/core' import { tl } from '@mtcute/tl' import sqlite3 from 'better-sqlite3' -import bigInt from 'big-integer' import { throttle } from '@mtcute/core' import { Logger } from '@mtcute/core/src/utils/logger' +import { TlBinaryReader, TlReaderMap, TlWriterMap } from '../tl-runtime' -function serializeAccessHash(hash: tl.Long): Buffer { - const arr = hash.toArray(256) - arr.value.push(arr.isNegative ? 1 : 0) - return Buffer.from(arr.value) -} - -function parseAccessHash(hash: Buffer): tl.Long { - const arr = hash.toJSON().data - return bigInt.fromArray(arr.slice(0, -1), 256, arr[arr.length - 1] as any) -} +// todo: add testMode to "self" function getInputPeer( row: SqliteEntity | ITelegramStorage.PeerInfo ): tl.TypeInputPeer { + const id = row.id + switch (row.type) { case 'user': return { _: 'inputPeerUser', - userId: row.id, + userId: id, accessHash: 'accessHash' in row ? row.accessHash - : parseAccessHash(row.hash), + : longFromFastString(row.hash), } case 'chat': return { _: 'inputPeerChat', - chatId: -row.id, + chatId: -id, } case 'channel': return { _: 'inputPeerChannel', - channelId: MAX_CHANNEL_ID - row.id, + channelId: toggleChannelIdMark(id), accessHash: 'accessHash' in row ? row.accessHash - : parseAccessHash(row.hash), + : longFromFastString(row.hash), } } throw new Error(`Invalid peer type: ${row.type}`) } -const CURRENT_VERSION = 1 +const CURRENT_VERSION = 2 + // language=SQLite const SCHEMA = ` create table kv ( @@ -82,7 +77,7 @@ const SCHEMA = ` create table entities ( id integer primary key, - hash blob not null, + hash text not null, type text not null, username text, phone text, @@ -105,7 +100,7 @@ const USERNAME_TTL = 86400000 // 24 hours interface SqliteEntity { id: number - hash: Buffer + hash: string type: string username?: string phone?: string @@ -173,7 +168,7 @@ export class SqliteStorage implements ITelegramStorage /*, IStateStorage */ { private _wal?: boolean - private _reader = new BinaryReader(EMPTY_BUFFER) + private _reader!: TlBinaryReader private _saveUnimportantLater: () => void @@ -181,6 +176,8 @@ export class SqliteStorage implements ITelegramStorage /*, IStateStorage */ { private _vacuumInterval: number private log!: Logger + private readerMap!: TlReaderMap + private writerMap!: TlWriterMap /** * @param filename Database file name, or `:memory:` for in-memory DB @@ -303,8 +300,11 @@ export class SqliteStorage implements ITelegramStorage /*, IStateStorage */ { // todo: add support for workers (idk if really needed, but still) } - setup(log: Logger): void { + setup(log: Logger, readerMap: TlReaderMap, writerMap: TlWriterMap): void { this.log = log.create('sqlite') + this.readerMap = readerMap + this.writerMap = writerMap + this._reader = new TlBinaryReader(readerMap, EMPTY_BUFFER) } private _readFullPeer(data: Buffer): tl.TypeUser | tl.TypeChat | null { @@ -352,7 +352,13 @@ export class SqliteStorage implements ITelegramStorage /*, IStateStorage */ { // eslint-disable-next-line @typescript-eslint/no-unused-vars private _upgradeDatabase(from: number): void { - // not needed (yet) since no versions except 1 // + if (from < 2 || from > CURRENT_VERSION) { + // 1 version was skipped during development + // yes i am too lazy to make auto-migrations for them + throw new Error( + 'Unsupported session version, please migrate manually' + ) + } } private _initializeStatements(): void { @@ -371,16 +377,24 @@ export class SqliteStorage implements ITelegramStorage /*, IStateStorage */ { if (hasTables) { // tables already exist, check version - this._initializeStatements() - const version = this._getFromKv('ver') + const version = this._db + .prepare("select value from kv where key = 'ver'") + .get().value + this.log.debug('current db version = %d', version) + if (version < CURRENT_VERSION) { this._upgradeDatabase(version) - this._setToKv('ver', CURRENT_VERSION, true) + this._db + .prepare("update kv set value = ? where key = 'ver'") + .run(CURRENT_VERSION) } + + // prepared statements expect latest schema, so we need to upgrade first + this._initializeStatements() } else { // create tables - this.log.debug('creating tables') + this.log.debug('creating tables, db version = %d', CURRENT_VERSION) this._db.exec(SCHEMA) this._initializeStatements() this._setToKv('ver', CURRENT_VERSION, true) @@ -394,7 +408,7 @@ export class SqliteStorage implements ITelegramStorage /*, IStateStorage */ { load(): void { this._db = sqlite3(this._filename, { - verbose: this.log.mgr.level === 4 ? this.log.debug : undefined, + verbose: this.log.mgr.level === 5 ? this.log.verbose : undefined, }) this._initialize() @@ -533,7 +547,7 @@ export class SqliteStorage implements ITelegramStorage /*, IStateStorage */ { peer.username, peer.phone, Date.now(), - BinaryWriter.serializeObject(peer.full), + TlBinaryWriter.serializeObject(this.writerMap, peer.full), peer.id, ] cached.full = peer.full @@ -544,12 +558,15 @@ export class SqliteStorage implements ITelegramStorage /*, IStateStorage */ { this._statements.upsertEnt, [ peer.id, - serializeAccessHash(peer.accessHash), + longToFastString(peer.accessHash), peer.type, peer.username, peer.phone, Date.now(), - BinaryWriter.serializeObject(peer.full), + TlBinaryWriter.serializeObject( + this.writerMap, + peer.full + ), ], ]) this._addToCache(peer.id, { diff --git a/packages/sqlite/package.json b/packages/sqlite/package.json index d94f2c30..de6b49c8 100644 --- a/packages/sqlite/package.json +++ b/packages/sqlite/package.json @@ -13,12 +13,13 @@ }, "dependencies": { "@mtcute/core": "^1.0.0", - "@mtcute/tl": "~1.131.0", + "@mtcute/tl-runtime": "^1.0.0", "better-sqlite3": "^7.4.0", - "big-integer": "1.6.48" + "long": "^4.0.0" }, "devDependencies": { "@types/sqlite3": "^3.1.7", - "@types/better-sqlite3": "^5.4.1" + "@types/better-sqlite3": "^5.4.1", + "@types/long": "^4.0.1" } } diff --git a/packages/tl-runtime/package.json b/packages/tl-runtime/package.json new file mode 100644 index 00000000..db784efa --- /dev/null +++ b/packages/tl-runtime/package.json @@ -0,0 +1,25 @@ +{ + "name": "@mtcute/tl-runtime", + "private": true, + "version": "1.0.0", + "description": "Runtime for TL", + "author": "Alisa Sireneva ", + "license": "LGPL-3.0", + "main": "src/index.ts", + "scripts": { + "test": "mocha -r ts-node/register tests/**/*.spec.ts", + "docs": "npx typedoc", + "build": "tsc" + }, + "browser": { + "./platform/gzip.js": "./platform/gzip.web.js" + }, + "dependencies": { + "long": "4.0.0", + "crc-32": "1.2.0", + "pako": "2.0.2" + }, + "devDependencies": { + "@types/long": "4.0.1" + } +} diff --git a/packages/tl-runtime/src/index.ts b/packages/tl-runtime/src/index.ts new file mode 100644 index 00000000..f7c89e20 --- /dev/null +++ b/packages/tl-runtime/src/index.ts @@ -0,0 +1,3 @@ +export * from './reader' +export * from './writer' +export * from './platform/gzip' diff --git a/packages/tl-runtime/src/platform/gzip.ts b/packages/tl-runtime/src/platform/gzip.ts new file mode 100644 index 00000000..febf5463 --- /dev/null +++ b/packages/tl-runtime/src/platform/gzip.ts @@ -0,0 +1,21 @@ +import { deflateSync, gunzipSync } from 'zlib' + +export function gzipInflate(buf: Buffer): Buffer { + return gunzipSync(buf) +} + +export function gzipDeflate(buf: Buffer, maxRatio?: number): Buffer | null { + if (maxRatio) { + try { + return deflateSync(buf, { maxOutputLength: Math.floor(buf.length * maxRatio) }) + } catch (e) { + if (e.code === 'ERR_BUFFER_TOO_LARGE') { + return null + } + + throw e + } + } + + return deflateSync(buf) +} diff --git a/packages/tl-runtime/src/platform/gzip.web.ts b/packages/tl-runtime/src/platform/gzip.web.ts new file mode 100644 index 00000000..3787f98e --- /dev/null +++ b/packages/tl-runtime/src/platform/gzip.web.ts @@ -0,0 +1,39 @@ +import { typedArrayToBuffer } from '../buffer-utils' +import { Data, Deflate, inflate } from 'pako' + +export function gzipInflate(buf: Buffer): Buffer { + return typedArrayToBuffer(inflate(buf)) +} + +class DeflateLimited extends Deflate { + constructor(readonly limit: number) { + super() + } + + _size = 0 + + onData(chunk: Data) { + this._size += chunk.length + + if (this._size > this.limit) { + throw 'ERR_SIZE' + } + + super.onData(chunk) + } +} + +export function gzipDeflate(buf: Buffer, maxRatio?: number): Buffer | null { + const deflator = maxRatio + ? new DeflateLimited(Math.floor(buf.length * maxRatio)) + : new Deflate() + + try { + deflator.push(buf, true) + } catch (e) { + if (e === 'ERR_SIZE') return null + throw e + } + + return typedArrayToBuffer(deflator.result as Uint8Array) +} diff --git a/packages/core/src/utils/binary/binary-reader.ts b/packages/tl-runtime/src/reader.ts similarity index 63% rename from packages/core/src/utils/binary/binary-reader.ts rename to packages/tl-runtime/src/reader.ts index a106e21f..74d7599a 100644 --- a/packages/core/src/utils/binary/binary-reader.ts +++ b/packages/tl-runtime/src/reader.ts @@ -1,64 +1,79 @@ -import bigInt, { BigInteger } from 'big-integer' -import { inflate } from 'pako' -import { typedArrayToBuffer } from '../buffer-utils' -import readerMap, { ITlBinaryReader } from '@mtcute/tl/binary/reader' -import { bufferToBigInt, longToUlong, ulongToLong } from '../bigint-utils' -import { tl } from '@mtcute/tl' +import { gzipInflate } from './platform/gzip' +import Long from 'long' -const isNativeBigIntAvailable = - typeof BigInt !== 'undefined' && 'readBigInt64LE' in Buffer.prototype +const TWO_PWR_32_DBL = (1 << 16) * (1 << 16) -export class BinaryReader implements ITlBinaryReader { +/** + * Mapping of TL object IDs to reader functions. + * + * Note that these types should never be present in the map + * and are parsed manually inside `.object()`: + * - `0x1cb5c415` aka `vector` + * - `0x3072cfa1` aka `gzip_packed` + * - `0xbc799737` aka `boolFalse` + * - `0x997275b5` aka `boolTrue` + * - `0x3fedd339` aka `true` + * - `0x56730bcc` aka `null` + */ +export type TlReaderMap = Record any> + +export class TlBinaryReader { data: Buffer pos = 0 - _objectsMap = readerMap - - constructor(data: Buffer, start = 0) { + constructor( + readonly objectsMap: TlReaderMap | undefined, + data: Buffer, + start = 0 + ) { this.data = data this.pos = start } - static deserializeObject(data: Buffer, start = 0): T { - return new BinaryReader(data, start).object() + static manual(data: Buffer, start = 0): TlBinaryReader { + return new TlBinaryReader(undefined, data, start) } - int32(): number { + static deserializeObject( + objectsMap: TlReaderMap, + data: Buffer, + start = 0 + ): any { + return new TlBinaryReader(objectsMap, data, start).object() + } + + int(): number { const res = this.data.readInt32LE(this.pos) this.pos += 4 return res } - uint32(): number { + uint(): number { const res = this.data.readUInt32LE(this.pos) this.pos += 4 return res } - long(unsigned = false): BigInteger { - let big: BigInteger - if (isNativeBigIntAvailable) { - const val = this.data.readBigInt64LE(this.pos) - big = bigInt(val) + peekUint(): number { + // e.g. for checking ctor number + return this.data.readUInt32LE(this.pos) + } - if (unsigned) big = longToUlong(big) - } else { - big = bufferToBigInt(this.data, this.pos, 8, true) - if (!unsigned) big = ulongToLong(big) - } + int53(): number { + // inlined toNumber from Long + const res = + (this.data.readInt32LE(this.pos) >>> 0) + + TWO_PWR_32_DBL * this.data.readInt32LE(this.pos + 4) + this.pos += 8 + return res + } + + long(unsigned = false): Long { + const lo = this.data.readInt32LE(this.pos) + const hi = this.data.readInt32LE(this.pos + 4) this.pos += 8 - - return big - } - - // for use in generated code - ulong(): BigInteger { - return this.long(true) - } - - rawLong(): Buffer { - return this.data.slice(this.pos, (this.pos += 8)) + return new Long(lo, hi, unsigned) } float(): number { @@ -74,7 +89,7 @@ export class BinaryReader implements ITlBinaryReader { } boolean(): boolean { - const val = this.uint32() + const val = this.uint() if (val === 0xbc799737) return false if (val === 0x997275b5) return true throw new Error( @@ -111,7 +126,7 @@ export class BinaryReader implements ITlBinaryReader { } const data = this.raw(length) - if (padding > 0) this.raw(4 - padding) + if (padding > 0) this.pos += 4 - padding return data } @@ -121,7 +136,7 @@ export class BinaryReader implements ITlBinaryReader { } object(): any { - const id = this.uint32() + const id = this.uint() if (id === 0x1cb5c415 /* vector */) return this.vector(this.object, true) @@ -133,7 +148,7 @@ export class BinaryReader implements ITlBinaryReader { // never used in the actual schema, but whatever if (id === 0x56730bcc /* null */) return null - const reader = this._objectsMap[id] + const reader = this.objectsMap![id] if (!reader) { // unknown type. bruh moment. // mtproto sucks and there's no way we can just skip it @@ -148,24 +163,25 @@ export class BinaryReader implements ITlBinaryReader { throw error } - return reader.call(this) + return reader(this) } gzip(): any { - return new BinaryReader( - typedArrayToBuffer(inflate(this.bytes())) + return new TlBinaryReader( + this.objectsMap, + gzipInflate(this.bytes()) ).object() } vector(reader = this.object, bare = false): any[] { if (!bare) { - if (this.uint32() !== 0x1cb5c415) + if (this.uint() !== 0x1cb5c415) throw new Error( 'Invalid object code, expected 0x1cb5c415 (vector)' ) } - const length = this.uint32() + const length = this.uint() const ret = [] for (let i = 0; i < length; i++) ret.push(reader.call(this)) return ret diff --git a/packages/tl-runtime/src/writer.ts b/packages/tl-runtime/src/writer.ts new file mode 100644 index 00000000..51b6fd2b --- /dev/null +++ b/packages/tl-runtime/src/writer.ts @@ -0,0 +1,254 @@ +import Long from 'long' + +const TWO_PWR_32_DBL = (1 << 16) * (1 << 16) + +/** + * Mapping of TL object names to writer functions. + */ +export type TlWriterMap = Record void> + +type SerializableObject = { _: string } + +export class TlSerializationCounter { + count = 0 + + constructor(readonly objectMap: TlWriterMap) {} + + static countNeededBytes( + objectMap: TlWriterMap, + obj: SerializableObject + ): number { + const cnt = new TlSerializationCounter(objectMap) + cnt.object(obj) + return cnt.count + } + + static countBytesOverhead(size: number): number { + let res = 0 + + let padding + if (size <= 253) { + res += 1 + padding = (size + 1) % 4 + } else { + res += 4 + padding = size % 4 + } + + if (padding > 0) res += 4 - padding + + return res + } + + boolean(): void { + this.count += 4 + } + + double(): void { + this.count += 8 + } + + float(): void { + this.count += 4 + } + + int128(): void { + this.count += 16 + } + + int256(): void { + this.count += 32 + } + + int(): void { + this.count += 4 + } + + uint(): void { + this.count += 4 + } + + int53(): void { + this.count += 8 + } + + long(): void { + this.count += 8 + } + + null(): void { + this.count += 4 + } + + raw(val: Buffer): void { + this.count += val.length + } + + bytes(val: Buffer): void { + this.count += + TlSerializationCounter.countBytesOverhead(val.length) + val.length + } + + string(val: string): void { + const length = Buffer.byteLength(val, 'utf8') + this.count += TlSerializationCounter.countBytesOverhead(length) + length + } + + object(obj: SerializableObject): void { + if (!this.objectMap[obj._]) throw new Error(`Unknown object ${obj._}`) + this.objectMap[obj._](this, obj) + } + + vector(fn: Function, items: unknown[]): void { + this.count += 8 + items.forEach((it) => fn.call(this, it)) + } +} + +export class TlBinaryWriter { + buffer: Buffer + pos: number + + constructor( + readonly objectMap: TlWriterMap | undefined, + buffer: Buffer, + start = 0 + ) { + this.buffer = buffer + this.pos = start + } + + static alloc(objectMap: TlWriterMap, size: number): TlBinaryWriter { + return new TlBinaryWriter(objectMap, Buffer.allocUnsafe(size)) + } + + static manual(buffer: Buffer, start = 0): TlBinaryWriter { + return new TlBinaryWriter(undefined, buffer, start) + } + + static manualAlloc(size: number): TlBinaryWriter { + return new TlBinaryWriter(undefined, Buffer.allocUnsafe(size)) + } + + static serializeObject( + objectMap: TlWriterMap, + obj: SerializableObject, + knownSize = -1 + ): Buffer { + if (knownSize === -1) + knownSize = TlSerializationCounter.countNeededBytes(objectMap, obj) + + const writer = TlBinaryWriter.alloc(objectMap, knownSize) + + writer.object(obj) + return writer.buffer + } + + int(val: number): void { + this.buffer.writeInt32LE(val, this.pos) + this.pos += 4 + } + + uint(val: number): void { + this.buffer.writeUInt32LE(val, this.pos) + this.pos += 4 + } + + int53(val: number): void { + // inlined fromNumber from Long + this.buffer.writeInt32LE((val % TWO_PWR_32_DBL) | 0, this.pos) + if (val < 0) { + this.buffer.writeInt32LE((val / TWO_PWR_32_DBL - 1) | 0, this.pos + 4) + } else { + this.buffer.writeInt32LE((val / TWO_PWR_32_DBL) | 0, this.pos + 4) + } + + this.pos += 8 + } + + null(): void { + this.uint(0x56730bcc) + } + + long(val: Long): void { + this.buffer.writeInt32LE(val.low, this.pos) + this.buffer.writeInt32LE(val.high, this.pos + 4) + + this.pos += 8 + } + + float(val: number): void { + this.buffer.writeFloatLE(val, this.pos) + this.pos += 4 + } + + double(val: number): void { + this.buffer.writeDoubleLE(val, this.pos) + this.pos += 8 + } + + boolean(val: boolean): void { + this.buffer.writeUInt32LE(val ? 0x997275b5 : 0xbc799737, this.pos) + this.pos += 4 + } + + raw(val: Buffer): void { + val.copy(this.buffer, this.pos) + this.pos += val.length + } + + int128(val: Buffer): void { + val.copy(this.buffer, this.pos) + this.pos += 16 + } + + int256(val: Buffer): void { + val.copy(this.buffer, this.pos) + this.pos += 32 + } + + bytes(val: Buffer): void { + const length = val.length + let padding + if (length <= 253) { + this.buffer[this.pos++] = val.length + padding = (length + 1) % 4 + } else { + this.buffer[this.pos++] = 254 + this.buffer[this.pos++] = val.length & 0xff + this.buffer[this.pos++] = (val.length >> 8) & 0xff + this.buffer[this.pos++] = (val.length >> 16) & 0xff + padding = length % 4 + } + + val.copy(this.buffer, this.pos) + this.pos += val.length + + if (padding > 0) { + padding = 4 - padding + + while (padding--) this.buffer[this.pos++] = 0 + } + } + + string(val: string): void { + this.bytes(Buffer.from(val, 'utf-8')) + } + + object(obj: SerializableObject): void { + const fn = this.objectMap![obj._] + if (!fn) throw new Error(`Unknown object ${obj._}`) + fn(this, obj) + } + + vector(fn: Function, val: unknown[], bare?: boolean): void { + if (!bare) this.uint(0x1cb5c415) + this.uint(val.length) + + val.forEach((it) => fn.call(this, it, bare)) + } + + result(): Buffer { + return this.buffer.slice(0, this.pos) + } +} diff --git a/packages/core/tests/binary-reader.spec.ts b/packages/tl-runtime/tests/binary-reader.spec.ts similarity index 63% rename from packages/core/tests/binary-reader.spec.ts rename to packages/tl-runtime/tests/binary-reader.spec.ts index c5a5bec7..522f8f26 100644 --- a/packages/core/tests/binary-reader.spec.ts +++ b/packages/tl-runtime/tests/binary-reader.spec.ts @@ -1,47 +1,75 @@ import { describe, it } from 'mocha' import { expect } from 'chai' -import { BinaryReader } from '../src/utils/binary/binary-reader' -import { randomBytes } from '../src/utils/buffer-utils' -import bigInt from 'big-integer' +import { TlBinaryReader, TlReaderMap } from '../src' +import Long from 'long' +import { randomBytes } from 'crypto' -describe('BinaryReader', () => { +describe('TlBinaryReader', () => { it('should read int32', () => { - expect(new BinaryReader(Buffer.from([0, 0, 0, 0])).int32()).eq(0) - expect(new BinaryReader(Buffer.from([1, 0, 0, 0])).int32()).eq(1) - expect(new BinaryReader(Buffer.from([1, 2, 3, 4])).int32()).eq(67305985) + expect(TlBinaryReader.manual(Buffer.from([0, 0, 0, 0])).int()).eq(0) + expect(TlBinaryReader.manual(Buffer.from([1, 0, 0, 0])).int()).eq(1) + expect(TlBinaryReader.manual(Buffer.from([1, 2, 3, 4])).int()).eq( + 67305985 + ) expect( - new BinaryReader(Buffer.from([0xff, 0xff, 0xff, 0xff])).int32() + new TlBinaryReader( + {}, + Buffer.from([0xff, 0xff, 0xff, 0xff]) + ).int() ).eq(-1) }) it('should read uint32', () => { - expect(new BinaryReader(Buffer.from([0, 0, 0, 0])).uint32()).eq(0) - expect(new BinaryReader(Buffer.from([1, 0, 0, 0])).uint32()).eq(1) - expect(new BinaryReader(Buffer.from([1, 2, 3, 4])).uint32()).eq( + expect(TlBinaryReader.manual(Buffer.from([0, 0, 0, 0])).uint()).eq(0) + expect(TlBinaryReader.manual(Buffer.from([1, 0, 0, 0])).uint()).eq(1) + expect(TlBinaryReader.manual(Buffer.from([1, 2, 3, 4])).uint()).eq( 67305985 ) expect( - new BinaryReader(Buffer.from([0xff, 0xff, 0xff, 0xff])).uint32() + new TlBinaryReader( + {}, + Buffer.from([0xff, 0xff, 0xff, 0xff]) + ).uint() ).eq(4294967295) }) + it('should read int53', () => { + expect(TlBinaryReader.manual(Buffer.from([0, 0, 0, 0, 0, 0, 0, 0])).int53()).eq(0) + expect(TlBinaryReader.manual(Buffer.from([1, 0, 0, 0, 0, 0, 0, 0])).int53()).eq(1) + expect(TlBinaryReader.manual(Buffer.from([1, 2, 3, 4, 0, 0, 0, 0])).int53()).eq( + 67305985 + ) + expect(TlBinaryReader.manual(Buffer.from([1, 0, 1, 0, 1, 0, 1, 0])).int53()).eq( + 281479271743489 + ) + expect( + new TlBinaryReader( + {}, + Buffer.from([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]) + ).int53() + ).eq(-1) + }) + it('should read long', () => { expect( - new BinaryReader( + new TlBinaryReader( + {}, Buffer.from([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]) ) .long() .toString() ).eq('-1') expect( - new BinaryReader( + new TlBinaryReader( + {}, Buffer.from([0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78]) ) .long() .toString() ).eq('8671175386481439762') expect( - new BinaryReader( + new TlBinaryReader( + {}, Buffer.from([0x15, 0xc4, 0x15, 0xb5, 0xc4, 0x1c, 0x03, 0xa3]) ) .long() @@ -51,52 +79,62 @@ describe('BinaryReader', () => { it('should read float', () => { expect( - new BinaryReader(Buffer.from([0, 0, 0x80, 0x3f])).float() + TlBinaryReader.manual(Buffer.from([0, 0, 0x80, 0x3f])).float() ).closeTo(1, 0.001) expect( - new BinaryReader(Buffer.from([0xb6, 0xf3, 0x9d, 0x3f])).float() + new TlBinaryReader( + {}, + Buffer.from([0xb6, 0xf3, 0x9d, 0x3f]) + ).float() ).closeTo(1.234, 0.001) expect( - new BinaryReader(Buffer.from([0xfa, 0x7e, 0x2a, 0x3f])).float() + new TlBinaryReader( + {}, + Buffer.from([0xfa, 0x7e, 0x2a, 0x3f]) + ).float() ).closeTo(0.666, 0.001) }) it('should read double', () => { expect( - new BinaryReader( + new TlBinaryReader( + {}, Buffer.from([0, 0, 0, 0, 0, 0, 0xf0, 0x3f]) ).double() ).closeTo(1, 0.001) expect( - new BinaryReader( + new TlBinaryReader( + {}, Buffer.from([0, 0, 0, 0, 0, 0, 0x25, 0x40]) ).double() ).closeTo(10.5, 0.001) expect( - new BinaryReader( + new TlBinaryReader( + {}, Buffer.from([0x9a, 0x99, 0x99, 0x99, 0x99, 0x99, 0x21, 0x40]) ).double() ).closeTo(8.8, 0.001) }) it('should read raw bytes', () => { - expect([...new BinaryReader(Buffer.from([1, 2, 3, 4])).raw(2)]).eql([ - 1, - 2, - ]) - expect([...new BinaryReader(Buffer.from([1, 2, 3, 4])).raw()]).eql([ - 1, - 2, - 3, - 4, - ]) - expect([...new BinaryReader(Buffer.from([1, 2, 3, 4])).raw(0)]).eql([]) + expect([ + ...TlBinaryReader.manual(Buffer.from([1, 2, 3, 4])).raw(2), + ]).eql([1, 2]) + expect([ + ...TlBinaryReader.manual(Buffer.from([1, 2, 3, 4])).raw(), + ]).eql([1, 2, 3, 4]) + expect([ + ...TlBinaryReader.manual(Buffer.from([1, 2, 3, 4])).raw(0), + ]).eql([]) }) it('should move cursor', () => { - const reader = new BinaryReader(Buffer.from([1, 2, 3, 4, 5, 6, 7, 8])) + const reader = new TlBinaryReader( + {}, + Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]) + ) - reader.int32() + reader.int() expect(reader.pos).eq(4) reader.seek(-4) expect(reader.pos).eq(0) @@ -104,7 +142,7 @@ describe('BinaryReader', () => { expect(() => reader.seek(-1)).to.throw(RangeError) expect(() => reader.seek(1000)).to.throw(RangeError) - reader.uint32() + reader.uint() expect(reader.pos).eq(4) reader.seekTo(0) expect(reader.pos).eq(0) @@ -125,12 +163,13 @@ describe('BinaryReader', () => { }) it('should read tg-encoded bytes', () => { - expect([...new BinaryReader(Buffer.from([1, 2, 3, 4])).bytes()]).eql([ - 2, - ]) + expect([ + ...TlBinaryReader.manual(Buffer.from([1, 2, 3, 4])).bytes(), + ]).eql([2]) const random250bytes = randomBytes(250) - let reader = new BinaryReader( + let reader = new TlBinaryReader( + {}, Buffer.from([250, ...random250bytes, 0, 0, 0, 0, 0]) ) expect([...reader.bytes()]).eql([...random250bytes]) @@ -141,21 +180,21 @@ describe('BinaryReader', () => { buffer[0] = 254 buffer.writeIntLE(1000, 1, 3) buffer.set(random1000bytes, 4) - reader = new BinaryReader(buffer) + reader = TlBinaryReader.manual(buffer) expect([...reader.bytes()]).eql([...random1000bytes]) expect(reader.pos).eq(1004) }) - const stubObjectsMap = { - 0xdeadbeef: function () { - return { a: this.int32(), b: this.object() } + const stubObjectsMap: TlReaderMap = { + 0xdeadbeef: function (r) { + return { a: r.int(), b: r.object() } }, - 0xbaadc0de: function () { - return this.uint32() + 0xbaadc0de: function (r) { + return r.uint() }, 0xfacedead: () => 42, - 0xbebf0c3d: function () { - return { vec: this.vector(this.uint32) } + 0xbebf0c3d: function (r) { + return { vec: r.vector(r.uint) } }, } @@ -183,8 +222,7 @@ describe('BinaryReader', () => { 0x00, 0x00, // int32 2 ]) - const reader = new BinaryReader(buffer) - reader._objectsMap = stubObjectsMap + const reader = new TlBinaryReader(stubObjectsMap, buffer) const deadBeef = reader.object() expect(deadBeef).eql({ a: 1, b: 42 }) @@ -246,8 +284,7 @@ describe('BinaryReader', () => { 0x00, 0x00, // int32 2 ]) - const reader = new BinaryReader(buffer) - reader._objectsMap = stubObjectsMap + const reader = new TlBinaryReader(stubObjectsMap, buffer) const vector = reader.vector() expect(vector).eql([{ a: 1, b: 42 }, 2, { vec: [1, 2] }]) @@ -262,6 +299,17 @@ describe('BinaryReader', () => { it('should be able to read resPQ', () => { const input = '000000000000000001c8831ec97ae55140000000632416053e0549828cca27e966b301a48fece2fca5cf4d33f4a11ea877ba4aa5739073300817ed48941a08f98100000015c4b51c01000000216be86c022bb4c3' + const map: TlReaderMap = { + 85337187: function (r) { + const ret: any = {} + ret._ = 'mt_resPQ' + ret.nonce = r.int128() + ret.serverNonce = r.int128() + ret.pq = r.bytes() + ret.serverPublicKeyFingerprints = r.vector(r.long) + return ret + }, + } const expected = { nonce: Buffer.from('3E0549828CCA27E966B301A48FECE2FC', 'hex'), @@ -270,13 +318,15 @@ describe('BinaryReader', () => { 'hex' ), pq: Buffer.from('17ED48941A08F981', 'hex'), - serverPublicKeyFingerprints: [bigInt('c3b42b026ce86b21', 16)], + serverPublicKeyFingerprints: [ + Long.fromString('c3b42b026ce86b21', false, 16), + ], } - const r = new BinaryReader(Buffer.from(input, 'hex')) + const r = new TlBinaryReader(map, Buffer.from(input, 'hex')) expect(r.long().toString()).eq('0') // authKeyId expect(r.long().toString(16)).eq('51E57AC91E83C801'.toLowerCase()) // messageId - expect(r.uint32()).eq(64) // messageLength + expect(r.uint()).eq(64) // messageLength const obj = r.object() expect(obj._).eq('mt_resPQ') diff --git a/packages/core/tests/binary-writer.spec.ts b/packages/tl-runtime/tests/binary-writer.spec.ts similarity index 58% rename from packages/core/tests/binary-writer.spec.ts rename to packages/tl-runtime/tests/binary-writer.spec.ts index d667e82d..15599eb6 100644 --- a/packages/core/tests/binary-writer.spec.ts +++ b/packages/tl-runtime/tests/binary-writer.spec.ts @@ -1,48 +1,56 @@ import { describe, it } from 'mocha' import { expect } from 'chai' import { - BinaryWriter, - SerializationCounter, -} from '../src/utils/binary/binary-writer' -import bigInt from 'big-integer' -import { randomBytes } from '../src/utils/buffer-utils' -import { tl } from '@mtcute/tl' -import { ITlBinaryWriter } from '@mtcute/tl/binary/writer' + TlBinaryWriter, + TlSerializationCounter, + TlWriterMap +} from '../src' +import Long from 'long' +import { randomBytes } from 'crypto' -describe('BinaryWriter', () => { +describe('TlBinaryWriter', () => { const testSingleMethod = ( size: number, - fn: (w: BinaryWriter) => void + fn: (w: TlBinaryWriter) => void, + map: TlWriterMap = {} ): string => { - const w = BinaryWriter.alloc(size) + const w = TlBinaryWriter.alloc(map, size) fn(w) expect(w.pos).eq(size) return w.buffer.toString('hex') } it('should write int32', () => { - expect(testSingleMethod(4, (w) => w.int32(0))).eq('00000000') - expect(testSingleMethod(4, (w) => w.int32(1))).eq('01000000') - expect(testSingleMethod(4, (w) => w.int32(67305985))).eq('01020304') - expect(testSingleMethod(4, (w) => w.int32(-1))).eq('ffffffff') + expect(testSingleMethod(4, (w) => w.int(0))).eq('00000000') + expect(testSingleMethod(4, (w) => w.int(1))).eq('01000000') + expect(testSingleMethod(4, (w) => w.int(67305985))).eq('01020304') + expect(testSingleMethod(4, (w) => w.int(-1))).eq('ffffffff') }) it('should write uint32', () => { - expect(testSingleMethod(4, (w) => w.uint32(0))).eq('00000000') - expect(testSingleMethod(4, (w) => w.uint32(1))).eq('01000000') - expect(testSingleMethod(4, (w) => w.uint32(67305985))).eq('01020304') - expect(testSingleMethod(4, (w) => w.uint32(4294967295))).eq('ffffffff') + expect(testSingleMethod(4, (w) => w.uint(0))).eq('00000000') + expect(testSingleMethod(4, (w) => w.uint(1))).eq('01000000') + expect(testSingleMethod(4, (w) => w.uint(67305985))).eq('01020304') + expect(testSingleMethod(4, (w) => w.uint(4294967295))).eq('ffffffff') + }) + + it('should write int53', () => { + expect(testSingleMethod(8, (w) => w.int53(0))).eq('0000000000000000') + expect(testSingleMethod(8, (w) => w.int53(1))).eq('0100000000000000') + expect(testSingleMethod(8, (w) => w.int53(67305985))).eq('0102030400000000') + expect(testSingleMethod(8, (w) => w.int53(281479271743489))).eq('0100010001000100') + expect(testSingleMethod(8, (w) => w.int53(-1))).eq('ffffffffffffffff') }) it('should write long', () => { - expect(testSingleMethod(8, (w) => w.long(bigInt(-1)))).eq( + expect(testSingleMethod(8, (w) => w.long(Long.NEG_ONE))).eq( 'ffffffffffffffff' ) expect( - testSingleMethod(8, (w) => w.long(bigInt('8671175386481439762'))) + testSingleMethod(8, (w) => w.long(Long.fromString('8671175386481439762'))) ).eq('1234567812345678') expect( - testSingleMethod(8, (w) => w.long(bigInt('-6700480189419895787'))) + testSingleMethod(8, (w) => w.long(Long.fromString('-6700480189419895787'))) ).eq('15c415b5c41c03a3') }) @@ -94,22 +102,22 @@ describe('BinaryWriter', () => { ) }) - const stubObjectsMap = { - deadbeef: function (this: ITlBinaryWriter, obj: any) { - this.uint32(0xdeadbeef) - this.int32(obj.a) - this.object(obj.b) + const stubObjectsMap: TlWriterMap = { + deadbeef: function (w, obj) { + w.uint(0xdeadbeef) + w.int(obj.a) + w.object(obj.b) }, - facedead: function (this: ITlBinaryWriter) { - this.uint32(0xfacedead) + facedead: function (w) { + w.uint(0xfacedead) }, - baadc0de: function (this: ITlBinaryWriter, val: any) { - this.uint32(0xbaadc0de) - this.uint32(val.n) + baadc0de: function (w, obj) { + w.uint(0xbaadc0de) + w.uint(obj.n) }, - bebf0c3d: function (this: ITlBinaryWriter, val: any) { - this.uint32(0xbebf0c3d) - this.vector(this.int32, val.vec) + bebf0c3d: function (w, obj) { + w.uint(0xbebf0c3d) + w.vector(w.int, obj.vec) }, } @@ -125,16 +133,15 @@ describe('BinaryWriter', () => { } const length = - SerializationCounter.countNeededBytes(object1, stubObjectsMap) + - SerializationCounter.countNeededBytes(object2, stubObjectsMap) + TlSerializationCounter.countNeededBytes(stubObjectsMap, object1) + + TlSerializationCounter.countNeededBytes(stubObjectsMap, object2) expect(length).eq(20) expect( testSingleMethod(length, (w) => { - w._objectMap = stubObjectsMap w.object(object1) w.object(object2) - }) + }, stubObjectsMap) ).eq('efbeadde01000000addecefadec0adba02000000') }) @@ -154,17 +161,16 @@ describe('BinaryWriter', () => { } const length = - SerializationCounter.countNeededBytes(object1, stubObjectsMap) + - SerializationCounter.countNeededBytes(object2, stubObjectsMap) + - SerializationCounter.countNeededBytes(object3, stubObjectsMap) + + TlSerializationCounter.countNeededBytes(stubObjectsMap, object1,) + + TlSerializationCounter.countNeededBytes(stubObjectsMap, object2,) + + TlSerializationCounter.countNeededBytes(stubObjectsMap, object3,) + 8 // because technically in tl vector can't be top-level, but whatever :shrug: expect(length).eq(48) expect( testSingleMethod(length, (w) => { - w._objectMap = stubObjectsMap - w.vector(w.object, [object1, object2, object3]) - }) + w.vector(w.object as any, [object1, object2, object3]) + }, stubObjectsMap) ).eq( '15c4b51c03000000efbeadde01000000addecefadec0adba020000003d0cbfbe15c4b51c020000000100000002000000' ) @@ -176,7 +182,7 @@ describe('BinaryWriter', () => { const expected = '000000000000000001c8831ec97ae55140000000632416053e0549828cca27e966b301a48fece2fca5cf4d33f4a11ea877ba4aa5739073300817ed48941a08f98100000015c4b51c01000000216be86c022bb4c3' - const resPq: tl.mtproto.RawResPQ = { + const resPq = { _: 'mt_resPQ', nonce: Buffer.from('3E0549828CCA27E966B301A48FECE2FC', 'hex'), serverNonce: Buffer.from( @@ -184,22 +190,32 @@ describe('BinaryWriter', () => { 'hex' ), pq: Buffer.from('17ED48941A08F981', 'hex'), - serverPublicKeyFingerprints: [bigInt('c3b42b026ce86b21', 16)], + serverPublicKeyFingerprints: [Long.fromString('c3b42b026ce86b21', 16)], + } + + const map: TlWriterMap = { + 'mt_resPQ': function (w, obj) { + w.uint(85337187) + w.int128(obj.nonce) + w.int128(obj.serverNonce) + w.bytes(obj.pq) + w.vector(w.long, obj.serverPublicKeyFingerprints) + } } const length = 20 + // mtproto header - SerializationCounter.countNeededBytes(resPq) + TlSerializationCounter.countNeededBytes(map, resPq) expect(length).eq(expected.length / 2) expect( testSingleMethod(length, (w) => { - w.long(bigInt.zero) // authKeyId - w.long(bigInt('51E57AC91E83C801', 16)) // messageId - w.uint32(64) // messageLength + w.long(Long.ZERO) // authKeyId + w.long(Long.fromString('51E57AC91E83C801', true, 16)) // messageId + w.uint(64) // messageLength w.object(resPq) - }) + }, map) ).eq(expected) }) }) diff --git a/packages/tl-runtime/tsconfig.json b/packages/tl-runtime/tsconfig.json new file mode 100644 index 00000000..0d69ea46 --- /dev/null +++ b/packages/tl-runtime/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist" + }, + "include": [ + "./src/reader.ts", + "./src/writer.ts", + ], + "typedocOptions": { + "name": "@mtcute/tl-runtime", + "includeVersion": true, + "out": "../../docs/packages/tl-runtime", + "listInvalidSymbolLinks": true, + "excludePrivate": true, + "entryPoints": [ + "./src/reader.ts", + "./src/writer.ts" + ] + } +} diff --git a/packages/tl-utils/package.json b/packages/tl-utils/package.json new file mode 100644 index 00000000..051b28b0 --- /dev/null +++ b/packages/tl-utils/package.json @@ -0,0 +1,25 @@ +{ + "name": "@mtcute/tl-utils", + "private": true, + "version": "1.0.0", + "description": "Utils for working with TL schema", + "author": "Alisa Sireneva ", + "license": "LGPL-3.0", + "main": "index.ts", + "scripts": { + "test": "mocha -r ts-node/register tests/**/*.spec.ts", + "docs": "npx typedoc", + "build": "tsc" + }, + "browser": { + "./platform/gzip.js": "./platform/gzip.web.js" + }, + "dependencies": { + "long": "4.0.0", + "crc-32": "1.2.0", + "pako": "2.0.2" + }, + "devDependencies": { + "@types/long": "4.0.1" + } +} diff --git a/packages/tl-utils/src/codegen/reader.ts b/packages/tl-utils/src/codegen/reader.ts new file mode 100644 index 00000000..9542b100 --- /dev/null +++ b/packages/tl-utils/src/codegen/reader.ts @@ -0,0 +1,133 @@ +import { TL_PRIMITIVES, TlEntry } from '../types' +import { computeConstructorIdFromEntry } from '../ctor-id' +import { snakeToCamel } from './utils' + +/** + * Returns code as an object entry + */ +export function generateReaderCodeForTlEntry(entry: TlEntry): string { + if (entry.id === 0) entry.id = computeConstructorIdFromEntry(entry) + + let ret = `${entry.id}:function(r){` + + if (!entry.arguments.length) { + return ret + `return{_:'${entry.name}'}},` + } + + ret += `var o={_:'${entry.name}',` + + let inObject = true + function finalizeObject(pos: number) { + if (!inObject) return + + for (let i = pos; i < entry.arguments.length; i++) { + const arg = entry.arguments[i] + + if (arg.type !== '#') { + ret += arg.name + ':void 0,' + } + } + ret += '};' + + inObject = false + } + + const flagsFields: Record = {} + + entry.arguments.forEach((arg, idx) => { + if (arg.type === '#') { + const code = `var ${arg.name}=r.uint();` + if (idx === 0) { + ret = ret.replace('var o=', code + 'var o=') + } else { + finalizeObject(idx) + ret += code + } + flagsFields[arg.name] = 1 + return + } + + const argName = snakeToCamel(arg.name) + + if (arg.predicate) { + const s = arg.predicate.split('.') + const fieldName = s[0] + const bitIndex = parseInt(s[1]) + + if (!(fieldName in flagsFields)) { + throw new Error(`Invalid predicate: ${arg.predicate} - unknown field`) + } + if (isNaN(bitIndex) || bitIndex < 0 || bitIndex > 32) { + throw new Error(`Invalid predicate: ${arg.predicate} - invalid bit`) + } + + const condition = `${fieldName}&${1 << bitIndex}` + + if (arg.type === 'true') { + if (inObject) { + ret += `${argName}:!!(${condition}),` + } else { + ret += `o.${argName}=!!(${condition});` + } + return + } + + if (inObject) { + ret += `${argName}:${condition}?` + } else { + ret += `if(${condition})o.${argName}=` + } + } else { + if (inObject) { + ret += `${argName}:` + } else { + ret += `o.${argName}=` + } + } + + let vector = false + let type = arg.type + const m = type.match(/^[Vv]ector[< ](.+?)[> ]$/) + if (m) { + vector = true + type = m[1] + } + + if (type in TL_PRIMITIVES) { + if (type === 'Bool') type = 'boolean' + } else { + type = 'object' + } + + if (vector) { + ret += `r.vector(r.${type})` + } else { + ret += `r.${type}()` + } + + if (arg.predicate && inObject) { + ret += ':void 0' + } + + ret += inObject ? ',' : ';' + }) + + if (inObject) { + // simple object, direct return + return ret.replace('var o=', 'return') + '}},' + } + + return ret + 'return o},' +} + +export function generateReaderCodeForTlEntries(entries: TlEntry[], varName: string, methods = true): string { + let ret = `var ${varName}={\n` + + entries.forEach((entry) => { + if (entry.kind === 'method' && !methods) return + + ret += generateReaderCodeForTlEntry(entry) + '\n' + }) + + return ret + '}' +} diff --git a/packages/tl-utils/src/codegen/types.ts b/packages/tl-utils/src/codegen/types.ts new file mode 100644 index 00000000..a2cf8d0f --- /dev/null +++ b/packages/tl-utils/src/codegen/types.ts @@ -0,0 +1,343 @@ +import { TlEntry, TlFullSchema } from '../types' +import { groupTlEntriesByNamespace, splitNameToNamespace } from '../utils' +import { camelToPascal, snakeToCamel } from './utils' + +const PRIMITIVE_TO_TS: Record = { + int: 'number', + long: 'Long', + int53: 'number', + int128: 'Int128', + int256: 'Int256', + double: 'Double', + string: 'string', + bytes: 'Buffer', + Bool: 'boolean', + true: 'boolean', + null: 'null', + any: 'any' +} + +function jsComment(s: string): string { + return ( + '/**' + + s + .replace(/(?![^\n]{1,60}$)([^\n]{1,60})\s/g, '$1\n') + .replace(/\n|^/g, '\n * ') + + '\n */' + ) +} + +function indent(size: number, s: string): string { + let prefix = '' + while (size--) prefix += ' ' + return prefix + s.replace(/\n/g, '\n' + prefix) +} + +function fullTypeName( + type: string, + baseNamespace: string, + namespace = true, + method = false +): string { + if (type in PRIMITIVE_TO_TS) return PRIMITIVE_TO_TS[type] + let m + if ((m = type.match(/^[Vv]ector[< ](.+?)[> ]$/))) { + return fullTypeName(m[1], baseNamespace) + '[]' + } + + const [ns, name] = splitNameToNamespace(type) + let res = baseNamespace + if (namespace && ns) res += ns + '.' + + if (name[0].match(/[A-Z]/)) { + res += 'Type' + } else { + res += 'Raw' + } + + res += camelToPascal(name) + + if (method) res += 'Request' + + return res +} + +function entryFullTypeName(entry: TlEntry): string { + return fullTypeName(entry.name, '', false, entry.kind === 'method') +} + +export function generateTypescriptDefinitionsForTlEntry( + entry: TlEntry, + baseNamespace = 'tl.' +): string { + let ret = '' + + let comment = '' + if (entry.comment) { + comment = entry.comment + } + if (entry.kind === 'method' && !entry.generics) { + if (comment) comment += '\n\n' + + comment += `RPC method returns {@see ${fullTypeName( + entry.type, + baseNamespace + )}}` + } + if (comment) ret += jsComment(comment) + '\n' + + ret += `interface ${entryFullTypeName(entry)} {\n _: '${entry.name}';\n` + + const genericsIndex: Record = {} + if (entry.generics) { + entry.generics.forEach((it) => { + genericsIndex[it.name] = it.type + }) + } + + entry.arguments.forEach((arg) => { + if (arg.type === '#') return + + if (arg.comment) { + ret += indent(4, jsComment(arg.comment)) + '\n' + } + + ret += ` ${snakeToCamel(arg.name)}` + + if (arg.predicate) ret += '?' + + let type = arg.type + let typeFinal = false + + if (type[0] === '!') type = type.substr(1) + + if (type in genericsIndex) { + type = genericsIndex[type] + + if (type === 'Type') { + type = 'any' + typeFinal = true + } + } + + if (!typeFinal) type = fullTypeName(arg.type, baseNamespace) + + ret += `: ${type};\n` + }) + + ret += '}' + + return ret +} + +const PRELUDE = ` +import _Long from 'long'; + +export declare namespace $NS$ { + const LAYER = $LAYER$; + + function $extendTypes(types: Record): void + + type Long = _Long; + type RawLong = Buffer; + type Int128 = Buffer; + type Int256 = Buffer; + type Double = number; + + type FindByName = Extract + + type Mutable = { + -readonly [P in keyof T]: T[P] + } +` + +const PRELUDE_JS = ` +exports.$NS$ = {}; +(function(ns) { +var _types = void 0; +function _isAny(type) { + return function (obj) { + return typeof obj === 'object' && obj && _types[obj._] == type + } +} +ns.$extendTypes = function(types) { + for (var i in types) { + types.hasOwnProperty(i) && (_types[i] = types[i]) + } +} +ns.LAYER = $LAYER$; +` + +// returns pair of generated ts and js code +export function generateTypescriptDefinitionsForTlSchema( + schema: TlFullSchema, + layer: number, + namespace = 'tl' +): [string, string] { + let ts = PRELUDE.replace('$NS$', namespace).replace('$LAYER$', layer + '') + let js = PRELUDE_JS.replace('$NS$', namespace).replace( + '$LAYER$', + layer + '' + ) + + const namespaces = groupTlEntriesByNamespace(schema.entries) + + for (const ns in namespaces) { + if (!namespaces.hasOwnProperty(ns)) continue + + const entries = namespaces[ns] + const indentSize = ns === '' ? 4 : 8 + + const unions: Record = {} + + if (ns !== '') { + ts += `\n namespace ${ns} {\n` + } + + entries.forEach((entry) => { + if (entry.kind === 'class') { + unions[entry.type] = 1 + } + + ts += + indent( + indentSize, + generateTypescriptDefinitionsForTlEntry( + entry, + namespace + '.' + ) + ) + '\n' + }) + + ts += indent(indentSize, 'interface RpcCallReturn') + if (ns === '') { + let first = true + for (const ns in namespaces) { + if (!namespaces.hasOwnProperty(ns)) continue + if (ns === '') continue + + if (first) { + first = false + ts += ' extends ' + } else { + ts += ', ' + } + + ts += ns + '.RpcCallReturn' + } + } + ts += ' {\n' + + entries.forEach((entry) => { + if (entry.kind !== 'method') return + + let type + if (entry.generics) { + for (let i = 0; i < entry.generics.length; i++) { + const g = entry.generics[i] + + if (g.name === entry.type) { + type = + g.type === 'Type' + ? 'any' + : fullTypeName(g.type, namespace + '.') + break + } + } + } + + if (!type) { + type = fullTypeName(entry.type, namespace + '.') + } + + ts += indent(indentSize + 4, `'${entry.name}': ${type}`) + '\n' + }) + + ts += indent(indentSize, '}') + '\n' + + if (ns) { + js += `ns.${ns} = {};\n(function(ns){\n` + } + + for (const name in unions) { + if (!unions.hasOwnProperty(name)) continue + + const union = schema.unions[name] + + if (union.comment) { + ts += indent(indentSize, jsComment(union.comment)) + '\n' + } + const typeName = fullTypeName(name, '', false) + const typeWithoutNs = typeName.substring(4) + ts += indent(indentSize, `type ${typeName} = `) + + union.classes.forEach((entry, idx) => { + if (idx !== 0) ts += ' | ' + ts += fullTypeName(entry.name, namespace + '.') + }) + + ts += '\n' + + ts += + indent( + indentSize, + `function isAny${typeWithoutNs}(o: object): o is ${typeName}` + ) + '\n' + js += `ns.isAny${typeWithoutNs} = _isAny('${name}');\n` + } + + if (ns) { + js += `})(ns.${ns});\n` + } + + if (ns !== '') { + ts += '}\n' + } + } + + let first = true + for (const name in schema.methods) { + if (!schema.methods.hasOwnProperty(name)) continue + + if (first) { + ts += indent(4, 'type RpcMethod =') + '\n' + first = false + } + + const entry = schema.methods[name] + ts += + indent( + 8, + '| ' + fullTypeName(entry.name, namespace + '.', true, true) + ) + '\n' + } + + ts += '\n' + indent(4, 'type TlObject =') + '\n' + + const _types: Record = {} + + schema.entries.forEach((entry) => { + if (entry.kind === 'class') { + _types[entry.name] = entry.type + } + + ts += + indent( + 8, + '| ' + + fullTypeName( + entry.name, + namespace + '.', + true, + entry.kind === 'method' + ) + ) + '\n' + }) + + ts += '}' + + js += `_types = JSON.parse('${JSON.stringify(_types)}');\n` + js += `})(exports.${namespace});` + + return [ts, js] +} diff --git a/packages/tl-utils/src/codegen/utils.ts b/packages/tl-utils/src/codegen/utils.ts new file mode 100644 index 00000000..70ec1af5 --- /dev/null +++ b/packages/tl-utils/src/codegen/utils.ts @@ -0,0 +1,8 @@ +export const snakeToCamel = (s: string): string => { + return s.replace(/(? { + return $1.substr(1).toUpperCase() + }) +} + +export const camelToPascal = (s: string): string => + s[0].toUpperCase() + s.substr(1) diff --git a/packages/tl-utils/src/codegen/writer.ts b/packages/tl-utils/src/codegen/writer.ts new file mode 100644 index 00000000..e404f5a6 --- /dev/null +++ b/packages/tl-utils/src/codegen/writer.ts @@ -0,0 +1,113 @@ +import { TL_PRIMITIVES, TlEntry } from '../types' +import { computeConstructorIdFromEntry } from '../ctor-id' +import { snakeToCamel } from './utils' + +export const TL_WRITER_PRELUDE = + 'function h(o, p){' + + 'var q=o[p];' + + 'if(q===void 0)' + + "throw Error('Object '+o._+' is missing required property '+p);" + + 'return q}\n' + +/** + * Returns code as an object entry. + * + * `h` (has) function should be available + */ +export function generateWriterCodeForTlEntry(entry: TlEntry): string { + if (entry.id === 0) entry.id = computeConstructorIdFromEntry(entry) + + let ret = `'${entry.name}':function(w${ + entry.arguments.length ? ',v' : '' + }){` + + ret += `w.uint(${entry.id});` + + const flagsFields: Record = {} + + entry.arguments.forEach((arg) => { + if (arg.type === '#') { + ret += `var ${arg.name}=0;` + + entry.arguments.forEach((arg1) => { + let s + if ( + !arg1.predicate || + (s = arg1.predicate.split('.'))[0] !== arg.name + ) + return + + const arg1Name = snakeToCamel(arg1.name) + + const bitIndex = parseInt(s[1]) + if (isNaN(bitIndex) || bitIndex < 0 || bitIndex > 32) { + throw new Error( + `Invalid predicate: ${arg1.predicate} - invalid bit` + ) + } + + const action = `${arg.name}|=${1 << bitIndex};` + + if (arg1.type === 'true') { + ret += `if(v.${arg1Name}===true)${action}` + } else if (arg1.type.match(/^[Vv]ector/)) { + ret += `var _${arg1Name}=v.${arg1Name}&&v.${arg1Name}.length;if(_${arg1Name})${action}` + } else { + ret += `var _${arg1Name}=v.${arg1Name}!==undefined;if(_${arg1Name})${action}` + } + }) + + ret += `w.uint(${arg.name});` + flagsFields[arg.name] = 1 + return + } + + const argName = snakeToCamel(arg.name) + + let vector = false + let type = arg.type + const m = type.match(/^[Vv]ector[< ](.+?)[> ]$/) + if (m) { + vector = true + type = m[1] + } + + if (arg.predicate) { + if (type === 'true') return // included in flags + + ret += `if(_${argName})` + } else { + ret += `h(v,'${argName}');` + } + + if (type in TL_PRIMITIVES) { + if (type === 'Bool') type = 'boolean' + } else { + type = 'object' + } + + if (vector) { + ret += `w.vector(w.${type}, v.${argName});` + } else { + ret += `w.${type}(v.${argName});` + } + }) + + return ret + '},' +} + +export function generateWriterCodeForTlEntries( + entries: TlEntry[], + varName: string, + prelude = true +): string { + let ret = '' + if (prelude) ret += TL_WRITER_PRELUDE + ret += `var ${varName}={\n` + + entries.forEach((entry) => { + ret += generateWriterCodeForTlEntry(entry) + '\n' + }) + + return ret + '}' +} diff --git a/packages/tl-utils/src/ctor-id.ts b/packages/tl-utils/src/ctor-id.ts new file mode 100644 index 00000000..f8ea3632 --- /dev/null +++ b/packages/tl-utils/src/ctor-id.ts @@ -0,0 +1,21 @@ +import CRC32 from 'crc-32' +import { TlEntry } from './types' +import { writeTlEntryToString } from './stringify' + +export function computeConstructorIdFromString(line: string): number { + return ( + CRC32.str( + // normalize + line + .replace(/[{};]|[a-zA-Z0-9_]+:flags\.[0-9]+\?true|#[0-9a-f]{1,8}/g, '') + .replace(/[<>]/g, ' ') + .replace(/ +/g, ' ') + .replace(':bytes', ':string') + .trim() + ) >>> 0 + ) +} + +export function computeConstructorIdFromEntry(entry: TlEntry): number { + return CRC32.str(writeTlEntryToString(entry, true)) >>> 0 +} diff --git a/packages/tl-utils/src/diff.ts b/packages/tl-utils/src/diff.ts new file mode 100644 index 00000000..f4cec83a --- /dev/null +++ b/packages/tl-utils/src/diff.ts @@ -0,0 +1,242 @@ +import { + TlArgument, + TlArgumentDiff, + TlEntry, + TlEntryDiff, + TlFullSchema, + TlSchemaDiff, +} from './types' +import { computeConstructorIdFromEntry } from './ctor-id' + +export function generateTlEntriesDifference( + a: TlEntry, + b: TlEntry +): TlEntryDiff { + if (a.kind !== b.kind || a.name !== b.name) { + throw new Error('Incompatible entries') + } + + const diff: TlEntryDiff = { + name: a.name, + } + + if (a.id !== b.id) { + let oldId = a.id + let newId = b.id + + if (oldId === 0) oldId = computeConstructorIdFromEntry(a) + if (newId === 0) newId = computeConstructorIdFromEntry(b) + + if (oldId !== newId) { + diff.id = { + old: oldId, + new: newId, + } + } + } + + if ( + !a.generics !== !b.generics || + (a.generics && + b.generics && + JSON.stringify(a.generics) !== JSON.stringify(b.generics)) + ) { + diff.generics = { + old: a.generics, + new: b.generics, + } + } + + const argsDiff: NonNullable = { + added: [], + removed: [], + modified: [], + } + + const oldArgsIndex: Record = {} + + a.arguments.forEach((arg) => { + oldArgsIndex[arg.name] = arg + }) + + const newArgsIndex: Record = {} + + b.arguments.forEach((arg) => { + newArgsIndex[arg.name] = 1 + + if (!(arg.name in oldArgsIndex)) { + argsDiff.added.push(arg) + return + } + + const oldArg = oldArgsIndex[arg.name] + + const diff: TlArgumentDiff = { + name: arg.name, + } + + if (arg.type !== oldArg.type) { + diff.type = { + old: oldArg.type, + new: arg.type, + } + } + + if (arg.predicate !== oldArg.predicate) { + diff.predicate = { + old: oldArg.predicate, + new: arg.predicate, + } + } + + if (arg.comment !== oldArg.comment) { + diff.comment = { + old: oldArg.comment, + new: arg.comment, + } + } + + if (diff.type || diff.predicate || diff.comment) { + argsDiff.modified.push(diff) + } + }) + + a.arguments.forEach((arg) => { + if (!(arg.name in newArgsIndex)) { + argsDiff.removed.push(arg) + } + }) + + if ( + argsDiff.added.length || + argsDiff.removed.length || + argsDiff.modified.length + ) { + diff.arguments = argsDiff + } + + return diff +} + +export function generateTlSchemasDifference( + a: TlFullSchema, + b: TlFullSchema +): TlSchemaDiff { + // schemas already contain indexes, so we don't need to make our own + + const diff: TlSchemaDiff = { + classes: { + added: [], + removed: [], + modified: [], + }, + methods: { + added: [], + removed: [], + modified: [], + }, + unions: { + added: [], + removed: [], + modified: [], + }, + } + + const unionDiffIndex: Record< + string, + TlSchemaDiff['unions']['modified'][number] + > = {} + const unionDiffIndex2: Record = {} + + a.entries.forEach((entry) => { + const kind = entry.kind === 'class' ? 'classes' : 'methods' + + // check union + const union = a.unions[entry.type] + if (!(entry.type in b.unions) && !(entry.type in unionDiffIndex2)) { + // deleted union + unionDiffIndex2[entry.type] = 1 + diff.unions.removed.push(union) + } + + if (!(entry.name in b[kind])) { + diff[kind].removed.push(entry) + + // we also need to diff the respective union + if (entry.type in b.unions) { + if (!(entry.type in unionDiffIndex)) { + unionDiffIndex[entry.type] = { + name: entry.type, + classes: { + added: [], + removed: [], + modified: [], + }, + methods: { + added: [], + removed: [], + modified: [], + }, + } + diff.unions.modified.push(unionDiffIndex[entry.type]) + } + const unionDiff = unionDiffIndex[entry.type] + unionDiff[kind].removed.push(entry) + } + return + } + + const other = b[kind][entry.name] + + const entryDiff = generateTlEntriesDifference(entry, other) + + if ( + entryDiff.id || + entryDiff.generics || + entryDiff.arguments + ) { + diff[kind].modified.push(entryDiff) + } + }) + + b.entries.forEach((entry) => { + const kind = entry.kind === 'class' ? 'classes' : 'methods' + + // check union + const union = b.unions[entry.type] + if (!(entry.type in a.unions) && !(entry.type in unionDiffIndex2)) { + // added union + unionDiffIndex2[entry.type] = 1 + diff.unions.added.push(union) + } + + if (!(entry.name in a[kind])) { + diff[kind].added.push(entry) + + // we also need to diff the respective union + if (entry.type in a.unions) { + if (!(entry.type in unionDiffIndex)) { + unionDiffIndex[entry.type] = { + name: entry.type, + classes: { + added: [], + removed: [], + modified: [], + }, + methods: { + added: [], + removed: [], + modified: [], + }, + } + diff.unions.modified.push(unionDiffIndex[entry.type]) + } + const unionDiff = unionDiffIndex[entry.type] + unionDiff[kind].added.push(entry) + } + return + } + }) + + return diff +} diff --git a/packages/tl-utils/src/merge.ts b/packages/tl-utils/src/merge.ts new file mode 100644 index 00000000..60e79f56 --- /dev/null +++ b/packages/tl-utils/src/merge.ts @@ -0,0 +1,182 @@ +import { TlArgument, TlEntry, TlFullSchema } from './types' +import { computeConstructorIdFromEntry } from './ctor-id' + +export function mergeTlEntries(entries: TlEntry[]): TlEntry | string { + const first = entries[0] + + const result: TlEntry = { + kind: first.kind, + name: first.name, + type: first.type, + id: first.id, + comment: first.comment, + generics: first.generics, + arguments: first.arguments, + } + + // even if the entry contains id, let's re-calculate it just to be sure + result.id = computeConstructorIdFromEntry(result) + + const argsIndex: Record = {} + let lastTrueFlagIdx = -1 + + result.arguments.forEach((arg, idx) => { + argsIndex[arg.name] = arg + + if (arg.predicate && arg.type === 'true') lastTrueFlagIdx = idx + }) + + for (let i = 1; i < entries.length; i++) { + const entry = entries[i] + + // the only thing we should actually merge are optional true flags + // anything other than that is *not compatible* and we must return null + // (and also comments fwiw) + + // even if the entry contains id, let's re-calculate it just to be sure + const ctorId = computeConstructorIdFromEntry(entry) + + // check if basic fields match + if ( + result.kind !== entry.kind || + result.name !== entry.name || + result.type !== entry.type || + result.id !== ctorId + ) + return 'basic info mismatch' + + // since we re-calculated id manually, we can skip checking + // generics and arguments, and get straight to merging + + if (!result.comment && entry.comment) { + result.comment = entry.comment + } + + for (let j = 0; j < entry.arguments.length; j++) { + const entryArgument = entry.arguments[j] + const resultArgument = argsIndex[entryArgument.name] + + if (!resultArgument) { + // yay a new arg + // we can only add optional true args, since any others will change id + // ids match, so this must be the case + + result.arguments.splice(lastTrueFlagIdx += 1, 0, entryArgument) + argsIndex[entryArgument.name] = entryArgument + } + + // args exists both in result and current entry + // since ctor ids match, it must be the same, so we don't need to check + } + } + + return result +} + +export async function mergeTlSchemas( + schemas: TlFullSchema[], + onConflict: ( + options: (TlEntry | undefined)[], + reason: string + ) => TlEntry | undefined | Promise +): Promise { + const result: TlFullSchema = { + entries: [], + classes: {}, + methods: {}, + unions: {}, + } + + const resolvedConflictsClasses: Record = {} + const resolvedConflictsMethods: Record = {} + + for (let i = 0; i < schemas.length; i++) { + const schema = schemas[i] + + for (let i1 = 0; i1 < schema.entries.length; i1++) { + const entry = schema.entries[i1] + + const kind = entry.kind === 'class' ? 'classes' : 'methods' + const index = result[kind] + const conflictIndex = + entry.kind === 'class' + ? resolvedConflictsClasses + : resolvedConflictsMethods + + if (entry.name in conflictIndex) { + // this entry was manually processed by user after a conflict + // and should be skipped + continue + } + + if (!(entry.name in index)) { + // new one, add as-is + index[entry.name] = entry + continue + } + + const existing = index[entry.name] + const merged = mergeTlEntries([existing, entry]) + + if (typeof merged === 'string') { + // merge conflict + // find all candidates from all schemas and let the user decide + const candidates = schemas.map( + (schema) => schema[kind][entry.name] + ) + + const chosen = await onConflict(candidates, merged) + + if (chosen) { + index[entry.name] = chosen + } else { + delete index[entry.name] + } + + conflictIndex[entry.name] = 1 + + continue + } + + index[entry.name] = merged + } + + for (const name in schema.unions) { + if (!schema.unions.hasOwnProperty(name)) continue + + const union = schema.unions[name] + + if (!(name in result.unions)) { + result.unions[name] = { + name, + classes: [], + } + } + const existing = result.unions[name] + + if (union.comment && !existing.comment) { + existing.comment = union.comment + } + } + } + + // for simplicity sake, entries and unions are generated after merging is done + for (let i = 0; i < 2; i++) { + const kind = i === 0 ? 'classes' : 'methods' + const index = result[kind] + + for (const name in index) { + if (!index.hasOwnProperty(name)) continue + + const entry = index[name] + + result.entries.push(entry) + + if (kind === 'classes') { + result.unions[entry.type].classes.push(entry) + } + } + } + + return result +} diff --git a/packages/tl-utils/src/parse.ts b/packages/tl-utils/src/parse.ts new file mode 100644 index 00000000..f96d14b3 --- /dev/null +++ b/packages/tl-utils/src/parse.ts @@ -0,0 +1,173 @@ +import { computeConstructorIdFromString } from './ctor-id' +import { TL_PRIMITIVES, TlEntry } from './types' +import { parseTdlibStyleComment } from './utils' + +const SINGLE_REGEX = /^(.+?)(?:#([0-9a-f]{1,8}))?(?: \?)?(?: {(.+?:.+?)})? ((?:.+? )*)= (.+);$/ + +function applyPrefix(prefix: string, type: string): string { + if (type in TL_PRIMITIVES) return type + + const m = type.match(/^[Vv]ector[< ](.+?)[> ]$/) + if (m) return `Vector<${applyPrefix(prefix, m[1])}>` + + return prefix + type +} + +export function parseTlToEntries( + tl: string, + params?: { + panicOnError?: boolean + onError?: (err: Error, line: string, num: number) => void + onOrphanComment?: (comment: string) => void + prefix?: string + applyPrefixToArguments?: boolean + } +): TlEntry[] { + const ret: TlEntry[] = [] + + const lines = tl.split('\n') + + let currentKind: TlEntry['kind'] = 'class' + let currentComment = '' + const prefix = params?.prefix ?? '' + + lines.forEach((line, idx) => { + line = line.trim() + + if (line === '') { + if (params?.onOrphanComment) { + params.onOrphanComment(currentComment) + } + + currentComment = '' + return + } + + if (line.match(/^\/\//)) { + if (currentComment) { + if (line[2] === '-') { + currentComment += '\n' + line.substr(3).trim() + } else { + currentComment += ' ' + line.substr(2).trim() + } + } else { + currentComment = line.substr(2).trim() + } + + return + } + + if (line === '---functions---') { + currentKind = 'method' + return + } + + if (line === '---types---') { + currentKind = 'class' + return + } + + const match = SINGLE_REGEX.exec(line) + if (!match) { + const err = new Error(`Failed to parse line ${idx + 1}: ${line}`) + + if (params?.panicOnError) { + throw err + } else if (params?.onError) { + params.onError(err, line, idx + 1) + } else { + console.warn(err) + } + + return + } + + const [, typeName, typeId, generics, args, type] = match + if (typeName in TL_PRIMITIVES) { + return + } + + const typeIdNum = typeId + ? parseInt(typeId, 16) + : computeConstructorIdFromString(line) + + const argsParsed = + args && !args.match(/\[ [a-z]+ ]/i) + ? args + .trim() + .split(' ') + .map((j) => j.split(':')) + : [] + + const entry: TlEntry = { + kind: currentKind, + name: prefix + typeName, + id: typeIdNum, + type, + arguments: [], + } + + if (generics) { + entry.generics = generics.split(',').map((it) => { + const [name, type] = it.split(':') + return { name, type } + }) + } + + if (argsParsed.length) { + argsParsed.forEach(([name, typ]) => { + let [predicate, type] = typ.split('?') + + if (!type) { + // no predicate, `predicate` is the type + + if (params?.applyPrefixToArguments) { + predicate = applyPrefix(prefix, predicate) + } + entry.arguments.push({ + name, + type: predicate, + }) + } else { + // there is a predicate + + if (params?.applyPrefixToArguments) { + type = applyPrefix(prefix, type) + } + entry.arguments.push({ + name, + type, + predicate, + }) + } + }) + } + + if (currentComment) { + if (currentComment.match(/^@description /)) { + // tdlib-style comment + const obj = parseTdlibStyleComment(currentComment) + + if (obj.description) entry.comment = obj.description + + entry.arguments.forEach((arg) => { + if (arg.name in obj) { + arg.comment = obj[arg.name] + } + }) + } else { + entry.comment = currentComment + } + + currentComment = '' + } + + ret.push(entry) + }) + + if (currentComment && params?.onOrphanComment) { + params.onOrphanComment(currentComment) + } + + return ret +} diff --git a/packages/tl-utils/src/patch.ts b/packages/tl-utils/src/patch.ts new file mode 100644 index 00000000..a43de651 --- /dev/null +++ b/packages/tl-utils/src/patch.ts @@ -0,0 +1,36 @@ +import { TlReaderMap, TlWriterMap } from '../../tl-runtime' +import { parseTlToEntries } from './parse' +import { generateReaderCodeForTlEntries } from './codegen/reader' +import { generateWriterCodeForTlEntries } from './codegen/writer' + +function evalForResult(js: string): any { + return new Function(js)() +} + +export function patchRuntimeTlSchema( + schema: string, + readers: TlReaderMap, + writers: TlWriterMap +): { + readerMap: TlReaderMap + writerMap: TlWriterMap +} { + const entries = parseTlToEntries(schema) + + const readersCode = generateReaderCodeForTlEntries(entries, '_', false) + const writersCode = generateWriterCodeForTlEntries(entries, '_', true) + + const newReaders = evalForResult(readersCode.replace('var _=', 'return')) + const newWriters = evalForResult(writersCode.replace('var _=', 'return')) + + return { + readerMap: { + ...readers, + ...newReaders, + }, + writerMap: { + ...writers, + ...newWriters, + }, + } +} diff --git a/packages/tl-utils/src/schema.ts b/packages/tl-utils/src/schema.ts new file mode 100644 index 00000000..f42f68c9 --- /dev/null +++ b/packages/tl-utils/src/schema.ts @@ -0,0 +1,106 @@ +import { TlEntry, TlFullSchema } from './types' +import { computeConstructorIdFromEntry } from './ctor-id' +import { writeTlEntryToString } from './stringify' + +const replaceNewlineInComment = (s: string): string => + s.replace(/\n/g, '\n//- ') + +export function parseFullTlSchema(entries: TlEntry[]): TlFullSchema { + const ret: TlFullSchema = { + entries, + classes: {}, + methods: {}, + unions: {}, + } + + entries.forEach((entry) => { + const kind = entry.kind === 'class' ? 'classes' : 'methods' + + ret[kind][entry.name] = entry + + if (kind === 'classes') { + const type = entry.type + + if (!(type in ret.unions)) { + ret.unions[type] = { + name: type, + classes: [], + } + } + ret.unions[type].classes.push(entry) + } + }) + + return ret +} + +export function writeTlEntriesToString( + entries: TlEntry[], + params?: { + computeIds?: boolean + tdlibComments?: boolean + omitPrimitives?: boolean + } +): string { + const lines: string[] = [] + + if (!params?.omitPrimitives) { + lines.push(`int ? = Int; +long ? = Long; +double ? = Double; +string ? = String; +int128 4*[ int ] = Int128; +int256 8*[ int ] = Int256; +bytes = Bytes; + +vector#1cb5c415 {t:Type} # [ t ] = Vector t; +true#3fedd339 = True; +boolFalse#bc799737 = Bool; +boolTrue#997275b5 = Bool; +`) + } + + let currentKind: TlEntry['kind'] = 'class' + + entries.forEach((entry) => { + if (entry.kind !== currentKind) { + if (entry.kind === 'class') { + lines.push('---types---') + } else { + lines.push('---functions---') + } + + currentKind = entry.kind + } + + if (entry.comment) { + if (params?.tdlibComments) { + lines.push( + `// @description ${replaceNewlineInComment(entry.comment)}` + ) + } else { + lines.push(`// ${replaceNewlineInComment(entry.comment)}`) + } + } + + if (params?.tdlibComments) { + entry.arguments.forEach((arg) => { + if (arg.comment) { + lines.push( + `// @${arg.name} ${replaceNewlineInComment( + arg.comment + )}` + ) + } + }) + } + + if (!entry.id && params?.computeIds !== false) { + entry.id = computeConstructorIdFromEntry(entry) + } + + lines.push(writeTlEntryToString(entry)) + }) + + return lines.join('\n') +} diff --git a/packages/tl-utils/src/stringify.ts b/packages/tl-utils/src/stringify.ts new file mode 100644 index 00000000..82fcb514 --- /dev/null +++ b/packages/tl-utils/src/stringify.ts @@ -0,0 +1,56 @@ +import { TlEntry } from './types' + +function normalizeType(s: string): string { + return s + .replace(//g, '') + .replace('bytes', 'string') + .replace('int53', 'long') +} + +export function writeTlEntryToString( + entry: TlEntry, + forIdComputation = false +): string { + let str = entry.name + + if (!forIdComputation && entry.id) { + str += '#' + entry.id.toString(16) + } + + str += ' ' + + if (entry.generics) { + for (const g of entry.generics) { + if (forIdComputation) { + str += g.name + ':' + g.type + ' ' + } else { + str += '{' + g.name + ':' + g.type + '} ' + } + } + } + + for (const arg of entry.arguments) { + if (forIdComputation && arg.predicate && arg.type === 'true') continue + + str += arg.name + ':' + + if (arg.predicate) { + str += arg.predicate + '?' + } + + if (forIdComputation) { + str += normalizeType(arg.type) + ' ' + } else { + str += arg.type + ' ' + } + } + + if (forIdComputation) { + str += '= ' + normalizeType(entry.type) + } else { + str += '= ' + entry.type + ';' + } + + return str +} diff --git a/packages/tl-utils/src/types.ts b/packages/tl-utils/src/types.ts new file mode 100644 index 00000000..bde2dd6a --- /dev/null +++ b/packages/tl-utils/src/types.ts @@ -0,0 +1,99 @@ +export interface TlArgument { + name: string + type: string + predicate?: string + comment?: string +} + +export interface TlGeneric { + name: string + type: string +} + +export interface TlEntry { + kind: 'method' | 'class' + name: string + id: number + type: string + comment?: string + generics?: TlGeneric[] + arguments: TlArgument[] + + // additional fields for methods, + // used primarily for documentation + throws?: { + code: number + name: string + comment?: string + }[] + available?: 'both' | 'bot' | 'user' +} + +export interface TlUnion { + name: string + comment?: string + classes: TlEntry[] +} + +export interface TlFullSchema { + entries: TlEntry[] + classes: Record + methods: Record + unions: Record +} + +interface BasicDiff { + added: T[] + removed: T[] + modified: K[] +} + +interface PropertyDiff { + old: T + new: T +} + +export interface TlArgumentDiff { + name: string + type?: PropertyDiff + predicate?: PropertyDiff + comment?: PropertyDiff +} + +export interface TlEntryDiff { + name: string + id?: PropertyDiff + generics?: PropertyDiff + arguments?: BasicDiff +} + +interface TlUnionDiff { + name: string + classes: BasicDiff + methods: BasicDiff +} + +export interface TlSchemaDiff { + classes: BasicDiff + methods: BasicDiff + unions: BasicDiff +} + +export const TL_PRIMITIVES = { + int: 1, + long: 1, + int53: 1, + int128: 1, + int256: 1, + double: 1, + string: 1, + bytes: 1, + vector: 1, + boolFalse: 1, + boolTrue: 1, + bool: 1, + Bool: 1, + true: 1, + null: 1, + Object: true +} as const diff --git a/packages/tl-utils/src/utils.ts b/packages/tl-utils/src/utils.ts new file mode 100644 index 00000000..3f7e6e87 --- /dev/null +++ b/packages/tl-utils/src/utils.ts @@ -0,0 +1,39 @@ +import { TlEntry } from './types' + +export function splitNameToNamespace(name: string): [string | null, string] { + const s = name.split('.') + if (s.length === 2) return s as [string, string] + return [null, name] +} + + +export function parseTdlibStyleComment(str: string): Record { + const obj: Record = {} + + let pos = str.indexOf('@') + while (pos !== -1 && pos < str.length) { + let nameEnd = str.indexOf(' ', pos) + if (nameEnd === -1) nameEnd = str.length + + const name = str.substring(pos + 1, nameEnd) + + pos = str.indexOf('@', nameEnd) + obj[name] = str.substring(nameEnd + 1, pos === -1 ? undefined : pos - 1) + } + + return obj +} + +export function groupTlEntriesByNamespace(entries: TlEntry[]): Record { + const ret: Record = {} + + entries.forEach((entry) => { + const [ns_] = splitNameToNamespace(entry.name) + const ns = ns_ === null ? '' : ns_ + + if (!(ns in ret)) ret[ns] = [] + ret[ns].push(entry) + }) + + return ret +} diff --git a/packages/tl-utils/tests/codegen/reader.spec.ts b/packages/tl-utils/tests/codegen/reader.spec.ts new file mode 100644 index 00000000..ef77feb0 --- /dev/null +++ b/packages/tl-utils/tests/codegen/reader.spec.ts @@ -0,0 +1,109 @@ +import { describe, it } from 'mocha' +import { expect } from 'chai' +import { generateReaderCodeForTlEntry } from '../../src/codegen/reader' +import { parseTlToEntries } from '../../src/parse' + +describe('generateReaderCodeForTlEntry', () => { + const test = (tl: string, ...js: string[]) => { + const entry = parseTlToEntries(tl)[0] + expect(generateReaderCodeForTlEntry(entry)).eq( + `${entry.id}:function(r){${js.join('')}},` + ) + } + + it('generates code for constructors without arguments', () => { + test( + 'topPeerCategoryBotsPM#ab661b5b = TopPeerCategory;', + "return{_:'topPeerCategoryBotsPM'}" + ) + }) + + it('generates code for constructors with simple arguments', () => { + test( + 'inputBotInlineMessageID#890c3d89 dc_id:int id:long access_hash:long = InputBotInlineMessageID;', + 'return{', + "_:'inputBotInlineMessageID',", + 'dcId:r.int(),', + 'id:r.long(),', + 'accessHash:r.long(),', + '}' + ) + test( + 'contact#145ade0b user_id:long mutual:Bool = Contact;', + 'return{', + "_:'contact',", + 'userId:r.long(),', + 'mutual:r.boolean(),', + '}' + ) + test( + 'maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords;', + 'return{', + "_:'maskCoords',", + 'n:r.int(),', + 'x:r.double(),', + 'y:r.double(),', + 'zoom:r.double(),', + '}' + ) + }) + + it('generates code for constructors with true flags', () => { + test( + 'messages.messageEditData#26b5dde6 flags:# caption:flags.0?true = messages.MessageEditData;', + 'var flags=r.uint();', + 'return{', + "_:'messages.messageEditData',", + 'caption:!!(flags&1),', + '}' + ) + }) + + it('generates code for constructors with optional arguments', () => { + test( + 'updates.channelDifferenceEmpty#3e11affb flags:# final:flags.0?true pts:int timeout:flags.1?int = updates.ChannelDifference;', + 'var flags=r.uint();', + 'return{', + "_:'updates.channelDifferenceEmpty',", + 'final:!!(flags&1),', + 'pts:r.int(),', + 'timeout:flags&2?r.int():void 0,', + '}' + ) + }) + + it('generates code for constructors with vector arguments', () => { + test( + 'contacts.resolvedPeer#7f077ad9 peer:Peer chats:Vector users:Vector = contacts.ResolvedPeer;', + 'return{', + "_:'contacts.resolvedPeer',", + 'peer:r.object(),', + 'chats:r.vector(r.object),', + 'users:r.vector(r.object),', + '}' + ) + }) + + it('generates code for constructors with optional vector arguments', () => { + test( + 'messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector = MessageMedia;', + 'var flags=r.uint();', + 'return{', + "_:'messages.getWebPagePreview',", + 'message:r.string(),', + 'entities:flags&8?r.vector(r.object):void 0,', + '}' + ) + }) + + it('generates code for constructors with generics', () => { + test( + 'invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;', + 'return{', + "_:'invokeWithLayer',", + 'layer:r.int(),', + 'query:r.object(),', + '}' + ) + }) +}) diff --git a/packages/tl-utils/tests/codegen/types.spec.ts b/packages/tl-utils/tests/codegen/types.spec.ts new file mode 100644 index 00000000..902abb59 --- /dev/null +++ b/packages/tl-utils/tests/codegen/types.spec.ts @@ -0,0 +1,319 @@ +import { describe, it } from 'mocha' +import { expect } from 'chai' +import { parseTlToEntries } from '../../src/parse' +import { + generateTypescriptDefinitionsForTlEntry, + generateTypescriptDefinitionsForTlSchema, +} from '../../src/codegen/types' +import { parseFullTlSchema } from '../../src/schema' + +describe('generateTypescriptDefinitionsForTlEntry', () => { + const test = (tl: string, ...ts: string[]) => { + const entry = parseTlToEntries(tl)[0] + expect(generateTypescriptDefinitionsForTlEntry(entry)).eq(ts.join('\n')) + } + + it('replaces primitive types', () => { + test( + 'test a:int b:long c:double d:string e:bytes f:Bool g:vector = Test;', + 'interface RawTest {', + " _: 'test';", + ' a: number;', + ' b: Long;', + ' c: Double;', + ' d: string;', + ' e: Buffer;', + ' f: boolean;', + ' g: number[];', + '}' + ) + }) + + it('ignores namespace for name', () => { + test( + 'test.test = Test;', + 'interface RawTest {', + " _: 'test.test';", + '}' + ) + }) + + it('renames non-primitive types', () => { + test( + 'test foo:Foo bar:vector baz:namespace.Baz egg:vector = Test;', + 'interface RawTest {', + " _: 'test';", + ' foo: tl.TypeFoo;', + ' bar: tl.TypeBar[];', + ' baz: tl.namespace.TypeBaz;', + ' egg: tl.namespace.TypeEgg[];', + '}' + ) + }) + + it('marks optional fields as optional', () => { + test( + 'test flags:# a:flags.0?true b:flags.1?string c:flags.2?Foo d:flags.3?vector = Test;', + 'interface RawTest {', + " _: 'test';", + ' a?: boolean;', + ' b?: string;', + ' c?: tl.TypeFoo;', + ' d?: tl.namespace.TypeFoo[];', + '}' + ) + }) + + it('adds comments', () => { + test( + '// This is a test constructor\ntest = Test;', + '/**', + ' * This is a test constructor', + ' */', + 'interface RawTest {', + " _: 'test';", + '}' + ) + test( + '// @description This is a test constructor\n' + + '// @field Some field\n' + + 'test field:int = Test;', + '/**', + ' * This is a test constructor', + ' */', + 'interface RawTest {', + " _: 'test';", + ' /**', + ' * Some field', + ' */', + ' field: number;', + '}' + ) + test( + '---functions---\n// This is a test method\ntest = Test;', + '/**', + ' * This is a test method', + ' * ', + ' * RPC method returns {@see tl.TypeTest}', + ' */', + 'interface RawTestRequest {', + " _: 'test';", + '}' + ) + + test( + '// This is a test constructor with a very very very very very very very very long comment\ntest = Test;', + '/**', + ' * This is a test constructor with a very very very very very', + ' * very very very long comment', + ' */', + 'interface RawTest {', + " _: 'test';", + '}' + ) + + test( + '---functions---\n// This is a test method with a very very very very very very very very long comment\ntest = Test;', + '/**', + ' * This is a test method with a very very very very very very', + ' * very very long comment', + ' * ', + ' * RPC method returns {@see tl.TypeTest}', + ' */', + 'interface RawTestRequest {', + " _: 'test';", + '}' + ) + }) + + it('writes generic types', () => { + test( + '---functions---\ninvokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X;', + 'interface RawInvokeWithoutUpdatesRequest {', + " _: 'invokeWithoutUpdates';", + " query: X;", + '}' + ) + }) +}) + +describe('generateTypescriptDefinitionsForTlSchema', () => { + const test = (tl: string, ts: string[], js: string[]) => { + const entries = parseTlToEntries(tl) + const schema = parseFullTlSchema(entries) + + let [codeTs, codeJs] = generateTypescriptDefinitionsForTlSchema( + schema, + 0 + ) + + // skip prelude + codeTs = codeTs.substring( + codeTs.indexOf('-readonly [P in keyof T]: T[P]') + 37, + codeTs.length - 1 + ) + // unindent first level + codeTs = codeTs.replace(/^ {4}/gm, '') + + // skip prelude + codeJs = codeJs.substring( + codeJs.indexOf('ns.LAYER = 0;') + 14, + codeJs.length - 15 + ) + + expect(codeTs.trim()).eq(ts.join('\n')) + expect(codeJs.trim()).eq(js.join('\n')) + } + + it('writes simple schemas', () => { + test( + 'test = Test;', + [ + 'interface RawTest {', + " _: 'test';", + '}', + 'interface RpcCallReturn {', + '}', + 'type TypeTest = tl.RawTest', + 'function isAnyTest(o: object): o is TypeTest', + '', + 'type TlObject =', + ' | tl.RawTest', + ], + [ + "ns.isAnyTest = _isAny('Test');", + '_types = JSON.parse(\'{"test":"Test"}\');', + ] + ) + }) + + it('writes schemas with multi-unions', () => { + test( + 'test = Test;\ntest2 = Test;', + [ + 'interface RawTest {', + " _: 'test';", + '}', + 'interface RawTest2 {', + " _: 'test2';", + '}', + 'interface RpcCallReturn {', + '}', + 'type TypeTest = tl.RawTest | tl.RawTest2', + 'function isAnyTest(o: object): o is TypeTest', + '', + 'type TlObject =', + ' | tl.RawTest', + ' | tl.RawTest2', + ], + [ + "ns.isAnyTest = _isAny('Test');", + '_types = JSON.parse(\'{"test":"Test","test2":"Test"}\');', + ] + ) + }) + + it('writes schemas with methods', () => { + test( + 'test = Test;\n---functions---\ngetTest = Test;', + [ + 'interface RawTest {', + " _: 'test';", + '}', + '/**', + ' * RPC method returns {@see tl.TypeTest}', + ' */', + 'interface RawGetTestRequest {', + " _: 'getTest';", + '}', + 'interface RpcCallReturn {', + " 'getTest': tl.TypeTest", + '}', + 'type TypeTest = tl.RawTest', + 'function isAnyTest(o: object): o is TypeTest', + 'type RpcMethod =', + ' | tl.RawGetTestRequest', + '', + 'type TlObject =', + ' | tl.RawTest', + ' | tl.RawGetTestRequest', + ], + [ + "ns.isAnyTest = _isAny('Test');", + '_types = JSON.parse(\'{"test":"Test"}\');', + ] + ) + }) + + it('writes schemas with namespaces', () => { + test( + 'test = Test;\n' + + 'test2 = Test;\n' + + 'test.test = test.Test;\n' + + 'test.test2 = test.Test;\n' + + '---functions---\n' + + 'getTest = Test;\n' + + 'test.getTest = test.Test;', + [ + ` +interface RawTest { + _: 'test'; +} +interface RawTest2 { + _: 'test2'; +} +/** + * RPC method returns {@see tl.TypeTest} + */ +interface RawGetTestRequest { + _: 'getTest'; +} +interface RpcCallReturn extends test.RpcCallReturn { + 'getTest': tl.TypeTest +} +type TypeTest = tl.RawTest | tl.RawTest2 +function isAnyTest(o: object): o is TypeTest + +namespace test { + interface RawTest { + _: 'test.test'; + } + interface RawTest2 { + _: 'test.test2'; + } + /** + * RPC method returns {@see tl.test.TypeTest} + */ + interface RawGetTestRequest { + _: 'test.getTest'; + } + interface RpcCallReturn { + 'test.getTest': tl.test.TypeTest + } + type TypeTest = tl.test.RawTest | tl.test.RawTest2 + function isAnyTest(o: object): o is TypeTest +} +type RpcMethod = + | tl.RawGetTestRequest + | tl.test.RawGetTestRequest + +type TlObject = + | tl.RawTest + | tl.RawTest2 + | tl.test.RawTest + | tl.test.RawTest2 + | tl.RawGetTestRequest + | tl.test.RawGetTestRequest +`.trim(), + ], + [` +ns.isAnyTest = _isAny('Test'); +ns.test = {}; +(function(ns){ +ns.isAnyTest = _isAny('test.Test'); +})(ns.test); +_types = JSON.parse('{"test":"Test","test2":"Test","test.test":"test.Test","test.test2":"test.Test"}'); +`.trim()] + ) + }) +}) diff --git a/packages/tl-utils/tests/codegen/writer.spec.ts b/packages/tl-utils/tests/codegen/writer.spec.ts new file mode 100644 index 00000000..79f600a0 --- /dev/null +++ b/packages/tl-utils/tests/codegen/writer.spec.ts @@ -0,0 +1,107 @@ +import { describe, it } from 'mocha' +import { expect } from 'chai' +import { parseTlToEntries } from '../../src/parse' +import { generateWriterCodeForTlEntry } from '../../src/codegen/writer' + +describe('generateWriterCodeForTlEntry', () => { + const test = (tl: string, ...js: string[]) => { + const entry = parseTlToEntries(tl)[0] + expect(generateWriterCodeForTlEntry(entry)).eq( + `'${entry.name}':function(w${ + entry.arguments.length ? ',v' : '' + }){w.uint(${entry.id});${js.join('')}},` + ) + } + + it('generates code for constructors without arguments', () => { + test('topPeerCategoryBotsPM#ab661b5b = TopPeerCategory;') + }) + + it('generates code for constructors with simple arguments', () => { + test( + 'inputBotInlineMessageID#890c3d89 dc_id:int id:long access_hash:long = InputBotInlineMessageID;', + "h(v,'dcId');", + 'w.int(v.dcId);', + "h(v,'id');", + 'w.long(v.id);', + "h(v,'accessHash');", + 'w.long(v.accessHash);' + ) + test( + 'contact#145ade0b user_id:long mutual:Bool = Contact;', + "h(v,'userId');", + 'w.long(v.userId);', + "h(v,'mutual');", + 'w.boolean(v.mutual);' + ) + test( + 'maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords;', + "h(v,'n');", + 'w.int(v.n);', + "h(v,'x');", + 'w.double(v.x);', + "h(v,'y');", + 'w.double(v.y);', + "h(v,'zoom');", + 'w.double(v.zoom);' + ) + }) + + it('generates code for constructors with true flags', () => { + test( + 'messages.messageEditData#26b5dde6 flags:# caption:flags.0?true = messages.MessageEditData;', + 'var flags=0;', + 'if(v.caption===true)flags|=1;', + 'w.uint(flags);' + ) + }) + + it('generates code for constructors with optional arguments', () => { + test( + 'updates.channelDifferenceEmpty#3e11affb flags:# final:flags.0?true pts:int timeout:flags.1?int = updates.ChannelDifference;', + 'var flags=0;', + 'if(v.final===true)flags|=1;', + 'var _timeout=v.timeout!==undefined;', + 'if(_timeout)flags|=2;', + 'w.uint(flags);', + "h(v,'pts');", + 'w.int(v.pts);', + 'if(_timeout)w.int(v.timeout);' + ) + }) + + it('generates code for constructors with vector arguments', () => { + test( + 'contacts.resolvedPeer#7f077ad9 peer:Peer chats:Vector users:Vector = contacts.ResolvedPeer;', + "h(v,'peer');", + 'w.object(v.peer);', + "h(v,'chats');", + 'w.vector(w.object, v.chats);', + "h(v,'users');", + 'w.vector(w.object, v.users);' + ) + }) + + it('generates code for constructors with optional vector arguments', () => { + test( + 'messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector = MessageMedia;', + 'var flags=0;', + 'var _entities=v.entities&&v.entities.length;', + 'if(_entities)flags|=8;', + 'w.uint(flags);', + "h(v,'message');", + 'w.string(v.message);', + 'if(_entities)w.vector(w.object, v.entities);' + ) + }) + + it('generates code for constructors with generics', () => { + test( + 'invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;', + "h(v,'layer');", + 'w.int(v.layer);', + "h(v,'query');", + 'w.object(v.query);' + ) + }) +}) diff --git a/packages/tl-utils/tests/ctor-id.spec.ts b/packages/tl-utils/tests/ctor-id.spec.ts new file mode 100644 index 00000000..beab8241 --- /dev/null +++ b/packages/tl-utils/tests/ctor-id.spec.ts @@ -0,0 +1,174 @@ +import { describe, it } from 'mocha' +import { expect } from 'chai' +import { + computeConstructorIdFromEntry, + computeConstructorIdFromString, +} from '../src/ctor-id' +import { TlEntry } from '../src/types' + +describe('computeConstructorIdFromString', () => { + const test = (tl: string, expected: number) => { + expect(computeConstructorIdFromString(tl)).eq(expected) + } + + it('computes for constructors without parameters', () => { + test('auth.logOut = Bool;', 0x5717da40) + test('auth.resetAuthorizations = Bool;', 0x9fab0d1a) + }) + + it('ignores existing constructor id', () => { + test('auth.logOut#aef001df = Bool;', 0x5717da40) + }) + + it('computes for constructors with simple parameters', () => { + test( + 'auth.exportAuthorization dc_id:int = auth.ExportedAuthorization;', + 0xe5bfffcd + ) + }) + + it('computes for constructors with vector parameters', () => { + test( + 'account.deleteSecureValue types:Vector = Bool;', + 0xb880bc4b + ) + }) + + it('computes for constructors with vector return type', () => { + test( + 'account.getSecureValue types:Vector = Vector;', + 0x73665bc2 + ) + }) + + it('computes for constructors with optional parameters', () => { + test( + 'account.uploadTheme flags:# file:InputFile thumb:flags.0?InputFile file_name:string mime_type:string = Document;', + 0x1c3db333 + ) + }) + + it('computes for constructors with optional true parameters', () => { + test( + 'account.installTheme flags:# dark:flags.0?true format:flags.1?string theme:flags.1?InputTheme = Bool;', + 0x7ae43737 + ) + }) + + it('computes for constructors with generics', () => { + test('invokeAfterMsg {X:Type} msg_id:long query:!X = X;', 0xcb9f372d) + }) +}) + +describe('computeConstructorIdFromEntry', () => { + const make = (name: string, type: string, ...args: string[]): TlEntry => ({ + kind: 'class', + id: 0, + name, + type, + arguments: args.map((arg) => { + const a = arg.split(':') + const t = a[1].split('?') + if (t[1]) { + return { + name: a[0], + type: t[1], + predicate: t[0], + } + } else { + return { + name: a[0], + type: t[0], + } + } + }), + }) + + const test = (tl: TlEntry, expected: number) => { + expect(computeConstructorIdFromEntry(tl)).eq(expected) + } + + it('computes for constructors without parameters', () => { + test(make('auth.logOut', 'Bool'), 0x5717da40) + test(make('auth.resetAuthorizations', 'Bool'), 0x9fab0d1a) + }) + + it('ignores existing constructor id', () => { + const entry = make('auth.logOut', 'Bool') + entry.id = 0xaef001df + test(entry, 0x5717da40) + }) + + it('computes for constructors with simple parameters', () => { + test( + make( + 'auth.exportAuthorization', + 'auth.ExportedAuthorization', + 'dc_id:int' + ), + 0xe5bfffcd + ) + }) + + it('computes for constructors with vector parameters', () => { + test( + make( + 'account.deleteSecureValue', + 'Bool', + 'types:Vector' + ), + 0xb880bc4b + ) + }) + + it('computes for constructors with vector return type', () => { + test( + make( + 'account.getSecureValue', + 'Vector', + 'types:Vector' + ), + 0x73665bc2 + ) + }) + + it('computes for constructors with optional parameters', () => { + test( + make( + 'account.uploadTheme', + 'Document', + 'flags:#', + 'file:InputFile', + 'thumb:flags.0?InputFile', + 'file_name:string', + 'mime_type:string' + ), + 0x1c3db333 + ) + }) + + it('computes for constructors with optional true parameters', () => { + test( + make( + 'account.installTheme', + 'Bool', + 'flags:#', + 'dark:flags.0?true', + 'format:flags.1?string', + 'theme:flags.1?InputTheme', + ), + 0x7ae43737 + ) + }) + + it('computes for constructors with generics', () => { + const entry = make('invokeAfterMsg', 'X', 'msg_id:long', 'query:!X') + entry.generics = [ + { + name: 'X', + type: 'Type' + } + ] + test(entry, 0xcb9f372d) + }) +}) diff --git a/packages/tl-utils/tests/diff.spec.ts b/packages/tl-utils/tests/diff.spec.ts new file mode 100644 index 00000000..bd180d13 --- /dev/null +++ b/packages/tl-utils/tests/diff.spec.ts @@ -0,0 +1,414 @@ +import { describe, it } from 'mocha' +import { expect } from 'chai' +import { parseTlToEntries } from '../src/parse' +import { TlEntryDiff, TlSchemaDiff } from '../src/types' +import { + generateTlEntriesDifference, + generateTlSchemasDifference, +} from '../src/diff' +import { parseFullTlSchema } from '../src/schema' + +describe('generateTlEntriesDifference', () => { + const test = (tl: string[], expected: TlEntryDiff) => { + const e = parseTlToEntries(tl.join('\n')) + const res = generateTlEntriesDifference(e[0], e[1]) + expect(res).eql(expected) + } + + it('shows id diff', () => { + test(['test#deadbeef = Test;', 'test#baadf00d = Test;'], { + name: 'test', + id: { + old: 0xdeadbeef, + new: 0xbaadf00d, + }, + }) + }) + + it('shows comments diff', () => { + test(['test = Test;', '// Some comment', 'test = Test;'], { + name: 'test', + comment: { + old: undefined, + new: 'Some comment', + }, + }) + }) + + it('shows generics diff', () => { + test(['test#1 {X:Type} = Test;', 'test#1 = Test;'], { + name: 'test', + generics: { + old: [ + { + name: 'X', + type: 'Type', + }, + ], + new: undefined, + }, + }) + test(['test#1 {X:Type} = Test;', 'test#1 {Y:Type} = Test;'], { + name: 'test', + generics: { + old: [ + { + name: 'X', + type: 'Type', + }, + ], + new: [ + { + name: 'Y', + type: 'Type', + }, + ], + }, + }) + }) + + it('shows args diff', () => { + test( + [ + 'test#1 foo:int bar:int egg:flags.0?Egg = Test;', + 'test#1 foo:Foo baz:int egg:flags.1?Egg = Test;', + ], + { + name: 'test', + arguments: { + added: [ + { + name: 'baz', + type: 'int', + }, + ], + removed: [ + { + name: 'bar', + type: 'int', + }, + ], + modified: [ + { + name: 'foo', + type: { + old: 'int', + new: 'Foo', + }, + }, + { + name: 'egg', + predicate: { + old: 'flags.0', + new: 'flags.1', + }, + }, + ], + }, + } + ) + }) +}) + +describe('generateTlSchemasDifference', () => { + const test = ( + tl1: string[], + tl2: string[], + expected: Partial + ) => { + const a = parseFullTlSchema(parseTlToEntries(tl1.join('\n'))) + const b = parseFullTlSchema(parseTlToEntries(tl2.join('\n'))) + const res: Partial = generateTlSchemasDifference(a, b) + + if (!('methods' in expected)) delete res.methods + if (!('classes' in expected)) delete res.classes + if (!('unions' in expected)) delete res.unions + + expect(res).eql(expected) + } + + it('shows added constructors', () => { + test(['test1 = Test;'], ['test1 = Test;', 'test2 = Test;'], { + classes: { + added: [ + { + kind: 'class', + name: 'test2', + id: 3847402009, + type: 'Test', + arguments: [], + }, + ], + removed: [], + modified: [], + }, + }) + }) + + it('shows removed constructors', () => { + test(['test1 = Test;', 'test2 = Test;'], ['test1 = Test;'], { + classes: { + removed: [ + { + kind: 'class', + name: 'test2', + id: 3847402009, + type: 'Test', + arguments: [], + }, + ], + added: [], + modified: [], + }, + }) + }) + + it('shows modified constructors', () => { + test(['test foo:int = Test;'], ['test foo:Foo = Test;'], { + classes: { + removed: [], + added: [], + modified: [ + { + name: 'test', + arguments: { + added: [], + removed: [], + modified: [ + { + name: 'foo', + type: { + old: 'int', + new: 'Foo', + }, + }, + ], + }, + id: { + new: 3348640942, + old: 1331975629, + }, + }, + ], + }, + }) + }) + + it('shows removed unions', () => { + test( + ['test foo:int = Test;', 'test1 = Test1;'], + ['test foo:Foo = Test;'], + { + unions: { + removed: [ + { + name: 'Test1', + classes: [ + { + kind: 'class', + name: 'test1', + id: 3739166976, + type: 'Test1', + arguments: [], + }, + ], + methods: [], + }, + ], + added: [], + modified: [], + }, + } + ) + }) + + it('shows added unions', () => { + test( + ['test foo:int = Test;'], + ['test foo:Foo = Test;', 'test1 = Test1;'], + { + unions: { + added: [ + { + name: 'Test1', + classes: [ + { + kind: 'class', + name: 'test1', + id: 3739166976, + type: 'Test1', + arguments: [], + }, + ], + methods: [], + }, + ], + removed: [], + modified: [], + }, + } + ) + }) + + it('shows modified unions', () => { + test( + ['test foo:int = Test;', 'test1 = Test;'], + ['test foo:Foo = Test;', 'test2 = Test;'], + { + unions: { + added: [], + removed: [], + modified: [ + { + name: 'Test', + classes: { + added: [ + { + kind: 'class', + name: 'test2', + id: 3847402009, + type: 'Test', + arguments: [], + }, + ], + removed: [ + { + kind: 'class', + name: 'test1', + id: 1809692154, + type: 'Test', + arguments: [], + }, + ], + modified: [] + }, + methods: { + added: [], + removed: [], + modified: [] + } + } + ], + }, + } + ) + + test( + ['test foo:int = Test;', 'test1 = Test;'], + ['test2 foo:Foo = Test;', 'test3 = Test;'], + { + unions: { + added: [], + removed: [], + modified: [ + { + name: 'Test', + classes: { + added: [ + { + kind: 'class', + name: 'test2', + id: 711487159, + type: 'Test', + arguments: [ + { + name: 'foo', + type: 'Foo' + } + ], + },{ + kind: 'class', + name: 'test3', + id: 704164487, + type: 'Test', + arguments: [], + }, + ], + removed: [ + { + kind: 'class', + name: 'test', + id: 1331975629, + type: 'Test', + arguments: [ + { + name: 'foo', + type: 'int' + }], + }, + { + kind: 'class', + name: 'test1', + id: 1809692154, + type: 'Test', + arguments: [], + }, + ], + modified: [] + }, + methods: { + added: [], + removed: [], + modified: [] + } + } + ], + }, + } + ) + + test( + ['test = Test;', 'test1 = Test;'], + ['test = Test1;', 'test1 = Test1;'], + { + unions: { + added: [ + { + name: 'Test1', + classes: [ + { + kind: 'class', + name: 'test', + id: 1997819349, + type: 'Test1', + arguments: [], + }, + { + kind: 'class', + name: 'test1', + id: 3739166976, + type: 'Test1', + arguments: [], + }, + ], + methods: [], + }, + ], + removed: [ + { + name: 'Test', + classes: [ + { + kind: 'class', + name: 'test', + id: 471282454, + type: 'Test', + arguments: [], + }, + { + kind: 'class', + name: 'test1', + id: 1809692154, + type: 'Test', + arguments: [], + }, + ], + methods: [], + }, + ], + modified: [], + }, + } + ) + }) +}) diff --git a/packages/tl-utils/tests/merge.spec.ts b/packages/tl-utils/tests/merge.spec.ts new file mode 100644 index 00000000..056953b5 --- /dev/null +++ b/packages/tl-utils/tests/merge.spec.ts @@ -0,0 +1,164 @@ +import { describe, it } from 'mocha' +import { expect } from 'chai' +import { mergeTlEntries, mergeTlSchemas } from '../src/merge' +import { parseTlToEntries } from '../src/parse' +import { writeTlEntryToString } from '../src/stringify' +import { parseFullTlSchema, writeTlEntriesToString } from '../src/schema' + +describe('mergeTlEntries', () => { + const test = (tl: string, expected: string) => { + const res = mergeTlEntries(parseTlToEntries(tl)) + if (typeof res === 'string') { + expect(res).eq(expected) + } else { + expect(writeTlEntryToString(res)).eq(expected) + } + } + + it('fails on conflicting kinds', () => { + test( + 'test = Test;\n---functions---\ntest = Test;', + 'basic info mismatch' + ) + }) + + it('fails on conflicting names', () => { + test('test1 = Test;\ntest2 = Test;', 'basic info mismatch') + }) + + it('fails on conflicting types', () => { + test('test = Test1;\ntest = Test2;', 'basic info mismatch') + }) + + it('fails on conflicting ids', () => { + test('test = Test;\ntest foo:int = Test;', 'basic info mismatch') + }) + + it('fails on conflicting ids', () => { + test('test = Test;\ntest foo:int = Test;', 'basic info mismatch') + }) + + it('merges true flags', () => { + test( + 'test = Test;\n' + + 'test foo:flags.0?true = Test;\n' + + 'test bar:flags.0?true = Test;\n' + + 'test baz:flags.1?true = Test;', + 'test#1c173316 foo:flags.0?true bar:flags.0?true baz:flags.1?true = Test;' + ) + test( + 'test foo:flags.0?true = Test;\n' + + 'test bar:flags.0?true = Test;\n' + + 'test baz:flags.1?true = Test;', + 'test#1c173316 foo:flags.0?true bar:flags.0?true baz:flags.1?true = Test;' + ) + test( + 'test foo:flags.0?true = Test;\n' + + 'test foo:flags.0?true bar:flags.0?true = Test;\n' + + 'test baz:flags.1?true = Test;\n' + + 'test bar:flags.0?true baz:flags.1?true = Test;', + 'test#1c173316 foo:flags.0?true bar:flags.0?true baz:flags.1?true = Test;' + ) + }) +}) + +describe('mergeTlSchemas', () => { + const test = async ( + schemas: string[][], + onConflict: number, + ...expected: string[] + ) => { + const res = await mergeTlSchemas( + schemas.map((tl) => + parseFullTlSchema(parseTlToEntries(tl.join('\n'))) + ), + (opts) => opts[onConflict] + ) + + expect( + writeTlEntriesToString(res.entries, { omitPrimitives: true, tdlibComments: true }) + ).eq(expected.join('\n')) + } + + it('merges different constructors', () => { + test( + [ + ['testClass = Test;'], + ['testClass2 = Test;'], + ['---functions---', 'testMethod = Test;'], + ], + 0, + 'testClass = Test;', + 'testClass2 = Test;', + '---functions---', + 'testMethod = Test;' + ) + }) + + it('merges true flags in constructors', () => { + test( + [ + ['test foo:flags.0?true = Test;'], + ['test bar:flags.0?true = Test;'], + ['test foo:flags.0?true bar:flags.0?true = Test;'], + ], + 0, + 'test#1c173316 foo:flags.0?true bar:flags.0?true = Test;' + ) + }) + + it('resolves conflict using user-provided option', () => { + test( + [ + ['test foo:int = Test;'], + ['test bar:int = Test;'], + ['test baz:int = Test;'], + ], + 0, + 'test foo:int = Test;' + ) + test( + [ + ['test foo:int = Test;'], + ['test bar:int = Test;'], + ['test baz:int = Test;'], + ], + 1, + 'test foo:int = Test;' + ) + test([['test foo:int = Test;'], [], ['test bar:int = Test;']], 1, '') + }) + + it('merges comments', () => { + test( + [ + ['test foo:flags.0?true = Test;'], + ['// test ctor', 'test bar:flags.0?true = Test;'], + [ + '// will be ignored', + 'test foo:flags.0?true bar:flags.0?true = Test;', + ], + ], + 0, + '// @description test ctor', + 'test#1c173316 foo:flags.0?true bar:flags.0?true = Test;' + ) + }) + + it('merges arguments comments', () => { + test( + [ + ['test foo:flags.0?true = Test;'], + ['// @bar bar comment', 'test bar:flags.0?true = Test;'], + [ + '// @foo foo comment', + 'test foo:flags.0?true bar:flags.0?true = Test;', + ], + ], + 0, + '// @foo foo comment', + '// @bar bar comment', + 'test#1c173316 foo:flags.0?true bar:flags.0?true = Test;' + ) + }) +}) diff --git a/packages/tl-utils/tests/parse.spec.ts b/packages/tl-utils/tests/parse.spec.ts new file mode 100644 index 00000000..6423b40b --- /dev/null +++ b/packages/tl-utils/tests/parse.spec.ts @@ -0,0 +1,301 @@ +import { describe, it } from 'mocha' +import { expect } from 'chai' +import { TlEntry } from '../src/types' +import { parseTlToEntries } from '../src/parse' + +describe('tl parser', () => { + const test = (tl: string, expected: TlEntry[]) => { + expect(parseTlToEntries(tl)).eql(expected) + } + + it('skips empty lines and comments', () => { + test('// this is a comment', []) + test('// this is a comment\n\n\n\n// another comment', []) + }) + + it('skips primitives declarations', () => { + test( + ` +int ? = Int; +long ? = Long; +double ? = Double; +string ? = String; +bytes = Bytes; + +vector {t:Type} # [ t ] = Vector t; + +int128 4*[ int ] = Int128; +int256 8*[ int ] = Int256; + +true#3fedd339 = True; +boolFalse#bc799737 = Bool; +boolTrue#997275b5 = Bool; +`, + [] + ) + }) + + it('parses classes w/out arguments', () => { + test('inputUserSelf#f7c1b13f = InputUser;', [ + { + kind: 'class', + name: 'inputUserSelf', + id: 0xf7c1b13f, + type: 'InputUser', + arguments: [], + }, + ]) + }) + + it('parses classes with arguments', () => { + test('inputUser#f21158c6 user_id:long access_hash:long = InputUser;', [ + { + kind: 'class', + name: 'inputUser', + id: 0xf21158c6, + type: 'InputUser', + arguments: [ + { + name: 'user_id', + type: 'long', + }, + { + name: 'access_hash', + type: 'long', + }, + ], + }, + ]) + }) + + it('parses methods with arguments', () => { + test( + '---functions---\nauth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization;', + [ + { + kind: 'method', + name: 'auth.exportAuthorization', + id: 0xe5bfffcd, + type: 'auth.ExportedAuthorization', + arguments: [ + { + name: 'dc_id', + type: 'int', + }, + ], + }, + ] + ) + }) + + it('parses multiple classes', () => { + test( + 'jsonNull#3f6d7b68 = JSONValue;\njsonBool#c7345e6a value:Bool = JSONValue;', + [ + { + kind: 'class', + name: 'jsonNull', + id: 0x3f6d7b68, + type: 'JSONValue', + arguments: [], + }, + { + kind: 'class', + name: 'jsonBool', + id: 0xc7345e6a, + type: 'JSONValue', + arguments: [ + { + name: 'value', + type: 'Bool', + }, + ], + }, + ] + ) + }) + + it('parses generics', () => { + test( + '---functions---\ninvokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;', + [ + { + kind: 'method', + name: 'invokeWithLayer', + id: 0xda9b0d0d, + type: 'X', + generics: [ + { + name: 'X', + type: 'Type', + }, + ], + arguments: [ + { + name: 'layer', + type: 'int', + }, + { + name: 'query', + type: '!X', + }, + ], + }, + ] + ) + }) + + it('parses predicates', () => { + test( + 'help.promoData#8c39793f flags:# proxy:flags.0?true expires:int peer:Peer chats:Vector users:Vector psa_type:flags.1?string psa_message:flags.2?string = help.PromoData;', + [ + { + kind: 'class', + name: 'help.promoData', + id: 0x8c39793f, + type: 'help.PromoData', + arguments: [ + { + name: 'flags', + type: '#', + }, + { + name: 'proxy', + type: 'true', + predicate: 'flags.0', + }, + { + name: 'expires', + type: 'int', + }, + { + name: 'peer', + type: 'Peer', + }, + { + name: 'chats', + type: 'Vector', + }, + { + name: 'users', + type: 'Vector', + }, + { + name: 'psa_type', + type: 'string', + predicate: 'flags.1', + }, + { + name: 'psa_message', + type: 'string', + predicate: 'flags.2', + }, + ], + }, + ] + ) + }) + + it('generates constructor id if missing', () => { + const items = parseTlToEntries( + ` +invokeWithLayer {X:Type} layer:int query:!X = X; +help.promoData flags:# proxy:flags.0?true expires:int peer:Peer chats:Vector users:Vector psa_type:flags.1?string psa_message:flags.2?string = help.PromoData; +account.getAccountTTL = AccountDaysTTL; +users.getUsers id:Vector = Vector; +` + ) + expect(items[0].id).eq(0xda9b0d0d) + expect(items[1].id).eq(0x8c39793f) + expect(items[2].id).eq(0x8fc711d) + expect(items[3].id).eq(0xd91a548) + }) + + it('parses preceding comments', () => { + test('// Self input user\ninputUserSelf#f7c1b13f = InputUser;', [ + { + kind: 'class', + name: 'inputUserSelf', + id: 0xf7c1b13f, + type: 'InputUser', + arguments: [], + comment: 'Self input user', + }, + ]) + + test('// Self input user\n//yes\ninputUserSelf#f7c1b13f = InputUser;', [ + { + kind: 'class', + name: 'inputUserSelf', + id: 0xf7c1b13f, + type: 'InputUser', + arguments: [], + comment: 'Self input user yes', + }, + ]) + }) + + it('ignores comments with an empty line', () => { + test('// Self input user\n\ninputUserSelf#f7c1b13f = InputUser;', [ + { + kind: 'class', + name: 'inputUserSelf', + id: 0xf7c1b13f, + type: 'InputUser', + arguments: [], + }, + ]) + }) + + it('parses tdlib-style comments', () => { + test( + '//@description A file defined by its remote ID. The remote ID is guaranteed to be usable ' + + 'only if the corresponding file is still accessible to the user and known to TDLib.\n' + + '//-For example, if the file is from a message, then the message must be not deleted and ' + + 'accessible to the user. If the file database is disabled, then the corresponding object ' + + 'with the file must be preloaded by the application\n' + + '//@id Remote file identifier\n' + + 'inputFileRemote id:string = InputFile;\n', + [ + { + kind: 'class', + name: 'inputFileRemote', + id: 0xf9968b3e, + type: 'InputFile', + arguments: [ + { + name: 'id', + type: 'string', + comment: 'Remote file identifier', + }, + ], + comment: + 'A file defined by its remote ID. The remote ID is guaranteed to be usable ' + + 'only if the corresponding file is still accessible to the user and known to TDLib.\n' + + 'For example, if the file is from a message, then the message must be not deleted and ' + + 'accessible to the user. If the file database is disabled, then the corresponding object ' + + 'with the file must be preloaded by the application', + }, + ] + ) + }) + + it('calls callback on orphan comment', () => { + const orphaned: string[] = [] + parseTlToEntries( + '// some comment idk\n\n' + + '//another comment\n' + + '//but multiline\n\n' + + '//yet another at the end', + { + onOrphanComment: (s) => orphaned.push(s) + } + ) + + expect(orphaned).eql([ + 'some comment idk', + 'another comment but multiline', + 'yet another at the end' + ]) + }) +}) diff --git a/packages/tl-utils/tests/schema.spec.ts b/packages/tl-utils/tests/schema.spec.ts new file mode 100644 index 00000000..9c46a5e3 --- /dev/null +++ b/packages/tl-utils/tests/schema.spec.ts @@ -0,0 +1,152 @@ +import { describe, it } from 'mocha' +import { expect } from 'chai' +import { TlEntry } from '../src/types' +import { writeTlEntriesToString } from '../src/schema' + +describe('writeTlEntriesToString', () => { + const test = ( + entries: TlEntry[], + params: Parameters[1], + ...expected: string[] + ) => { + expect( + writeTlEntriesToString(entries, { + omitPrimitives: true, + ...params, + }) + ).eq(expected.join('\n')) + } + + it('computes missing ids', () => { + const obj: TlEntry = { + kind: 'class', + name: 'error', + type: 'Error', + id: 0, + arguments: [ + { + name: 'code', + type: 'int', + }, + { + name: 'text', + type: 'string', + }, + ], + } + + test( + [obj], + { computeIds: false }, + 'error code:int text:string = Error;' + ) + test([obj], {}, 'error#c4b9f9bb code:int text:string = Error;') + }) + + it('writes comments along with the constructor', () => { + const obj: TlEntry = { + kind: 'class', + name: 'error', + type: 'Error', + id: 0, + arguments: [ + { + name: 'code', + type: 'int', + comment: 'Error code', + }, + { + name: 'text', + type: 'string', + comment: 'Error description', + }, + ], + comment: 'An error', + } + + test( + [obj], + {}, + '// An error', + 'error#c4b9f9bb code:int text:string = Error;' + ) + + obj.comment += '\nVery error' + test( + [obj], + {}, + '// An error', + '//- Very error', + 'error#c4b9f9bb code:int text:string = Error;' + ) + }) + + it('writes tdlib-style comments', () => { + const obj: TlEntry = { + kind: 'class', + name: 'error', + type: 'Error', + id: 0, + arguments: [ + { + name: 'code', + type: 'int', + comment: 'Error code', + }, + { + name: 'text', + type: 'string', + comment: 'Error description', + }, + ], + comment: 'An error\nVery error', + } + + test( + [obj], + { tdlibComments: true }, + '// @description An error', + '//- Very error', + '// @code Error code', + '// @text Error description', + 'error#c4b9f9bb code:int text:string = Error;' + ) + }) + + it('inserts kind annotation when kind changes', () => { + const cls: TlEntry = { + kind: 'class', + name: 'error', + type: 'Error', + id: 0, + arguments: [ + { + name: 'code', + type: 'int', + }, + { + name: 'text', + type: 'string', + }, + ], + } + const method: TlEntry = { + ...cls, + kind: 'method' + } + + test( + [method, cls, cls, method, cls], + {}, + '---functions---', + 'error#c4b9f9bb code:int text:string = Error;', + '---types---', + 'error#c4b9f9bb code:int text:string = Error;', + 'error#c4b9f9bb code:int text:string = Error;', + '---functions---', + 'error#c4b9f9bb code:int text:string = Error;', + '---types---', + 'error#c4b9f9bb code:int text:string = Error;', + ) + }) +}) diff --git a/packages/tl-utils/tests/stringify.spec.ts b/packages/tl-utils/tests/stringify.spec.ts new file mode 100644 index 00000000..43c4acc0 --- /dev/null +++ b/packages/tl-utils/tests/stringify.spec.ts @@ -0,0 +1,117 @@ +import { describe, it } from 'mocha' +import { expect } from 'chai' +import { TlEntry } from '../src/types' +import { writeTlEntryToString } from '../src/stringify' + +describe('writeTlEntryToString', () => { + const make = (name: string, type: string, ...args: string[]): TlEntry => ({ + kind: 'class', + id: 0, + name, + type, + arguments: args.map((arg) => { + const a = arg.split(':') + const t = a[1].split('?') + if (t[1]) { + return { + name: a[0], + type: t[1], + predicate: t[0], + } + } else { + return { + name: a[0], + type: t[0], + } + } + }), + }) + + const test = (tl: TlEntry, expected: string) => { + expect(writeTlEntryToString(tl)).eq(expected) + } + + it('writes constructors without parameters', () => { + test(make('auth.logOut', 'Bool'), 'auth.logOut = Bool;') + test(make('auth.resetAuthorizations', 'Bool'), 'auth.resetAuthorizations = Bool;') + }) + + it('writes constructor id if available', () => { + const entry = make('auth.logOut', 'Bool') + entry.id = 0xaef001df + test(entry, 'auth.logOut#aef001df = Bool;') + }) + + it('writes constructors with simple parameters', () => { + test( + make( + 'auth.exportAuthorization', + 'auth.ExportedAuthorization', + 'dc_id:int' + ), + 'auth.exportAuthorization dc_id:int = auth.ExportedAuthorization;' + ) + }) + + it('computes for constructors with vector parameters', () => { + test( + make( + 'account.deleteSecureValue', + 'Bool', + 'types:Vector' + ), + 'account.deleteSecureValue types:Vector = Bool;' + ) + }) + + it('computes for constructors with vector return type', () => { + test( + make( + 'account.getSecureValue', + 'Vector', + 'types:Vector' + ), + 'account.getSecureValue types:Vector = Vector;' + ) + }) + + it('computes for constructors with optional parameters', () => { + test( + make( + 'account.uploadTheme', + 'Document', + 'flags:#', + 'file:InputFile', + 'thumb:flags.0?InputFile', + 'file_name:string', + 'mime_type:string' + ), + 'account.uploadTheme flags:# file:InputFile thumb:flags.0?InputFile file_name:string mime_type:string = Document;' + ) + }) + + it('computes for constructors with optional true parameters', () => { + test( + make( + 'account.installTheme', + 'Bool', + 'flags:#', + 'dark:flags.0?true', + 'format:flags.1?string', + 'theme:flags.1?InputTheme', + ), + 'account.installTheme flags:# dark:flags.0?true format:flags.1?string theme:flags.1?InputTheme = Bool;' + ) + }) + + it('computes for constructors with generics', () => { + const entry = make('invokeAfterMsg', 'X', 'msg_id:long', 'query:!X') + entry.generics = [ + { + name: 'X', + type: 'Type' + } + ] + test(entry, 'invokeAfterMsg {X:Type} msg_id:long query:!X = X;') + }) +}) diff --git a/packages/tl-utils/tsconfig.json b/packages/tl-utils/tsconfig.json new file mode 100644 index 00000000..228c1001 --- /dev/null +++ b/packages/tl-utils/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist" + }, + "include": [ + "./src/**/*.ts" + ], + "typedocOptions": { + "name": "@mtcute/tl-utils", + "includeVersion": true, + "out": "../../docs/packages/tl-utils", + "listInvalidSymbolLinks": true, + "excludePrivate": true, + "entryPoints": [ + "./src/**/*.ts" + ] + } +} diff --git a/packages/tl/README.md b/packages/tl/README.md index 677a575c..3f7311cf 100644 --- a/packages/tl/README.md +++ b/packages/tl/README.md @@ -2,21 +2,14 @@ > TL schema and related utils used for MTCute. -Generated from TL layer **131** (last updated on 25.07.2021). +Generated from TL layer **134** (last updated on 20.11.2021). ## About This package contains JSON schema, type declarations, binary (de-)serialization, errors, RSA keys and helper functions. -Package's minor version is always TL schema layer number, -so version `1.42.0` means that this version was generated from TL layer 42. - -> ⚠️ **Warning**: Always use strict or tilde constraint to ensure -> the same schema is used. -> -> I.e. use `"@mtcute/tl": "~1.42.0"` or `"@mtcute/tl": "1.42.0"` -> instead of `"@mtcute/tl": "^1.42.0"`, since the former would also -> match `1.43.0, 1.44.0, ...` and will probably break your build or runtime +Package's major version is always TL schema layer number, +so version `42.0.0` means that this version was generated from TL layer 42. - JSON schema, types, binary (de-)serialization and helper functions are generated directly from `.tl` files that are automatically fetched from [TDesktop repository](https://github.com/telegramdesktop/tdesktop/). diff --git a/packages/tl/api-schema.json b/packages/tl/api-schema.json new file mode 100644 index 00000000..9e4b81bb --- /dev/null +++ b/packages/tl/api-schema.json @@ -0,0 +1 @@ +{"l":134,"e":[{"kind":"class","name":"error","type":"Error","id":3300522427,"comment":"Error.","arguments":[{"name":"code","type":"int","comment":"Error code"},{"name":"text","type":"string","comment":"Message"}]},{"kind":"class","name":"ipPort","id":3560156531,"type":"IpPort","arguments":[{"name":"ipv4","type":"int"},{"name":"port","type":"int"}]},{"kind":"class","name":"ipPortSecret","id":932718150,"type":"IpPort","arguments":[{"name":"ipv4","type":"int"},{"name":"port","type":"int"},{"name":"secret","type":"bytes"}]},{"kind":"class","name":"accessPointRule","id":1182381663,"type":"AccessPointRule","arguments":[{"name":"phone_prefix_rules","type":"string"},{"name":"dc_id","type":"int"},{"name":"ips","type":"vector"}]},{"kind":"class","name":"help.configSimple","id":1515793004,"type":"help.ConfigSimple","arguments":[{"name":"date","type":"int"},{"name":"expires","type":"int"},{"name":"rules","type":"vector"}]},{"kind":"class","name":"inputPeerPhotoFileLocationLegacy","id":668375447,"type":"InputFileLocation","arguments":[{"name":"flags","type":"#"},{"name":"big","type":"true","predicate":"flags.0"},{"name":"peer","type":"InputPeer"},{"name":"volume_id","type":"long"},{"name":"local_id","type":"int"}]},{"kind":"class","name":"inputStickerSetThumbLegacy","id":230353641,"type":"InputFileLocation","arguments":[{"name":"stickerset","type":"InputStickerSet"},{"name":"volume_id","type":"long"},{"name":"local_id","type":"int"}]},{"kind":"class","name":"inputPeerEmpty","type":"InputPeer","id":2134579434,"comment":"An empty constructor, no user or chat is defined.","arguments":[]},{"kind":"class","name":"inputPeerSelf","type":"InputPeer","id":2107670217,"comment":"Defines the current user.","arguments":[]},{"kind":"class","name":"inputPeerChat","type":"InputPeer","id":900291769,"comment":"Defines a chat for further interaction.","arguments":[{"name":"chat_id","type":"int53"}]},{"kind":"class","name":"inputPeerUser","type":"InputPeer","id":3723011404,"comment":"Defines a user for further interaction.","arguments":[{"name":"user_id","type":"int53"},{"name":"access_hash","type":"long"}]},{"kind":"class","name":"inputPeerChannel","type":"InputPeer","id":666680316,"comment":"Defines a channel for further interaction.","arguments":[{"name":"channel_id","type":"int53"},{"name":"access_hash","type":"long"}]},{"kind":"class","name":"inputPeerUserFromMessage","type":"InputPeer","id":2826635804,"comment":"Defines a min user that was seen in a certain message of a certain chat.","arguments":[{"name":"peer","type":"InputPeer","comment":"The chat where the user was seen"},{"name":"msg_id","type":"int"},{"name":"user_id","type":"int53"}]},{"kind":"class","name":"inputPeerChannelFromMessage","type":"InputPeer","id":3173648448,"comment":"Defines a min channel that was seen in a certain message of a certain chat.","arguments":[{"name":"peer","type":"InputPeer","comment":"The chat where the channel's message was seen"},{"name":"msg_id","type":"int"},{"name":"channel_id","type":"int53"}]},{"kind":"class","name":"inputUserEmpty","type":"InputUser","id":3112732367,"comment":"Empty constructor, does not define a user.","arguments":[]},{"kind":"class","name":"inputUserSelf","type":"InputUser","id":4156666175,"comment":"Defines the current user.","arguments":[]},{"kind":"class","name":"inputUser","type":"InputUser","id":4061223110,"comment":"Defines a user for further interaction.","arguments":[{"name":"user_id","type":"int53"},{"name":"access_hash","type":"long"}]},{"kind":"class","name":"inputUserFromMessage","type":"InputUser","id":497305826,"comment":"Defines a min user that was seen in a certain message of a certain chat.","arguments":[{"name":"peer","type":"InputPeer","comment":"The chat where the user was seen"},{"name":"msg_id","type":"int"},{"name":"user_id","type":"int53"}]},{"kind":"class","name":"inputPhoneContact","type":"InputContact","id":4086478836,"comment":"Phone contact. The client_id is just an arbitrary contact ID: it should be set, for example, to an incremental number when using {@link contacts.importContacts}, in order to retry importing only the contacts that weren't imported successfully.","arguments":[{"name":"client_id","type":"long"},{"name":"phone","type":"string","comment":"Phone number"},{"name":"first_name","type":"string"},{"name":"last_name","type":"string"}]},{"kind":"class","name":"inputFile","type":"InputFile","id":4113560191,"comment":"Defines a file saved in parts using the method {@link upload.saveFilePart}.","arguments":[{"name":"id","type":"long","comment":"Random file identifier created by the client"},{"name":"parts","type":"int","comment":"Number of parts saved"},{"name":"name","type":"string","comment":"Full name of the file"},{"name":"md5_checksum","type":"string"}]},{"kind":"class","name":"inputFileBig","type":"InputFile","id":4199484341,"comment":"Assigns a big file (over 10Mb in size), saved in part using the method {@link upload.saveBigFilePart}.","arguments":[{"name":"id","type":"long","comment":"Random file id, created by the client"},{"name":"parts","type":"int","comment":"Number of parts saved"},{"name":"name","type":"string","comment":"Full file name"}]},{"kind":"class","name":"inputMediaEmpty","type":"InputMedia","id":2523198847,"comment":"Empty media content of a message.","arguments":[]},{"kind":"class","name":"inputMediaUploadedPhoto","type":"InputMedia","id":505969924,"comment":"Photo","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"file","type":"InputFile","comment":"The uploaded file"},{"name":"stickers","type":"Vector","predicate":"flags.0","comment":"Attached mask stickers"},{"name":"ttl_seconds","type":"int","predicate":"flags.1"}]},{"kind":"class","name":"inputMediaPhoto","type":"InputMedia","id":3015312949,"comment":"Forwarded photo","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"id","type":"InputPhoto","comment":"Photo to be forwarded"},{"name":"ttl_seconds","type":"int","predicate":"flags.0"}]},{"kind":"class","name":"inputMediaGeoPoint","type":"InputMedia","id":4190388548,"comment":"Map.","arguments":[{"name":"geo_point","type":"InputGeoPoint"}]},{"kind":"class","name":"inputMediaContact","type":"InputMedia","id":4171988475,"comment":"Phonebook contact","arguments":[{"name":"phone_number","type":"string"},{"name":"first_name","type":"string"},{"name":"last_name","type":"string"},{"name":"vcard","type":"string","comment":"Contact vcard"}]},{"kind":"class","name":"inputMediaUploadedDocument","type":"InputMedia","id":1530447553,"comment":"New document","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"nosound_video","type":"true","predicate":"flags.3"},{"name":"force_file","type":"true","predicate":"flags.4"},{"name":"file","type":"InputFile","comment":"The uploaded file"},{"name":"thumb","type":"InputFile","predicate":"flags.2","comment":"Thumbnail of the document, uploaded as for the file"},{"name":"mime_type","type":"string"},{"name":"attributes","type":"Vector","comment":"Attributes that specify the type of the document (video, audio, voice, sticker, etc.)"},{"name":"stickers","type":"Vector","predicate":"flags.0","comment":"Attached stickers"},{"name":"ttl_seconds","type":"int","predicate":"flags.1"}]},{"kind":"class","name":"inputMediaDocument","type":"InputMedia","id":860303448,"comment":"Forwarded document","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"id","type":"InputDocument","comment":"The document to be forwarded."},{"name":"ttl_seconds","type":"int","predicate":"flags.0"},{"name":"query","type":"string","predicate":"flags.1","comment":"Text query or emoji that was used by the user to find this sticker or GIF: used to improve search result relevance."}]},{"kind":"class","name":"inputMediaVenue","type":"InputMedia","id":3242007569,"comment":"Can be used to send a venue geolocation.","arguments":[{"name":"geo_point","type":"InputGeoPoint"},{"name":"title","type":"string","comment":"Venue name"},{"name":"address","type":"string","comment":"Physical address of the venue"},{"name":"provider","type":"string","comment":"Venue provider: currently only \"foursquare\" and \"gplaces\" need to be supported"},{"name":"venue_id","type":"string"},{"name":"venue_type","type":"string"}]},{"kind":"class","name":"inputMediaPhotoExternal","type":"InputMedia","id":3854302746,"comment":"New photo that will be uploaded by the server using the specified URL","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"url","type":"string","comment":"URL of the photo"},{"name":"ttl_seconds","type":"int","predicate":"flags.0"}]},{"kind":"class","name":"inputMediaDocumentExternal","type":"InputMedia","id":4216511641,"comment":"Document that will be downloaded by the telegram servers","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"url","type":"string","comment":"URL of the document"},{"name":"ttl_seconds","type":"int","predicate":"flags.0"}]},{"kind":"class","name":"inputMediaGame","type":"InputMedia","id":3544138739,"comment":"A game","arguments":[{"name":"id","type":"InputGame","comment":"The game to forward"}]},{"kind":"class","name":"inputMediaInvoice","type":"InputMedia","id":3648624756,"comment":"Generated invoice of a bot payment","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"title","type":"string","comment":"Product name, 1-32 characters"},{"name":"description","type":"string","comment":"Product description, 1-255 characters"},{"name":"photo","type":"InputWebDocument","predicate":"flags.0","comment":"URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for."},{"name":"invoice","type":"Invoice","comment":"The actual invoice"},{"name":"payload","type":"bytes","comment":"Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes."},{"name":"provider","type":"string","comment":"Payments provider token, obtained via Botfather"},{"name":"provider_data","type":"DataJSON"},{"name":"start_param","type":"string","predicate":"flags.1"}]},{"kind":"class","name":"inputMediaGeoLive","type":"InputMedia","id":2535434307,"comment":"Live geolocation","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"stopped","type":"true","predicate":"flags.0","comment":"Whether sending of the geolocation was stopped"},{"name":"geo_point","type":"InputGeoPoint"},{"name":"heading","type":"int","predicate":"flags.2","comment":"For live locations, a direction in which the location moves, in degrees; 1-360."},{"name":"period","type":"int","predicate":"flags.1","comment":"Validity period of the current location"},{"name":"proximity_notification_radius","type":"int","predicate":"flags.3"}]},{"kind":"class","name":"inputMediaPoll","type":"InputMedia","id":4059867057,"comment":"A poll","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"poll","type":"Poll","comment":"The poll to send"},{"name":"correct_answers","type":"Vector","predicate":"flags.0"},{"name":"solution","type":"string","predicate":"flags.1","comment":"Explanation of quiz solution"},{"name":"solution_entities","type":"Vector","predicate":"flags.1"}]},{"kind":"class","name":"inputMediaDice","type":"InputMedia","id":3866083195,"comment":"Send a dice-based animated sticker","arguments":[{"name":"emoticon","type":"string","comment":"The emoji, for now \"🏀\", \"🎲\" and \"🎯\" are supported"}]},{"kind":"class","name":"inputChatPhotoEmpty","type":"InputChatPhoto","id":480546647,"comment":"Empty constructor, remove group photo.","arguments":[]},{"kind":"class","name":"inputChatUploadedPhoto","type":"InputChatPhoto","id":3326243406,"comment":"New photo to be set as group profile photo.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"file","type":"InputFile","predicate":"flags.0","comment":"File saved in parts using the method {@link upload.saveFilePart}"},{"name":"video","type":"InputFile","predicate":"flags.1","comment":"Square video for animated profile picture"},{"name":"video_start_ts","type":"double","predicate":"flags.2"}]},{"kind":"class","name":"inputChatPhoto","type":"InputChatPhoto","id":2303962423,"comment":"Existing photo to be set as a chat profile photo.","arguments":[{"name":"id","type":"InputPhoto","comment":"Existing photo"}]},{"kind":"class","name":"inputGeoPointEmpty","type":"InputGeoPoint","id":3837862870,"comment":"Empty GeoPoint constructor.","arguments":[]},{"kind":"class","name":"inputGeoPoint","type":"InputGeoPoint","id":1210199983,"comment":"Defines a GeoPoint by its coordinates.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"lat","type":"double","comment":"Latitide"},{"name":"long","type":"double","comment":"Longtitude"},{"name":"accuracy_radius","type":"int","predicate":"flags.0"}]},{"kind":"class","name":"inputPhotoEmpty","type":"InputPhoto","id":483901197,"comment":"Empty constructor.","arguments":[]},{"kind":"class","name":"inputPhoto","type":"InputPhoto","id":1001634122,"comment":"Defines a photo for further interaction.","arguments":[{"name":"id","type":"long","comment":"Photo identifier"},{"name":"access_hash","type":"long"},{"name":"file_reference","type":"bytes"}]},{"kind":"class","name":"inputFileLocation","type":"InputFileLocation","id":3755650017,"comment":"DEPRECATED location of a photo","arguments":[{"name":"volume_id","type":"long"},{"name":"local_id","type":"int"},{"name":"secret","type":"long","comment":"Check sum to access the file"},{"name":"file_reference","type":"bytes"}]},{"kind":"class","name":"inputEncryptedFileLocation","type":"InputFileLocation","id":4112735573,"comment":"Location of encrypted secret chat file.","arguments":[{"name":"id","type":"long","comment":"File ID, id parameter value from {@link encryptedFile}"},{"name":"access_hash","type":"long"}]},{"kind":"class","name":"inputDocumentFileLocation","type":"InputFileLocation","id":3134223748,"comment":"Document location (video, voice, audio, basically every type except photo)","arguments":[{"name":"id","type":"long","comment":"Document ID"},{"name":"access_hash","type":"long"},{"name":"file_reference","type":"bytes"},{"name":"thumb_size","type":"string"}]},{"kind":"class","name":"inputSecureFileLocation","type":"InputFileLocation","id":3418877480,"comment":"Location of encrypted telegram passport file.","arguments":[{"name":"id","type":"long","comment":"File ID, id parameter value from {@link secureFile}"},{"name":"access_hash","type":"long"}]},{"kind":"class","name":"inputTakeoutFileLocation","type":"InputFileLocation","id":700340377,"comment":"Empty constructor for takeout","arguments":[]},{"kind":"class","name":"inputPhotoFileLocation","type":"InputFileLocation","id":1075322878,"comment":"Use this object to download a photo with {@link upload.getFile} method","arguments":[{"name":"id","type":"long","comment":"Photo ID, obtained from the {@link photo} object"},{"name":"access_hash","type":"long"},{"name":"file_reference","type":"bytes"},{"name":"thumb_size","type":"string"}]},{"kind":"class","name":"inputPhotoLegacyFileLocation","type":"InputFileLocation","id":3627312883,"comment":"DEPRECATED legacy photo file location","arguments":[{"name":"id","type":"long","comment":"Photo ID"},{"name":"access_hash","type":"long"},{"name":"file_reference","type":"bytes"},{"name":"volume_id","type":"long"},{"name":"local_id","type":"int"},{"name":"secret","type":"long","comment":"Secret"}]},{"kind":"class","name":"inputPeerPhotoFileLocation","type":"InputFileLocation","id":925204121,"comment":"Location of profile photo of channel/group/supergroup/user","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"big","type":"true","predicate":"flags.0","comment":"Whether to download the high-quality version of the picture"},{"name":"peer","type":"InputPeer","comment":"The peer whose profile picture should be downloaded"},{"name":"photo_id","type":"long"}]},{"kind":"class","name":"inputStickerSetThumb","type":"InputFileLocation","id":2642736091,"comment":"Location of stickerset thumbnail (see files)","arguments":[{"name":"stickerset","type":"InputStickerSet","comment":"Sticker set"},{"name":"thumb_version","type":"int"}]},{"kind":"class","name":"inputGroupCallStream","type":"InputFileLocation","id":93890858,"comment":"Chunk of a livestream","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"call","type":"InputGroupCall","comment":"Livestream info"},{"name":"time_ms","type":"long"},{"name":"scale","type":"int","comment":"Specifies the duration of the video segment to fetch in milliseconds, by bitshifting 1000 to the right scale times: duration_ms := 1000 >> scale"},{"name":"video_channel","type":"int","predicate":"flags.0"},{"name":"video_quality","type":"int","predicate":"flags.0"}]},{"kind":"class","name":"peerUser","type":"Peer","id":1498486562,"comment":"Chat partner","arguments":[{"name":"user_id","type":"int53"}]},{"kind":"class","name":"peerChat","type":"Peer","id":918946202,"comment":"Group.","arguments":[{"name":"chat_id","type":"int53"}]},{"kind":"class","name":"peerChannel","type":"Peer","id":2728736542,"comment":"Channel/supergroup","arguments":[{"name":"channel_id","type":"int53"}]},{"kind":"class","name":"storage.fileUnknown","type":"storage.FileType","id":2861972229,"comment":"Unknown type.","arguments":[]},{"kind":"class","name":"storage.filePartial","type":"storage.FileType","id":1086091090,"comment":"Part of a bigger file.","arguments":[]},{"kind":"class","name":"storage.fileJpeg","type":"storage.FileType","id":8322574,"comment":"JPEG image. MIME type: image/jpeg.","arguments":[]},{"kind":"class","name":"storage.fileGif","type":"storage.FileType","id":3403786975,"comment":"GIF image. MIME type: image/gif.","arguments":[]},{"kind":"class","name":"storage.filePng","type":"storage.FileType","id":172975040,"comment":"PNG image. MIME type: image/png.","arguments":[]},{"kind":"class","name":"storage.filePdf","type":"storage.FileType","id":2921222285,"comment":"PDF document image. MIME type: application/pdf.","arguments":[]},{"kind":"class","name":"storage.fileMp3","type":"storage.FileType","id":1384777335,"comment":"Mp3 audio. MIME type: audio/mpeg.","arguments":[]},{"kind":"class","name":"storage.fileMov","type":"storage.FileType","id":1258941372,"comment":"Quicktime video. MIME type: video/quicktime.","arguments":[]},{"kind":"class","name":"storage.fileMp4","type":"storage.FileType","id":3016663268,"comment":"MPEG-4 video. MIME type: video/mp4.","arguments":[]},{"kind":"class","name":"storage.fileWebp","type":"storage.FileType","id":276907596,"comment":"WEBP image. MIME type: image/webp.","arguments":[]},{"kind":"class","name":"userEmpty","type":"User","id":3552332666,"comment":"Empty constructor, non-existent user.","arguments":[{"name":"id","type":"int53","comment":"User identifier or 0"}]},{"kind":"class","name":"user","type":"User","id":1073147056,"comment":"Indicates info about a certain user","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"self","type":"true","predicate":"flags.10","comment":"Whether this user indicates the currently logged in user"},{"name":"contact","type":"true","predicate":"flags.11","comment":"Whether this user is a contact"},{"name":"mutual_contact","type":"true","predicate":"flags.12"},{"name":"deleted","type":"true","predicate":"flags.13","comment":"Whether the account of this user was deleted"},{"name":"bot","type":"true","predicate":"flags.14","comment":"Is this user a bot?"},{"name":"bot_chat_history","type":"true","predicate":"flags.15"},{"name":"bot_nochats","type":"true","predicate":"flags.16"},{"name":"verified","type":"true","predicate":"flags.17","comment":"Whether this user is verified"},{"name":"restricted","type":"true","predicate":"flags.18","comment":"Access to this user must be restricted for the reason specified in restriction_reason"},{"name":"min","type":"true","predicate":"flags.20","comment":"See min"},{"name":"bot_inline_geo","type":"true","predicate":"flags.21"},{"name":"support","type":"true","predicate":"flags.23","comment":"Whether this is an official support user"},{"name":"scam","type":"true","predicate":"flags.24","comment":"This may be a scam user"},{"name":"apply_min_photo","type":"true","predicate":"flags.25"},{"name":"fake","type":"true","predicate":"flags.26","comment":"If set, this user was reported by many users as a fake or scam user: be careful when interacting with them."},{"name":"id","type":"int53","comment":"ID of the user"},{"name":"access_hash","type":"long","predicate":"flags.0"},{"name":"first_name","type":"string","predicate":"flags.1"},{"name":"last_name","type":"string","predicate":"flags.2"},{"name":"username","type":"string","predicate":"flags.3","comment":"Username"},{"name":"phone","type":"string","predicate":"flags.4","comment":"Phone number"},{"name":"photo","type":"UserProfilePhoto","predicate":"flags.5","comment":"Profile picture of user"},{"name":"status","type":"UserStatus","predicate":"flags.6","comment":"Online status of user"},{"name":"bot_info_version","type":"int","predicate":"flags.14"},{"name":"restriction_reason","type":"Vector","predicate":"flags.18"},{"name":"bot_inline_placeholder","type":"string","predicate":"flags.19"},{"name":"lang_code","type":"string","predicate":"flags.22"}]},{"kind":"class","name":"userProfilePhotoEmpty","type":"UserProfilePhoto","id":1326562017,"comment":"Profile photo has not been set, or was hidden.","arguments":[]},{"kind":"class","name":"userProfilePhoto","type":"UserProfilePhoto","id":2194798342,"comment":"User profile photo.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"has_video","type":"true","predicate":"flags.0"},{"name":"photo_id","type":"long"},{"name":"stripped_thumb","type":"bytes","predicate":"flags.1"},{"name":"dc_id","type":"int"}]},{"kind":"class","name":"userStatusEmpty","type":"UserStatus","id":164646985,"comment":"User status has not been set yet.","arguments":[]},{"kind":"class","name":"userStatusOnline","type":"UserStatus","id":3988339017,"comment":"Online status of the user.","arguments":[{"name":"expires","type":"int","comment":"Time to expiration of the current online status"}]},{"kind":"class","name":"userStatusOffline","type":"UserStatus","id":9203775,"comment":"The user's offline status.","arguments":[{"name":"was_online","type":"int"}]},{"kind":"class","name":"userStatusRecently","type":"UserStatus","id":3798942449,"comment":"Online status: last seen recently","arguments":[]},{"kind":"class","name":"userStatusLastWeek","type":"UserStatus","id":129960444,"comment":"Online status: last seen last week","arguments":[]},{"kind":"class","name":"userStatusLastMonth","type":"UserStatus","id":2011940674,"comment":"Online status: last seen last month","arguments":[]},{"kind":"class","name":"chatEmpty","type":"Chat","id":693512293,"comment":"Empty constructor, group doesn't exist","arguments":[{"name":"id","type":"int53","comment":"Group identifier"}]},{"kind":"class","name":"chat","type":"Chat","id":1103884886,"comment":"Info about a group","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"creator","type":"true","predicate":"flags.0","comment":"Whether the current user is the creator of the group"},{"name":"kicked","type":"true","predicate":"flags.1","comment":"Whether the current user was kicked from the group"},{"name":"left","type":"true","predicate":"flags.2","comment":"Whether the current user has left the group"},{"name":"deactivated","type":"true","predicate":"flags.5","comment":"Whether the group was migrated"},{"name":"call_active","type":"true","predicate":"flags.23"},{"name":"call_not_empty","type":"true","predicate":"flags.24"},{"name":"noforwards","type":"true","predicate":"flags.25"},{"name":"id","type":"int53","comment":"ID of the group"},{"name":"title","type":"string","comment":"Title"},{"name":"photo","type":"ChatPhoto","comment":"Chat photo"},{"name":"participants_count","type":"int"},{"name":"date","type":"int","comment":"Date of creation of the group"},{"name":"version","type":"int","comment":"Used in basic groups to reorder updates and make sure that all of them were received."},{"name":"migrated_to","type":"InputChannel","predicate":"flags.6"},{"name":"admin_rights","type":"ChatAdminRights","predicate":"flags.14"},{"name":"default_banned_rights","type":"ChatBannedRights","predicate":"flags.18"}]},{"kind":"class","name":"chatForbidden","type":"Chat","id":1704108455,"comment":"A group to which the user has no access. E.g., because the user was kicked from the group.","arguments":[{"name":"id","type":"int53","comment":"User identifier"},{"name":"title","type":"string","comment":"Group name"}]},{"kind":"class","name":"channel","type":"Chat","id":2187439201,"comment":"Channel/supergroup info","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"creator","type":"true","predicate":"flags.0","comment":"Whether the current user is the creator of this channel"},{"name":"left","type":"true","predicate":"flags.2","comment":"Whether the current user has left this channel"},{"name":"broadcast","type":"true","predicate":"flags.5","comment":"Is this a channel?"},{"name":"verified","type":"true","predicate":"flags.7","comment":"Is this channel verified by telegram?"},{"name":"megagroup","type":"true","predicate":"flags.8","comment":"Is this a supergroup?"},{"name":"restricted","type":"true","predicate":"flags.9","comment":"Whether viewing/writing in this channel for a reason (see restriction_reason"},{"name":"signatures","type":"true","predicate":"flags.11","comment":"Whether signatures are enabled (channels)"},{"name":"min","type":"true","predicate":"flags.12","comment":"See min"},{"name":"scam","type":"true","predicate":"flags.19","comment":"This channel/supergroup is probably a scam"},{"name":"has_link","type":"true","predicate":"flags.20"},{"name":"has_geo","type":"true","predicate":"flags.21"},{"name":"slowmode_enabled","type":"true","predicate":"flags.22"},{"name":"call_active","type":"true","predicate":"flags.23"},{"name":"call_not_empty","type":"true","predicate":"flags.24"},{"name":"fake","type":"true","predicate":"flags.25","comment":"If set, this supergroup/channel was reported by many users as a fake or scam: be careful when interacting with it."},{"name":"gigagroup","type":"true","predicate":"flags.26","comment":"Whether this supergroup is a gigagroup"},{"name":"noforwards","type":"true","predicate":"flags.27"},{"name":"id","type":"int53","comment":"ID of the channel"},{"name":"access_hash","type":"long","predicate":"flags.13"},{"name":"title","type":"string","comment":"Title"},{"name":"username","type":"string","predicate":"flags.6","comment":"Username"},{"name":"photo","type":"ChatPhoto","comment":"Profile photo"},{"name":"date","type":"int","comment":"Date when the user joined the supergroup/channel, or if the user isn't a member, its creation date"},{"name":"restriction_reason","type":"Vector","predicate":"flags.9"},{"name":"admin_rights","type":"ChatAdminRights","predicate":"flags.14"},{"name":"banned_rights","type":"ChatBannedRights","predicate":"flags.15"},{"name":"default_banned_rights","type":"ChatBannedRights","predicate":"flags.18"},{"name":"participants_count","type":"int","predicate":"flags.17"}]},{"kind":"class","name":"channelForbidden","type":"Chat","id":399807445,"comment":"Indicates a channel/supergroup we can't access because we were banned, or for some other reason.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"broadcast","type":"true","predicate":"flags.5","comment":"Is this a channel"},{"name":"megagroup","type":"true","predicate":"flags.8","comment":"Is this a supergroup"},{"name":"id","type":"int53","comment":"Channel ID"},{"name":"access_hash","type":"long"},{"name":"title","type":"string","comment":"Title"},{"name":"until_date","type":"int","predicate":"flags.16"}]},{"kind":"class","name":"chatFull","type":"ChatFull","id":1185349556,"comment":"Detailed chat info","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"can_set_username","type":"true","predicate":"flags.7"},{"name":"has_scheduled","type":"true","predicate":"flags.8"},{"name":"id","type":"int53","comment":"ID of the chat"},{"name":"about","type":"string","comment":"About string for this chat"},{"name":"participants","type":"ChatParticipants","comment":"Participant list"},{"name":"chat_photo","type":"Photo","predicate":"flags.2"},{"name":"notify_settings","type":"PeerNotifySettings"},{"name":"exported_invite","type":"ExportedChatInvite","predicate":"flags.13"},{"name":"bot_info","type":"Vector","predicate":"flags.3"},{"name":"pinned_msg_id","type":"int","predicate":"flags.6"},{"name":"folder_id","type":"int","predicate":"flags.11"},{"name":"call","type":"InputGroupCall","predicate":"flags.12","comment":"Group call information"},{"name":"ttl_period","type":"int","predicate":"flags.14"},{"name":"groupcall_default_join_as","type":"Peer","predicate":"flags.15"},{"name":"theme_emoticon","type":"string","predicate":"flags.16"},{"name":"requests_pending","type":"int","predicate":"flags.17"},{"name":"recent_requesters","type":"Vector","predicate":"flags.17"}]},{"kind":"class","name":"channelFull","type":"ChatFull","id":1506802019,"comment":"Full info about a channel/supergroup","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"can_view_participants","type":"true","predicate":"flags.3"},{"name":"can_set_username","type":"true","predicate":"flags.6"},{"name":"can_set_stickers","type":"true","predicate":"flags.7"},{"name":"hidden_prehistory","type":"true","predicate":"flags.10"},{"name":"can_set_location","type":"true","predicate":"flags.16"},{"name":"has_scheduled","type":"true","predicate":"flags.19"},{"name":"can_view_stats","type":"true","predicate":"flags.20"},{"name":"blocked","type":"true","predicate":"flags.22","comment":"Whether any anonymous admin of this supergroup was blocked: if set, you won't receive messages from anonymous group admins in discussion replies via @replies"},{"name":"id","type":"int53","comment":"ID of the channel"},{"name":"about","type":"string","comment":"Info about the channel"},{"name":"participants_count","type":"int","predicate":"flags.0"},{"name":"admins_count","type":"int","predicate":"flags.1"},{"name":"kicked_count","type":"int","predicate":"flags.2"},{"name":"banned_count","type":"int","predicate":"flags.2"},{"name":"online_count","type":"int","predicate":"flags.13"},{"name":"read_inbox_max_id","type":"int"},{"name":"read_outbox_max_id","type":"int"},{"name":"unread_count","type":"int"},{"name":"chat_photo","type":"Photo"},{"name":"notify_settings","type":"PeerNotifySettings"},{"name":"exported_invite","type":"ExportedChatInvite","predicate":"flags.23"},{"name":"bot_info","type":"Vector"},{"name":"migrated_from_chat_id","type":"long","predicate":"flags.4"},{"name":"migrated_from_max_id","type":"int","predicate":"flags.4"},{"name":"pinned_msg_id","type":"int","predicate":"flags.5"},{"name":"stickerset","type":"StickerSet","predicate":"flags.8","comment":"Associated stickerset"},{"name":"available_min_id","type":"int","predicate":"flags.9"},{"name":"folder_id","type":"int","predicate":"flags.11"},{"name":"linked_chat_id","type":"int53","predicate":"flags.14"},{"name":"location","type":"ChannelLocation","predicate":"flags.15","comment":"Location of the geo group"},{"name":"slowmode_seconds","type":"int","predicate":"flags.17"},{"name":"slowmode_next_send_date","type":"int","predicate":"flags.18"},{"name":"stats_dc","type":"int","predicate":"flags.12"},{"name":"pts","type":"int","comment":"Latest PTS for this channel"},{"name":"call","type":"InputGroupCall","predicate":"flags.21","comment":"Livestream or group call information"},{"name":"ttl_period","type":"int","predicate":"flags.24"},{"name":"pending_suggestions","type":"Vector","predicate":"flags.25"},{"name":"groupcall_default_join_as","type":"Peer","predicate":"flags.26"},{"name":"theme_emoticon","type":"string","predicate":"flags.27"},{"name":"requests_pending","type":"int","predicate":"flags.28"},{"name":"recent_requesters","type":"Vector","predicate":"flags.28"}]},{"kind":"class","name":"chatParticipant","type":"ChatParticipant","id":3224190983,"comment":"Group member.","arguments":[{"name":"user_id","type":"int53"},{"name":"inviter_id","type":"int53"},{"name":"date","type":"int","comment":"Date added to the group"}]},{"kind":"class","name":"chatParticipantCreator","type":"ChatParticipant","id":3832270564,"comment":"Represents the creator of the group","arguments":[{"name":"user_id","type":"int53"}]},{"kind":"class","name":"chatParticipantAdmin","type":"ChatParticipant","id":2694004571,"comment":"Chat admin","arguments":[{"name":"user_id","type":"int53"},{"name":"inviter_id","type":"int53"},{"name":"date","type":"int","comment":"Date when the user was added"}]},{"kind":"class","name":"chatParticipantsForbidden","type":"ChatParticipants","id":2271466465,"comment":"Info on members is unavailable","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"chat_id","type":"int53"},{"name":"self_participant","type":"ChatParticipant","predicate":"flags.0"}]},{"kind":"class","name":"chatParticipants","type":"ChatParticipants","id":1018991608,"comment":"Group members.","arguments":[{"name":"chat_id","type":"int53"},{"name":"participants","type":"Vector","comment":"List of group members"},{"name":"version","type":"int","comment":"Group version number"}]},{"kind":"class","name":"chatPhotoEmpty","type":"ChatPhoto","id":935395612,"comment":"Group photo is not set.","arguments":[]},{"kind":"class","name":"chatPhoto","type":"ChatPhoto","id":476978193,"comment":"Group profile photo.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"has_video","type":"true","predicate":"flags.0"},{"name":"photo_id","type":"long"},{"name":"stripped_thumb","type":"bytes","predicate":"flags.1"},{"name":"dc_id","type":"int"}]},{"kind":"class","name":"messageEmpty","type":"Message","id":2426849924,"comment":"Empty constructor, non-existent message.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"id","type":"int","comment":"Message identifier"},{"name":"peer_id","type":"Peer","predicate":"flags.0"}]},{"kind":"class","name":"message","type":"Message","id":2245446626,"comment":"A message","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"out","type":"true","predicate":"flags.1","comment":"Is this an outgoing message"},{"name":"mentioned","type":"true","predicate":"flags.4","comment":"Whether we were mentioned in this message"},{"name":"media_unread","type":"true","predicate":"flags.5"},{"name":"silent","type":"true","predicate":"flags.13","comment":"Whether this is a silent message (no notification triggered)"},{"name":"post","type":"true","predicate":"flags.14","comment":"Whether this is a channel post"},{"name":"from_scheduled","type":"true","predicate":"flags.18"},{"name":"legacy","type":"true","predicate":"flags.19","comment":"This is a legacy message: it has to be refetched with the new layer"},{"name":"edit_hide","type":"true","predicate":"flags.21"},{"name":"pinned","type":"true","predicate":"flags.24","comment":"Whether this message is pinned"},{"name":"id","type":"int","comment":"ID of the message"},{"name":"from_id","type":"Peer","predicate":"flags.8"},{"name":"peer_id","type":"Peer"},{"name":"fwd_from","type":"MessageFwdHeader","predicate":"flags.2"},{"name":"via_bot_id","type":"int53","predicate":"flags.11"},{"name":"reply_to","type":"MessageReplyHeader","predicate":"flags.3"},{"name":"date","type":"int","comment":"Date of the message"},{"name":"message","type":"string","comment":"The message"},{"name":"media","type":"MessageMedia","predicate":"flags.9","comment":"Media attachment"},{"name":"reply_markup","type":"ReplyMarkup","predicate":"flags.6"},{"name":"entities","type":"Vector","predicate":"flags.7","comment":"Message entities for styled text"},{"name":"views","type":"int","predicate":"flags.10","comment":"View count for channel posts"},{"name":"forwards","type":"int","predicate":"flags.10","comment":"Forward counter"},{"name":"replies","type":"MessageReplies","predicate":"flags.23","comment":"Info about post comments (for channels) or message replies (for groups)"},{"name":"edit_date","type":"int","predicate":"flags.15"},{"name":"post_author","type":"string","predicate":"flags.16"},{"name":"grouped_id","type":"long","predicate":"flags.17"},{"name":"restriction_reason","type":"Vector","predicate":"flags.22"},{"name":"ttl_period","type":"int","predicate":"flags.25"}]},{"kind":"class","name":"messageService","type":"Message","id":721967202,"comment":"Indicates a service message","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"out","type":"true","predicate":"flags.1","comment":"Whether the message is outgoing"},{"name":"mentioned","type":"true","predicate":"flags.4","comment":"Whether we were mentioned in the message"},{"name":"media_unread","type":"true","predicate":"flags.5"},{"name":"silent","type":"true","predicate":"flags.13","comment":"Whether the message is silent"},{"name":"post","type":"true","predicate":"flags.14","comment":"Whether it's a channel post"},{"name":"legacy","type":"true","predicate":"flags.19","comment":"This is a legacy message: it has to be refetched with the new layer"},{"name":"id","type":"int","comment":"Message ID"},{"name":"from_id","type":"Peer","predicate":"flags.8"},{"name":"peer_id","type":"Peer"},{"name":"reply_to","type":"MessageReplyHeader","predicate":"flags.3"},{"name":"date","type":"int","comment":"Message date"},{"name":"action","type":"MessageAction","comment":"Event connected with the service message"},{"name":"ttl_period","type":"int","predicate":"flags.25"}]},{"kind":"class","name":"messageMediaEmpty","type":"MessageMedia","id":1038967584,"comment":"Empty constructor.","arguments":[]},{"kind":"class","name":"messageMediaPhoto","type":"MessageMedia","id":1766936791,"comment":"Attached photo.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"photo","type":"Photo","predicate":"flags.0","comment":"Photo"},{"name":"ttl_seconds","type":"int","predicate":"flags.2"}]},{"kind":"class","name":"messageMediaGeo","type":"MessageMedia","id":1457575028,"comment":"Attached map.","arguments":[{"name":"geo","type":"GeoPoint","comment":"GeoPoint"}]},{"kind":"class","name":"messageMediaContact","type":"MessageMedia","id":1882335561,"comment":"Attached contact.","arguments":[{"name":"phone_number","type":"string"},{"name":"first_name","type":"string"},{"name":"last_name","type":"string"},{"name":"vcard","type":"string","comment":"VCARD of contact"},{"name":"user_id","type":"int53"}]},{"kind":"class","name":"messageMediaUnsupported","type":"MessageMedia","id":2676290718,"comment":"Current version of the client does not support this media type.","arguments":[]},{"kind":"class","name":"messageMediaDocument","type":"MessageMedia","id":2628808919,"comment":"Document (video, audio, voice, sticker, any media type except photo)","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"document","type":"Document","predicate":"flags.0","comment":"Attached document"},{"name":"ttl_seconds","type":"int","predicate":"flags.2"}]},{"kind":"class","name":"messageMediaWebPage","type":"MessageMedia","id":2737690112,"comment":"Preview of webpage","arguments":[{"name":"webpage","type":"WebPage","comment":"Webpage preview"}]},{"kind":"class","name":"messageMediaVenue","type":"MessageMedia","id":784356159,"comment":"Venue","arguments":[{"name":"geo","type":"GeoPoint","comment":"Geolocation of venue"},{"name":"title","type":"string","comment":"Venue name"},{"name":"address","type":"string","comment":"Address"},{"name":"provider","type":"string","comment":"Venue provider: currently only \"foursquare\" and \"gplaces\" need to be supported"},{"name":"venue_id","type":"string"},{"name":"venue_type","type":"string"}]},{"kind":"class","name":"messageMediaGame","type":"MessageMedia","id":4256272392,"comment":"Telegram game","arguments":[{"name":"game","type":"Game","comment":"Game"}]},{"kind":"class","name":"messageMediaInvoice","type":"MessageMedia","id":2220168007,"comment":"Invoice","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"shipping_address_requested","type":"true","predicate":"flags.1"},{"name":"test","type":"true","predicate":"flags.3","comment":"Whether this is an example invoice"},{"name":"title","type":"string","comment":"Product name, 1-32 characters"},{"name":"description","type":"string","comment":"Product description, 1-255 characters"},{"name":"photo","type":"WebDocument","predicate":"flags.0","comment":"URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for."},{"name":"receipt_msg_id","type":"int","predicate":"flags.2"},{"name":"currency","type":"string","comment":"Three-letter ISO 4217 currency code"},{"name":"total_amount","type":"long"},{"name":"start_param","type":"string"}]},{"kind":"class","name":"messageMediaGeoLive","type":"MessageMedia","id":3108030054,"comment":"Indicates a live geolocation","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"geo","type":"GeoPoint","comment":"Geolocation"},{"name":"heading","type":"int","predicate":"flags.0","comment":"For live locations, a direction in which the location moves, in degrees; 1-360"},{"name":"period","type":"int","comment":"Validity period of provided geolocation"},{"name":"proximity_notification_radius","type":"int","predicate":"flags.1"}]},{"kind":"class","name":"messageMediaPoll","type":"MessageMedia","id":1272375192,"comment":"Poll","arguments":[{"name":"poll","type":"Poll","comment":"The poll"},{"name":"results","type":"PollResults","comment":"The results of the poll"}]},{"kind":"class","name":"messageMediaDice","type":"MessageMedia","id":1065280907,"comment":"Dice-based animated sticker","arguments":[{"name":"value","type":"int","comment":"Dice value"},{"name":"emoticon","type":"string","comment":"The emoji, for now \"🏀\", \"🎲\" and \"🎯\" are supported"}]},{"kind":"class","name":"messageActionEmpty","type":"MessageAction","id":3064919984,"comment":"Empty constructor.","arguments":[]},{"kind":"class","name":"messageActionChatCreate","type":"MessageAction","id":3175599021,"comment":"Group created","arguments":[{"name":"title","type":"string","comment":"Group name"},{"name":"users","type":"vector","comment":"List of group members"}]},{"kind":"class","name":"messageActionChatEditTitle","type":"MessageAction","id":3047280218,"comment":"Group name changed.","arguments":[{"name":"title","type":"string","comment":"New group name"}]},{"kind":"class","name":"messageActionChatEditPhoto","type":"MessageAction","id":2144015272,"comment":"Group profile changed","arguments":[{"name":"photo","type":"Photo","comment":"New group pofile photo"}]},{"kind":"class","name":"messageActionChatDeletePhoto","type":"MessageAction","id":2514746351,"comment":"Group profile photo removed.","arguments":[]},{"kind":"class","name":"messageActionChatAddUser","type":"MessageAction","id":365886720,"comment":"New member in the group","arguments":[{"name":"users","type":"vector","comment":"Users that were invited to the chat"}]},{"kind":"class","name":"messageActionChatDeleteUser","type":"MessageAction","id":2755604684,"comment":"User left the group.","arguments":[{"name":"user_id","type":"int53"}]},{"kind":"class","name":"messageActionChatJoinedByLink","type":"MessageAction","id":51520707,"comment":"A user joined the chat via an invite link","arguments":[{"name":"inviter_id","type":"int53"}]},{"kind":"class","name":"messageActionChannelCreate","type":"MessageAction","id":2513611922,"comment":"The channel was created","arguments":[{"name":"title","type":"string","comment":"Original channel/supergroup title"}]},{"kind":"class","name":"messageActionChatMigrateTo","type":"MessageAction","id":3775102866,"comment":"Indicates the chat was migrated to the specified supergroup","arguments":[{"name":"channel_id","type":"int53"}]},{"kind":"class","name":"messageActionChannelMigrateFrom","type":"MessageAction","id":3929622761,"comment":"Indicates the channel was migrated from the specified chat","arguments":[{"name":"title","type":"string","comment":"The old chat tite"},{"name":"chat_id","type":"int53"}]},{"kind":"class","name":"messageActionPinMessage","type":"MessageAction","id":2495428845,"comment":"A message was pinned","arguments":[]},{"kind":"class","name":"messageActionHistoryClear","type":"MessageAction","id":2679813636,"comment":"Chat history was cleared","arguments":[]},{"kind":"class","name":"messageActionGameScore","type":"MessageAction","id":2460428406,"comment":"Someone scored in a game","arguments":[{"name":"game_id","type":"long"},{"name":"score","type":"int","comment":"Score"}]},{"kind":"class","name":"messageActionPaymentSentMe","type":"MessageAction","id":2402399015,"comment":"A user just sent a payment to me (a bot)","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"currency","type":"string","comment":"Three-letter ISO 4217 currency code"},{"name":"total_amount","type":"long"},{"name":"payload","type":"bytes","comment":"Bot specified invoice payload"},{"name":"info","type":"PaymentRequestedInfo","predicate":"flags.0","comment":"Order info provided by the user"},{"name":"shipping_option_id","type":"string","predicate":"flags.1"},{"name":"charge","type":"PaymentCharge","comment":"Provider payment identifier"}]},{"kind":"class","name":"messageActionPaymentSent","type":"MessageAction","id":1080663248,"comment":"A payment was sent","arguments":[{"name":"currency","type":"string","comment":"Three-letter ISO 4217 currency code"},{"name":"total_amount","type":"long"}]},{"kind":"class","name":"messageActionPhoneCall","type":"MessageAction","id":2162236031,"comment":"A phone call","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"video","type":"true","predicate":"flags.2","comment":"Is this a video call?"},{"name":"call_id","type":"long"},{"name":"reason","type":"PhoneCallDiscardReason","predicate":"flags.0","comment":"If the call has ended, the reason why it ended"},{"name":"duration","type":"int","predicate":"flags.1","comment":"Duration of the call in seconds"}]},{"kind":"class","name":"messageActionScreenshotTaken","type":"MessageAction","id":1200788123,"comment":"A screenshot of the chat was taken","arguments":[]},{"kind":"class","name":"messageActionCustomAction","type":"MessageAction","id":4209418070,"comment":"Custom action (most likely not supported by the current layer, an upgrade might be needed)","arguments":[{"name":"message","type":"string","comment":"Action message"}]},{"kind":"class","name":"messageActionBotAllowed","type":"MessageAction","id":2884218878,"comment":"The domain name of the website on which the user has logged in. More about Telegram Login »","arguments":[{"name":"domain","type":"string","comment":"The domain name of the website on which the user has logged in."}]},{"kind":"class","name":"messageActionSecureValuesSentMe","type":"MessageAction","id":455635795,"comment":"Secure telegram passport values were received","arguments":[{"name":"values","type":"Vector","comment":"Vector with information about documents and other Telegram Passport elements that were shared with the bot"},{"name":"credentials","type":"SecureCredentialsEncrypted","comment":"Encrypted credentials required to decrypt the data"}]},{"kind":"class","name":"messageActionSecureValuesSent","type":"MessageAction","id":3646710100,"comment":"Request for secure telegram passport values was sent","arguments":[{"name":"types","type":"Vector","comment":"Secure value types"}]},{"kind":"class","name":"messageActionContactSignUp","type":"MessageAction","id":4092747638,"comment":"A contact just signed up to telegram","arguments":[]},{"kind":"class","name":"messageActionGeoProximityReached","type":"MessageAction","id":2564871831,"comment":"A user of the chat is now in proximity of another user","arguments":[{"name":"from_id","type":"Peer"},{"name":"to_id","type":"Peer"},{"name":"distance","type":"int","comment":"Distance, in meters (0-100000)"}]},{"kind":"class","name":"messageActionGroupCall","type":"MessageAction","id":2047704898,"comment":"The group call has ended","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"call","type":"InputGroupCall","comment":"Group call"},{"name":"duration","type":"int","predicate":"flags.0","comment":"Group call duration"}]},{"kind":"class","name":"messageActionInviteToGroupCall","type":"MessageAction","id":1345295095,"comment":"A set of users was invited to the group call","arguments":[{"name":"call","type":"InputGroupCall","comment":"The group call"},{"name":"users","type":"vector","comment":"The invited users"}]},{"kind":"class","name":"messageActionSetMessagesTTL","type":"MessageAction","id":2853895165,"comment":"The Time-To-Live of messages in this chat was changed.","arguments":[{"name":"period","type":"int","comment":"New Time-To-Live"}]},{"kind":"class","name":"messageActionGroupCallScheduled","type":"MessageAction","id":3013637729,"comment":"A group call was scheduled","arguments":[{"name":"call","type":"InputGroupCall","comment":"The group call"},{"name":"schedule_date","type":"int"}]},{"kind":"class","name":"messageActionSetChatTheme","type":"MessageAction","id":2860016453,"comment":"The chat theme was changed","arguments":[{"name":"emoticon","type":"string","comment":"The emoji that identifies a chat theme"}]},{"kind":"class","name":"messageActionChatJoinedByRequest","type":"MessageAction","id":3955008459,"arguments":[]},{"kind":"class","name":"dialog","type":"Dialog","id":739712882,"comment":"Chat","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"pinned","type":"true","predicate":"flags.2","comment":"Is the dialog pinned"},{"name":"unread_mark","type":"true","predicate":"flags.3"},{"name":"peer","type":"Peer","comment":"The chat"},{"name":"top_message","type":"int"},{"name":"read_inbox_max_id","type":"int"},{"name":"read_outbox_max_id","type":"int"},{"name":"unread_count","type":"int"},{"name":"unread_mentions_count","type":"int"},{"name":"notify_settings","type":"PeerNotifySettings"},{"name":"pts","type":"int","predicate":"flags.0","comment":"PTS"},{"name":"draft","type":"DraftMessage","predicate":"flags.1","comment":"Message draft"},{"name":"folder_id","type":"int","predicate":"flags.4"}]},{"kind":"class","name":"dialogFolder","type":"Dialog","id":1908216652,"comment":"Dialog in folder","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"pinned","type":"true","predicate":"flags.2","comment":"Is this folder pinned"},{"name":"folder","type":"Folder","comment":"The folder"},{"name":"peer","type":"Peer","comment":"Peer in folder"},{"name":"top_message","type":"int"},{"name":"unread_muted_peers_count","type":"int"},{"name":"unread_unmuted_peers_count","type":"int"},{"name":"unread_muted_messages_count","type":"int"},{"name":"unread_unmuted_messages_count","type":"int"}]},{"kind":"class","name":"photoEmpty","type":"Photo","id":590459437,"comment":"Empty constructor, non-existent photo","arguments":[{"name":"id","type":"long","comment":"Photo identifier"}]},{"kind":"class","name":"photo","type":"Photo","id":4212750949,"comment":"Photo","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"has_stickers","type":"true","predicate":"flags.0"},{"name":"id","type":"long","comment":"ID"},{"name":"access_hash","type":"long"},{"name":"file_reference","type":"bytes"},{"name":"date","type":"int","comment":"Date of upload"},{"name":"sizes","type":"Vector","comment":"Available sizes for download"},{"name":"video_sizes","type":"Vector","predicate":"flags.1"},{"name":"dc_id","type":"int"}]},{"kind":"class","name":"photoSizeEmpty","type":"PhotoSize","id":236446268,"comment":"Empty constructor. Image with this thumbnail is unavailable.","arguments":[{"name":"type","type":"string","comment":"Thumbnail type (see. {@link photoSize})"}]},{"kind":"class","name":"photoSize","type":"PhotoSize","id":1976012384,"comment":"Image description.","arguments":[{"name":"type","type":"string","comment":"Thumbnail type"},{"name":"w","type":"int","comment":"Image width"},{"name":"h","type":"int","comment":"Image height"},{"name":"size","type":"int","comment":"File size"}]},{"kind":"class","name":"photoCachedSize","type":"PhotoSize","id":35527382,"comment":"Description of an image and its content.","arguments":[{"name":"type","type":"string","comment":"Thumbnail type"},{"name":"w","type":"int","comment":"Image width"},{"name":"h","type":"int","comment":"Image height"},{"name":"bytes","type":"bytes","comment":"Binary data, file content"}]},{"kind":"class","name":"photoStrippedSize","type":"PhotoSize","id":3769678894,"comment":"A low-resolution compressed JPG payload","arguments":[{"name":"type","type":"string","comment":"Thumbnail type"},{"name":"bytes","type":"bytes","comment":"Thumbnail data, see here for more info on decompression »"}]},{"kind":"class","name":"photoSizeProgressive","type":"PhotoSize","id":4198431637,"comment":"Progressively encoded photosize","arguments":[{"name":"type","type":"string","comment":"Photosize type"},{"name":"w","type":"int","comment":"Photo width"},{"name":"h","type":"int","comment":"Photo height"},{"name":"sizes","type":"Vector","comment":"Sizes of progressive JPEG file prefixes, which can be used to preliminarily show the image."}]},{"kind":"class","name":"photoPathSize","type":"PhotoSize","id":3626061121,"comment":"Messages with animated stickers can have a compressed svg (< 300 bytes) to show the outline of the sticker before fetching the actual lottie animation.","arguments":[{"name":"type","type":"string","comment":"Always j"},{"name":"bytes","type":"bytes","comment":"Compressed SVG path payload, see here for decompression instructions"}]},{"kind":"class","name":"geoPointEmpty","type":"GeoPoint","id":286776671,"comment":"Empty constructor.","arguments":[]},{"kind":"class","name":"geoPoint","type":"GeoPoint","id":2997024355,"comment":"GeoPoint.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"long","type":"double","comment":"Longtitude"},{"name":"lat","type":"double","comment":"Latitude"},{"name":"access_hash","type":"long"},{"name":"accuracy_radius","type":"int","predicate":"flags.0"}]},{"kind":"class","name":"auth.sentCode","type":"auth.SentCode","id":1577067778,"comment":"Contains info about a sent verification code.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"type","type":"auth.SentCodeType","comment":"Phone code type"},{"name":"phone_code_hash","type":"string"},{"name":"next_type","type":"auth.CodeType","predicate":"flags.1"},{"name":"timeout","type":"int","predicate":"flags.2","comment":"Timeout for reception of the phone code"}]},{"kind":"class","name":"auth.authorization","type":"auth.Authorization","id":3439659286,"comment":"Contains user authorization info.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"tmp_sessions","type":"int","predicate":"flags.0"},{"name":"user","type":"User","comment":"Info on authorized user"}]},{"kind":"class","name":"auth.authorizationSignUpRequired","type":"auth.Authorization","id":1148485274,"comment":"An account with this phone number doesn't exist on telegram: the user has to enter basic information and sign up","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"terms_of_service","type":"help.TermsOfService","predicate":"flags.0"}]},{"kind":"class","name":"auth.exportedAuthorization","type":"auth.ExportedAuthorization","id":3023364792,"comment":"Data for copying of authorization between data centres.","arguments":[{"name":"id","type":"long","comment":"current user identifier"},{"name":"bytes","type":"bytes","comment":"authorizes key"}]},{"kind":"class","name":"inputNotifyPeer","type":"InputNotifyPeer","id":3099351820,"comment":"Notifications generated by a certain user or group.","arguments":[{"name":"peer","type":"InputPeer","comment":"User or group"}]},{"kind":"class","name":"inputNotifyUsers","type":"InputNotifyPeer","id":423314455,"comment":"Notifications generated by all users.","arguments":[]},{"kind":"class","name":"inputNotifyChats","type":"InputNotifyPeer","id":1251338318,"comment":"Notifications generated by all groups.","arguments":[]},{"kind":"class","name":"inputNotifyBroadcasts","type":"InputNotifyPeer","id":2983951486,"comment":"All channels","arguments":[]},{"kind":"class","name":"inputPeerNotifySettings","type":"InputPeerNotifySettings","id":2621249934,"comment":"Notification settings.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"show_previews","type":"Bool","predicate":"flags.0"},{"name":"silent","type":"Bool","predicate":"flags.1","comment":"Peer was muted?"},{"name":"mute_until","type":"int","predicate":"flags.2"},{"name":"sound","type":"string","predicate":"flags.3","comment":"Name of an audio file for notification"}]},{"kind":"class","name":"peerNotifySettings","type":"PeerNotifySettings","id":2941295904,"comment":"Notification settings.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"show_previews","type":"Bool","predicate":"flags.0"},{"name":"silent","type":"Bool","predicate":"flags.1","comment":"Mute peer?"},{"name":"mute_until","type":"int","predicate":"flags.2"},{"name":"sound","type":"string","predicate":"flags.3","comment":"Audio file name for notifications"}]},{"kind":"class","name":"peerSettings","type":"PeerSettings","id":1933519201,"comment":"Peer settings","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"report_spam","type":"true","predicate":"flags.0"},{"name":"add_contact","type":"true","predicate":"flags.1"},{"name":"block_contact","type":"true","predicate":"flags.2"},{"name":"share_contact","type":"true","predicate":"flags.3"},{"name":"need_contacts_exception","type":"true","predicate":"flags.4"},{"name":"report_geo","type":"true","predicate":"flags.5"},{"name":"autoarchived","type":"true","predicate":"flags.7","comment":"Whether this peer was automatically archived according to {@link globalPrivacySettings}"},{"name":"invite_members","type":"true","predicate":"flags.8"},{"name":"geo_distance","type":"int","predicate":"flags.6"}]},{"kind":"class","name":"wallPaper","type":"WallPaper","id":2755118061,"comment":"Wallpaper settings.","arguments":[{"name":"id","type":"long","comment":"Identifier"},{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"creator","type":"true","predicate":"flags.0","comment":"Creator of the wallpaper"},{"name":"default","type":"true","predicate":"flags.1","comment":"Whether this is the default wallpaper"},{"name":"pattern","type":"true","predicate":"flags.3","comment":"Pattern"},{"name":"dark","type":"true","predicate":"flags.4","comment":"Dark mode"},{"name":"access_hash","type":"long"},{"name":"slug","type":"string","comment":"Unique wallpaper ID"},{"name":"document","type":"Document","comment":"The actual wallpaper"},{"name":"settings","type":"WallPaperSettings","predicate":"flags.2","comment":"Wallpaper settings"}]},{"kind":"class","name":"wallPaperNoFile","type":"WallPaper","id":3766501654,"comment":"Wallpaper with no file access hash, used for example when deleting (unsave=true) wallpapers using {@link account.saveWallPaper}, specifying just the wallpaper ID.\nAlso used for some default wallpapers which contain only colours.","arguments":[{"name":"id","type":"long","comment":"Wallpaper ID"},{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"default","type":"true","predicate":"flags.1","comment":"Whether this is the default wallpaper"},{"name":"dark","type":"true","predicate":"flags.4","comment":"Dark mode"},{"name":"settings","type":"WallPaperSettings","predicate":"flags.2","comment":"Wallpaper settings"}]},{"kind":"class","name":"inputReportReasonSpam","type":"ReportReason","id":1490799288,"comment":"Report for spam","arguments":[]},{"kind":"class","name":"inputReportReasonViolence","type":"ReportReason","id":505595789,"comment":"Report for violence","arguments":[]},{"kind":"class","name":"inputReportReasonPornography","type":"ReportReason","id":777640226,"comment":"Report for pornography","arguments":[]},{"kind":"class","name":"inputReportReasonChildAbuse","type":"ReportReason","id":2918469347,"comment":"Report for child abuse","arguments":[]},{"kind":"class","name":"inputReportReasonOther","type":"ReportReason","id":3252986545,"comment":"Other","arguments":[]},{"kind":"class","name":"inputReportReasonCopyright","type":"ReportReason","id":2609510714,"comment":"Report for copyrighted content","arguments":[]},{"kind":"class","name":"inputReportReasonGeoIrrelevant","type":"ReportReason","id":3688169197,"comment":"Report an irrelevant geo group","arguments":[]},{"kind":"class","name":"inputReportReasonFake","type":"ReportReason","id":4124956391,"comment":"Report for impersonation","arguments":[]},{"kind":"class","name":"userFull","type":"UserFull","id":3600285445,"comment":"Extended user info","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"blocked","type":"true","predicate":"flags.0","comment":"Whether you have blocked this user"},{"name":"phone_calls_available","type":"true","predicate":"flags.4"},{"name":"phone_calls_private","type":"true","predicate":"flags.5"},{"name":"can_pin_message","type":"true","predicate":"flags.7"},{"name":"has_scheduled","type":"true","predicate":"flags.12"},{"name":"video_calls_available","type":"true","predicate":"flags.13"},{"name":"user","type":"User","comment":"Remaining user info"},{"name":"about","type":"string","predicate":"flags.1","comment":"Bio of the user"},{"name":"settings","type":"PeerSettings","comment":"Peer settings"},{"name":"profile_photo","type":"Photo","predicate":"flags.2"},{"name":"notify_settings","type":"PeerNotifySettings"},{"name":"bot_info","type":"BotInfo","predicate":"flags.3"},{"name":"pinned_msg_id","type":"int","predicate":"flags.6"},{"name":"common_chats_count","type":"int"},{"name":"folder_id","type":"int","predicate":"flags.11"},{"name":"ttl_period","type":"int","predicate":"flags.14"},{"name":"theme_emoticon","type":"string","predicate":"flags.15"}]},{"kind":"class","name":"contact","type":"Contact","id":341499403,"comment":"A contact of the current user that is registered in the system.","arguments":[{"name":"user_id","type":"int53"},{"name":"mutual","type":"Bool","comment":"Current user is in the user's contact list"}]},{"kind":"class","name":"importedContact","type":"ImportedContact","id":3242081360,"comment":"Successfully imported contact.","arguments":[{"name":"user_id","type":"int53"},{"name":"client_id","type":"long"}]},{"kind":"class","name":"contactStatus","type":"ContactStatus","id":383348795,"comment":"Contact status: online / offline.","arguments":[{"name":"user_id","type":"int53"},{"name":"status","type":"UserStatus","comment":"Online status"}]},{"kind":"class","name":"contacts.contactsNotModified","type":"contacts.Contacts","id":3075189202,"comment":"Contact list on the server is the same as the list on the client.","arguments":[]},{"kind":"class","name":"contacts.contacts","type":"contacts.Contacts","id":3941105218,"comment":"The current user's contact list and info on users.","arguments":[{"name":"contacts","type":"Vector","comment":"Contact list"},{"name":"saved_count","type":"int"},{"name":"users","type":"Vector","comment":"User list"}]},{"kind":"class","name":"contacts.importedContacts","type":"contacts.ImportedContacts","id":2010127419,"comment":"Info on succesfully imported contacts.","arguments":[{"name":"imported","type":"Vector","comment":"List of succesfully imported contacts"},{"name":"popular_invites","type":"Vector"},{"name":"retry_contacts","type":"Vector"},{"name":"users","type":"Vector","comment":"List of users"}]},{"kind":"class","name":"contacts.blocked","type":"contacts.Blocked","id":182326673,"comment":"Full list of blocked users.","arguments":[{"name":"blocked","type":"Vector","comment":"List of blocked users"},{"name":"chats","type":"Vector","comment":"Blocked chats"},{"name":"users","type":"Vector","comment":"List of users"}]},{"kind":"class","name":"contacts.blockedSlice","type":"contacts.Blocked","id":3781575060,"comment":"Incomplete list of blocked users.","arguments":[{"name":"count","type":"int","comment":"Total number of elements in the list"},{"name":"blocked","type":"Vector","comment":"List of blocked users"},{"name":"chats","type":"Vector","comment":"Blocked chats"},{"name":"users","type":"Vector","comment":"List of users"}]},{"kind":"class","name":"messages.dialogs","type":"messages.Dialogs","id":364538944,"comment":"Full list of chats with messages and auxiliary data.","arguments":[{"name":"dialogs","type":"Vector","comment":"List of chats"},{"name":"messages","type":"Vector","comment":"List of last messages from each chat"},{"name":"chats","type":"Vector","comment":"List of groups mentioned in the chats"},{"name":"users","type":"Vector","comment":"List of users mentioned in messages and groups"}]},{"kind":"class","name":"messages.dialogsSlice","type":"messages.Dialogs","id":1910543603,"comment":"Incomplete list of dialogs with messages and auxiliary data.","arguments":[{"name":"count","type":"int","comment":"Total number of dialogs"},{"name":"dialogs","type":"Vector","comment":"List of dialogs"},{"name":"messages","type":"Vector","comment":"List of last messages from dialogs"},{"name":"chats","type":"Vector","comment":"List of chats mentioned in dialogs"},{"name":"users","type":"Vector","comment":"List of users mentioned in messages and chats"}]},{"kind":"class","name":"messages.dialogsNotModified","type":"messages.Dialogs","id":4041467286,"comment":"Dialogs haven't changed","arguments":[{"name":"count","type":"int","comment":"Number of dialogs found server-side by the query"}]},{"kind":"class","name":"messages.messages","type":"messages.Messages","id":2356252295,"comment":"Full list of messages with auxilary data.","arguments":[{"name":"messages","type":"Vector","comment":"List of messages"},{"name":"chats","type":"Vector","comment":"List of chats mentioned in dialogs"},{"name":"users","type":"Vector","comment":"List of users mentioned in messages and chats"}]},{"kind":"class","name":"messages.messagesSlice","type":"messages.Messages","id":978610270,"comment":"Incomplete list of messages and auxiliary data.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"inexact","type":"true","predicate":"flags.1","comment":"If set, indicates that the results may be inexact"},{"name":"count","type":"int","comment":"Total number of messages in the list"},{"name":"next_rate","type":"int","predicate":"flags.0"},{"name":"offset_id_offset","type":"int","predicate":"flags.2"},{"name":"messages","type":"Vector","comment":"List of messages"},{"name":"chats","type":"Vector","comment":"List of chats mentioned in messages"},{"name":"users","type":"Vector","comment":"List of users mentioned in messages and chats"}]},{"kind":"class","name":"messages.channelMessages","type":"messages.Messages","id":1682413576,"comment":"Channel messages","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"inexact","type":"true","predicate":"flags.1","comment":"If set, returned results may be inexact"},{"name":"pts","type":"int","comment":"Event count after generation"},{"name":"count","type":"int","comment":"Total number of results were found server-side (may not be all included here)"},{"name":"offset_id_offset","type":"int","predicate":"flags.2"},{"name":"messages","type":"Vector","comment":"Found messages"},{"name":"chats","type":"Vector","comment":"Chats"},{"name":"users","type":"Vector","comment":"Users"}]},{"kind":"class","name":"messages.messagesNotModified","type":"messages.Messages","id":1951620897,"comment":"No new messages matching the query were found","arguments":[{"name":"count","type":"int","comment":"Number of results found server-side by the given query"}]},{"kind":"class","name":"messages.chats","type":"messages.Chats","id":1694474197,"comment":"List of chats with auxiliary data.","arguments":[{"name":"chats","type":"Vector","comment":"List of chats"}]},{"kind":"class","name":"messages.chatsSlice","type":"messages.Chats","id":2631405892,"comment":"Partial list of chats, more would have to be fetched with pagination","arguments":[{"name":"count","type":"int","comment":"Total number of results that were found server-side (not all are included in chats)"},{"name":"chats","type":"Vector","comment":"Chats"}]},{"kind":"class","name":"messages.chatFull","type":"messages.ChatFull","id":3856126364,"comment":"Extended info on chat and auxiliary data.","arguments":[{"name":"full_chat","type":"ChatFull"},{"name":"chats","type":"Vector","comment":"List containing basic info on chat"},{"name":"users","type":"Vector","comment":"List of users mentioned above"}]},{"kind":"class","name":"messages.affectedHistory","type":"messages.AffectedHistory","id":3025955281,"comment":"Affected part of communication history with the user or in a chat.","arguments":[{"name":"pts","type":"int","comment":"Number of events occured in a text box"},{"name":"pts_count","type":"int"},{"name":"offset","type":"int","comment":"If a parameter contains positive value, it is necessary to repeat the method call using the given value; during the proceeding of all the history the value itself shall gradually decrease"}]},{"kind":"class","name":"inputMessagesFilterEmpty","type":"MessagesFilter","id":1474492012,"comment":"Filter is absent.","arguments":[]},{"kind":"class","name":"inputMessagesFilterPhotos","type":"MessagesFilter","id":2517214492,"comment":"Filter for messages containing photos.","arguments":[]},{"kind":"class","name":"inputMessagesFilterVideo","type":"MessagesFilter","id":2680163941,"comment":"Filter for messages containing videos.","arguments":[]},{"kind":"class","name":"inputMessagesFilterPhotoVideo","type":"MessagesFilter","id":1458172132,"comment":"Filter for messages containing photos or videos.","arguments":[]},{"kind":"class","name":"inputMessagesFilterDocument","type":"MessagesFilter","id":2665345416,"comment":"Filter for messages containing documents.","arguments":[]},{"kind":"class","name":"inputMessagesFilterUrl","type":"MessagesFilter","id":2129714567,"comment":"Return only messages containing URLs","arguments":[]},{"kind":"class","name":"inputMessagesFilterGif","type":"MessagesFilter","id":4291323271,"comment":"Return only messages containing gifs","arguments":[]},{"kind":"class","name":"inputMessagesFilterVoice","type":"MessagesFilter","id":1358283666,"comment":"Return only messages containing voice notes","arguments":[]},{"kind":"class","name":"inputMessagesFilterMusic","type":"MessagesFilter","id":928101534,"comment":"Return only messages containing audio files","arguments":[]},{"kind":"class","name":"inputMessagesFilterChatPhotos","type":"MessagesFilter","id":975236280,"comment":"Return only chat photo changes","arguments":[]},{"kind":"class","name":"inputMessagesFilterPhoneCalls","type":"MessagesFilter","id":2160695144,"comment":"Return only phone calls","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"missed","type":"true","predicate":"flags.0","comment":"Return only missed phone calls"}]},{"kind":"class","name":"inputMessagesFilterRoundVoice","type":"MessagesFilter","id":2054952868,"comment":"Return only round videos and voice notes","arguments":[]},{"kind":"class","name":"inputMessagesFilterRoundVideo","type":"MessagesFilter","id":3041516115,"comment":"Return only round videos","arguments":[]},{"kind":"class","name":"inputMessagesFilterMyMentions","type":"MessagesFilter","id":3254314650,"comment":"Return only messages where the current user was mentioned.","arguments":[]},{"kind":"class","name":"inputMessagesFilterGeo","type":"MessagesFilter","id":3875695885,"comment":"Return only messages containing geolocations","arguments":[]},{"kind":"class","name":"inputMessagesFilterContacts","type":"MessagesFilter","id":3764575107,"comment":"Return only messages containing contacts","arguments":[]},{"kind":"class","name":"inputMessagesFilterPinned","type":"MessagesFilter","id":464520273,"comment":"Fetch only pinned messages","arguments":[]},{"kind":"class","name":"updateNewMessage","type":"Update","id":522914557,"comment":"New message in a private chat or in a legacy group.","arguments":[{"name":"message","type":"Message","comment":"Message"},{"name":"pts","type":"int","comment":"New quantity of actions in a message box"},{"name":"pts_count","type":"int"}]},{"kind":"class","name":"updateMessageID","type":"Update","id":1318109142,"comment":"Sent message with random_id client identifier was assigned an identifier.","arguments":[{"name":"id","type":"int","comment":"id identifier of a respective Message"},{"name":"random_id","type":"long"}]},{"kind":"class","name":"updateDeleteMessages","type":"Update","id":2718806245,"comment":"Messages were deleted.","arguments":[{"name":"messages","type":"Vector","comment":"List of identifiers of deleted messages"},{"name":"pts","type":"int","comment":"New quality of actions in a message box"},{"name":"pts_count","type":"int"}]},{"kind":"class","name":"updateUserTyping","type":"Update","id":3223225727,"comment":"The user is preparing a message; typing, recording, uploading, etc. This update is valid for 6 seconds. If no repeated update received after 6 seconds, it should be considered that the user stopped doing whatever he's been doing.","arguments":[{"name":"user_id","type":"int53"},{"name":"action","type":"SendMessageAction","comment":"Action type
Param added in Layer 17."}]},{"kind":"class","name":"updateChatUserTyping","type":"Update","id":2202565360,"comment":"The user is preparing a message in a group; typing, recording, uploading, etc. This update is valid for 6 seconds. If no repeated update received after 6 seconds, it should be considered that the user stopped doing whatever he's been doing.","arguments":[{"name":"chat_id","type":"int53"},{"name":"from_id","type":"Peer"},{"name":"action","type":"SendMessageAction","comment":"Type of action
Parameter added in Layer 17."}]},{"kind":"class","name":"updateChatParticipants","type":"Update","id":125178264,"comment":"Composition of chat participants changed.","arguments":[{"name":"participants","type":"ChatParticipants","comment":"Updated chat participants"}]},{"kind":"class","name":"updateUserStatus","type":"Update","id":3854432478,"comment":"Contact status update.","arguments":[{"name":"user_id","type":"int53"},{"name":"status","type":"UserStatus","comment":"New status"}]},{"kind":"class","name":"updateUserName","type":"Update","id":3287417568,"comment":"Changes the user's first name, last name and username.","arguments":[{"name":"user_id","type":"int53"},{"name":"first_name","type":"string"},{"name":"last_name","type":"string"},{"name":"username","type":"string","comment":"New username.
Parameter added in Layer 18."}]},{"kind":"class","name":"updateUserPhoto","type":"Update","id":4062676620,"comment":"Change of contact's profile photo.","arguments":[{"name":"user_id","type":"int53"},{"name":"date","type":"int","comment":"Date of photo update."},{"name":"photo","type":"UserProfilePhoto","comment":"New profile photo"},{"name":"previous","type":"Bool","comment":"({@link boolTrue}), if one of the previously used photos is set a profile photo."}]},{"kind":"class","name":"updateNewEncryptedMessage","type":"Update","id":314359194,"comment":"New encrypted message.","arguments":[{"name":"message","type":"EncryptedMessage","comment":"Message"},{"name":"qts","type":"int","comment":"New qts value, see updates » for more info."}]},{"kind":"class","name":"updateEncryptedChatTyping","type":"Update","id":386986326,"comment":"Interlocutor is typing a message in an encrypted chat. Update period is 6 second. If upon this time there is no repeated update, it shall be considered that the interlocutor stopped typing.","arguments":[{"name":"chat_id","type":"int"}]},{"kind":"class","name":"updateEncryption","type":"Update","id":3030575245,"comment":"Change of state in an encrypted chat.","arguments":[{"name":"chat","type":"EncryptedChat","comment":"Encrypted chat"},{"name":"date","type":"int","comment":"Date of change"}]},{"kind":"class","name":"updateEncryptedMessagesRead","type":"Update","id":956179895,"comment":"Communication history in an encrypted chat was marked as read.","arguments":[{"name":"chat_id","type":"int"},{"name":"max_date","type":"int"},{"name":"date","type":"int","comment":"Time when messages were read"}]},{"kind":"class","name":"updateChatParticipantAdd","type":"Update","id":1037718609,"comment":"New group member.","arguments":[{"name":"chat_id","type":"int53"},{"name":"user_id","type":"int53"},{"name":"inviter_id","type":"int53"},{"name":"date","type":"int","comment":"When was the participant added"},{"name":"version","type":"int","comment":"Chat version number"}]},{"kind":"class","name":"updateChatParticipantDelete","type":"Update","id":3811523959,"comment":"A member has left the group.","arguments":[{"name":"chat_id","type":"int53"},{"name":"user_id","type":"int53"},{"name":"version","type":"int","comment":"Used in basic groups to reorder updates and make sure that all of them was received."}]},{"kind":"class","name":"updateDcOptions","type":"Update","id":2388564083,"comment":"Changes in the data center configuration options.","arguments":[{"name":"dc_options","type":"Vector"}]},{"kind":"class","name":"updateNotifySettings","type":"Update","id":3200411887,"comment":"Changes in notification settings.","arguments":[{"name":"peer","type":"NotifyPeer","comment":"Nofication source"},{"name":"notify_settings","type":"PeerNotifySettings"}]},{"kind":"class","name":"updateServiceNotification","type":"Update","id":3957614617,"comment":"The app must show the message to the user upon receiving this update. In case the popup parameter was passed, the text message must be displayed in a popup alert immediately upon receipt. It is recommended to handle the text as you would an ordinary message in terms of highlighting links, etc. The message must also be stored locally as part of the message history with the user id 777000 (Telegram Notifications).\n\nA service message for the user.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"popup","type":"true","predicate":"flags.0","comment":"(boolTrue) if the message must be displayed in a popup."},{"name":"inbox_date","type":"int","predicate":"flags.1"},{"name":"type","type":"string","comment":"String, identical in format and contents to the type field in API errors. Describes type of service message. It is acceptable to ignore repeated messages of the same type within a short period of time (15 minutes)."},{"name":"message","type":"string","comment":"Message text"},{"name":"media","type":"MessageMedia","comment":"Media content (optional)"},{"name":"entities","type":"Vector","comment":"Message entities for styled text"}]},{"kind":"class","name":"updatePrivacy","type":"Update","id":3996854058,"comment":"Privacy rules were changed","arguments":[{"name":"key","type":"PrivacyKey","comment":"Peers to which the privacy rules apply"},{"name":"rules","type":"Vector","comment":"New privacy rules"}]},{"kind":"class","name":"updateUserPhone","type":"Update","id":88680979,"comment":"A user's phone number was changed","arguments":[{"name":"user_id","type":"int53"},{"name":"phone","type":"string","comment":"New phone number"}]},{"kind":"class","name":"updateReadHistoryInbox","type":"Update","id":2627162079,"comment":"Incoming messages were read","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"folder_id","type":"int","predicate":"flags.0"},{"name":"peer","type":"Peer","comment":"Peer"},{"name":"max_id","type":"int"},{"name":"still_unread_count","type":"int"},{"name":"pts","type":"int","comment":"Event count after generation"},{"name":"pts_count","type":"int"}]},{"kind":"class","name":"updateReadHistoryOutbox","type":"Update","id":791617983,"comment":"Outgoing messages were read","arguments":[{"name":"peer","type":"Peer","comment":"Peer"},{"name":"max_id","type":"int"},{"name":"pts","type":"int","comment":"Event count after generation"},{"name":"pts_count","type":"int"}]},{"kind":"class","name":"updateWebPage","type":"Update","id":2139689491,"comment":"An instant view webpage preview was generated","arguments":[{"name":"webpage","type":"WebPage","comment":"Webpage preview"},{"name":"pts","type":"int","comment":"Event count after generation"},{"name":"pts_count","type":"int"}]},{"kind":"class","name":"updateReadMessagesContents","type":"Update","id":1757493555,"comment":"Contents of messages in the common message box were read","arguments":[{"name":"messages","type":"Vector","comment":"IDs of read messages"},{"name":"pts","type":"int","comment":"Event count after generation"},{"name":"pts_count","type":"int"}]},{"kind":"class","name":"updateChannelTooLong","type":"Update","id":277713951,"comment":"There are new updates in the specified channel, the client must fetch them.\nIf the difference is too long or if the channel isn't currently in the states, start fetching from the specified pts.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"channel_id","type":"int53"},{"name":"pts","type":"int","predicate":"flags.0","comment":"The PTS."}]},{"kind":"class","name":"updateChannel","type":"Update","id":1666927625,"comment":"A new channel is available","arguments":[{"name":"channel_id","type":"int53"}]},{"kind":"class","name":"updateNewChannelMessage","type":"Update","id":1656358105,"comment":"A new message was sent in a channel/supergroup","arguments":[{"name":"message","type":"Message","comment":"New message"},{"name":"pts","type":"int","comment":"Event count after generation"},{"name":"pts_count","type":"int"}]},{"kind":"class","name":"updateReadChannelInbox","type":"Update","id":2452516368,"comment":"Incoming messages in a channel/supergroup were read","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"folder_id","type":"int","predicate":"flags.0"},{"name":"channel_id","type":"int53"},{"name":"max_id","type":"int"},{"name":"still_unread_count","type":"int"},{"name":"pts","type":"int","comment":"Event count after generation"}]},{"kind":"class","name":"updateDeleteChannelMessages","type":"Update","id":3274529554,"comment":"Some messages in a supergroup/channel were deleted","arguments":[{"name":"channel_id","type":"int53"},{"name":"messages","type":"Vector","comment":"IDs of messages that were deleted"},{"name":"pts","type":"int","comment":"Event count after generation"},{"name":"pts_count","type":"int"}]},{"kind":"class","name":"updateChannelMessageViews","type":"Update","id":4062620680,"comment":"The view counter of a message in a channel has changed","arguments":[{"name":"channel_id","type":"int53"},{"name":"id","type":"int","comment":"ID of the message"},{"name":"views","type":"int","comment":"New view counter"}]},{"kind":"class","name":"updateChatParticipantAdmin","type":"Update","id":3620364706,"comment":"Admin permissions of a user in a legacy group were changed","arguments":[{"name":"chat_id","type":"int53"},{"name":"user_id","type":"int53"},{"name":"is_admin","type":"Bool"},{"name":"version","type":"int","comment":"Used in basic groups to reorder updates and make sure that all of them was received."}]},{"kind":"class","name":"updateNewStickerSet","type":"Update","id":1753886890,"comment":"A new stickerset was installed","arguments":[{"name":"stickerset","type":"messages.StickerSet","comment":"The installed stickerset"}]},{"kind":"class","name":"updateStickerSetsOrder","type":"Update","id":196268545,"comment":"The order of stickersets was changed","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"masks","type":"true","predicate":"flags.0","comment":"Whether the updated stickers are mask stickers"},{"name":"order","type":"Vector","comment":"New sticker order by sticker ID"}]},{"kind":"class","name":"updateStickerSets","type":"Update","id":1135492588,"comment":"Installed stickersets have changed, the client should refetch them using {@link messages.getAllStickers}","arguments":[]},{"kind":"class","name":"updateSavedGifs","type":"Update","id":2473931806,"comment":"The saved gif list has changed, the client should refetch it using {@link messages.getSavedGifs}","arguments":[]},{"kind":"class","name":"updateBotInlineQuery","type":"Update","id":1232025500,"comment":"An incoming inline query","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"query_id","type":"long"},{"name":"user_id","type":"int53"},{"name":"query","type":"string","comment":"Text of query"},{"name":"geo","type":"GeoPoint","predicate":"flags.0","comment":"Attached geolocation"},{"name":"peer_type","type":"InlineQueryPeerType","predicate":"flags.1"},{"name":"offset","type":"string","comment":"Offset to navigate through results"}]},{"kind":"class","name":"updateBotInlineSend","type":"Update","id":317794823,"comment":"The result of an inline query that was chosen by a user and sent to their chat partner. Please see our documentation on the feedback collecting for details on how to enable these updates for your bot.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"user_id","type":"int53"},{"name":"query","type":"string","comment":"The query that was used to obtain the result"},{"name":"geo","type":"GeoPoint","predicate":"flags.0","comment":"Optional. Sender location, only for bots that require user location"},{"name":"id","type":"string","comment":"The unique identifier for the result that was chosen"},{"name":"msg_id","type":"InputBotInlineMessageID","predicate":"flags.1"}]},{"kind":"class","name":"updateEditChannelMessage","type":"Update","id":457133559,"comment":"A message was edited in a channel/supergroup","arguments":[{"name":"message","type":"Message","comment":"The new message"},{"name":"pts","type":"int","comment":"Event count after generation"},{"name":"pts_count","type":"int"}]},{"kind":"class","name":"updateBotCallbackQuery","type":"Update","id":3117401229,"comment":"A callback button was pressed, and the button data was sent to the bot that created the button","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"query_id","type":"long"},{"name":"user_id","type":"int53"},{"name":"peer","type":"Peer","comment":"Chat where the inline keyboard was sent"},{"name":"msg_id","type":"int"},{"name":"chat_instance","type":"long"},{"name":"data","type":"bytes","predicate":"flags.0","comment":"Callback data"},{"name":"game_short_name","type":"string","predicate":"flags.1"}]},{"kind":"class","name":"updateEditMessage","type":"Update","id":3825430691,"comment":"A message was edited","arguments":[{"name":"message","type":"Message","comment":"The new edited message"},{"name":"pts","type":"int","comment":"PTS"},{"name":"pts_count","type":"int"}]},{"kind":"class","name":"updateInlineBotCallbackQuery","type":"Update","id":1763610706,"comment":"This notification is received by bots when a button is pressed","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"query_id","type":"long"},{"name":"user_id","type":"int53"},{"name":"msg_id","type":"InputBotInlineMessageID"},{"name":"chat_instance","type":"long"},{"name":"data","type":"bytes","predicate":"flags.0","comment":"Data associated with the callback button. Be aware that a bad client can send arbitrary data in this field."},{"name":"game_short_name","type":"string","predicate":"flags.1"}]},{"kind":"class","name":"updateReadChannelOutbox","type":"Update","id":3076495785,"comment":"Outgoing messages in a channel/supergroup were read","arguments":[{"name":"channel_id","type":"int53"},{"name":"max_id","type":"int"}]},{"kind":"class","name":"updateDraftMessage","type":"Update","id":3995842921,"comment":"Notifies a change of a message draft.","arguments":[{"name":"peer","type":"Peer","comment":"The peer to which the draft is associated"},{"name":"draft","type":"DraftMessage","comment":"The draft"}]},{"kind":"class","name":"updateReadFeaturedStickers","type":"Update","id":1461528386,"comment":"Some featured stickers were marked as read","arguments":[]},{"kind":"class","name":"updateRecentStickers","type":"Update","id":2588027936,"comment":"The recent sticker list was updated","arguments":[]},{"kind":"class","name":"updateConfig","type":"Update","id":2720652550,"comment":"The server-side configuration has changed; the client should re-fetch the config using {@link help.getConfig}","arguments":[]},{"kind":"class","name":"updatePtsChanged","type":"Update","id":861169551,"comment":"Common message box sequence PTS has changed, state has to be refetched using updates.getState","arguments":[]},{"kind":"class","name":"updateChannelWebPage","type":"Update","id":791390623,"comment":"A webpage preview of a link in a channel/supergroup message was generated","arguments":[{"name":"channel_id","type":"int53"},{"name":"webpage","type":"WebPage","comment":"Generated webpage preview"},{"name":"pts","type":"int","comment":"Event count after generation"},{"name":"pts_count","type":"int"}]},{"kind":"class","name":"updateDialogPinned","type":"Update","id":1852826908,"comment":"A dialog was pinned/unpinned","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"pinned","type":"true","predicate":"flags.0","comment":"Whether the dialog was pinned"},{"name":"folder_id","type":"int","predicate":"flags.1"},{"name":"peer","type":"DialogPeer","comment":"The dialog"}]},{"kind":"class","name":"updatePinnedDialogs","type":"Update","id":4195302562,"comment":"Pinned dialogs were updated","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"folder_id","type":"int","predicate":"flags.1"},{"name":"order","type":"Vector","predicate":"flags.0","comment":"New order of pinned dialogs"}]},{"kind":"class","name":"updateBotWebhookJSON","type":"Update","id":2199371971,"comment":"A new incoming event; for bots only","arguments":[{"name":"data","type":"DataJSON","comment":"The event"}]},{"kind":"class","name":"updateBotWebhookJSONQuery","type":"Update","id":2610053286,"comment":"A new incoming query; for bots only","arguments":[{"name":"query_id","type":"long"},{"name":"data","type":"DataJSON","comment":"Query data"},{"name":"timeout","type":"int","comment":"Query timeout"}]},{"kind":"class","name":"updateBotShippingQuery","type":"Update","id":3048144253,"comment":"This object contains information about an incoming shipping query.","arguments":[{"name":"query_id","type":"long"},{"name":"user_id","type":"int53"},{"name":"payload","type":"bytes","comment":"Bot specified invoice payload"},{"name":"shipping_address","type":"PostAddress"}]},{"kind":"class","name":"updateBotPrecheckoutQuery","type":"Update","id":2359990934,"comment":"This object contains information about an incoming pre-checkout query.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"query_id","type":"long"},{"name":"user_id","type":"int53"},{"name":"payload","type":"bytes","comment":"Bot specified invoice payload"},{"name":"info","type":"PaymentRequestedInfo","predicate":"flags.0","comment":"Order info provided by the user"},{"name":"shipping_option_id","type":"string","predicate":"flags.1"},{"name":"currency","type":"string","comment":"Three-letter ISO 4217 currency code"},{"name":"total_amount","type":"long"}]},{"kind":"class","name":"updatePhoneCall","type":"Update","id":2869914398,"comment":"An incoming phone call","arguments":[{"name":"phone_call","type":"PhoneCall"}]},{"kind":"class","name":"updateLangPackTooLong","type":"Update","id":1180041828,"comment":"A language pack has changed, the client should manually fetch the changed strings using {@link langpack.getDifference}","arguments":[{"name":"lang_code","type":"string"}]},{"kind":"class","name":"updateLangPack","type":"Update","id":1442983757,"comment":"Language pack updated","arguments":[{"name":"difference","type":"LangPackDifference","comment":"Changed strings"}]},{"kind":"class","name":"updateFavedStickers","type":"Update","id":3843135853,"comment":"The list of favorited stickers was changed, the client should call {@link messages.getFavedStickers} to refetch the new list","arguments":[]},{"kind":"class","name":"updateChannelReadMessagesContents","type":"Update","id":1153291573,"comment":"The specified channel/supergroup messages were read","arguments":[{"name":"channel_id","type":"int53"},{"name":"messages","type":"Vector","comment":"IDs of messages that were read"}]},{"kind":"class","name":"updateContactsReset","type":"Update","id":1887741886,"comment":"All contacts were deleted","arguments":[]},{"kind":"class","name":"updateChannelAvailableMessages","type":"Update","id":2990524056,"comment":"The history of a channel/supergroup was hidden.","arguments":[{"name":"channel_id","type":"int53"},{"name":"available_min_id","type":"int"}]},{"kind":"class","name":"updateDialogUnreadMark","type":"Update","id":3781450179,"comment":"The manual unread mark of a chat was changed","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"unread","type":"true","predicate":"flags.0","comment":"Was the chat marked or unmarked as read"},{"name":"peer","type":"DialogPeer","comment":"The dialog"}]},{"kind":"class","name":"updateMessagePoll","type":"Update","id":2896258427,"comment":"The results of a poll have changed","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"poll_id","type":"long"},{"name":"poll","type":"Poll","predicate":"flags.0","comment":"If the server knows the client hasn't cached this poll yet, the poll itself"},{"name":"results","type":"PollResults","comment":"New poll results"}]},{"kind":"class","name":"updateChatDefaultBannedRights","type":"Update","id":1421875280,"comment":"Default banned rights in a normal chat were updated","arguments":[{"name":"peer","type":"Peer","comment":"The chat"},{"name":"default_banned_rights","type":"ChatBannedRights"},{"name":"version","type":"int","comment":"Version"}]},{"kind":"class","name":"updateFolderPeers","type":"Update","id":422972864,"comment":"The peer list of a peer folder was updated","arguments":[{"name":"folder_peers","type":"Vector"},{"name":"pts","type":"int","comment":"Event count after generation"},{"name":"pts_count","type":"int"}]},{"kind":"class","name":"updatePeerSettings","type":"Update","id":1786671974,"comment":"Settings of a certain peer have changed","arguments":[{"name":"peer","type":"Peer","comment":"The peer"},{"name":"settings","type":"PeerSettings","comment":"Associated peer settings"}]},{"kind":"class","name":"updatePeerLocated","type":"Update","id":3031420848,"comment":"List of peers near you was updated","arguments":[{"name":"peers","type":"Vector","comment":"Geolocated peer list update"}]},{"kind":"class","name":"updateNewScheduledMessage","type":"Update","id":967122427,"comment":"A message was added to the schedule queue of a chat","arguments":[{"name":"message","type":"Message","comment":"Message"}]},{"kind":"class","name":"updateDeleteScheduledMessages","type":"Update","id":2424728814,"comment":"Some scheduled messages were deleted from the schedule queue of a chat","arguments":[{"name":"peer","type":"Peer","comment":"Peer"},{"name":"messages","type":"Vector","comment":"Deleted scheduled messages"}]},{"kind":"class","name":"updateTheme","type":"Update","id":2182544291,"comment":"A cloud theme was updated","arguments":[{"name":"theme","type":"Theme","comment":"Theme"}]},{"kind":"class","name":"updateGeoLiveViewed","type":"Update","id":2267003193,"comment":"Live geo position message was viewed","arguments":[{"name":"peer","type":"Peer","comment":"The user that viewed the live geo position"},{"name":"msg_id","type":"int"}]},{"kind":"class","name":"updateLoginToken","type":"Update","id":1448076945,"comment":"A login token (for login via QR code) was accepted.","arguments":[]},{"kind":"class","name":"updateMessagePollVote","type":"Update","id":2022357899,"comment":"A specific user has voted in a poll","arguments":[{"name":"poll_id","type":"long"},{"name":"user_id","type":"int53"},{"name":"options","type":"Vector","comment":"Chosen option(s)"},{"name":"qts","type":"int","comment":"New qts value, see updates » for more info."}]},{"kind":"class","name":"updateDialogFilter","type":"Update","id":654302845,"comment":"A new folder was added","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"id","type":"int","comment":"Folder ID"},{"name":"filter","type":"DialogFilter","predicate":"flags.0","comment":"Folder info"}]},{"kind":"class","name":"updateDialogFilterOrder","type":"Update","id":2782339333,"comment":"New folder order","arguments":[{"name":"order","type":"Vector","comment":"Ordered folder IDs"}]},{"kind":"class","name":"updateDialogFilters","type":"Update","id":889491791,"comment":"Clients should update folder info","arguments":[]},{"kind":"class","name":"updatePhoneCallSignalingData","type":"Update","id":643940105,"comment":"Incoming phone call signaling payload","arguments":[{"name":"phone_call_id","type":"long"},{"name":"data","type":"bytes","comment":"Signaling payload"}]},{"kind":"class","name":"updateChannelMessageForwards","type":"Update","id":3533318132,"comment":"The forward counter of a message in a channel has changed","arguments":[{"name":"channel_id","type":"int53"},{"name":"id","type":"int","comment":"ID of the message"},{"name":"forwards","type":"int","comment":"New forward counter"}]},{"kind":"class","name":"updateReadChannelDiscussionInbox","type":"Update","id":3601962310,"comment":"Incoming comments in a discussion thread were marked as read","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"channel_id","type":"int53"},{"name":"top_msg_id","type":"int"},{"name":"read_max_id","type":"int"},{"name":"broadcast_id","type":"int53","predicate":"flags.0"},{"name":"broadcast_post","type":"int","predicate":"flags.0"}]},{"kind":"class","name":"updateReadChannelDiscussionOutbox","type":"Update","id":1767677564,"comment":"Outgoing comments in a discussion thread were marked as read","arguments":[{"name":"channel_id","type":"int53"},{"name":"top_msg_id","type":"int"},{"name":"read_max_id","type":"int"}]},{"kind":"class","name":"updatePeerBlocked","type":"Update","id":610945826,"comment":"A peer was blocked","arguments":[{"name":"peer_id","type":"Peer"},{"name":"blocked","type":"Bool","comment":"Whether the peer was blocked or unblocked"}]},{"kind":"class","name":"updateChannelUserTyping","type":"Update","id":2357774627,"comment":"A user is typing in a supergroup, channel or message thread","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"channel_id","type":"int53"},{"name":"top_msg_id","type":"int","predicate":"flags.0"},{"name":"from_id","type":"Peer"},{"name":"action","type":"SendMessageAction","comment":"Whether the user is typing, sending a media or doing something else"}]},{"kind":"class","name":"updatePinnedMessages","type":"Update","id":3984976565,"comment":"Some messages were pinned in a chat","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"pinned","type":"true","predicate":"flags.0","comment":"Whether the messages were pinned or unpinned"},{"name":"peer","type":"Peer","comment":"Peer"},{"name":"messages","type":"Vector","comment":"Message IDs"},{"name":"pts","type":"int","comment":"Event count after generation"},{"name":"pts_count","type":"int"}]},{"kind":"class","name":"updatePinnedChannelMessages","type":"Update","id":1538885128,"comment":"Messages were pinned/unpinned in a channel/supergroup","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"pinned","type":"true","predicate":"flags.0","comment":"Whether the messages were pinned or unpinned"},{"name":"channel_id","type":"int53"},{"name":"messages","type":"Vector","comment":"Messages"},{"name":"pts","type":"int","comment":"Event count after generation"},{"name":"pts_count","type":"int"}]},{"kind":"class","name":"updateChat","type":"Update","id":4170869326,"comment":"A new chat is available","arguments":[{"name":"chat_id","type":"int53"}]},{"kind":"class","name":"updateGroupCallParticipants","type":"Update","id":4075543374,"comment":"The participant list of a certain group call has changed","arguments":[{"name":"call","type":"InputGroupCall","comment":"Group call"},{"name":"participants","type":"Vector","comment":"New participant list"},{"name":"version","type":"int","comment":"Version"}]},{"kind":"class","name":"updateGroupCall","type":"Update","id":347227392,"comment":"A new groupcall was started","arguments":[{"name":"chat_id","type":"int53"},{"name":"call","type":"GroupCall","comment":"Info about the group call or livestream"}]},{"kind":"class","name":"updatePeerHistoryTTL","type":"Update","id":3147544997,"comment":"The Time-To-Live for messages sent by the current user in a specific chat has changed","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"peer","type":"Peer","comment":"The chat"},{"name":"ttl_period","type":"int","predicate":"flags.0"}]},{"kind":"class","name":"updateChatParticipant","type":"Update","id":3498534458,"comment":"A user has joined or left a specific chat","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"chat_id","type":"int53"},{"name":"date","type":"int","comment":"When did this event occur"},{"name":"actor_id","type":"int53"},{"name":"user_id","type":"int53"},{"name":"prev_participant","type":"ChatParticipant","predicate":"flags.0"},{"name":"new_participant","type":"ChatParticipant","predicate":"flags.1"},{"name":"invite","type":"ExportedChatInvite","predicate":"flags.2","comment":"The invite that was used to join the group"},{"name":"qts","type":"int","comment":"New qts value, see updates » for more info."}]},{"kind":"class","name":"updateChannelParticipant","type":"Update","id":2556246715,"comment":"A participant has left, joined, was banned or admined in a channel or supergroup.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"channel_id","type":"int53"},{"name":"date","type":"int","comment":"Date of the event"},{"name":"actor_id","type":"int53"},{"name":"user_id","type":"int53"},{"name":"prev_participant","type":"ChannelParticipant","predicate":"flags.0"},{"name":"new_participant","type":"ChannelParticipant","predicate":"flags.1"},{"name":"invite","type":"ExportedChatInvite","predicate":"flags.2","comment":"Chat invite used to join the channel/supergroup"},{"name":"qts","type":"int","comment":"New qts value, see updates » for more info."}]},{"kind":"class","name":"updateBotStopped","type":"Update","id":3297184329,"comment":"A bot was stopped or re-started.","arguments":[{"name":"user_id","type":"int53"},{"name":"date","type":"int","comment":"When did this action occur"},{"name":"stopped","type":"Bool","comment":"Whether the bot was stopped or started"},{"name":"qts","type":"int","comment":"New qts value, see updates » for more info."}]},{"kind":"class","name":"updateGroupCallConnection","type":"Update","id":192428418,"comment":"New WebRTC parameters","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"presentation","type":"true","predicate":"flags.0","comment":"Are these parameters related to the screen capture session currently in progress?"},{"name":"params","type":"DataJSON","comment":"WebRTC parameters"}]},{"kind":"class","name":"updateBotCommands","type":"Update","id":1299263278,"comment":"The command set of a certain bot in a certain chat has changed.","arguments":[{"name":"peer","type":"Peer","comment":"The affected chat"},{"name":"bot_id","type":"int53"},{"name":"commands","type":"Vector","comment":"New bot commands"}]},{"kind":"class","name":"updatePendingJoinRequests","type":"Update","id":1885586395,"arguments":[{"name":"peer","type":"Peer"},{"name":"requests_pending","type":"int"},{"name":"recent_requesters","type":"Vector"}]},{"kind":"class","name":"updateBotChatInviteRequester","type":"Update","id":299870598,"arguments":[{"name":"peer","type":"Peer"},{"name":"date","type":"int"},{"name":"user_id","type":"long"},{"name":"about","type":"string"},{"name":"invite","type":"ExportedChatInvite"},{"name":"qts","type":"int"}]},{"kind":"class","name":"updates.state","type":"updates.State","id":2775329342,"comment":"Updates state.","arguments":[{"name":"pts","type":"int","comment":"Number of events occured in a text box"},{"name":"qts","type":"int","comment":"Position in a sequence of updates in secret chats. For further detailes refer to article secret chats
Parameter was added in eigth layer."},{"name":"date","type":"int","comment":"Date of condition"},{"name":"seq","type":"int","comment":"Number of sent updates"},{"name":"unread_count","type":"int"}]},{"kind":"class","name":"updates.differenceEmpty","type":"updates.Difference","id":1567990072,"comment":"No events.","arguments":[{"name":"date","type":"int","comment":"Current date"},{"name":"seq","type":"int","comment":"Number of sent updates"}]},{"kind":"class","name":"updates.difference","type":"updates.Difference","id":16030880,"comment":"Full list of occurred events.","arguments":[{"name":"new_messages","type":"Vector"},{"name":"new_encrypted_messages","type":"Vector"},{"name":"other_updates","type":"Vector"},{"name":"chats","type":"Vector","comment":"List of chats mentioned in events"},{"name":"users","type":"Vector","comment":"List of users mentioned in events"},{"name":"state","type":"updates.State","comment":"Current state"}]},{"kind":"class","name":"updates.differenceSlice","type":"updates.Difference","id":2835028353,"comment":"Incomplete list of occurred events.","arguments":[{"name":"new_messages","type":"Vector"},{"name":"new_encrypted_messages","type":"Vector"},{"name":"other_updates","type":"Vector"},{"name":"chats","type":"Vector","comment":"List of chats mentioned in events"},{"name":"users","type":"Vector","comment":"List of users mentioned in events"},{"name":"intermediate_state","type":"updates.State"}]},{"kind":"class","name":"updates.differenceTooLong","type":"updates.Difference","id":1258196845,"comment":"The difference is too long, and the specified state must be used to refetch updates.","arguments":[{"name":"pts","type":"int","comment":"The new state to use."}]},{"kind":"class","name":"updatesTooLong","type":"Updates","id":3809980286,"comment":"Too many updates, it is necessary to execute {@link updates.getDifference}.","arguments":[]},{"kind":"class","name":"updateShortMessage","type":"Updates","id":826001400,"comment":"Info about a message sent to (received from) another user","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"out","type":"true","predicate":"flags.1","comment":"Whether the message is outgoing"},{"name":"mentioned","type":"true","predicate":"flags.4","comment":"Whether we were mentioned in the message"},{"name":"media_unread","type":"true","predicate":"flags.5"},{"name":"silent","type":"true","predicate":"flags.13","comment":"If true, the message is a silent message, no notifications should be triggered"},{"name":"id","type":"int","comment":"The message ID"},{"name":"user_id","type":"int53"},{"name":"message","type":"string","comment":"The message"},{"name":"pts","type":"int","comment":"PTS"},{"name":"pts_count","type":"int"},{"name":"date","type":"int","comment":"date"},{"name":"fwd_from","type":"MessageFwdHeader","predicate":"flags.2"},{"name":"via_bot_id","type":"int53","predicate":"flags.11"},{"name":"reply_to","type":"MessageReplyHeader","predicate":"flags.3"},{"name":"entities","type":"Vector","predicate":"flags.7","comment":"Entities for styled text"},{"name":"ttl_period","type":"int","predicate":"flags.25"}]},{"kind":"class","name":"updateShortChatMessage","type":"Updates","id":1299050149,"comment":"Shortened constructor containing info on one new incoming text message from a chat","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"out","type":"true","predicate":"flags.1","comment":"Whether the message is outgoing"},{"name":"mentioned","type":"true","predicate":"flags.4","comment":"Whether we were mentioned in this message"},{"name":"media_unread","type":"true","predicate":"flags.5"},{"name":"silent","type":"true","predicate":"flags.13","comment":"If true, the message is a silent message, no notifications should be triggered"},{"name":"id","type":"int","comment":"ID of the message"},{"name":"from_id","type":"int53"},{"name":"chat_id","type":"int53"},{"name":"message","type":"string","comment":"Message"},{"name":"pts","type":"int","comment":"PTS"},{"name":"pts_count","type":"int"},{"name":"date","type":"int","comment":"date"},{"name":"fwd_from","type":"MessageFwdHeader","predicate":"flags.2"},{"name":"via_bot_id","type":"int53","predicate":"flags.11"},{"name":"reply_to","type":"MessageReplyHeader","predicate":"flags.3"},{"name":"entities","type":"Vector","predicate":"flags.7","comment":"Entities for styled text"},{"name":"ttl_period","type":"int","predicate":"flags.25"}]},{"kind":"class","name":"updateShort","type":"Updates","id":2027216577,"comment":"Shortened constructor containing info on one update not requiring auxiliary data","arguments":[{"name":"update","type":"Update","comment":"Update"},{"name":"date","type":"int","comment":"Date of event"}]},{"kind":"class","name":"updatesCombined","type":"Updates","id":1918567619,"comment":"Constructor for a group of updates.","arguments":[{"name":"updates","type":"Vector","comment":"List of updates"},{"name":"users","type":"Vector","comment":"List of users mentioned in updates"},{"name":"chats","type":"Vector","comment":"List of chats mentioned in updates"},{"name":"date","type":"int","comment":"Current date"},{"name":"seq_start","type":"int"},{"name":"seq","type":"int","comment":"Value seq for the latest update in a group"}]},{"kind":"class","name":"updates","type":"Updates","id":1957577280,"comment":"Full constructor of updates","arguments":[{"name":"updates","type":"Vector","comment":"List of updates"},{"name":"users","type":"Vector","comment":"List of users mentioned in updates"},{"name":"chats","type":"Vector","comment":"List of chats mentioned in updates"},{"name":"date","type":"int","comment":"Current date"},{"name":"seq","type":"int","comment":"Total number of sent updates"}]},{"kind":"class","name":"updateShortSentMessage","type":"Updates","id":2417352961,"comment":"Shortened constructor containing info on one outgoing message to a contact (the destination chat has to be extracted from the method call that returned this object).","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"out","type":"true","predicate":"flags.1","comment":"Whether the message is outgoing"},{"name":"id","type":"int","comment":"ID of the sent message"},{"name":"pts","type":"int","comment":"PTS"},{"name":"pts_count","type":"int"},{"name":"date","type":"int","comment":"date"},{"name":"media","type":"MessageMedia","predicate":"flags.9","comment":"Attached media"},{"name":"entities","type":"Vector","predicate":"flags.7","comment":"Entities for styled text"},{"name":"ttl_period","type":"int","predicate":"flags.25"}]},{"kind":"class","name":"photos.photos","type":"photos.Photos","id":2378853029,"comment":"Full list of photos with auxiliary data.","arguments":[{"name":"photos","type":"Vector","comment":"List of photos"},{"name":"users","type":"Vector","comment":"List of mentioned users"}]},{"kind":"class","name":"photos.photosSlice","type":"photos.Photos","id":352657236,"comment":"Incomplete list of photos with auxiliary data.","arguments":[{"name":"count","type":"int","comment":"Total number of photos"},{"name":"photos","type":"Vector","comment":"List of photos"},{"name":"users","type":"Vector","comment":"List of mentioned users"}]},{"kind":"class","name":"photos.photo","type":"photos.Photo","id":539045032,"comment":"Photo with auxiliary data.","arguments":[{"name":"photo","type":"Photo","comment":"Photo"},{"name":"users","type":"Vector","comment":"Users"}]},{"kind":"class","name":"upload.file","type":"upload.File","id":157948117,"comment":"File content.","arguments":[{"name":"type","type":"storage.FileType","comment":"File type"},{"name":"mtime","type":"int","comment":"Modification type"},{"name":"bytes","type":"bytes","comment":"Binary data, file content"}]},{"kind":"class","name":"upload.fileCdnRedirect","type":"upload.File","id":4052539972,"comment":"The file must be downloaded from a CDN DC.","arguments":[{"name":"dc_id","type":"int"},{"name":"file_token","type":"bytes"},{"name":"encryption_key","type":"bytes"},{"name":"encryption_iv","type":"bytes"},{"name":"file_hashes","type":"Vector"}]},{"kind":"class","name":"dcOption","type":"DcOption","id":414687501,"comment":"Data centre","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"ipv6","type":"true","predicate":"flags.0","comment":"Whether the specified IP is an IPv6 address"},{"name":"media_only","type":"true","predicate":"flags.1"},{"name":"tcpo_only","type":"true","predicate":"flags.2"},{"name":"cdn","type":"true","predicate":"flags.3","comment":"Whether this is a CDN DC."},{"name":"static","type":"true","predicate":"flags.4","comment":"If set, this IP should be used when connecting through a proxy"},{"name":"id","type":"int","comment":"DC ID"},{"name":"ip_address","type":"string"},{"name":"port","type":"int","comment":"Port"},{"name":"secret","type":"bytes","predicate":"flags.10","comment":"If the tcpo_only flag is set, specifies the secret to use when connecting using transport obfuscation"}]},{"kind":"class","name":"config","type":"Config","id":856375399,"comment":"Current configuration","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"phonecalls_enabled","type":"true","predicate":"flags.1"},{"name":"default_p2p_contacts","type":"true","predicate":"flags.3"},{"name":"preload_featured_stickers","type":"true","predicate":"flags.4"},{"name":"ignore_phone_entities","type":"true","predicate":"flags.5"},{"name":"revoke_pm_inbox","type":"true","predicate":"flags.6"},{"name":"blocked_mode","type":"true","predicate":"flags.8"},{"name":"pfs_enabled","type":"true","predicate":"flags.13"},{"name":"date","type":"int","comment":"Current date at the server"},{"name":"expires","type":"int","comment":"Expiration date of this config: when it expires it'll have to be refetched using {@link help.getConfig}"},{"name":"test_mode","type":"Bool"},{"name":"this_dc","type":"int"},{"name":"dc_options","type":"Vector"},{"name":"dc_txt_domain_name","type":"string"},{"name":"chat_size_max","type":"int"},{"name":"megagroup_size_max","type":"int"},{"name":"forwarded_count_max","type":"int"},{"name":"online_update_period_ms","type":"int"},{"name":"offline_blur_timeout_ms","type":"int"},{"name":"offline_idle_timeout_ms","type":"int"},{"name":"online_cloud_timeout_ms","type":"int"},{"name":"notify_cloud_delay_ms","type":"int"},{"name":"notify_default_delay_ms","type":"int"},{"name":"push_chat_period_ms","type":"int"},{"name":"push_chat_limit","type":"int"},{"name":"saved_gifs_limit","type":"int"},{"name":"edit_time_limit","type":"int"},{"name":"revoke_time_limit","type":"int"},{"name":"revoke_pm_time_limit","type":"int"},{"name":"rating_e_decay","type":"int"},{"name":"stickers_recent_limit","type":"int"},{"name":"stickers_faved_limit","type":"int"},{"name":"channels_read_media_period","type":"int"},{"name":"tmp_sessions","type":"int","predicate":"flags.0"},{"name":"pinned_dialogs_count_max","type":"int"},{"name":"pinned_infolder_count_max","type":"int"},{"name":"call_receive_timeout_ms","type":"int"},{"name":"call_ring_timeout_ms","type":"int"},{"name":"call_connect_timeout_ms","type":"int"},{"name":"call_packet_timeout_ms","type":"int"},{"name":"me_url_prefix","type":"string"},{"name":"autoupdate_url_prefix","type":"string","predicate":"flags.7"},{"name":"gif_search_username","type":"string","predicate":"flags.9"},{"name":"venue_search_username","type":"string","predicate":"flags.10"},{"name":"img_search_username","type":"string","predicate":"flags.11"},{"name":"static_maps_provider","type":"string","predicate":"flags.12"},{"name":"caption_length_max","type":"int"},{"name":"message_length_max","type":"int"},{"name":"webfile_dc_id","type":"int"},{"name":"suggested_lang_code","type":"string","predicate":"flags.2"},{"name":"lang_pack_version","type":"int","predicate":"flags.2"},{"name":"base_lang_pack_version","type":"int","predicate":"flags.2"}]},{"kind":"class","name":"nearestDc","type":"NearestDc","id":2384074613,"comment":"Nearest data centre, according to geo-ip.","arguments":[{"name":"country","type":"string","comment":"Country code determined by geo-ip"},{"name":"this_dc","type":"int"},{"name":"nearest_dc","type":"int"}]},{"kind":"class","name":"help.appUpdate","type":"help.AppUpdate","id":3434860080,"comment":"An update is available for the application.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"can_not_skip","type":"true","predicate":"flags.0"},{"name":"id","type":"int","comment":"Update ID"},{"name":"version","type":"string","comment":"New version name"},{"name":"text","type":"string","comment":"Text description of the update"},{"name":"entities","type":"Vector","comment":"Message entities for styled text"},{"name":"document","type":"Document","predicate":"flags.1","comment":"Application binary"},{"name":"url","type":"string","predicate":"flags.2","comment":"Application download URL"},{"name":"sticker","type":"Document","predicate":"flags.3","comment":"Associated sticker"}]},{"kind":"class","name":"help.noAppUpdate","type":"help.AppUpdate","id":3294258486,"comment":"No updates are available for the application.","arguments":[]},{"kind":"class","name":"help.inviteText","type":"help.InviteText","id":415997816,"comment":"Text of a text message with an invitation to install Telegram.","arguments":[{"name":"message","type":"string","comment":"Text of the message"}]},{"kind":"class","name":"encryptedChatEmpty","type":"EncryptedChat","id":2877210784,"comment":"Empty constructor.","arguments":[{"name":"id","type":"int","comment":"Chat ID"}]},{"kind":"class","name":"encryptedChatWaiting","type":"EncryptedChat","id":1722964307,"comment":"Chat waiting for approval of second participant.","arguments":[{"name":"id","type":"int","comment":"Chat ID"},{"name":"access_hash","type":"long"},{"name":"date","type":"int","comment":"Date of chat creation"},{"name":"admin_id","type":"int53"},{"name":"participant_id","type":"int53"}]},{"kind":"class","name":"encryptedChatRequested","type":"EncryptedChat","id":1223809356,"comment":"Request to create an encrypted chat.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"folder_id","type":"int","predicate":"flags.0"},{"name":"id","type":"int","comment":"Chat ID"},{"name":"access_hash","type":"long"},{"name":"date","type":"int","comment":"Chat creation date"},{"name":"admin_id","type":"int53"},{"name":"participant_id","type":"int53"},{"name":"g_a","type":"bytes"}]},{"kind":"class","name":"encryptedChat","type":"EncryptedChat","id":1643173063,"comment":"Encrypted chat","arguments":[{"name":"id","type":"int","comment":"Chat ID"},{"name":"access_hash","type":"long"},{"name":"date","type":"int","comment":"Date chat was created"},{"name":"admin_id","type":"int53"},{"name":"participant_id","type":"int53"},{"name":"g_a_or_b","type":"bytes"},{"name":"key_fingerprint","type":"long"}]},{"kind":"class","name":"encryptedChatDiscarded","type":"EncryptedChat","id":505183301,"comment":"Discarded or deleted chat.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"history_deleted","type":"true","predicate":"flags.0"},{"name":"id","type":"int","comment":"Chat ID"}]},{"kind":"class","name":"inputEncryptedChat","type":"InputEncryptedChat","id":4047615457,"comment":"Creates an encrypted chat.","arguments":[{"name":"chat_id","type":"int"},{"name":"access_hash","type":"long"}]},{"kind":"class","name":"encryptedFileEmpty","type":"EncryptedFile","id":3256830334,"comment":"Empty constructor, unexisitng file.","arguments":[]},{"kind":"class","name":"encryptedFile","type":"EncryptedFile","id":1248893260,"comment":"Encrypted file.","arguments":[{"name":"id","type":"long","comment":"File ID"},{"name":"access_hash","type":"long"},{"name":"size","type":"int","comment":"File size in bytes"},{"name":"dc_id","type":"int"},{"name":"key_fingerprint","type":"int"}]},{"kind":"class","name":"inputEncryptedFileEmpty","type":"InputEncryptedFile","id":406307684,"comment":"Empty constructor.","arguments":[]},{"kind":"class","name":"inputEncryptedFileUploaded","type":"InputEncryptedFile","id":1690108678,"comment":"Sets new encrypted file saved by parts using upload.saveFilePart method.","arguments":[{"name":"id","type":"long","comment":"Random file ID created by clien"},{"name":"parts","type":"int","comment":"Number of saved parts"},{"name":"md5_checksum","type":"string"},{"name":"key_fingerprint","type":"int"}]},{"kind":"class","name":"inputEncryptedFile","type":"InputEncryptedFile","id":1511503333,"comment":"Sets forwarded encrypted file for attachment.","arguments":[{"name":"id","type":"long","comment":"File ID, value of id parameter from {@link encryptedFile}"},{"name":"access_hash","type":"long"}]},{"kind":"class","name":"inputEncryptedFileBigUploaded","type":"InputEncryptedFile","id":767652808,"comment":"Assigns a new big encrypted file (over 10Mb in size), saved in parts using the method {@link upload.saveBigFilePart}.","arguments":[{"name":"id","type":"long","comment":"Random file id, created by the client"},{"name":"parts","type":"int","comment":"Number of saved parts"},{"name":"key_fingerprint","type":"int"}]},{"kind":"class","name":"encryptedMessage","type":"EncryptedMessage","id":3977822488,"comment":"Encrypted message.","arguments":[{"name":"random_id","type":"long"},{"name":"chat_id","type":"int"},{"name":"date","type":"int","comment":"Date of sending"},{"name":"bytes","type":"bytes","comment":"TL-serialising of DecryptedMessage type, encrypted with the key creatied at stage of chat initialization"},{"name":"file","type":"EncryptedFile","comment":"Attached encrypted file"}]},{"kind":"class","name":"encryptedMessageService","type":"EncryptedMessage","id":594758406,"comment":"Encrypted service message","arguments":[{"name":"random_id","type":"long"},{"name":"chat_id","type":"int"},{"name":"date","type":"int","comment":"Date of sending"},{"name":"bytes","type":"bytes","comment":"TL-serialising of DecryptedMessage type, encrypted with the key creatied at stage of chat initialization"}]},{"kind":"class","name":"messages.dhConfigNotModified","type":"messages.DhConfig","id":3236054581,"comment":"Configuring parameters did not change.","arguments":[{"name":"random","type":"bytes","comment":"Random sequence of bytes of assigned length"}]},{"kind":"class","name":"messages.dhConfig","type":"messages.DhConfig","id":740433629,"comment":"New set of configuring parameters.","arguments":[{"name":"g","type":"int","comment":"New value prime, see Wikipedia"},{"name":"p","type":"bytes","comment":"New value primitive root, see Wikipedia"},{"name":"version","type":"int","comment":"Vestion of set of parameters"},{"name":"random","type":"bytes","comment":"Random sequence of bytes of assigned length"}]},{"kind":"class","name":"messages.sentEncryptedMessage","type":"messages.SentEncryptedMessage","id":1443858741,"comment":"Message without file attachemts sent to an encrypted file.","arguments":[{"name":"date","type":"int","comment":"Date of sending"}]},{"kind":"class","name":"messages.sentEncryptedFile","type":"messages.SentEncryptedMessage","id":2492727090,"comment":"Message with a file enclosure sent to a protected chat","arguments":[{"name":"date","type":"int","comment":"Sending date"},{"name":"file","type":"EncryptedFile","comment":"Attached file"}]},{"kind":"class","name":"inputDocumentEmpty","type":"InputDocument","id":1928391342,"comment":"Empty constructor.","arguments":[]},{"kind":"class","name":"inputDocument","type":"InputDocument","id":448771445,"comment":"Defines a video for subsequent interaction.","arguments":[{"name":"id","type":"long","comment":"Document ID"},{"name":"access_hash","type":"long"},{"name":"file_reference","type":"bytes"}]},{"kind":"class","name":"documentEmpty","type":"Document","id":922273905,"comment":"Empty constructor, document doesn't exist.","arguments":[{"name":"id","type":"long","comment":"Document ID or 0"}]},{"kind":"class","name":"document","type":"Document","id":512177195,"comment":"Document","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"id","type":"long","comment":"Document ID"},{"name":"access_hash","type":"long"},{"name":"file_reference","type":"bytes"},{"name":"date","type":"int","comment":"Creation date"},{"name":"mime_type","type":"string"},{"name":"size","type":"int","comment":"Size"},{"name":"thumbs","type":"Vector","predicate":"flags.0","comment":"Thumbnails"},{"name":"video_thumbs","type":"Vector","predicate":"flags.1"},{"name":"dc_id","type":"int"},{"name":"attributes","type":"Vector","comment":"Attributes"}]},{"kind":"class","name":"help.support","type":"help.Support","id":398898678,"comment":"Info on support user.","arguments":[{"name":"phone_number","type":"string"},{"name":"user","type":"User","comment":"User"}]},{"kind":"class","name":"notifyPeer","type":"NotifyPeer","id":2681474008,"comment":"Notifications generated by a certain user or group.","arguments":[{"name":"peer","type":"Peer","comment":"user or group"}]},{"kind":"class","name":"notifyUsers","type":"NotifyPeer","id":3033021260,"comment":"Notifications generated by all users.","arguments":[]},{"kind":"class","name":"notifyChats","type":"NotifyPeer","id":3221737155,"comment":"Notifications generated by all groups.","arguments":[]},{"kind":"class","name":"notifyBroadcasts","type":"NotifyPeer","id":3591563503,"comment":"Channel notification settings","arguments":[]},{"kind":"class","name":"sendMessageTypingAction","type":"SendMessageAction","id":381645902,"comment":"User is typing.","arguments":[]},{"kind":"class","name":"sendMessageCancelAction","type":"SendMessageAction","id":4250847477,"comment":"Invalidate all previous action updates. E.g. when user deletes entered text or aborts a video upload.","arguments":[]},{"kind":"class","name":"sendMessageRecordVideoAction","type":"SendMessageAction","id":2710034031,"comment":"User is recording a video.","arguments":[]},{"kind":"class","name":"sendMessageUploadVideoAction","type":"SendMessageAction","id":3916839660,"comment":"User is uploading a video.","arguments":[{"name":"progress","type":"int","comment":"Progress percentage"}]},{"kind":"class","name":"sendMessageRecordAudioAction","type":"SendMessageAction","id":3576656887,"comment":"User is recording a voice message.","arguments":[]},{"kind":"class","name":"sendMessageUploadAudioAction","type":"SendMessageAction","id":4082227115,"comment":"User is uploading a voice message.","arguments":[{"name":"progress","type":"int","comment":"Progress percentage"}]},{"kind":"class","name":"sendMessageUploadPhotoAction","type":"SendMessageAction","id":3520285222,"comment":"User is uploading a photo.","arguments":[{"name":"progress","type":"int","comment":"Progress percentage"}]},{"kind":"class","name":"sendMessageUploadDocumentAction","type":"SendMessageAction","id":2852968932,"comment":"User is uploading a file.","arguments":[{"name":"progress","type":"int","comment":"Progress percentage"}]},{"kind":"class","name":"sendMessageGeoLocationAction","type":"SendMessageAction","id":393186209,"comment":"User is selecting a location to share.","arguments":[]},{"kind":"class","name":"sendMessageChooseContactAction","type":"SendMessageAction","id":1653390447,"comment":"User is selecting a contact to share.","arguments":[]},{"kind":"class","name":"sendMessageGamePlayAction","type":"SendMessageAction","id":3714748232,"comment":"User is playing a game","arguments":[]},{"kind":"class","name":"sendMessageRecordRoundAction","type":"SendMessageAction","id":2297593788,"comment":"User is recording a round video to share","arguments":[]},{"kind":"class","name":"sendMessageUploadRoundAction","type":"SendMessageAction","id":608050278,"comment":"User is uploading a round video","arguments":[{"name":"progress","type":"int","comment":"Progress percentage"}]},{"kind":"class","name":"speakingInGroupCallAction","type":"SendMessageAction","id":3643548293,"comment":"User is currently speaking in the group call","arguments":[]},{"kind":"class","name":"sendMessageHistoryImportAction","type":"SendMessageAction","id":3688534598,"comment":"Chat history is being imported","arguments":[{"name":"progress","type":"int","comment":"Progress percentage"}]},{"kind":"class","name":"sendMessageChooseStickerAction","type":"SendMessageAction","id":2958739121,"comment":"User is choosing a sticker","arguments":[]},{"kind":"class","name":"sendMessageEmojiInteraction","type":"SendMessageAction","id":630664139,"comment":"User has clicked on an animated emoji triggering a reaction, click here for more info ».","arguments":[{"name":"emoticon","type":"string","comment":"Emoji"},{"name":"msg_id","type":"int"},{"name":"interaction","type":"DataJSON","comment":"A JSON object with interaction info, click here for more info »"}]},{"kind":"class","name":"sendMessageEmojiInteractionSeen","type":"SendMessageAction","id":3060109358,"comment":"User is watching an animated emoji reaction triggered by another user, click here for more info ».","arguments":[{"name":"emoticon","type":"string","comment":"Emoji"}]},{"kind":"class","name":"contacts.found","type":"contacts.Found","id":3004386717,"comment":"Users found by name substring and auxiliary data.","arguments":[{"name":"my_results","type":"Vector"},{"name":"results","type":"Vector","comment":"List of found user identifiers"},{"name":"chats","type":"Vector","comment":"Found chats"},{"name":"users","type":"Vector","comment":"List of users"}]},{"kind":"class","name":"inputPrivacyKeyStatusTimestamp","type":"InputPrivacyKey","id":1335282456,"comment":"Whether we can see the exact last online timestamp of the user","arguments":[]},{"kind":"class","name":"inputPrivacyKeyChatInvite","type":"InputPrivacyKey","id":3187344422,"comment":"Whether the user can be invited to chats","arguments":[]},{"kind":"class","name":"inputPrivacyKeyPhoneCall","type":"InputPrivacyKey","id":4206550111,"comment":"Whether the user will accept phone calls","arguments":[]},{"kind":"class","name":"inputPrivacyKeyPhoneP2P","type":"InputPrivacyKey","id":3684593874,"comment":"Whether the user allows P2P communication during VoIP calls","arguments":[]},{"kind":"class","name":"inputPrivacyKeyForwards","type":"InputPrivacyKey","id":2765966344,"comment":"Whether messages forwarded from this user will be anonymous","arguments":[]},{"kind":"class","name":"inputPrivacyKeyProfilePhoto","type":"InputPrivacyKey","id":1461304012,"comment":"Whether people will be able to see the user's profile picture","arguments":[]},{"kind":"class","name":"inputPrivacyKeyPhoneNumber","type":"InputPrivacyKey","id":55761658,"comment":"Whether people will be able to see the user's phone number","arguments":[]},{"kind":"class","name":"inputPrivacyKeyAddedByPhone","type":"InputPrivacyKey","id":3508640733,"comment":"Whether people can add you to their contact list by your phone number","arguments":[]},{"kind":"class","name":"privacyKeyStatusTimestamp","type":"PrivacyKey","id":3157175088,"comment":"Whether we can see the last online timestamp","arguments":[]},{"kind":"class","name":"privacyKeyChatInvite","type":"PrivacyKey","id":1343122938,"comment":"Whether the user can be invited to chats","arguments":[]},{"kind":"class","name":"privacyKeyPhoneCall","type":"PrivacyKey","id":1030105979,"comment":"Whether the user accepts phone calls","arguments":[]},{"kind":"class","name":"privacyKeyPhoneP2P","type":"PrivacyKey","id":961092808,"comment":"Whether P2P connections in phone calls are allowed","arguments":[]},{"kind":"class","name":"privacyKeyForwards","type":"PrivacyKey","id":1777096355,"comment":"Whether messages forwarded from the user will be anonymously forwarded","arguments":[]},{"kind":"class","name":"privacyKeyProfilePhoto","type":"PrivacyKey","id":2517966829,"comment":"Whether the profile picture of the user is visible","arguments":[]},{"kind":"class","name":"privacyKeyPhoneNumber","type":"PrivacyKey","id":3516589165,"comment":"Whether the user allows us to see their phone number","arguments":[]},{"kind":"class","name":"privacyKeyAddedByPhone","type":"PrivacyKey","id":1124062251,"comment":"Whether people can add you to their contact list by your phone number","arguments":[]},{"kind":"class","name":"inputPrivacyValueAllowContacts","type":"InputPrivacyRule","id":218751099,"comment":"Allow only contacts","arguments":[]},{"kind":"class","name":"inputPrivacyValueAllowAll","type":"InputPrivacyRule","id":407582158,"comment":"Allow all users","arguments":[]},{"kind":"class","name":"inputPrivacyValueAllowUsers","type":"InputPrivacyRule","id":320652927,"comment":"Allow only certain users","arguments":[{"name":"users","type":"Vector","comment":"Allowed users"}]},{"kind":"class","name":"inputPrivacyValueDisallowContacts","type":"InputPrivacyRule","id":195371015,"comment":"Disallow only contacts","arguments":[]},{"kind":"class","name":"inputPrivacyValueDisallowAll","type":"InputPrivacyRule","id":3597362889,"comment":"Disallow all","arguments":[]},{"kind":"class","name":"inputPrivacyValueDisallowUsers","type":"InputPrivacyRule","id":2417034343,"comment":"Disallow only certain users","arguments":[{"name":"users","type":"Vector","comment":"Users to disallow"}]},{"kind":"class","name":"inputPrivacyValueAllowChatParticipants","type":"InputPrivacyRule","id":2215004623,"comment":"Allow only participants of certain chats","arguments":[{"name":"chats","type":"vector","comment":"Allowed chat IDs"}]},{"kind":"class","name":"inputPrivacyValueDisallowChatParticipants","type":"InputPrivacyRule","id":3914272646,"comment":"Disallow only participants of certain chats","arguments":[{"name":"chats","type":"vector","comment":"Disallowed chat IDs"}]},{"kind":"class","name":"privacyValueAllowContacts","type":"PrivacyRule","id":4294843308,"comment":"Allow all contacts","arguments":[]},{"kind":"class","name":"privacyValueAllowAll","type":"PrivacyRule","id":1698855810,"comment":"Allow all users","arguments":[]},{"kind":"class","name":"privacyValueAllowUsers","type":"PrivacyRule","id":3096469426,"comment":"Allow only certain users","arguments":[{"name":"users","type":"vector","comment":"Allowed users"}]},{"kind":"class","name":"privacyValueDisallowContacts","type":"PrivacyRule","id":4169726490,"comment":"Disallow only contacts","arguments":[]},{"kind":"class","name":"privacyValueDisallowAll","type":"PrivacyRule","id":2339628899,"comment":"Disallow all users","arguments":[]},{"kind":"class","name":"privacyValueDisallowUsers","type":"PrivacyRule","id":3831632193,"comment":"Disallow only certain users","arguments":[{"name":"users","type":"vector","comment":"Disallowed users"}]},{"kind":"class","name":"privacyValueAllowChatParticipants","type":"PrivacyRule","id":1796427406,"comment":"Allow all participants of certain chats","arguments":[{"name":"chats","type":"vector","comment":"Allowed chats"}]},{"kind":"class","name":"privacyValueDisallowChatParticipants","type":"PrivacyRule","id":1103656293,"comment":"Disallow only participants of certain chats","arguments":[{"name":"chats","type":"vector","comment":"Disallowed chats"}]},{"kind":"class","name":"account.privacyRules","type":"account.PrivacyRules","id":1352683077,"comment":"Privacy rules","arguments":[{"name":"rules","type":"Vector","comment":"Privacy rules"},{"name":"chats","type":"Vector","comment":"Chats to which the rules apply"},{"name":"users","type":"Vector","comment":"Users to which the rules apply"}]},{"kind":"class","name":"accountDaysTTL","type":"AccountDaysTTL","id":3100684255,"comment":"Time to live in days of the current account","arguments":[{"name":"days","type":"int","comment":"This account will self-destruct in the specified number of days"}]},{"kind":"class","name":"documentAttributeImageSize","type":"DocumentAttribute","id":1815593308,"comment":"Defines the width and height of an image uploaded as document","arguments":[{"name":"w","type":"int","comment":"Width of image"},{"name":"h","type":"int","comment":"Height of image"}]},{"kind":"class","name":"documentAttributeAnimated","type":"DocumentAttribute","id":297109817,"comment":"Defines an animated GIF","arguments":[]},{"kind":"class","name":"documentAttributeSticker","type":"DocumentAttribute","id":1662637586,"comment":"Defines a sticker","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"mask","type":"true","predicate":"flags.1","comment":"Whether this is a mask sticker"},{"name":"alt","type":"string","comment":"Alternative emoji representation of sticker"},{"name":"stickerset","type":"InputStickerSet","comment":"Associated stickerset"},{"name":"mask_coords","type":"MaskCoords","predicate":"flags.0"}]},{"kind":"class","name":"documentAttributeVideo","type":"DocumentAttribute","id":250621158,"comment":"Defines a video","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"round_message","type":"true","predicate":"flags.0"},{"name":"supports_streaming","type":"true","predicate":"flags.1"},{"name":"duration","type":"int","comment":"Duration in seconds"},{"name":"w","type":"int","comment":"Video width"},{"name":"h","type":"int","comment":"Video height"}]},{"kind":"class","name":"documentAttributeAudio","type":"DocumentAttribute","id":2555574726,"comment":"Represents an audio file","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"voice","type":"true","predicate":"flags.10","comment":"Whether this is a voice message"},{"name":"duration","type":"int","comment":"Duration in seconds"},{"name":"title","type":"string","predicate":"flags.0","comment":"Name of song"},{"name":"performer","type":"string","predicate":"flags.1","comment":"Performer"},{"name":"waveform","type":"bytes","predicate":"flags.2","comment":"Waveform"}]},{"kind":"class","name":"documentAttributeFilename","type":"DocumentAttribute","id":358154344,"comment":"A simple document with a file name","arguments":[{"name":"file_name","type":"string"}]},{"kind":"class","name":"documentAttributeHasStickers","type":"DocumentAttribute","id":2550256375,"comment":"Whether the current document has stickers attached","arguments":[]},{"kind":"class","name":"messages.stickersNotModified","type":"messages.Stickers","id":4050950690,"comment":"No new stickers were found for the given query","arguments":[]},{"kind":"class","name":"messages.stickers","type":"messages.Stickers","id":816245886,"comment":"Found stickers","arguments":[{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"},{"name":"stickers","type":"Vector","comment":"Stickers"}]},{"kind":"class","name":"stickerPack","type":"StickerPack","id":313694676,"comment":"A stickerpack is a group of stickers associated to the same emoji.\nIt is not a sticker pack the way it is usually intended, you may be looking for a StickerSet.","arguments":[{"name":"emoticon","type":"string","comment":"Emoji"},{"name":"documents","type":"Vector","comment":"Stickers"}]},{"kind":"class","name":"messages.allStickersNotModified","type":"messages.AllStickers","id":3898999491,"comment":"Info about all installed stickers hasn't changed","arguments":[]},{"kind":"class","name":"messages.allStickers","type":"messages.AllStickers","id":3451637435,"comment":"Info about all installed stickers","arguments":[{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"},{"name":"sets","type":"Vector","comment":"All stickersets"}]},{"kind":"class","name":"messages.affectedMessages","type":"messages.AffectedMessages","id":2228326789,"comment":"Events affected by operation","arguments":[{"name":"pts","type":"int","comment":"Event count after generation"},{"name":"pts_count","type":"int"}]},{"kind":"class","name":"webPageEmpty","type":"WebPage","id":3943987176,"comment":"No preview is available for the webpage","arguments":[{"name":"id","type":"long","comment":"Preview ID"}]},{"kind":"class","name":"webPagePending","type":"WebPage","id":3313949212,"comment":"A preview of the webpage is currently being generated","arguments":[{"name":"id","type":"long","comment":"ID of preview"},{"name":"date","type":"int","comment":"When was the processing started"}]},{"kind":"class","name":"webPage","type":"WebPage","id":3902555570,"comment":"Webpage preview","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"id","type":"long","comment":"Preview ID"},{"name":"url","type":"string","comment":"URL of previewed webpage"},{"name":"display_url","type":"string"},{"name":"hash","type":"int","comment":"Hash for pagination, for more info click here"},{"name":"type","type":"string","predicate":"flags.0","comment":"Type of the web page. Can be: article, photo, audio, video, document, profile, app, or something else"},{"name":"site_name","type":"string","predicate":"flags.1"},{"name":"title","type":"string","predicate":"flags.2","comment":"Title of the content"},{"name":"description","type":"string","predicate":"flags.3","comment":"Content description"},{"name":"photo","type":"Photo","predicate":"flags.4","comment":"Image representing the content"},{"name":"embed_url","type":"string","predicate":"flags.5"},{"name":"embed_type","type":"string","predicate":"flags.5"},{"name":"embed_width","type":"int","predicate":"flags.6"},{"name":"embed_height","type":"int","predicate":"flags.6"},{"name":"duration","type":"int","predicate":"flags.7","comment":"Duration of the content, in seconds"},{"name":"author","type":"string","predicate":"flags.8","comment":"Author of the content"},{"name":"document","type":"Document","predicate":"flags.9","comment":"Preview of the content as a media file"},{"name":"cached_page","type":"Page","predicate":"flags.10"},{"name":"attributes","type":"Vector","predicate":"flags.12","comment":"Webpage attributes"}]},{"kind":"class","name":"webPageNotModified","type":"WebPage","id":1930545681,"comment":"The preview of the webpage hasn't changed","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"cached_page_views","type":"int","predicate":"flags.0"}]},{"kind":"class","name":"authorization","type":"Authorization","id":2902578717,"comment":"Logged-in session","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"current","type":"true","predicate":"flags.0","comment":"Whether this is the current session"},{"name":"official_app","type":"true","predicate":"flags.1"},{"name":"password_pending","type":"true","predicate":"flags.2"},{"name":"encrypted_requests_disabled","type":"true","predicate":"flags.3"},{"name":"hash","type":"long","comment":"Identifier"},{"name":"device_model","type":"string"},{"name":"platform","type":"string","comment":"Platform"},{"name":"system_version","type":"string"},{"name":"api_id","type":"int"},{"name":"app_name","type":"string"},{"name":"app_version","type":"string"},{"name":"date_created","type":"int"},{"name":"date_active","type":"int"},{"name":"ip","type":"string","comment":"Last known IP"},{"name":"country","type":"string","comment":"Country determined from IP"},{"name":"region","type":"string","comment":"Region determined from IP"}]},{"kind":"class","name":"account.authorizations","type":"account.Authorizations","id":307276766,"comment":"Logged-in sessions","arguments":[{"name":"authorizations","type":"Vector","comment":"Logged-in sessions"}]},{"kind":"class","name":"account.password","type":"account.Password","id":408623183,"comment":"Configuration for two-factor authorization","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"has_recovery","type":"true","predicate":"flags.0"},{"name":"has_secure_values","type":"true","predicate":"flags.1"},{"name":"has_password","type":"true","predicate":"flags.2"},{"name":"current_algo","type":"PasswordKdfAlgo","predicate":"flags.2"},{"name":"srp_B","type":"bytes","predicate":"flags.2"},{"name":"srp_id","type":"long","predicate":"flags.2"},{"name":"hint","type":"string","predicate":"flags.3","comment":"Text hint for the password"},{"name":"email_unconfirmed_pattern","type":"string","predicate":"flags.4"},{"name":"new_algo","type":"PasswordKdfAlgo"},{"name":"new_secure_algo","type":"SecurePasswordKdfAlgo"},{"name":"secure_random","type":"bytes"},{"name":"pending_reset_date","type":"int","predicate":"flags.5"}]},{"kind":"class","name":"account.passwordSettings","type":"account.PasswordSettings","id":2589733861,"comment":"Private info associated to the password info (recovery email, telegram passport info & so on)","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"email","type":"string","predicate":"flags.0","comment":"2FA Recovery email"},{"name":"secure_settings","type":"SecureSecretSettings","predicate":"flags.1"}]},{"kind":"class","name":"account.passwordInputSettings","type":"account.PasswordInputSettings","id":3258394569,"comment":"Settings for setting up a new password","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"new_algo","type":"PasswordKdfAlgo","predicate":"flags.0"},{"name":"new_password_hash","type":"bytes","predicate":"flags.0"},{"name":"hint","type":"string","predicate":"flags.0","comment":"Text hint for the password"},{"name":"email","type":"string","predicate":"flags.1","comment":"Password recovery email"},{"name":"new_secure_settings","type":"SecureSecretSettings","predicate":"flags.2"}]},{"kind":"class","name":"auth.passwordRecovery","type":"auth.PasswordRecovery","id":326715557,"comment":"Recovery info of a 2FA password, only for accounts with a recovery email configured.","arguments":[{"name":"email_pattern","type":"string"}]},{"kind":"class","name":"receivedNotifyMessage","type":"ReceivedNotifyMessage","id":2743383929,"comment":"Message ID, for which PUSH-notifications were cancelled.","arguments":[{"name":"id","type":"int","comment":"Message ID, for which PUSH-notifications were canceled"},{"name":"flags","type":"int","comment":"Reserved for future use"}]},{"kind":"class","name":"chatInviteExported","type":"ExportedChatInvite","id":179611673,"comment":"Exported chat invite","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"revoked","type":"true","predicate":"flags.0","comment":"Whether this chat invite was revoked"},{"name":"permanent","type":"true","predicate":"flags.5","comment":"Whether this chat invite has no expiration"},{"name":"request_needed","type":"true","predicate":"flags.6"},{"name":"link","type":"string","comment":"Chat invitation link"},{"name":"admin_id","type":"int53"},{"name":"date","type":"int","comment":"When was this chat invite created"},{"name":"start_date","type":"int","predicate":"flags.4"},{"name":"expire_date","type":"int","predicate":"flags.1"},{"name":"usage_limit","type":"int","predicate":"flags.2"},{"name":"usage","type":"int","predicate":"flags.3","comment":"How many users joined using this link"},{"name":"requested","type":"int","predicate":"flags.7"},{"name":"title","type":"string","predicate":"flags.8"}]},{"kind":"class","name":"chatInviteAlready","type":"ChatInvite","id":1516793212,"comment":"The user has already joined this chat","arguments":[{"name":"chat","type":"Chat","comment":"The chat connected to the invite"}]},{"kind":"class","name":"chatInvite","type":"ChatInvite","id":806110401,"comment":"Chat invite info","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"channel","type":"true","predicate":"flags.0","comment":"Whether this is a channel/supergroup or a normal group"},{"name":"broadcast","type":"true","predicate":"flags.1","comment":"Whether this is a channel"},{"name":"public","type":"true","predicate":"flags.2","comment":"Whether this is a public channel/supergroup"},{"name":"megagroup","type":"true","predicate":"flags.3","comment":"Whether this is a supergroup"},{"name":"request_needed","type":"true","predicate":"flags.6"},{"name":"title","type":"string","comment":"Chat/supergroup/channel title"},{"name":"about","type":"string","predicate":"flags.5"},{"name":"photo","type":"Photo","comment":"Chat/supergroup/channel photo"},{"name":"participants_count","type":"int"},{"name":"participants","type":"Vector","predicate":"flags.4","comment":"A few of the participants that are in the group"}]},{"kind":"class","name":"chatInvitePeek","type":"ChatInvite","id":1634294960,"comment":"A chat invitation that also allows peeking into the group to read messages without joining it.","arguments":[{"name":"chat","type":"Chat","comment":"Chat information"},{"name":"expires","type":"int","comment":"Read-only anonymous access to this group will be revoked at this date"}]},{"kind":"class","name":"inputStickerSetEmpty","type":"InputStickerSet","id":4290128789,"comment":"Empty constructor","arguments":[]},{"kind":"class","name":"inputStickerSetID","type":"InputStickerSet","id":2649203305,"comment":"Stickerset by ID","arguments":[{"name":"id","type":"long","comment":"ID"},{"name":"access_hash","type":"long"}]},{"kind":"class","name":"inputStickerSetShortName","type":"InputStickerSet","id":2250033312,"comment":"Stickerset by short name, from tg://addstickers?set=short_name","arguments":[{"name":"short_name","type":"string"}]},{"kind":"class","name":"inputStickerSetAnimatedEmoji","type":"InputStickerSet","id":42402760,"comment":"Animated emojis stickerset","arguments":[]},{"kind":"class","name":"inputStickerSetDice","type":"InputStickerSet","id":3867103758,"comment":"Used for fetching animated dice stickers","arguments":[{"name":"emoticon","type":"string","comment":"The emoji, for now \"🏀\", \"🎲\" and \"🎯\" are supported"}]},{"kind":"class","name":"inputStickerSetAnimatedEmojiAnimations","type":"InputStickerSet","id":215889721,"comment":"Animated emoji reaction stickerset (contains animations to play when a user clicks on a given animated emoji)","arguments":[]},{"kind":"class","name":"stickerSet","type":"StickerSet","id":3621724538,"comment":"Represents a stickerset (stickerpack)","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"archived","type":"true","predicate":"flags.1","comment":"Whether this stickerset was archived (due to too many saved stickers in the current account)"},{"name":"official","type":"true","predicate":"flags.2","comment":"Is this stickerset official"},{"name":"masks","type":"true","predicate":"flags.3","comment":"Is this a mask stickerset"},{"name":"animated","type":"true","predicate":"flags.5","comment":"Is this an animated stickerpack"},{"name":"installed_date","type":"int","predicate":"flags.0"},{"name":"id","type":"long","comment":"ID of the stickerset"},{"name":"access_hash","type":"long"},{"name":"title","type":"string","comment":"Title of stickerset"},{"name":"short_name","type":"string"},{"name":"thumbs","type":"Vector","predicate":"flags.4","comment":"Stickerset thumbnail"},{"name":"thumb_dc_id","type":"int","predicate":"flags.4"},{"name":"thumb_version","type":"int","predicate":"flags.4"},{"name":"count","type":"int","comment":"Number of stickers in pack"},{"name":"hash","type":"int","comment":"Hash"}]},{"kind":"class","name":"messages.stickerSet","type":"messages.StickerSet","id":3054118054,"comment":"Stickerset and stickers inside it","arguments":[{"name":"set","type":"StickerSet","comment":"The stickerset"},{"name":"packs","type":"Vector","comment":"Emoji info for stickers"},{"name":"documents","type":"Vector","comment":"Stickers in stickerset"}]},{"kind":"class","name":"botCommand","type":"BotCommand","id":3262826695,"comment":"Describes a bot command that can be used in a chat","arguments":[{"name":"command","type":"string","comment":"/command name"},{"name":"description","type":"string","comment":"Description of the command"}]},{"kind":"class","name":"botInfo","type":"BotInfo","id":460632885,"comment":"Info about bots (available bot commands, etc)","arguments":[{"name":"user_id","type":"int53"},{"name":"description","type":"string","comment":"Description of the bot"},{"name":"commands","type":"Vector","comment":"Bot commands that can be used in the chat"}]},{"kind":"class","name":"keyboardButton","type":"KeyboardButton","id":2734311552,"comment":"Bot keyboard button","arguments":[{"name":"text","type":"string","comment":"Button text"}]},{"kind":"class","name":"keyboardButtonUrl","type":"KeyboardButton","id":629866245,"comment":"URL button","arguments":[{"name":"text","type":"string","comment":"Button label"},{"name":"url","type":"string","comment":"URL"}]},{"kind":"class","name":"keyboardButtonCallback","type":"KeyboardButton","id":901503851,"comment":"Callback button","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"requires_password","type":"true","predicate":"flags.0"},{"name":"text","type":"string","comment":"Button text"},{"name":"data","type":"bytes","comment":"Callback data"}]},{"kind":"class","name":"keyboardButtonRequestPhone","type":"KeyboardButton","id":2976541737,"comment":"Button to request a user's phone number","arguments":[{"name":"text","type":"string","comment":"Button text"}]},{"kind":"class","name":"keyboardButtonRequestGeoLocation","type":"KeyboardButton","id":4235815743,"comment":"Button to request a user's geolocation","arguments":[{"name":"text","type":"string","comment":"Button text"}]},{"kind":"class","name":"keyboardButtonSwitchInline","type":"KeyboardButton","id":90744648,"comment":"Button to force a user to switch to inline mode Pressing the button will prompt the user to select one of their chats, open that chat and insert the bot‘s username and the specified inline query in the input field.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"same_peer","type":"true","predicate":"flags.0"},{"name":"text","type":"string","comment":"Button label"},{"name":"query","type":"string","comment":"The inline query to use"}]},{"kind":"class","name":"keyboardButtonGame","type":"KeyboardButton","id":1358175439,"comment":"Button to start a game","arguments":[{"name":"text","type":"string","comment":"Button text"}]},{"kind":"class","name":"keyboardButtonBuy","type":"KeyboardButton","id":2950250427,"comment":"Button to buy a product","arguments":[{"name":"text","type":"string","comment":"Button text"}]},{"kind":"class","name":"keyboardButtonUrlAuth","type":"KeyboardButton","id":280464681,"comment":"Button to request a user to authorize via URL using Seamless Telegram Login. When the user clicks on such a button, {@link messages.requestUrlAuth} should be called, providing the button_id and the ID of the container message. The returned {@link urlAuthResultRequest} object will contain more details about the authorization request (request_write_access if the bot would like to send messages to the user along with the username of the bot which will be used for user authorization). Finally, the user can choose to call {@link messages.acceptUrlAuth} to get a {@link urlAuthResultAccepted} with the URL to open instead of the url of this constructor, or a {@link urlAuthResultDefault}, in which case the url of this constructor must be opened, instead. If the user refuses the authorization request but still wants to open the link, the url of this constructor must be used.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"text","type":"string","comment":"Button label"},{"name":"fwd_text","type":"string","predicate":"flags.0"},{"name":"url","type":"string","comment":"An HTTP URL to be opened with user authorization data added to the query string when the button is pressed. If the user refuses to provide authorization data, the original URL without information about the user will be opened. The data added is the same as described in Receiving authorization data.

NOTE: Services must always check the hash of the received data to verify the authentication and the integrity of the data as described in Checking authorization."},{"name":"button_id","type":"int"}]},{"kind":"class","name":"inputKeyboardButtonUrlAuth","type":"KeyboardButton","id":3492708308,"comment":"Button to request a user to {@link messages.acceptUrlAuth} via URL using Seamless Telegram Login.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"request_write_access","type":"true","predicate":"flags.0"},{"name":"text","type":"string","comment":"Button text"},{"name":"fwd_text","type":"string","predicate":"flags.1"},{"name":"url","type":"string","comment":"An HTTP URL to be opened with user authorization data added to the query string when the button is pressed. If the user refuses to provide authorization data, the original URL without information about the user will be opened. The data added is the same as described in Receiving authorization data.
NOTE: You must always check the hash of the received data to verify the authentication and the integrity of the data as described in Checking authorization."},{"name":"bot","type":"InputUser","comment":"Username of a bot, which will be used for user authorization. See Setting up a bot for more details. If not specified, the current bot's username will be assumed. The url's domain must be the same as the domain linked with the bot. See Linking your domain to the bot for more details."}]},{"kind":"class","name":"keyboardButtonRequestPoll","type":"KeyboardButton","id":3150401885,"comment":"A button that allows the user to create and send a poll when pressed; available only in private","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"quiz","type":"Bool","predicate":"flags.0","comment":"If set, only quiz polls can be sent"},{"name":"text","type":"string","comment":"Button text"}]},{"kind":"class","name":"keyboardButtonRow","type":"KeyboardButtonRow","id":2002815875,"comment":"Inline keyboard row","arguments":[{"name":"buttons","type":"Vector","comment":"Bot or inline keyboard buttons"}]},{"kind":"class","name":"replyKeyboardHide","type":"ReplyMarkup","id":2688441221,"comment":"Hide sent bot keyboard","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"selective","type":"true","predicate":"flags.2","comment":"Use this flag if you want to remove the keyboard for specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.

Example: A user votes in a poll, bot returns confirmation message in reply to the vote and removes the keyboard for that user, while still showing the keyboard with poll options to users who haven't voted yet"}]},{"kind":"class","name":"replyKeyboardForceReply","type":"ReplyMarkup","id":2259946248,"comment":"Force the user to send a reply","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"single_use","type":"true","predicate":"flags.1"},{"name":"selective","type":"true","predicate":"flags.2","comment":"Use this parameter if you want to show the keyboard to specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.
Example: A user requests to change the bot‘s language, bot replies to the request with a keyboard to select the new language. Other users in the group don’t see the keyboard."},{"name":"placeholder","type":"string","predicate":"flags.3","comment":"The placeholder to be shown in the input field when the keyboard is active; 1-64 characters."}]},{"kind":"class","name":"replyKeyboardMarkup","type":"ReplyMarkup","id":2245892561,"comment":"Bot keyboard","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"resize","type":"true","predicate":"flags.0","comment":"Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). If not set, the custom keyboard is always of the same height as the app's standard keyboard."},{"name":"single_use","type":"true","predicate":"flags.1"},{"name":"selective","type":"true","predicate":"flags.2","comment":"Use this parameter if you want to show the keyboard to specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.

Example: A user requests to change the bot‘s language, bot replies to the request with a keyboard to select the new language. Other users in the group don’t see the keyboard."},{"name":"rows","type":"Vector","comment":"Button row"},{"name":"placeholder","type":"string","predicate":"flags.3","comment":"The placeholder to be shown in the input field when the keyboard is active; 1-64 characters."}]},{"kind":"class","name":"replyInlineMarkup","type":"ReplyMarkup","id":1218642516,"comment":"Bot or inline keyboard","arguments":[{"name":"rows","type":"Vector","comment":"Bot or inline keyboard rows"}]},{"kind":"class","name":"messageEntityUnknown","type":"MessageEntity","id":3146955413,"comment":"Unknown message entity","arguments":[{"name":"offset","type":"int","comment":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"int","comment":"Length of message entity within message (in UTF-8 codepoints)"}]},{"kind":"class","name":"messageEntityMention","type":"MessageEntity","id":4194588573,"comment":"Message entity mentioning the current user","arguments":[{"name":"offset","type":"int","comment":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"int","comment":"Length of message entity within message (in UTF-8 codepoints)"}]},{"kind":"class","name":"messageEntityHashtag","type":"MessageEntity","id":1868782349,"comment":"#hashtag message entity","arguments":[{"name":"offset","type":"int","comment":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"int","comment":"Length of message entity within message (in UTF-8 codepoints)"}]},{"kind":"class","name":"messageEntityBotCommand","type":"MessageEntity","id":1827637959,"comment":"Message entity representing a bot /command","arguments":[{"name":"offset","type":"int","comment":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"int","comment":"Length of message entity within message (in UTF-8 codepoints)"}]},{"kind":"class","name":"messageEntityUrl","type":"MessageEntity","id":1859134776,"comment":"Message entity representing an in-text url: https://google.com; for text urls, use {@link messageEntityTextUrl}.","arguments":[{"name":"offset","type":"int","comment":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"int","comment":"Length of message entity within message (in UTF-8 codepoints)"}]},{"kind":"class","name":"messageEntityEmail","type":"MessageEntity","id":1692693954,"comment":"Message entity representing an email@example.com.","arguments":[{"name":"offset","type":"int","comment":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"int","comment":"Length of message entity within message (in UTF-8 codepoints)"}]},{"kind":"class","name":"messageEntityBold","type":"MessageEntity","id":3177253833,"comment":"Message entity representing bold text.","arguments":[{"name":"offset","type":"int","comment":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"int","comment":"Length of message entity within message (in UTF-8 codepoints)"}]},{"kind":"class","name":"messageEntityItalic","type":"MessageEntity","id":2188348256,"comment":"Message entity representing italic text.","arguments":[{"name":"offset","type":"int","comment":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"int","comment":"Length of message entity within message (in UTF-8 codepoints)"}]},{"kind":"class","name":"messageEntityCode","type":"MessageEntity","id":681706865,"comment":"Message entity representing a codeblock.","arguments":[{"name":"offset","type":"int","comment":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"int","comment":"Length of message entity within message (in UTF-8 codepoints)"}]},{"kind":"class","name":"messageEntityPre","type":"MessageEntity","id":1938967520,"comment":"Message entity representing a preformatted codeblock, allowing the user to specify a programming language for the codeblock.","arguments":[{"name":"offset","type":"int","comment":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"int","comment":"Length of message entity within message (in UTF-8 codepoints)"},{"name":"language","type":"string","comment":"Programming language of the code"}]},{"kind":"class","name":"messageEntityTextUrl","type":"MessageEntity","id":1990644519,"comment":"Message entity representing a text url: for in-text urls like https://google.com use {@link messageEntityUrl}.","arguments":[{"name":"offset","type":"int","comment":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"int","comment":"Length of message entity within message (in UTF-8 codepoints)"},{"name":"url","type":"string","comment":"The actual URL"}]},{"kind":"class","name":"messageEntityMentionName","type":"MessageEntity","id":3699052864,"comment":"Message entity representing a user mention: for creating a mention use {@link inputMessageEntityMentionName}.","arguments":[{"name":"offset","type":"int","comment":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"int","comment":"Length of message entity within message (in UTF-8 codepoints)"},{"name":"user_id","type":"int53"}]},{"kind":"class","name":"inputMessageEntityMentionName","type":"MessageEntity","id":546203849,"comment":"Message entity that can be used to create a user user mention: received mentions use the {@link messageEntityMentionName} constructor, instead.","arguments":[{"name":"offset","type":"int","comment":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"int","comment":"Length of message entity within message (in UTF-8 codepoints)"},{"name":"user_id","type":"InputUser"}]},{"kind":"class","name":"messageEntityPhone","type":"MessageEntity","id":2607407947,"comment":"Message entity representing a phone number.","arguments":[{"name":"offset","type":"int","comment":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"int","comment":"Length of message entity within message (in UTF-8 codepoints)"}]},{"kind":"class","name":"messageEntityCashtag","type":"MessageEntity","id":1280209983,"comment":"Message entity representing a $cashtag.","arguments":[{"name":"offset","type":"int","comment":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"int","comment":"Length of message entity within message (in UTF-8 codepoints)"}]},{"kind":"class","name":"messageEntityUnderline","type":"MessageEntity","id":2622389899,"comment":"Message entity representing underlined text.","arguments":[{"name":"offset","type":"int","comment":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"int","comment":"Length of message entity within message (in UTF-8 codepoints)"}]},{"kind":"class","name":"messageEntityStrike","type":"MessageEntity","id":3204879316,"comment":"Message entity representing strikethrough text.","arguments":[{"name":"offset","type":"int","comment":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"int","comment":"Length of message entity within message (in UTF-8 codepoints)"}]},{"kind":"class","name":"messageEntityBlockquote","type":"MessageEntity","id":34469328,"comment":"Message entity representing a block quote.","arguments":[{"name":"offset","type":"int","comment":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"int","comment":"Length of message entity within message (in UTF-8 codepoints)"}]},{"kind":"class","name":"messageEntityBankCard","type":"MessageEntity","id":1981704948,"comment":"Indicates a credit card number","arguments":[{"name":"offset","type":"int","comment":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"int","comment":"Length of message entity within message (in UTF-8 codepoints)"}]},{"kind":"class","name":"inputChannelEmpty","type":"InputChannel","id":4002160262,"comment":"Represents the absence of a channel","arguments":[]},{"kind":"class","name":"inputChannel","type":"InputChannel","id":4082822184,"comment":"Represents a channel","arguments":[{"name":"channel_id","type":"int53"},{"name":"access_hash","type":"long"}]},{"kind":"class","name":"inputChannelFromMessage","type":"InputChannel","id":1536380829,"comment":"Defines a min channel that was seen in a certain message of a certain chat.","arguments":[{"name":"peer","type":"InputPeer","comment":"The chat where the channel was seen"},{"name":"msg_id","type":"int"},{"name":"channel_id","type":"int53"}]},{"kind":"class","name":"contacts.resolvedPeer","type":"contacts.ResolvedPeer","id":2131196633,"comment":"Resolved peer","arguments":[{"name":"peer","type":"Peer","comment":"The peer"},{"name":"chats","type":"Vector","comment":"Chats"},{"name":"users","type":"Vector","comment":"Users"}]},{"kind":"class","name":"messageRange","type":"MessageRange","id":182649427,"comment":"Indicates a range of chat messages","arguments":[{"name":"min_id","type":"int"},{"name":"max_id","type":"int"}]},{"kind":"class","name":"updates.channelDifferenceEmpty","type":"updates.ChannelDifference","id":1041346555,"comment":"There are no new updates","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"final","type":"true","predicate":"flags.0","comment":"Whether there are more updates that must be fetched (always false)"},{"name":"pts","type":"int","comment":"The latest PTS"},{"name":"timeout","type":"int","predicate":"flags.1","comment":"Clients are supposed to refetch the channel difference after timeout seconds have elapsed"}]},{"kind":"class","name":"updates.channelDifferenceTooLong","type":"updates.ChannelDifference","id":2763835134,"comment":"The provided pts + limit < remote pts. Simply, there are too many updates to be fetched (more than limit), the client has to resolve the update gap in one of the following ways:","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"final","type":"true","predicate":"flags.0","comment":"Whether there are more updates that must be fetched (always false)"},{"name":"timeout","type":"int","predicate":"flags.1","comment":"Clients are supposed to refetch the channel difference after timeout seconds have elapsed"},{"name":"dialog","type":"Dialog","comment":"Dialog containing the latest PTS that can be used to reset the channel state"},{"name":"messages","type":"Vector","comment":"The latest messages"},{"name":"chats","type":"Vector","comment":"Chats from messages"},{"name":"users","type":"Vector","comment":"Users from messages"}]},{"kind":"class","name":"updates.channelDifference","type":"updates.ChannelDifference","id":543450958,"comment":"The new updates","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"final","type":"true","predicate":"flags.0","comment":"Whether there are more updates to be fetched using getDifference, starting from the provided pts"},{"name":"pts","type":"int","comment":"The PTS from which to start getting updates the next time"},{"name":"timeout","type":"int","predicate":"flags.1","comment":"Clients are supposed to refetch the channel difference after timeout seconds have elapsed"},{"name":"new_messages","type":"Vector"},{"name":"other_updates","type":"Vector"},{"name":"chats","type":"Vector","comment":"Chats"},{"name":"users","type":"Vector","comment":"Users"}]},{"kind":"class","name":"channelMessagesFilterEmpty","type":"ChannelMessagesFilter","id":2496933607,"comment":"No filter","arguments":[]},{"kind":"class","name":"channelMessagesFilter","type":"ChannelMessagesFilter","id":3447183703,"comment":"Filter for getting only certain types of channel messages","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"exclude_new_messages","type":"true","predicate":"flags.1"},{"name":"ranges","type":"Vector","comment":"A range of messages to fetch"}]},{"kind":"class","name":"channelParticipant","type":"ChannelParticipant","id":3222013888,"comment":"Channel/supergroup participant","arguments":[{"name":"user_id","type":"int53"},{"name":"date","type":"int","comment":"Date joined"}]},{"kind":"class","name":"channelParticipantSelf","type":"ChannelParticipant","id":900251559,"comment":"Myself","arguments":[{"name":"flags","type":"#"},{"name":"via_invite","type":"true","predicate":"flags.0"},{"name":"user_id","type":"int53"},{"name":"inviter_id","type":"int53"},{"name":"date","type":"int","comment":"When did I join the channel/supergroup"}]},{"kind":"class","name":"channelParticipantCreator","type":"ChannelParticipant","id":803602899,"comment":"Channel/supergroup creator","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"user_id","type":"int53"},{"name":"admin_rights","type":"ChatAdminRights"},{"name":"rank","type":"string","predicate":"flags.0","comment":"The role (rank) of the group creator in the group: just an arbitrary string, admin by default"}]},{"kind":"class","name":"channelParticipantAdmin","type":"ChannelParticipant","id":885242707,"comment":"Admin","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"can_edit","type":"true","predicate":"flags.0"},{"name":"self","type":"true","predicate":"flags.1","comment":"Is this the current user"},{"name":"user_id","type":"int53"},{"name":"inviter_id","type":"int53","predicate":"flags.1"},{"name":"promoted_by","type":"int53"},{"name":"date","type":"int","comment":"When did the user join"},{"name":"admin_rights","type":"ChatAdminRights"},{"name":"rank","type":"string","predicate":"flags.2","comment":"The role (rank) of the admin in the group: just an arbitrary string, admin by default"}]},{"kind":"class","name":"channelParticipantBanned","type":"ChannelParticipant","id":1844969806,"comment":"Banned/kicked user","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"left","type":"true","predicate":"flags.0","comment":"Whether the user has left the group"},{"name":"peer","type":"Peer","comment":"The banned peer"},{"name":"kicked_by","type":"int53"},{"name":"date","type":"int","comment":"When did the user join the group"},{"name":"banned_rights","type":"ChatBannedRights"}]},{"kind":"class","name":"channelParticipantLeft","type":"ChannelParticipant","id":453242886,"comment":"A participant that left the channel/supergroup","arguments":[{"name":"peer","type":"Peer","comment":"The peer that left"}]},{"kind":"class","name":"channelParticipantsRecent","type":"ChannelParticipantsFilter","id":3728686201,"comment":"Fetch only recent participants","arguments":[]},{"kind":"class","name":"channelParticipantsAdmins","type":"ChannelParticipantsFilter","id":3026225513,"comment":"Fetch only admin participants","arguments":[]},{"kind":"class","name":"channelParticipantsKicked","type":"ChannelParticipantsFilter","id":2746567045,"comment":"Fetch only kicked participants","arguments":[{"name":"q","type":"string","comment":"Optional filter for searching kicked participants by name (otherwise empty)"}]},{"kind":"class","name":"channelParticipantsBots","type":"ChannelParticipantsFilter","id":2966521435,"comment":"Fetch only bot participants","arguments":[]},{"kind":"class","name":"channelParticipantsBanned","type":"ChannelParticipantsFilter","id":338142689,"comment":"Fetch only banned participants","arguments":[{"name":"q","type":"string","comment":"Optional filter for searching banned participants by name (otherwise empty)"}]},{"kind":"class","name":"channelParticipantsSearch","type":"ChannelParticipantsFilter","id":106343499,"comment":"Query participants by name","arguments":[{"name":"q","type":"string","comment":"Search query"}]},{"kind":"class","name":"channelParticipantsContacts","type":"ChannelParticipantsFilter","id":3144345741,"comment":"Fetch only participants that are also contacts","arguments":[{"name":"q","type":"string","comment":"Optional search query for searching contact participants by name"}]},{"kind":"class","name":"channelParticipantsMentions","type":"ChannelParticipantsFilter","id":3763035371,"comment":"This filter is used when looking for supergroup members to mention.\nThis filter will automatically remove anonymous admins, and return even non-participant users that replied to a specific thread through the comment section of a channel.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"q","type":"string","predicate":"flags.0","comment":"Filter by user name or username"},{"name":"top_msg_id","type":"int","predicate":"flags.1"}]},{"kind":"class","name":"channels.channelParticipants","type":"channels.ChannelParticipants","id":2595290799,"comment":"Represents multiple channel participants","arguments":[{"name":"count","type":"int","comment":"Total number of participants that correspond to the given query"},{"name":"participants","type":"Vector","comment":"Participants"},{"name":"chats","type":"Vector","comment":"Mentioned chats"},{"name":"users","type":"Vector","comment":"Users mentioned in participant info"}]},{"kind":"class","name":"channels.channelParticipantsNotModified","type":"channels.ChannelParticipants","id":4028055529,"comment":"No new participant info could be found","arguments":[]},{"kind":"class","name":"channels.channelParticipant","type":"channels.ChannelParticipant","id":3753378583,"comment":"Represents a channel participant","arguments":[{"name":"participant","type":"ChannelParticipant","comment":"The channel participant"},{"name":"chats","type":"Vector","comment":"Mentioned chats"},{"name":"users","type":"Vector","comment":"Users"}]},{"kind":"class","name":"help.termsOfService","type":"help.TermsOfService","id":2013922064,"comment":"Info about the latest telegram Terms Of Service","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"popup","type":"true","predicate":"flags.0","comment":"Whether a prompt must be showed to the user, in order to accept the new terms."},{"name":"id","type":"DataJSON","comment":"ID of the new terms"},{"name":"text","type":"string","comment":"Text of the new terms"},{"name":"entities","type":"Vector","comment":"Message entities for styled text"},{"name":"min_age_confirm","type":"int","predicate":"flags.1"}]},{"kind":"class","name":"messages.savedGifsNotModified","type":"messages.SavedGifs","id":3892468898,"comment":"No new saved gifs were found","arguments":[]},{"kind":"class","name":"messages.savedGifs","type":"messages.SavedGifs","id":2225089037,"comment":"Saved gifs","arguments":[{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"},{"name":"gifs","type":"Vector","comment":"List of saved gifs"}]},{"kind":"class","name":"inputBotInlineMessageMediaAuto","type":"InputBotInlineMessage","id":864077702,"comment":"A media","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"message","type":"string","comment":"Caption"},{"name":"entities","type":"Vector","predicate":"flags.1","comment":"Message entities for styled text"},{"name":"reply_markup","type":"ReplyMarkup","predicate":"flags.2"}]},{"kind":"class","name":"inputBotInlineMessageText","type":"InputBotInlineMessage","id":1036876423,"comment":"Simple text message","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"no_webpage","type":"true","predicate":"flags.0"},{"name":"message","type":"string","comment":"Message"},{"name":"entities","type":"Vector","predicate":"flags.1","comment":"Message entities for styled text"},{"name":"reply_markup","type":"ReplyMarkup","predicate":"flags.2"}]},{"kind":"class","name":"inputBotInlineMessageMediaGeo","type":"InputBotInlineMessage","id":2526190213,"comment":"Geolocation","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"geo_point","type":"InputGeoPoint"},{"name":"heading","type":"int","predicate":"flags.0","comment":"For live locations, a direction in which the location moves, in degrees; 1-360"},{"name":"period","type":"int","predicate":"flags.1","comment":"Validity period"},{"name":"proximity_notification_radius","type":"int","predicate":"flags.3"},{"name":"reply_markup","type":"ReplyMarkup","predicate":"flags.2"}]},{"kind":"class","name":"inputBotInlineMessageMediaVenue","type":"InputBotInlineMessage","id":1098628881,"comment":"Venue","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"geo_point","type":"InputGeoPoint"},{"name":"title","type":"string","comment":"Venue name"},{"name":"address","type":"string","comment":"Address"},{"name":"provider","type":"string","comment":"Venue provider: currently only \"foursquare\" and \"gplaces\" need to be supported"},{"name":"venue_id","type":"string"},{"name":"venue_type","type":"string"},{"name":"reply_markup","type":"ReplyMarkup","predicate":"flags.2"}]},{"kind":"class","name":"inputBotInlineMessageMediaContact","type":"InputBotInlineMessage","id":2800599037,"comment":"A contact","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"phone_number","type":"string"},{"name":"first_name","type":"string"},{"name":"last_name","type":"string"},{"name":"vcard","type":"string","comment":"VCard info"},{"name":"reply_markup","type":"ReplyMarkup","predicate":"flags.2"}]},{"kind":"class","name":"inputBotInlineMessageGame","type":"InputBotInlineMessage","id":1262639204,"comment":"A game","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"reply_markup","type":"ReplyMarkup","predicate":"flags.2"}]},{"kind":"class","name":"inputBotInlineMessageMediaInvoice","type":"InputBotInlineMessage","id":3622273573,"comment":"An invoice","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"title","type":"string","comment":"Product name, 1-32 characters"},{"name":"description","type":"string","comment":"Product description, 1-255 characters"},{"name":"photo","type":"InputWebDocument","predicate":"flags.0","comment":"Invoice photo"},{"name":"invoice","type":"Invoice","comment":"The invoice"},{"name":"payload","type":"bytes","comment":"Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes."},{"name":"provider","type":"string","comment":"Payments provider token, obtained via Botfather"},{"name":"provider_data","type":"DataJSON"},{"name":"reply_markup","type":"ReplyMarkup","predicate":"flags.2"}]},{"kind":"class","name":"inputBotInlineResult","type":"InputBotInlineResult","id":2294256409,"comment":"An inline bot result","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"id","type":"string","comment":"ID of result"},{"name":"type","type":"string","comment":"Result type (see bot API docs)"},{"name":"title","type":"string","predicate":"flags.1","comment":"Result title"},{"name":"description","type":"string","predicate":"flags.2","comment":"Result description"},{"name":"url","type":"string","predicate":"flags.3","comment":"URL of result"},{"name":"thumb","type":"InputWebDocument","predicate":"flags.4","comment":"Thumbnail for result"},{"name":"content","type":"InputWebDocument","predicate":"flags.5","comment":"Result contents"},{"name":"send_message","type":"InputBotInlineMessage"}]},{"kind":"class","name":"inputBotInlineResultPhoto","type":"InputBotInlineResult","id":2832753831,"comment":"Photo","arguments":[{"name":"id","type":"string","comment":"Result ID"},{"name":"type","type":"string","comment":"Result type (see bot API docs)"},{"name":"photo","type":"InputPhoto","comment":"Photo to send"},{"name":"send_message","type":"InputBotInlineMessage"}]},{"kind":"class","name":"inputBotInlineResultDocument","type":"InputBotInlineResult","id":4294507972,"comment":"Document (media of any type except for photos)","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"id","type":"string","comment":"Result ID"},{"name":"type","type":"string","comment":"Result type (see bot API docs)"},{"name":"title","type":"string","predicate":"flags.1","comment":"Result title"},{"name":"description","type":"string","predicate":"flags.2","comment":"Result description"},{"name":"document","type":"InputDocument","comment":"Document to send"},{"name":"send_message","type":"InputBotInlineMessage"}]},{"kind":"class","name":"inputBotInlineResultGame","type":"InputBotInlineResult","id":1336154098,"comment":"Game","arguments":[{"name":"id","type":"string","comment":"Result ID"},{"name":"short_name","type":"string"},{"name":"send_message","type":"InputBotInlineMessage"}]},{"kind":"class","name":"botInlineMessageMediaAuto","type":"BotInlineMessage","id":1984755728,"comment":"Send whatever media is attached to the {@link botInlineMediaResult}","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"message","type":"string","comment":"Caption"},{"name":"entities","type":"Vector","predicate":"flags.1","comment":"Message entities for styled text"},{"name":"reply_markup","type":"ReplyMarkup","predicate":"flags.2"}]},{"kind":"class","name":"botInlineMessageText","type":"BotInlineMessage","id":2357159394,"comment":"Send a simple text message","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"no_webpage","type":"true","predicate":"flags.0"},{"name":"message","type":"string","comment":"The message"},{"name":"entities","type":"Vector","predicate":"flags.1","comment":"Message entities for styled text"},{"name":"reply_markup","type":"ReplyMarkup","predicate":"flags.2"}]},{"kind":"class","name":"botInlineMessageMediaGeo","type":"BotInlineMessage","id":85477117,"comment":"Send a geolocation","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"geo","type":"GeoPoint","comment":"Geolocation"},{"name":"heading","type":"int","predicate":"flags.0","comment":"For live locations, a direction in which the location moves, in degrees; 1-360."},{"name":"period","type":"int","predicate":"flags.1","comment":"Validity period"},{"name":"proximity_notification_radius","type":"int","predicate":"flags.3"},{"name":"reply_markup","type":"ReplyMarkup","predicate":"flags.2"}]},{"kind":"class","name":"botInlineMessageMediaVenue","type":"BotInlineMessage","id":2324063644,"comment":"Send a venue","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"geo","type":"GeoPoint","comment":"Geolocation of venue"},{"name":"title","type":"string","comment":"Venue name"},{"name":"address","type":"string","comment":"Address"},{"name":"provider","type":"string","comment":"Venue provider: currently only \"foursquare\" and \"gplaces\" need to be supported"},{"name":"venue_id","type":"string"},{"name":"venue_type","type":"string"},{"name":"reply_markup","type":"ReplyMarkup","predicate":"flags.2"}]},{"kind":"class","name":"botInlineMessageMediaContact","type":"BotInlineMessage","id":416402882,"comment":"Send a contact","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"phone_number","type":"string"},{"name":"first_name","type":"string"},{"name":"last_name","type":"string"},{"name":"vcard","type":"string","comment":"VCard info"},{"name":"reply_markup","type":"ReplyMarkup","predicate":"flags.2"}]},{"kind":"class","name":"botInlineMessageMediaInvoice","type":"BotInlineMessage","id":894081801,"comment":"Send an invoice","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"shipping_address_requested","type":"true","predicate":"flags.1"},{"name":"test","type":"true","predicate":"flags.3","comment":"Test invoice"},{"name":"title","type":"string","comment":"Product name, 1-32 characters"},{"name":"description","type":"string","comment":"Product description, 1-255 characters"},{"name":"photo","type":"WebDocument","predicate":"flags.0","comment":"Product photo"},{"name":"currency","type":"string","comment":"Three-letter ISO 4217 currency code"},{"name":"total_amount","type":"long"},{"name":"reply_markup","type":"ReplyMarkup","predicate":"flags.2"}]},{"kind":"class","name":"botInlineResult","type":"BotInlineResult","id":295067450,"comment":"Generic result","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"id","type":"string","comment":"Result ID"},{"name":"type","type":"string","comment":"Result type (see bot API docs)"},{"name":"title","type":"string","predicate":"flags.1","comment":"Result title"},{"name":"description","type":"string","predicate":"flags.2","comment":"Result description"},{"name":"url","type":"string","predicate":"flags.3","comment":"URL of article or webpage"},{"name":"thumb","type":"WebDocument","predicate":"flags.4","comment":"Thumbnail for the result"},{"name":"content","type":"WebDocument","predicate":"flags.5","comment":"Content of the result"},{"name":"send_message","type":"BotInlineMessage"}]},{"kind":"class","name":"botInlineMediaResult","type":"BotInlineResult","id":400266251,"comment":"Media result","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"id","type":"string","comment":"Result ID"},{"name":"type","type":"string","comment":"Result type (see bot API docs)"},{"name":"photo","type":"Photo","predicate":"flags.0","comment":"If type is photo, the photo to send"},{"name":"document","type":"Document","predicate":"flags.1","comment":"If type is document, the document to send"},{"name":"title","type":"string","predicate":"flags.2","comment":"Result title"},{"name":"description","type":"string","predicate":"flags.3","comment":"Description"},{"name":"send_message","type":"BotInlineMessage"}]},{"kind":"class","name":"messages.botResults","type":"messages.BotResults","id":2491197512,"comment":"Result of a query to an inline bot","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"gallery","type":"true","predicate":"flags.0","comment":"Whether the result is a picture gallery"},{"name":"query_id","type":"long"},{"name":"next_offset","type":"string","predicate":"flags.1"},{"name":"switch_pm","type":"InlineBotSwitchPM","predicate":"flags.2"},{"name":"results","type":"Vector","comment":"The results"},{"name":"cache_time","type":"int"},{"name":"users","type":"Vector","comment":"Users mentioned in the results"}]},{"kind":"class","name":"exportedMessageLink","type":"ExportedMessageLink","id":1571494644,"comment":"Link to a message in a supergroup/channel","arguments":[{"name":"link","type":"string","comment":"URL"},{"name":"html","type":"string","comment":"Embed code"}]},{"kind":"class","name":"messageFwdHeader","type":"MessageFwdHeader","id":1601666510,"comment":"Info about a forwarded message","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"imported","type":"true","predicate":"flags.7","comment":"Whether this message was imported from a foreign chat service, click here for more info »"},{"name":"from_id","type":"Peer","predicate":"flags.0"},{"name":"from_name","type":"string","predicate":"flags.5"},{"name":"date","type":"int","comment":"When was the message originally sent"},{"name":"channel_post","type":"int","predicate":"flags.2"},{"name":"post_author","type":"string","predicate":"flags.3"},{"name":"saved_from_peer","type":"Peer","predicate":"flags.4"},{"name":"saved_from_msg_id","type":"int","predicate":"flags.4"},{"name":"psa_type","type":"string","predicate":"flags.6"}]},{"kind":"class","name":"auth.codeTypeSms","type":"auth.CodeType","id":1923290508,"comment":"Type of verification code that will be sent next if you call the resendCode method: SMS code","arguments":[]},{"kind":"class","name":"auth.codeTypeCall","type":"auth.CodeType","id":1948046307,"comment":"Type of verification code that will be sent next if you call the resendCode method: SMS code","arguments":[]},{"kind":"class","name":"auth.codeTypeFlashCall","type":"auth.CodeType","id":577556219,"comment":"Type of verification code that will be sent next if you call the resendCode method: SMS code","arguments":[]},{"kind":"class","name":"auth.sentCodeTypeApp","type":"auth.SentCodeType","id":1035688326,"comment":"The code was sent through the telegram app","arguments":[{"name":"length","type":"int","comment":"Length of the code in bytes"}]},{"kind":"class","name":"auth.sentCodeTypeSms","type":"auth.SentCodeType","id":3221273506,"comment":"The code was sent via SMS","arguments":[{"name":"length","type":"int","comment":"Length of the code in bytes"}]},{"kind":"class","name":"auth.sentCodeTypeCall","type":"auth.SentCodeType","id":1398007207,"comment":"The code will be sent via a phone call: a synthesized voice will tell the user which verification code to input.","arguments":[{"name":"length","type":"int","comment":"Length of the verification code"}]},{"kind":"class","name":"auth.sentCodeTypeFlashCall","type":"auth.SentCodeType","id":2869151449,"comment":"The code will be sent via a flash phone call, that will be closed immediately. The phone code will then be the phone number itself, just make sure that the phone number matches the specified pattern.","arguments":[{"name":"pattern","type":"string","comment":"pattern to match"}]},{"kind":"class","name":"messages.botCallbackAnswer","type":"messages.BotCallbackAnswer","id":911761060,"comment":"Callback answer sent by the bot in response to a button press","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"alert","type":"true","predicate":"flags.1","comment":"Whether an alert should be shown to the user instead of a toast notification"},{"name":"has_url","type":"true","predicate":"flags.3"},{"name":"native_ui","type":"true","predicate":"flags.4"},{"name":"message","type":"string","predicate":"flags.0","comment":"Alert to show"},{"name":"url","type":"string","predicate":"flags.2","comment":"URL to open"},{"name":"cache_time","type":"int"}]},{"kind":"class","name":"messages.messageEditData","type":"messages.MessageEditData","id":649453030,"comment":"Message edit data for media","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"caption","type":"true","predicate":"flags.0","comment":"Media caption, if the specified media's caption can be edited"}]},{"kind":"class","name":"inputBotInlineMessageID","type":"InputBotInlineMessageID","id":2299280777,"comment":"Represents a sent inline message from the perspective of a bot (legacy constructor)","arguments":[{"name":"dc_id","type":"int"},{"name":"id","type":"long","comment":"ID of message, contains both the (32-bit, legacy) owner ID and the message ID, used only for Bot API backwards compatibility with 32-bit user ID."},{"name":"access_hash","type":"long"}]},{"kind":"class","name":"inputBotInlineMessageID64","type":"InputBotInlineMessageID","id":3067680215,"comment":"Represents a sent inline message from the perspective of a bot","arguments":[{"name":"dc_id","type":"int"},{"name":"owner_id","type":"long"},{"name":"id","type":"int","comment":"ID of message"},{"name":"access_hash","type":"long"}]},{"kind":"class","name":"inlineBotSwitchPM","type":"InlineBotSwitchPM","id":1008755359,"comment":"The bot requested the user to message them in private","arguments":[{"name":"text","type":"string","comment":"Text for the button that switches the user to a private chat with the bot and sends the bot a start message with the parameter start_parameter (can be empty)"},{"name":"start_param","type":"string"}]},{"kind":"class","name":"messages.peerDialogs","type":"messages.PeerDialogs","id":863093588,"comment":"Dialog info of multiple peers","arguments":[{"name":"dialogs","type":"Vector","comment":"Dialog info"},{"name":"messages","type":"Vector","comment":"Messages mentioned in dialog info"},{"name":"chats","type":"Vector","comment":"Chats"},{"name":"users","type":"Vector","comment":"Users"},{"name":"state","type":"updates.State","comment":"Current update state of dialog"}]},{"kind":"class","name":"topPeer","type":"TopPeer","id":3989684315,"comment":"Top peer","arguments":[{"name":"peer","type":"Peer","comment":"Peer"},{"name":"rating","type":"double","comment":"Rating as computed in top peer rating »"}]},{"kind":"class","name":"topPeerCategoryBotsPM","type":"TopPeerCategory","id":2875595611,"comment":"Most used bots","arguments":[]},{"kind":"class","name":"topPeerCategoryBotsInline","type":"TopPeerCategory","id":344356834,"comment":"Most used inline bots","arguments":[]},{"kind":"class","name":"topPeerCategoryCorrespondents","type":"TopPeerCategory","id":104314861,"comment":"Users we've chatted most frequently with","arguments":[]},{"kind":"class","name":"topPeerCategoryGroups","type":"TopPeerCategory","id":3172442442,"comment":"Often-opened groups and supergroups","arguments":[]},{"kind":"class","name":"topPeerCategoryChannels","type":"TopPeerCategory","id":371037736,"comment":"Most frequently visited channels","arguments":[]},{"kind":"class","name":"topPeerCategoryPhoneCalls","type":"TopPeerCategory","id":511092620,"comment":"Most frequently called users","arguments":[]},{"kind":"class","name":"topPeerCategoryForwardUsers","type":"TopPeerCategory","id":2822794409,"comment":"Users to which the users often forwards messages to","arguments":[]},{"kind":"class","name":"topPeerCategoryForwardChats","type":"TopPeerCategory","id":4226728176,"comment":"Chats to which the users often forwards messages to","arguments":[]},{"kind":"class","name":"topPeerCategoryPeers","type":"TopPeerCategoryPeers","id":4219683473,"comment":"Top peer category","arguments":[{"name":"category","type":"TopPeerCategory","comment":"Top peer category of peers"},{"name":"count","type":"int","comment":"Count of peers"},{"name":"peers","type":"Vector","comment":"Peers"}]},{"kind":"class","name":"contacts.topPeersNotModified","type":"contacts.TopPeers","id":3727060725,"comment":"Top peer info hasn't changed","arguments":[]},{"kind":"class","name":"contacts.topPeers","type":"contacts.TopPeers","id":1891070632,"comment":"Top peers","arguments":[{"name":"categories","type":"Vector","comment":"Top peers by top peer category"},{"name":"chats","type":"Vector","comment":"Chats"},{"name":"users","type":"Vector","comment":"Users"}]},{"kind":"class","name":"contacts.topPeersDisabled","type":"contacts.TopPeers","id":3039597469,"comment":"Top peers disabled","arguments":[]},{"kind":"class","name":"draftMessageEmpty","type":"DraftMessage","id":453805082,"comment":"Empty draft","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"date","type":"int","predicate":"flags.0","comment":"When was the draft last updated"}]},{"kind":"class","name":"draftMessage","type":"DraftMessage","id":4253970719,"comment":"Represents a message draft.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"no_webpage","type":"true","predicate":"flags.1"},{"name":"reply_to_msg_id","type":"int","predicate":"flags.0"},{"name":"message","type":"string","comment":"The draft"},{"name":"entities","type":"Vector","predicate":"flags.3","comment":"Message entities for styled text."},{"name":"date","type":"int","comment":"Date of last update of the draft."}]},{"kind":"class","name":"messages.featuredStickersNotModified","type":"messages.FeaturedStickers","id":3336309862,"comment":"Featured stickers haven't changed","arguments":[{"name":"count","type":"int","comment":"Total number of featured stickers"}]},{"kind":"class","name":"messages.featuredStickers","type":"messages.FeaturedStickers","id":2227184400,"comment":"Featured stickersets","arguments":[{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"},{"name":"count","type":"int","comment":"Total number of featured stickers"},{"name":"sets","type":"Vector","comment":"Featured stickersets"},{"name":"unread","type":"Vector","comment":"IDs of new featured stickersets"}]},{"kind":"class","name":"messages.recentStickersNotModified","type":"messages.RecentStickers","id":186120336,"comment":"No new recent sticker was found","arguments":[]},{"kind":"class","name":"messages.recentStickers","type":"messages.RecentStickers","id":2295561302,"comment":"Recently used stickers","arguments":[{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"},{"name":"packs","type":"Vector","comment":"Emojis associated to stickers"},{"name":"stickers","type":"Vector","comment":"Recent stickers"},{"name":"dates","type":"Vector","comment":"When was each sticker last used"}]},{"kind":"class","name":"messages.archivedStickers","type":"messages.ArchivedStickers","id":1338747336,"comment":"Archived stickersets","arguments":[{"name":"count","type":"int","comment":"Number of archived stickers"},{"name":"sets","type":"Vector","comment":"Archived stickersets"}]},{"kind":"class","name":"messages.stickerSetInstallResultSuccess","type":"messages.StickerSetInstallResult","id":946083368,"comment":"The stickerset was installed successfully","arguments":[]},{"kind":"class","name":"messages.stickerSetInstallResultArchive","type":"messages.StickerSetInstallResult","id":904138920,"comment":"The stickerset was installed, but since there are too many stickersets some were archived","arguments":[{"name":"sets","type":"Vector","comment":"Archived stickersets"}]},{"kind":"class","name":"stickerSetCovered","type":"StickerSetCovered","id":1678812626,"comment":"Stickerset, with a specific sticker as preview","arguments":[{"name":"set","type":"StickerSet","comment":"Stickerset"},{"name":"cover","type":"Document","comment":"Preview"}]},{"kind":"class","name":"stickerSetMultiCovered","type":"StickerSetCovered","id":872932635,"comment":"Stickerset, with a specific stickers as preview","arguments":[{"name":"set","type":"StickerSet","comment":"Stickerset"},{"name":"covers","type":"Vector","comment":"Preview stickers"}]},{"kind":"class","name":"maskCoords","type":"MaskCoords","id":2933316530,"comment":"The n position indicates where the mask should be placed:\n\nPosition on a photo where a mask should be placed","arguments":[{"name":"n","type":"int","comment":"Part of the face, relative to which the mask should be placed"},{"name":"x","type":"double","comment":"Shift by X-axis measured in widths of the mask scaled to the face size, from left to right. (For example, -1.0 will place the mask just to the left of the default mask position)"},{"name":"y","type":"double","comment":"Shift by Y-axis measured in widths of the mask scaled to the face size, from left to right. (For example, -1.0 will place the mask just to the left of the default mask position)"},{"name":"zoom","type":"double","comment":"Mask scaling coefficient. (For example, 2.0 means a doubled size)"}]},{"kind":"class","name":"inputStickeredMediaPhoto","type":"InputStickeredMedia","id":1251549527,"comment":"A photo with stickers attached","arguments":[{"name":"id","type":"InputPhoto","comment":"The photo"}]},{"kind":"class","name":"inputStickeredMediaDocument","type":"InputStickeredMedia","id":70813275,"comment":"A document with stickers attached","arguments":[{"name":"id","type":"InputDocument","comment":"The document"}]},{"kind":"class","name":"game","type":"Game","id":3187238203,"comment":"Indicates an already sent game","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"id","type":"long","comment":"ID of the game"},{"name":"access_hash","type":"long"},{"name":"short_name","type":"string"},{"name":"title","type":"string","comment":"Title of the game"},{"name":"description","type":"string","comment":"Game description"},{"name":"photo","type":"Photo","comment":"Game preview"},{"name":"document","type":"Document","predicate":"flags.0","comment":"Optional attached document"}]},{"kind":"class","name":"inputGameID","type":"InputGame","id":53231223,"comment":"Indicates an already sent game","arguments":[{"name":"id","type":"long","comment":"game ID from Game constructor"},{"name":"access_hash","type":"long"}]},{"kind":"class","name":"inputGameShortName","type":"InputGame","id":3274827786,"comment":"Game by short name","arguments":[{"name":"bot_id","type":"InputUser"},{"name":"short_name","type":"string"}]},{"kind":"class","name":"highScore","type":"HighScore","id":1940093419,"comment":"Game highscore","arguments":[{"name":"pos","type":"int","comment":"Position in highscore list"},{"name":"user_id","type":"int53"},{"name":"score","type":"int","comment":"Score"}]},{"kind":"class","name":"messages.highScores","type":"messages.HighScores","id":2587622809,"comment":"Highscores in a game","arguments":[{"name":"scores","type":"Vector","comment":"Highscores"},{"name":"users","type":"Vector","comment":"Users, associated to the highscores"}]},{"kind":"class","name":"textEmpty","type":"RichText","id":3695018575,"comment":"Empty rich text element","arguments":[]},{"kind":"class","name":"textPlain","type":"RichText","id":1950782688,"comment":"Plain text","arguments":[{"name":"text","type":"string","comment":"Text"}]},{"kind":"class","name":"textBold","type":"RichText","id":1730456516,"comment":"Bold text","arguments":[{"name":"text","type":"RichText","comment":"Text"}]},{"kind":"class","name":"textItalic","type":"RichText","id":3641877916,"comment":"Italic text","arguments":[{"name":"text","type":"RichText","comment":"Text"}]},{"kind":"class","name":"textUnderline","type":"RichText","id":3240501956,"comment":"Underlined text","arguments":[{"name":"text","type":"RichText","comment":"Text"}]},{"kind":"class","name":"textStrike","type":"RichText","id":2616769429,"comment":"Strikethrough text","arguments":[{"name":"text","type":"RichText","comment":"Text"}]},{"kind":"class","name":"textFixed","type":"RichText","id":1816074681,"comment":"fixed-width rich text","arguments":[{"name":"text","type":"RichText","comment":"Text"}]},{"kind":"class","name":"textUrl","type":"RichText","id":1009288385,"comment":"Link","arguments":[{"name":"text","type":"RichText","comment":"Text of link"},{"name":"url","type":"string","comment":"Webpage HTTP URL"},{"name":"webpage_id","type":"long"}]},{"kind":"class","name":"textEmail","type":"RichText","id":3730443734,"comment":"Rich text email link","arguments":[{"name":"text","type":"RichText","comment":"Link text"},{"name":"email","type":"string","comment":"Email address"}]},{"kind":"class","name":"textConcat","type":"RichText","id":2120376535,"comment":"Concatenation of rich texts","arguments":[{"name":"texts","type":"Vector","comment":"Concatenated rich texts"}]},{"kind":"class","name":"textSubscript","type":"RichText","id":3983181060,"comment":"Subscript text","arguments":[{"name":"text","type":"RichText","comment":"Text"}]},{"kind":"class","name":"textSuperscript","type":"RichText","id":3355139585,"comment":"Superscript text","arguments":[{"name":"text","type":"RichText","comment":"Text"}]},{"kind":"class","name":"textMarked","type":"RichText","id":55281185,"comment":"Highlighted text","arguments":[{"name":"text","type":"RichText","comment":"Text"}]},{"kind":"class","name":"textPhone","type":"RichText","id":483104362,"comment":"Rich text linked to a phone number","arguments":[{"name":"text","type":"RichText","comment":"Text"},{"name":"phone","type":"string","comment":"Phone number"}]},{"kind":"class","name":"textImage","type":"RichText","id":136105807,"comment":"Inline image","arguments":[{"name":"document_id","type":"long"},{"name":"w","type":"int","comment":"Width"},{"name":"h","type":"int","comment":"Height"}]},{"kind":"class","name":"textAnchor","type":"RichText","id":894777186,"comment":"Text linking to another section of the page","arguments":[{"name":"text","type":"RichText","comment":"Text"},{"name":"name","type":"string","comment":"Section name"}]},{"kind":"class","name":"pageBlockUnsupported","type":"PageBlock","id":324435594,"comment":"Unsupported IV element","arguments":[]},{"kind":"class","name":"pageBlockTitle","type":"PageBlock","id":1890305021,"comment":"Title","arguments":[{"name":"text","type":"RichText","comment":"Title"}]},{"kind":"class","name":"pageBlockSubtitle","type":"PageBlock","id":2415565343,"comment":"Subtitle","arguments":[{"name":"text","type":"RichText","comment":"Text"}]},{"kind":"class","name":"pageBlockAuthorDate","type":"PageBlock","id":3132089824,"comment":"Author and date of creation of article","arguments":[{"name":"author","type":"RichText","comment":"Author name"},{"name":"published_date","type":"int"}]},{"kind":"class","name":"pageBlockHeader","type":"PageBlock","id":3218105580,"comment":"Page header","arguments":[{"name":"text","type":"RichText","comment":"Contents"}]},{"kind":"class","name":"pageBlockSubheader","type":"PageBlock","id":4046173921,"comment":"Subheader","arguments":[{"name":"text","type":"RichText","comment":"Subheader"}]},{"kind":"class","name":"pageBlockParagraph","type":"PageBlock","id":1182402406,"comment":"A paragraph","arguments":[{"name":"text","type":"RichText","comment":"Text"}]},{"kind":"class","name":"pageBlockPreformatted","type":"PageBlock","id":3228621118,"comment":"Preformatted (
 text)","arguments":[{"name":"text","type":"RichText","comment":"Text"},{"name":"language","type":"string","comment":"Programming language of preformatted text"}]},{"kind":"class","name":"pageBlockFooter","type":"PageBlock","id":1216809369,"comment":"Page footer","arguments":[{"name":"text","type":"RichText","comment":"Contents"}]},{"kind":"class","name":"pageBlockDivider","type":"PageBlock","id":3676352904,"comment":"An empty block separating a page","arguments":[]},{"kind":"class","name":"pageBlockAnchor","type":"PageBlock","id":3456972720,"comment":"Link to section within the page itself (like anchor)","arguments":[{"name":"name","type":"string","comment":"Name of target section"}]},{"kind":"class","name":"pageBlockList","type":"PageBlock","id":3840442385,"comment":"Unordered list of IV blocks","arguments":[{"name":"items","type":"Vector","comment":"List of blocks in an IV page"}]},{"kind":"class","name":"pageBlockBlockquote","type":"PageBlock","id":641563686,"comment":"Quote (equivalent to the HTML 
)","arguments":[{"name":"text","type":"RichText","comment":"Quote contents"},{"name":"caption","type":"RichText","comment":"Caption"}]},{"kind":"class","name":"pageBlockPullquote","type":"PageBlock","id":1329878739,"comment":"Pullquote","arguments":[{"name":"text","type":"RichText","comment":"Text"},{"name":"caption","type":"RichText","comment":"Caption"}]},{"kind":"class","name":"pageBlockPhoto","type":"PageBlock","id":391759200,"comment":"A photo","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"photo_id","type":"long"},{"name":"caption","type":"PageCaption","comment":"Caption"},{"name":"url","type":"string","predicate":"flags.0","comment":"HTTP URL of page the photo leads to when clicked"},{"name":"webpage_id","type":"long","predicate":"flags.0"}]},{"kind":"class","name":"pageBlockVideo","type":"PageBlock","id":2089805750,"comment":"Video","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"autoplay","type":"true","predicate":"flags.0","comment":"Whether the video is set to autoplay"},{"name":"loop","type":"true","predicate":"flags.1","comment":"Whether the video is set to loop"},{"name":"video_id","type":"long"},{"name":"caption","type":"PageCaption","comment":"Caption"}]},{"kind":"class","name":"pageBlockCover","type":"PageBlock","id":972174080,"comment":"A page cover","arguments":[{"name":"cover","type":"PageBlock","comment":"Cover"}]},{"kind":"class","name":"pageBlockEmbed","type":"PageBlock","id":2826014149,"comment":"An embedded webpage","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"full_width","type":"true","predicate":"flags.0"},{"name":"allow_scrolling","type":"true","predicate":"flags.3"},{"name":"url","type":"string","predicate":"flags.1","comment":"Web page URL, if available"},{"name":"html","type":"string","predicate":"flags.2","comment":"HTML-markup of the embedded page"},{"name":"poster_photo_id","type":"long","predicate":"flags.4"},{"name":"w","type":"int","predicate":"flags.5","comment":"Block width, if known"},{"name":"h","type":"int","predicate":"flags.5","comment":"Block height, if known"},{"name":"caption","type":"PageCaption","comment":"Caption"}]},{"kind":"class","name":"pageBlockEmbedPost","type":"PageBlock","id":4065961995,"comment":"An embedded post","arguments":[{"name":"url","type":"string","comment":"Web page URL"},{"name":"webpage_id","type":"long"},{"name":"author_photo_id","type":"long"},{"name":"author","type":"string","comment":"Author name"},{"name":"date","type":"int","comment":"Creation date"},{"name":"blocks","type":"Vector","comment":"Post contents"},{"name":"caption","type":"PageCaption","comment":"Caption"}]},{"kind":"class","name":"pageBlockCollage","type":"PageBlock","id":1705048653,"comment":"Collage of media","arguments":[{"name":"items","type":"Vector","comment":"Media elements"},{"name":"caption","type":"PageCaption","comment":"Caption"}]},{"kind":"class","name":"pageBlockSlideshow","type":"PageBlock","id":52401552,"comment":"Slideshow","arguments":[{"name":"items","type":"Vector","comment":"Slideshow items"},{"name":"caption","type":"PageCaption","comment":"Caption"}]},{"kind":"class","name":"pageBlockChannel","type":"PageBlock","id":4011282869,"comment":"Reference to a telegram channel","arguments":[{"name":"channel","type":"Chat","comment":"The channel/supergroup/chat"}]},{"kind":"class","name":"pageBlockAudio","type":"PageBlock","id":2151899626,"comment":"Audio","arguments":[{"name":"audio_id","type":"long"},{"name":"caption","type":"PageCaption","comment":"Audio caption"}]},{"kind":"class","name":"pageBlockKicker","type":"PageBlock","id":504660880,"comment":"Kicker","arguments":[{"name":"text","type":"RichText","comment":"Contents"}]},{"kind":"class","name":"pageBlockTable","type":"PageBlock","id":3209554562,"comment":"Table","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"bordered","type":"true","predicate":"flags.0","comment":"Does the table have a visible border?"},{"name":"striped","type":"true","predicate":"flags.1","comment":"Is the table striped?"},{"name":"title","type":"RichText","comment":"Title"},{"name":"rows","type":"Vector","comment":"Table rows"}]},{"kind":"class","name":"pageBlockOrderedList","type":"PageBlock","id":2592793057,"comment":"Ordered list of IV blocks","arguments":[{"name":"items","type":"Vector","comment":"List items"}]},{"kind":"class","name":"pageBlockDetails","type":"PageBlock","id":1987480557,"comment":"A collapsible details block","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"open","type":"true","predicate":"flags.0","comment":"Whether the block is open by default"},{"name":"blocks","type":"Vector","comment":"Block contents"},{"name":"title","type":"RichText","comment":"Always visible heading for the block"}]},{"kind":"class","name":"pageBlockRelatedArticles","type":"PageBlock","id":370236054,"comment":"Related articles","arguments":[{"name":"title","type":"RichText","comment":"Title"},{"name":"articles","type":"Vector","comment":"Related articles"}]},{"kind":"class","name":"pageBlockMap","type":"PageBlock","id":2756656886,"comment":"A map","arguments":[{"name":"geo","type":"GeoPoint","comment":"Location of the map center"},{"name":"zoom","type":"int","comment":"Map zoom level; 13-20"},{"name":"w","type":"int","comment":"Map width in pixels before applying scale; 16-102"},{"name":"h","type":"int","comment":"Map height in pixels before applying scale; 16-1024"},{"name":"caption","type":"PageCaption","comment":"Caption"}]},{"kind":"class","name":"phoneCallDiscardReasonMissed","type":"PhoneCallDiscardReason","id":2246320897,"comment":"The phone call was missed","arguments":[]},{"kind":"class","name":"phoneCallDiscardReasonDisconnect","type":"PhoneCallDiscardReason","id":3767910816,"comment":"The phone call was disconnected","arguments":[]},{"kind":"class","name":"phoneCallDiscardReasonHangup","type":"PhoneCallDiscardReason","id":1471006352,"comment":"The phone call was ended normally","arguments":[]},{"kind":"class","name":"phoneCallDiscardReasonBusy","type":"PhoneCallDiscardReason","id":4210550985,"comment":"The phone call was discared because the user is busy in another call","arguments":[]},{"kind":"class","name":"dataJSON","type":"DataJSON","id":2104790276,"comment":"Represents a json-encoded object","arguments":[{"name":"data","type":"string","comment":"JSON-encoded object"}]},{"kind":"class","name":"labeledPrice","type":"LabeledPrice","id":3408489464,"comment":"This object represents a portion of the price for goods or services.","arguments":[{"name":"label","type":"string","comment":"Portion label"},{"name":"amount","type":"long","comment":"Price of the product in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies)."}]},{"kind":"class","name":"invoice","type":"Invoice","id":215516896,"comment":"Invoice","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"test","type":"true","predicate":"flags.0","comment":"Test invoice"},{"name":"name_requested","type":"true","predicate":"flags.1"},{"name":"phone_requested","type":"true","predicate":"flags.2"},{"name":"email_requested","type":"true","predicate":"flags.3"},{"name":"shipping_address_requested","type":"true","predicate":"flags.4"},{"name":"flexible","type":"true","predicate":"flags.5","comment":"Set this flag if the final price depends on the shipping method"},{"name":"phone_to_provider","type":"true","predicate":"flags.6"},{"name":"email_to_provider","type":"true","predicate":"flags.7"},{"name":"currency","type":"string","comment":"Three-letter ISO 4217 currency code"},{"name":"prices","type":"Vector","comment":"Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)"},{"name":"max_tip_amount","type":"long","predicate":"flags.8"},{"name":"suggested_tip_amounts","type":"Vector","predicate":"flags.8"}]},{"kind":"class","name":"paymentCharge","type":"PaymentCharge","id":3926049406,"comment":"Payment identifier","arguments":[{"name":"id","type":"string","comment":"Telegram payment identifier"},{"name":"provider_charge_id","type":"string"}]},{"kind":"class","name":"postAddress","type":"PostAddress","id":512535275,"comment":"Shipping address","arguments":[{"name":"street_line1","type":"string"},{"name":"street_line2","type":"string"},{"name":"city","type":"string","comment":"City"},{"name":"state","type":"string","comment":"State, if applicable (empty otherwise)"},{"name":"country_iso2","type":"string"},{"name":"post_code","type":"string"}]},{"kind":"class","name":"paymentRequestedInfo","type":"PaymentRequestedInfo","id":2426158996,"comment":"Order info provided by the user","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"name","type":"string","predicate":"flags.0","comment":"User's full name"},{"name":"phone","type":"string","predicate":"flags.1","comment":"User's phone number"},{"name":"email","type":"string","predicate":"flags.2","comment":"User's email address"},{"name":"shipping_address","type":"PostAddress","predicate":"flags.3"}]},{"kind":"class","name":"paymentSavedCredentialsCard","type":"PaymentSavedCredentials","id":3452074527,"comment":"Saved credit card","arguments":[{"name":"id","type":"string","comment":"Card ID"},{"name":"title","type":"string","comment":"Title"}]},{"kind":"class","name":"webDocument","type":"WebDocument","id":475467473,"comment":"Remote document","arguments":[{"name":"url","type":"string","comment":"Document URL"},{"name":"access_hash","type":"long"},{"name":"size","type":"int","comment":"File size"},{"name":"mime_type","type":"string"},{"name":"attributes","type":"Vector","comment":"Attributes for media types"}]},{"kind":"class","name":"webDocumentNoProxy","type":"WebDocument","id":4190682310,"comment":"Remote document that can be downloaded without proxying through telegram","arguments":[{"name":"url","type":"string","comment":"Document URL"},{"name":"size","type":"int","comment":"File size"},{"name":"mime_type","type":"string"},{"name":"attributes","type":"Vector","comment":"Attributes for media types"}]},{"kind":"class","name":"inputWebDocument","type":"InputWebDocument","id":2616017741,"comment":"The document","arguments":[{"name":"url","type":"string","comment":"Remote document URL to be downloaded using the appropriate method"},{"name":"size","type":"int","comment":"Remote file size"},{"name":"mime_type","type":"string"},{"name":"attributes","type":"Vector","comment":"Attributes for media types"}]},{"kind":"class","name":"inputWebFileLocation","type":"InputWebFileLocation","id":3258570374,"comment":"Location of a remote HTTP(s) file","arguments":[{"name":"url","type":"string","comment":"HTTP URL of file"},{"name":"access_hash","type":"long"}]},{"kind":"class","name":"inputWebFileGeoPointLocation","type":"InputWebFileLocation","id":2669814217,"comment":"Geolocation","arguments":[{"name":"geo_point","type":"InputGeoPoint"},{"name":"access_hash","type":"long"},{"name":"w","type":"int","comment":"Map width in pixels before applying scale; 16-1024"},{"name":"h","type":"int","comment":"Map height in pixels before applying scale; 16-1024"},{"name":"zoom","type":"int","comment":"Map zoom level; 13-20"},{"name":"scale","type":"int","comment":"Map scale; 1-3"}]},{"kind":"class","name":"upload.webFile","type":"upload.WebFile","id":568808380,"comment":"Represents a chunk of an HTTP webfile downloaded through telegram's secure MTProto servers","arguments":[{"name":"size","type":"int","comment":"File size"},{"name":"mime_type","type":"string"},{"name":"file_type","type":"storage.FileType"},{"name":"mtime","type":"int","comment":"Modified time"},{"name":"bytes","type":"bytes","comment":"Data"}]},{"kind":"class","name":"payments.paymentForm","type":"payments.PaymentForm","id":378828315,"comment":"Payment form","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"can_save_credentials","type":"true","predicate":"flags.2"},{"name":"password_missing","type":"true","predicate":"flags.3"},{"name":"form_id","type":"long"},{"name":"bot_id","type":"int53"},{"name":"invoice","type":"Invoice","comment":"Invoice"},{"name":"provider_id","type":"long"},{"name":"url","type":"string","comment":"Payment form URL"},{"name":"native_provider","type":"string","predicate":"flags.4"},{"name":"native_params","type":"DataJSON","predicate":"flags.4"},{"name":"saved_info","type":"PaymentRequestedInfo","predicate":"flags.0"},{"name":"saved_credentials","type":"PaymentSavedCredentials","predicate":"flags.1"},{"name":"users","type":"Vector","comment":"Users"}]},{"kind":"class","name":"payments.validatedRequestedInfo","type":"payments.ValidatedRequestedInfo","id":3510966403,"comment":"Validated user-provided info","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"id","type":"string","predicate":"flags.0","comment":"ID"},{"name":"shipping_options","type":"Vector","predicate":"flags.1"}]},{"kind":"class","name":"payments.paymentResult","type":"payments.PaymentResult","id":1314881805,"comment":"Payment result","arguments":[{"name":"updates","type":"Updates","comment":"Info about the payment"}]},{"kind":"class","name":"payments.paymentVerificationNeeded","type":"payments.PaymentResult","id":3628142905,"comment":"Payment was not successful, additional verification is needed","arguments":[{"name":"url","type":"string","comment":"URL for additional payment credentials verification"}]},{"kind":"class","name":"payments.paymentReceipt","type":"payments.PaymentReceipt","id":1891958275,"comment":"Receipt","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"date","type":"int","comment":"Date of generation"},{"name":"bot_id","type":"int53"},{"name":"provider_id","type":"long"},{"name":"title","type":"string","comment":"Title"},{"name":"description","type":"string","comment":"Description"},{"name":"photo","type":"WebDocument","predicate":"flags.2","comment":"Photo"},{"name":"invoice","type":"Invoice","comment":"Invoice"},{"name":"info","type":"PaymentRequestedInfo","predicate":"flags.0","comment":"Info"},{"name":"shipping","type":"ShippingOption","predicate":"flags.1","comment":"Selected shipping option"},{"name":"tip_amount","type":"long","predicate":"flags.3"},{"name":"currency","type":"string","comment":"Three-letter ISO 4217 currency code"},{"name":"total_amount","type":"long"},{"name":"credentials_title","type":"string"},{"name":"users","type":"Vector","comment":"Users"}]},{"kind":"class","name":"payments.savedInfo","type":"payments.SavedInfo","id":4220511292,"comment":"Saved server-side order information","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"has_saved_credentials","type":"true","predicate":"flags.1"},{"name":"saved_info","type":"PaymentRequestedInfo","predicate":"flags.0"}]},{"kind":"class","name":"inputPaymentCredentialsSaved","type":"InputPaymentCredentials","id":3238965967,"comment":"Saved payment credentials","arguments":[{"name":"id","type":"string","comment":"Credential ID"},{"name":"tmp_password","type":"bytes"}]},{"kind":"class","name":"inputPaymentCredentials","type":"InputPaymentCredentials","id":873977640,"comment":"Payment credentials","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"save","type":"true","predicate":"flags.0","comment":"Save payment credential for future use"},{"name":"data","type":"DataJSON","comment":"Payment credentials"}]},{"kind":"class","name":"inputPaymentCredentialsApplePay","type":"InputPaymentCredentials","id":178373535,"comment":"Apple pay payment credentials","arguments":[{"name":"payment_data","type":"DataJSON"}]},{"kind":"class","name":"inputPaymentCredentialsGooglePay","type":"InputPaymentCredentials","id":2328045569,"comment":"Google Pay payment credentials","arguments":[{"name":"payment_token","type":"DataJSON"}]},{"kind":"class","name":"account.tmpPassword","type":"account.TmpPassword","id":3680828724,"comment":"Temporary payment password","arguments":[{"name":"tmp_password","type":"bytes"},{"name":"valid_until","type":"int"}]},{"kind":"class","name":"shippingOption","type":"ShippingOption","id":3055631583,"comment":"Shipping option","arguments":[{"name":"id","type":"string","comment":"Option ID"},{"name":"title","type":"string","comment":"Title"},{"name":"prices","type":"Vector","comment":"List of price portions"}]},{"kind":"class","name":"inputStickerSetItem","type":"InputStickerSetItem","id":4288717974,"comment":"Sticker in a stickerset","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"document","type":"InputDocument","comment":"The sticker"},{"name":"emoji","type":"string","comment":"Associated emoji"},{"name":"mask_coords","type":"MaskCoords","predicate":"flags.0"}]},{"kind":"class","name":"inputPhoneCall","type":"InputPhoneCall","id":506920429,"comment":"Phone call","arguments":[{"name":"id","type":"long","comment":"Call ID"},{"name":"access_hash","type":"long"}]},{"kind":"class","name":"phoneCallEmpty","type":"PhoneCall","id":1399245077,"comment":"Empty constructor","arguments":[{"name":"id","type":"long","comment":"Call ID"}]},{"kind":"class","name":"phoneCallWaiting","type":"PhoneCall","id":3307368215,"comment":"Incoming phone call","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"video","type":"true","predicate":"flags.6","comment":"Is this a video call"},{"name":"id","type":"long","comment":"Call ID"},{"name":"access_hash","type":"long"},{"name":"date","type":"int","comment":"Date"},{"name":"admin_id","type":"int53"},{"name":"participant_id","type":"int53"},{"name":"protocol","type":"PhoneCallProtocol","comment":"Phone call protocol info"},{"name":"receive_date","type":"int","predicate":"flags.0"}]},{"kind":"class","name":"phoneCallRequested","type":"PhoneCall","id":347139340,"comment":"Requested phone call","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"video","type":"true","predicate":"flags.6","comment":"Whether this is a video call"},{"name":"id","type":"long","comment":"Phone call ID"},{"name":"access_hash","type":"long"},{"name":"date","type":"int","comment":"When was the phone call created"},{"name":"admin_id","type":"int53"},{"name":"participant_id","type":"int53"},{"name":"g_a_hash","type":"bytes"},{"name":"protocol","type":"PhoneCallProtocol","comment":"Call protocol info to be passed to libtgvoip"}]},{"kind":"class","name":"phoneCallAccepted","type":"PhoneCall","id":912311057,"comment":"An accepted phone call","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"video","type":"true","predicate":"flags.6","comment":"Whether this is a video call"},{"name":"id","type":"long","comment":"ID of accepted phone call"},{"name":"access_hash","type":"long"},{"name":"date","type":"int","comment":"When was the call accepted"},{"name":"admin_id","type":"int53"},{"name":"participant_id","type":"int53"},{"name":"g_b","type":"bytes"},{"name":"protocol","type":"PhoneCallProtocol","comment":"Protocol to use for phone call"}]},{"kind":"class","name":"phoneCall","type":"PhoneCall","id":2524937319,"comment":"Phone call","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"p2p_allowed","type":"true","predicate":"flags.5"},{"name":"video","type":"true","predicate":"flags.6","comment":"Whether this is a video call"},{"name":"id","type":"long","comment":"Call ID"},{"name":"access_hash","type":"long"},{"name":"date","type":"int","comment":"Date of creation of the call"},{"name":"admin_id","type":"int53"},{"name":"participant_id","type":"int53"},{"name":"g_a_or_b","type":"bytes"},{"name":"key_fingerprint","type":"long"},{"name":"protocol","type":"PhoneCallProtocol","comment":"Call protocol info to be passed to libtgvoip"},{"name":"connections","type":"Vector","comment":"List of endpoints the user can connect to to exchange call data"},{"name":"start_date","type":"int"}]},{"kind":"class","name":"phoneCallDiscarded","type":"PhoneCall","id":1355435489,"comment":"Indicates a discarded phone call","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"need_rating","type":"true","predicate":"flags.2"},{"name":"need_debug","type":"true","predicate":"flags.3"},{"name":"video","type":"true","predicate":"flags.6","comment":"Whether the call was a video call"},{"name":"id","type":"long","comment":"Call ID"},{"name":"reason","type":"PhoneCallDiscardReason","predicate":"flags.0","comment":"Why was the phone call discarded"},{"name":"duration","type":"int","predicate":"flags.1","comment":"Duration of the phone call in seconds"}]},{"kind":"class","name":"phoneConnection","type":"PhoneConnection","id":2639009728,"comment":"Identifies an endpoint that can be used to connect to the other user in a phone call","arguments":[{"name":"id","type":"long","comment":"Endpoint ID"},{"name":"ip","type":"string","comment":"IP address of endpoint"},{"name":"ipv6","type":"string","comment":"IPv6 address of endpoint"},{"name":"port","type":"int","comment":"Port ID"},{"name":"peer_tag","type":"bytes"}]},{"kind":"class","name":"phoneConnectionWebrtc","type":"PhoneConnection","id":1667228533,"comment":"WebRTC connection parameters","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"turn","type":"true","predicate":"flags.0","comment":"Whether this is a TURN endpoint"},{"name":"stun","type":"true","predicate":"flags.1","comment":"Whether this is a STUN endpoint"},{"name":"id","type":"long","comment":"Endpoint ID"},{"name":"ip","type":"string","comment":"IP address"},{"name":"ipv6","type":"string","comment":"IPv6 address"},{"name":"port","type":"int","comment":"Port"},{"name":"username","type":"string","comment":"Username"},{"name":"password","type":"string","comment":"Password"}]},{"kind":"class","name":"phoneCallProtocol","type":"PhoneCallProtocol","id":4236742600,"comment":"Protocol info for libtgvoip","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"udp_p2p","type":"true","predicate":"flags.0"},{"name":"udp_reflector","type":"true","predicate":"flags.1"},{"name":"min_layer","type":"int"},{"name":"max_layer","type":"int"},{"name":"library_versions","type":"Vector"}]},{"kind":"class","name":"phone.phoneCall","type":"phone.PhoneCall","id":3968000320,"comment":"A VoIP phone call","arguments":[{"name":"phone_call","type":"PhoneCall"},{"name":"users","type":"Vector","comment":"VoIP phone call participants"}]},{"kind":"class","name":"upload.cdnFileReuploadNeeded","type":"upload.CdnFile","id":4004045934,"comment":"The file was cleared from the temporary RAM cache of the CDN and has to be reuploaded.","arguments":[{"name":"request_token","type":"bytes"}]},{"kind":"class","name":"upload.cdnFile","type":"upload.CdnFile","id":2845821519,"comment":"Represent a chunk of a CDN file.","arguments":[{"name":"bytes","type":"bytes","comment":"The data"}]},{"kind":"class","name":"cdnPublicKey","type":"CdnPublicKey","id":3380800186,"comment":"Public key to use only during handshakes to CDN DCs.","arguments":[{"name":"dc_id","type":"int"},{"name":"public_key","type":"string"}]},{"kind":"class","name":"cdnConfig","type":"CdnConfig","id":1462101002,"comment":"Configuration for CDN file downloads.","arguments":[{"name":"public_keys","type":"Vector"}]},{"kind":"class","name":"langPackString","type":"LangPackString","id":3402727926,"comment":"Translated localization string","arguments":[{"name":"key","type":"string","comment":"Language key"},{"name":"value","type":"string","comment":"Value"}]},{"kind":"class","name":"langPackStringPluralized","type":"LangPackString","id":1816636575,"comment":"A language pack string which has different forms based on the number of some object it mentions. See https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html for more info","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"key","type":"string","comment":"Localization key"},{"name":"zero_value","type":"string","predicate":"flags.0"},{"name":"one_value","type":"string","predicate":"flags.1"},{"name":"two_value","type":"string","predicate":"flags.2"},{"name":"few_value","type":"string","predicate":"flags.3"},{"name":"many_value","type":"string","predicate":"flags.4"},{"name":"other_value","type":"string"}]},{"kind":"class","name":"langPackStringDeleted","type":"LangPackString","id":695856818,"comment":"Deleted localization string","arguments":[{"name":"key","type":"string","comment":"Localization key"}]},{"kind":"class","name":"langPackDifference","type":"LangPackDifference","id":4085629430,"comment":"Changes to the app's localization pack","arguments":[{"name":"lang_code","type":"string"},{"name":"from_version","type":"int"},{"name":"version","type":"int","comment":"New version number"},{"name":"strings","type":"Vector","comment":"Localized strings"}]},{"kind":"class","name":"langPackLanguage","type":"LangPackLanguage","id":4006239459,"comment":"Identifies a localization pack","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"official","type":"true","predicate":"flags.0","comment":"Whether the language pack is official"},{"name":"rtl","type":"true","predicate":"flags.2","comment":"Is this a localization pack for an RTL language"},{"name":"beta","type":"true","predicate":"flags.3","comment":"Is this a beta localization pack?"},{"name":"name","type":"string","comment":"Language name"},{"name":"native_name","type":"string"},{"name":"lang_code","type":"string"},{"name":"base_lang_code","type":"string","predicate":"flags.1"},{"name":"plural_code","type":"string"},{"name":"strings_count","type":"int"},{"name":"translated_count","type":"int"},{"name":"translations_url","type":"string"}]},{"kind":"class","name":"channelAdminLogEventActionChangeTitle","type":"ChannelAdminLogEventAction","id":3873421349,"comment":"Channel/supergroup title was changed","arguments":[{"name":"prev_value","type":"string"},{"name":"new_value","type":"string"}]},{"kind":"class","name":"channelAdminLogEventActionChangeAbout","type":"ChannelAdminLogEventAction","id":1427671598,"comment":"The description was changed","arguments":[{"name":"prev_value","type":"string"},{"name":"new_value","type":"string"}]},{"kind":"class","name":"channelAdminLogEventActionChangeUsername","type":"ChannelAdminLogEventAction","id":1783299128,"comment":"Channel/supergroup username was changed","arguments":[{"name":"prev_value","type":"string"},{"name":"new_value","type":"string"}]},{"kind":"class","name":"channelAdminLogEventActionChangePhoto","type":"ChannelAdminLogEventAction","id":1129042607,"comment":"The channel/supergroup's picture was changed","arguments":[{"name":"prev_photo","type":"Photo"},{"name":"new_photo","type":"Photo"}]},{"kind":"class","name":"channelAdminLogEventActionToggleInvites","type":"ChannelAdminLogEventAction","id":460916654,"comment":"Invites were enabled/disabled","arguments":[{"name":"new_value","type":"Bool"}]},{"kind":"class","name":"channelAdminLogEventActionToggleSignatures","type":"ChannelAdminLogEventAction","id":648939889,"comment":"Channel signatures were enabled/disabled","arguments":[{"name":"new_value","type":"Bool"}]},{"kind":"class","name":"channelAdminLogEventActionUpdatePinned","type":"ChannelAdminLogEventAction","id":3924306968,"comment":"A message was pinned","arguments":[{"name":"message","type":"Message","comment":"The message that was pinned"}]},{"kind":"class","name":"channelAdminLogEventActionEditMessage","type":"ChannelAdminLogEventAction","id":1889215493,"comment":"A message was edited","arguments":[{"name":"prev_message","type":"Message"},{"name":"new_message","type":"Message"}]},{"kind":"class","name":"channelAdminLogEventActionDeleteMessage","type":"ChannelAdminLogEventAction","id":1121994683,"comment":"A message was deleted","arguments":[{"name":"message","type":"Message","comment":"The message that was deleted"}]},{"kind":"class","name":"channelAdminLogEventActionParticipantJoin","type":"ChannelAdminLogEventAction","id":405815507,"comment":"A user has joined the group (in the case of big groups, info of the user that has joined isn't shown)","arguments":[]},{"kind":"class","name":"channelAdminLogEventActionParticipantLeave","type":"ChannelAdminLogEventAction","id":4170676210,"comment":"A user left the channel/supergroup (in the case of big groups, info of the user that has joined isn't shown)","arguments":[]},{"kind":"class","name":"channelAdminLogEventActionParticipantInvite","type":"ChannelAdminLogEventAction","id":3810276568,"comment":"A user was invited to the group","arguments":[{"name":"participant","type":"ChannelParticipant","comment":"The user that was invited"}]},{"kind":"class","name":"channelAdminLogEventActionParticipantToggleBan","type":"ChannelAdminLogEventAction","id":3872931198,"comment":"The banned rights of a user were changed","arguments":[{"name":"prev_participant","type":"ChannelParticipant"},{"name":"new_participant","type":"ChannelParticipant"}]},{"kind":"class","name":"channelAdminLogEventActionParticipantToggleAdmin","type":"ChannelAdminLogEventAction","id":3580323600,"comment":"The admin rights of a user were changed","arguments":[{"name":"prev_participant","type":"ChannelParticipant"},{"name":"new_participant","type":"ChannelParticipant"}]},{"kind":"class","name":"channelAdminLogEventActionChangeStickerSet","type":"ChannelAdminLogEventAction","id":2982398631,"comment":"The supergroup's stickerset was changed","arguments":[{"name":"prev_stickerset","type":"InputStickerSet"},{"name":"new_stickerset","type":"InputStickerSet"}]},{"kind":"class","name":"channelAdminLogEventActionTogglePreHistoryHidden","type":"ChannelAdminLogEventAction","id":1599903217,"comment":"The hidden prehistory setting was {@link channels.togglePreHistoryHidden}","arguments":[{"name":"new_value","type":"Bool"}]},{"kind":"class","name":"channelAdminLogEventActionDefaultBannedRights","type":"ChannelAdminLogEventAction","id":771095562,"comment":"The default banned rights were modified","arguments":[{"name":"prev_banned_rights","type":"ChatBannedRights"},{"name":"new_banned_rights","type":"ChatBannedRights"}]},{"kind":"class","name":"channelAdminLogEventActionStopPoll","type":"ChannelAdminLogEventAction","id":2399639107,"comment":"A poll was stopped","arguments":[{"name":"message","type":"Message","comment":"The poll that was stopped"}]},{"kind":"class","name":"channelAdminLogEventActionChangeLinkedChat","type":"ChannelAdminLogEventAction","id":84703944,"comment":"The linked chat was changed","arguments":[{"name":"prev_value","type":"int53"},{"name":"new_value","type":"int53"}]},{"kind":"class","name":"channelAdminLogEventActionChangeLocation","type":"ChannelAdminLogEventAction","id":241923758,"comment":"The geo group location was changed","arguments":[{"name":"prev_value","type":"ChannelLocation"},{"name":"new_value","type":"ChannelLocation"}]},{"kind":"class","name":"channelAdminLogEventActionToggleSlowMode","type":"ChannelAdminLogEventAction","id":1401984889,"comment":"{@link channels.toggleSlowMode}","arguments":[{"name":"prev_value","type":"int"},{"name":"new_value","type":"int"}]},{"kind":"class","name":"channelAdminLogEventActionStartGroupCall","type":"ChannelAdminLogEventAction","id":589338437,"comment":"A group call was started","arguments":[{"name":"call","type":"InputGroupCall","comment":"Group call"}]},{"kind":"class","name":"channelAdminLogEventActionDiscardGroupCall","type":"ChannelAdminLogEventAction","id":3684667712,"comment":"A group call was terminated","arguments":[{"name":"call","type":"InputGroupCall","comment":"The group call that was terminated"}]},{"kind":"class","name":"channelAdminLogEventActionParticipantMute","type":"ChannelAdminLogEventAction","id":4179895506,"comment":"A group call participant was muted","arguments":[{"name":"participant","type":"GroupCallParticipant","comment":"The participant that was muted"}]},{"kind":"class","name":"channelAdminLogEventActionParticipantUnmute","type":"ChannelAdminLogEventAction","id":3863226816,"comment":"A group call participant was unmuted","arguments":[{"name":"participant","type":"GroupCallParticipant","comment":"The participant that was unmuted"}]},{"kind":"class","name":"channelAdminLogEventActionToggleGroupCallSetting","type":"ChannelAdminLogEventAction","id":1456906823,"comment":"Group call settings were changed","arguments":[{"name":"join_muted","type":"Bool"}]},{"kind":"class","name":"channelAdminLogEventActionParticipantJoinByInvite","type":"ChannelAdminLogEventAction","id":1557846647,"comment":"A user joined the supergroup/channel using a specific invite link","arguments":[{"name":"invite","type":"ExportedChatInvite","comment":"The invite link used to join the supergroup/channel"}]},{"kind":"class","name":"channelAdminLogEventActionExportedInviteDelete","type":"ChannelAdminLogEventAction","id":1515256996,"comment":"A chat invite was deleted","arguments":[{"name":"invite","type":"ExportedChatInvite","comment":"The deleted chat invite"}]},{"kind":"class","name":"channelAdminLogEventActionExportedInviteRevoke","type":"ChannelAdminLogEventAction","id":1091179342,"comment":"A specific invite link was revoked","arguments":[{"name":"invite","type":"ExportedChatInvite","comment":"The invite link that was revoked"}]},{"kind":"class","name":"channelAdminLogEventActionExportedInviteEdit","type":"ChannelAdminLogEventAction","id":3910056793,"comment":"A chat invite was edited","arguments":[{"name":"prev_invite","type":"ExportedChatInvite"},{"name":"new_invite","type":"ExportedChatInvite"}]},{"kind":"class","name":"channelAdminLogEventActionParticipantVolume","type":"ChannelAdminLogEventAction","id":1048537159,"comment":"channelAdminLogEvent.user_id has set the volume of participant.peer to participant.volume","arguments":[{"name":"participant","type":"GroupCallParticipant","comment":"The participant whose volume was changed"}]},{"kind":"class","name":"channelAdminLogEventActionChangeHistoryTTL","type":"ChannelAdminLogEventAction","id":1855199800,"comment":"The Time-To-Live of messages in this chat was changed","arguments":[{"name":"prev_value","type":"int"},{"name":"new_value","type":"int"}]},{"kind":"class","name":"channelAdminLogEventActionParticipantJoinByRequest","type":"ChannelAdminLogEventAction","id":2947945546,"arguments":[{"name":"invite","type":"ExportedChatInvite"},{"name":"approved_by","type":"long"}]},{"kind":"class","name":"channelAdminLogEvent","type":"ChannelAdminLogEvent","id":531458253,"comment":"Admin log event","arguments":[{"name":"id","type":"long","comment":"Event ID"},{"name":"date","type":"int","comment":"Date"},{"name":"user_id","type":"int53"},{"name":"action","type":"ChannelAdminLogEventAction","comment":"Action"}]},{"kind":"class","name":"channels.adminLogResults","type":"channels.AdminLogResults","id":3985307469,"comment":"Admin log events","arguments":[{"name":"events","type":"Vector","comment":"Admin log events"},{"name":"chats","type":"Vector","comment":"Chats mentioned in events"},{"name":"users","type":"Vector","comment":"Users mentioned in events"}]},{"kind":"class","name":"channelAdminLogEventsFilter","type":"ChannelAdminLogEventsFilter","id":3926948580,"comment":"Filter only certain admin log events","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"join","type":"true","predicate":"flags.0","comment":"{@link channelAdminLogEventActionParticipantJoin}"},{"name":"leave","type":"true","predicate":"flags.1","comment":"{@link channelAdminLogEventActionParticipantLeave}"},{"name":"invite","type":"true","predicate":"flags.2","comment":"{@link channelAdminLogEventActionParticipantInvite}"},{"name":"ban","type":"true","predicate":"flags.3","comment":"{@link channelAdminLogEventActionParticipantToggleBan}"},{"name":"unban","type":"true","predicate":"flags.4","comment":"{@link channelAdminLogEventActionParticipantToggleBan}"},{"name":"kick","type":"true","predicate":"flags.5","comment":"{@link channelAdminLogEventActionParticipantToggleBan}"},{"name":"unkick","type":"true","predicate":"flags.6","comment":"{@link channelAdminLogEventActionParticipantToggleBan}"},{"name":"promote","type":"true","predicate":"flags.7","comment":"{@link channelAdminLogEventActionParticipantToggleAdmin}"},{"name":"demote","type":"true","predicate":"flags.8","comment":"{@link channelAdminLogEventActionParticipantToggleAdmin}"},{"name":"info","type":"true","predicate":"flags.9","comment":"Info change events (when {@link channelAdminLogEventActionChangeAbout}, {@link channelAdminLogEventActionChangeLinkedChat}, {@link channelAdminLogEventActionChangeLocation}, {@link channelAdminLogEventActionChangePhoto}, {@link channelAdminLogEventActionChangeStickerSet}, {@link channelAdminLogEventActionChangeTitle} or {@link channelAdminLogEventActionChangeUsername} data of a channel gets modified)"},{"name":"settings","type":"true","predicate":"flags.10","comment":"Settings change events ({@link channelAdminLogEventActionToggleInvites}, {@link channelAdminLogEventActionTogglePreHistoryHidden}, {@link channelAdminLogEventActionToggleSignatures}, {@link channelAdminLogEventActionDefaultBannedRights})"},{"name":"pinned","type":"true","predicate":"flags.11","comment":"{@link channelAdminLogEventActionUpdatePinned}"},{"name":"edit","type":"true","predicate":"flags.12","comment":"{@link channelAdminLogEventActionEditMessage}"},{"name":"delete","type":"true","predicate":"flags.13","comment":"{@link channelAdminLogEventActionDeleteMessage}"},{"name":"group_call","type":"true","predicate":"flags.14"},{"name":"invites","type":"true","predicate":"flags.15","comment":"Invite events"}]},{"kind":"class","name":"popularContact","type":"PopularContact","id":1558266229,"comment":"Popular contact","arguments":[{"name":"client_id","type":"long"},{"name":"importers","type":"int","comment":"How many people imported this contact"}]},{"kind":"class","name":"messages.favedStickersNotModified","type":"messages.FavedStickers","id":2660214483,"comment":"No new favorited stickers were found","arguments":[]},{"kind":"class","name":"messages.favedStickers","type":"messages.FavedStickers","id":750063767,"comment":"Favorited stickers","arguments":[{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"},{"name":"packs","type":"Vector","comment":"Emojis associated to stickers"},{"name":"stickers","type":"Vector","comment":"Favorited stickers"}]},{"kind":"class","name":"recentMeUrlUnknown","type":"RecentMeUrl","id":1189204285,"comment":"Unknown t.me url","arguments":[{"name":"url","type":"string","comment":"URL"}]},{"kind":"class","name":"recentMeUrlUser","type":"RecentMeUrl","id":3106671074,"comment":"Recent t.me link to a user","arguments":[{"name":"url","type":"string","comment":"URL"},{"name":"user_id","type":"int53"}]},{"kind":"class","name":"recentMeUrlChat","type":"RecentMeUrl","id":3000660434,"comment":"Recent t.me link to a chat","arguments":[{"name":"url","type":"string","comment":"t.me URL"},{"name":"chat_id","type":"int53"}]},{"kind":"class","name":"recentMeUrlChatInvite","type":"RecentMeUrl","id":3947431965,"comment":"Recent t.me invite link to a chat","arguments":[{"name":"url","type":"string","comment":"t.me URL"},{"name":"chat_invite","type":"ChatInvite"}]},{"kind":"class","name":"recentMeUrlStickerSet","type":"RecentMeUrl","id":3154794460,"comment":"Recent t.me stickerset installation URL","arguments":[{"name":"url","type":"string","comment":"t.me URL"},{"name":"set","type":"StickerSetCovered","comment":"Stickerset"}]},{"kind":"class","name":"help.recentMeUrls","type":"help.RecentMeUrls","id":235081943,"comment":"Recent t.me URLs","arguments":[{"name":"urls","type":"Vector","comment":"URLs"},{"name":"chats","type":"Vector","comment":"Chats"},{"name":"users","type":"Vector","comment":"Users"}]},{"kind":"class","name":"inputSingleMedia","type":"InputSingleMedia","id":482797855,"comment":"A single media in an album or grouped media sent with {@link messages.sendMultiMedia}.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"media","type":"InputMedia","comment":"The media"},{"name":"random_id","type":"long"},{"name":"message","type":"string","comment":"A caption for the media"},{"name":"entities","type":"Vector","predicate":"flags.0","comment":"Message entities for styled text"}]},{"kind":"class","name":"webAuthorization","type":"WebAuthorization","id":2801333330,"comment":"Represents a bot logged in using the Telegram login widget","arguments":[{"name":"hash","type":"long","comment":"Authorization hash"},{"name":"bot_id","type":"int53"},{"name":"domain","type":"string","comment":"The domain name of the website on which the user has logged in."},{"name":"browser","type":"string","comment":"Browser user-agent"},{"name":"platform","type":"string","comment":"Platform"},{"name":"date_created","type":"int"},{"name":"date_active","type":"int"},{"name":"ip","type":"string","comment":"IP address"},{"name":"region","type":"string","comment":"Region, determined from IP address"}]},{"kind":"class","name":"account.webAuthorizations","type":"account.WebAuthorizations","id":3981887996,"comment":"Web authorizations","arguments":[{"name":"authorizations","type":"Vector","comment":"Web authorization list"},{"name":"users","type":"Vector","comment":"Users"}]},{"kind":"class","name":"inputMessageID","type":"InputMessage","id":2792792866,"comment":"Message by ID","arguments":[{"name":"id","type":"int","comment":"Message ID"}]},{"kind":"class","name":"inputMessageReplyTo","type":"InputMessage","id":3134751637,"comment":"Message to which the specified message replies to","arguments":[{"name":"id","type":"int","comment":"ID of the message that replies to the message we need"}]},{"kind":"class","name":"inputMessagePinned","type":"InputMessage","id":2257003832,"comment":"Pinned message","arguments":[]},{"kind":"class","name":"inputMessageCallbackQuery","type":"InputMessage","id":2902071934,"comment":"Used by bots for fetching information about the message that originated a callback query","arguments":[{"name":"id","type":"int","comment":"Message ID"},{"name":"query_id","type":"long"}]},{"kind":"class","name":"inputDialogPeer","type":"InputDialogPeer","id":4239064759,"comment":"A peer","arguments":[{"name":"peer","type":"InputPeer","comment":"Peer"}]},{"kind":"class","name":"inputDialogPeerFolder","type":"InputDialogPeer","id":1684014375,"comment":"All peers in a peer folder","arguments":[{"name":"folder_id","type":"int"}]},{"kind":"class","name":"dialogPeer","type":"DialogPeer","id":3849174789,"comment":"Peer","arguments":[{"name":"peer","type":"Peer","comment":"Peer"}]},{"kind":"class","name":"dialogPeerFolder","type":"DialogPeer","id":1363483106,"comment":"Peer folder","arguments":[{"name":"folder_id","type":"int"}]},{"kind":"class","name":"messages.foundStickerSetsNotModified","type":"messages.FoundStickerSets","id":223655517,"comment":"No further results were found","arguments":[]},{"kind":"class","name":"messages.foundStickerSets","type":"messages.FoundStickerSets","id":2331024850,"comment":"Found stickersets","arguments":[{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"},{"name":"sets","type":"Vector","comment":"Found stickersets"}]},{"kind":"class","name":"fileHash","type":"FileHash","id":1648543603,"comment":"SHA256 Hash of an uploaded file, to be checked for validity after download","arguments":[{"name":"offset","type":"int","comment":"Offset from where to start computing SHA-256 hash"},{"name":"limit","type":"int","comment":"Length"},{"name":"hash","type":"bytes","comment":"SHA-256 Hash of file chunk, to be checked for validity after download"}]},{"kind":"class","name":"inputClientProxy","type":"InputClientProxy","id":1968737087,"comment":"Info about an MTProxy used to connect.","arguments":[{"name":"address","type":"string","comment":"Proxy address"},{"name":"port","type":"int","comment":"Proxy port"}]},{"kind":"class","name":"help.termsOfServiceUpdateEmpty","type":"help.TermsOfServiceUpdate","id":3811614591,"comment":"No changes were made to telegram's terms of service","arguments":[{"name":"expires","type":"int","comment":"New TOS updates will have to be queried using {@link help.getTermsOfServiceUpdate} in expires seconds"}]},{"kind":"class","name":"help.termsOfServiceUpdate","type":"help.TermsOfServiceUpdate","id":686618977,"comment":"Info about an update of telegram's terms of service. If the terms of service are declined, then the {@link account.deleteAccount} method should be called with the reason \"Decline ToS update\"","arguments":[{"name":"expires","type":"int","comment":"New TOS updates will have to be queried using {@link help.getTermsOfServiceUpdate} in expires seconds"},{"name":"terms_of_service","type":"help.TermsOfService"}]},{"kind":"class","name":"inputSecureFileUploaded","type":"InputSecureFile","id":859091184,"comment":"Uploaded secure file, for more info see the passport docs »","arguments":[{"name":"id","type":"long","comment":"Secure file ID"},{"name":"parts","type":"int","comment":"Secure file part count"},{"name":"md5_checksum","type":"string"},{"name":"file_hash","type":"bytes"},{"name":"secret","type":"bytes","comment":"Secret"}]},{"kind":"class","name":"inputSecureFile","type":"InputSecureFile","id":1399317950,"comment":"Preuploaded passport file, for more info see the passport docs »","arguments":[{"name":"id","type":"long","comment":"Secure file ID"},{"name":"access_hash","type":"long"}]},{"kind":"class","name":"secureFileEmpty","type":"SecureFile","id":1679398724,"comment":"Empty constructor","arguments":[]},{"kind":"class","name":"secureFile","type":"SecureFile","id":3760683618,"comment":"Secure passport file, for more info see the passport docs »","arguments":[{"name":"id","type":"long","comment":"ID"},{"name":"access_hash","type":"long"},{"name":"size","type":"int","comment":"File size"},{"name":"dc_id","type":"int"},{"name":"date","type":"int","comment":"Date of upload"},{"name":"file_hash","type":"bytes"},{"name":"secret","type":"bytes","comment":"Secret"}]},{"kind":"class","name":"secureData","type":"SecureData","id":2330640067,"comment":"Secure passport data, for more info see the passport docs »","arguments":[{"name":"data","type":"bytes","comment":"Data"},{"name":"data_hash","type":"bytes"},{"name":"secret","type":"bytes","comment":"Secret"}]},{"kind":"class","name":"securePlainPhone","type":"SecurePlainData","id":2103482845,"comment":"Phone number to use in telegram passport: it must be verified, first ».","arguments":[{"name":"phone","type":"string","comment":"Phone number"}]},{"kind":"class","name":"securePlainEmail","type":"SecurePlainData","id":569137759,"comment":"Email address to use in telegram passport: it must be verified, first ».","arguments":[{"name":"email","type":"string","comment":"Email address"}]},{"kind":"class","name":"secureValueTypePersonalDetails","type":"SecureValueType","id":2636808675,"comment":"Personal details","arguments":[]},{"kind":"class","name":"secureValueTypePassport","type":"SecureValueType","id":1034709504,"comment":"Passport","arguments":[]},{"kind":"class","name":"secureValueTypeDriverLicense","type":"SecureValueType","id":115615172,"comment":"Driver's license","arguments":[]},{"kind":"class","name":"secureValueTypeIdentityCard","type":"SecureValueType","id":2698015819,"comment":"Identity card","arguments":[]},{"kind":"class","name":"secureValueTypeInternalPassport","type":"SecureValueType","id":2577698595,"comment":"Internal passport","arguments":[]},{"kind":"class","name":"secureValueTypeAddress","type":"SecureValueType","id":3420659238,"comment":"Address","arguments":[]},{"kind":"class","name":"secureValueTypeUtilityBill","type":"SecureValueType","id":4231435598,"comment":"Utility bill","arguments":[]},{"kind":"class","name":"secureValueTypeBankStatement","type":"SecureValueType","id":2299755533,"comment":"Bank statement","arguments":[]},{"kind":"class","name":"secureValueTypeRentalAgreement","type":"SecureValueType","id":2340959368,"comment":"Rental agreement","arguments":[]},{"kind":"class","name":"secureValueTypePassportRegistration","type":"SecureValueType","id":2581823594,"comment":"Internal registration passport","arguments":[]},{"kind":"class","name":"secureValueTypeTemporaryRegistration","type":"SecureValueType","id":3926060083,"comment":"Temporary registration","arguments":[]},{"kind":"class","name":"secureValueTypePhone","type":"SecureValueType","id":3005262555,"comment":"Phone","arguments":[]},{"kind":"class","name":"secureValueTypeEmail","type":"SecureValueType","id":2386339822,"comment":"Email","arguments":[]},{"kind":"class","name":"secureValue","type":"SecureValue","id":411017418,"comment":"Secure value","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"type","type":"SecureValueType","comment":"Secure passport value type"},{"name":"data","type":"SecureData","predicate":"flags.0","comment":"Encrypted Telegram Passport element data"},{"name":"front_side","type":"SecureFile","predicate":"flags.1"},{"name":"reverse_side","type":"SecureFile","predicate":"flags.2"},{"name":"selfie","type":"SecureFile","predicate":"flags.3","comment":"Encrypted passport file with a selfie of the user holding the document"},{"name":"translation","type":"Vector","predicate":"flags.6","comment":"Array of encrypted passport files with translated versions of the provided documents"},{"name":"files","type":"Vector","predicate":"flags.4","comment":"Array of encrypted passport files with photos the of the documents"},{"name":"plain_data","type":"SecurePlainData","predicate":"flags.5"},{"name":"hash","type":"bytes","comment":"Data hash"}]},{"kind":"class","name":"inputSecureValue","type":"InputSecureValue","id":3676426407,"comment":"Secure value, for more info see the passport docs »","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"type","type":"SecureValueType","comment":"Secure passport value type"},{"name":"data","type":"SecureData","predicate":"flags.0","comment":"Encrypted Telegram Passport element data"},{"name":"front_side","type":"InputSecureFile","predicate":"flags.1"},{"name":"reverse_side","type":"InputSecureFile","predicate":"flags.2"},{"name":"selfie","type":"InputSecureFile","predicate":"flags.3","comment":"Encrypted passport file with a selfie of the user holding the document"},{"name":"translation","type":"Vector","predicate":"flags.6","comment":"Array of encrypted passport files with translated versions of the provided documents"},{"name":"files","type":"Vector","predicate":"flags.4","comment":"Array of encrypted passport files with photos the of the documents"},{"name":"plain_data","type":"SecurePlainData","predicate":"flags.5"}]},{"kind":"class","name":"secureValueHash","type":"SecureValueHash","id":3978218928,"comment":"Secure value hash","arguments":[{"name":"type","type":"SecureValueType","comment":"Secure value type"},{"name":"hash","type":"bytes","comment":"Hash"}]},{"kind":"class","name":"secureValueErrorData","type":"SecureValueError","id":3903065049,"comment":"Represents an issue in one of the data fields that was provided by the user. The error is considered resolved when the field's value changes.","arguments":[{"name":"type","type":"SecureValueType","comment":"The section of the user's Telegram Passport which has the error, one of {@link secureValueTypePersonalDetails}, {@link secureValueTypePassport}, {@link secureValueTypeDriverLicense}, {@link secureValueTypeIdentityCard}, {@link secureValueTypeInternalPassport}, {@link secureValueTypeAddress}"},{"name":"data_hash","type":"bytes"},{"name":"field","type":"string","comment":"Name of the data field which has the error"},{"name":"text","type":"string","comment":"Error message"}]},{"kind":"class","name":"secureValueErrorFrontSide","type":"SecureValueError","id":12467706,"comment":"Represents an issue with the front side of a document. The error is considered resolved when the file with the front side of the document changes.","arguments":[{"name":"type","type":"SecureValueType","comment":"One of {@link secureValueTypePassport}, {@link secureValueTypeDriverLicense}, {@link secureValueTypeIdentityCard}, {@link secureValueTypeInternalPassport}"},{"name":"file_hash","type":"bytes"},{"name":"text","type":"string","comment":"Error message"}]},{"kind":"class","name":"secureValueErrorReverseSide","type":"SecureValueError","id":2257201829,"comment":"Represents an issue with the reverse side of a document. The error is considered resolved when the file with reverse side of the document changes.","arguments":[{"name":"type","type":"SecureValueType","comment":"One of {@link secureValueTypeDriverLicense}, {@link secureValueTypeIdentityCard}"},{"name":"file_hash","type":"bytes"},{"name":"text","type":"string","comment":"Error message"}]},{"kind":"class","name":"secureValueErrorSelfie","type":"SecureValueError","id":3845639894,"comment":"Represents an issue with the selfie with a document. The error is considered resolved when the file with the selfie changes.","arguments":[{"name":"type","type":"SecureValueType","comment":"One of {@link secureValueTypePassport}, {@link secureValueTypeDriverLicense}, {@link secureValueTypeIdentityCard}, {@link secureValueTypeInternalPassport}"},{"name":"file_hash","type":"bytes"},{"name":"text","type":"string","comment":"Error message"}]},{"kind":"class","name":"secureValueErrorFile","type":"SecureValueError","id":2054162547,"comment":"Represents an issue with a document scan. The error is considered resolved when the file with the document scan changes.","arguments":[{"name":"type","type":"SecureValueType","comment":"One of {@link secureValueTypeUtilityBill}, {@link secureValueTypeBankStatement}, {@link secureValueTypeRentalAgreement}, {@link secureValueTypePassportRegistration}, {@link secureValueTypeTemporaryRegistration}"},{"name":"file_hash","type":"bytes"},{"name":"text","type":"string","comment":"Error message"}]},{"kind":"class","name":"secureValueErrorFiles","type":"SecureValueError","id":1929644607,"comment":"Represents an issue with a list of scans. The error is considered resolved when the list of files containing the scans changes.","arguments":[{"name":"type","type":"SecureValueType","comment":"One of {@link secureValueTypeUtilityBill}, {@link secureValueTypeBankStatement}, {@link secureValueTypeRentalAgreement}, {@link secureValueTypePassportRegistration}, {@link secureValueTypeTemporaryRegistration}"},{"name":"file_hash","type":"Vector"},{"name":"text","type":"string","comment":"Error message"}]},{"kind":"class","name":"secureValueError","type":"SecureValueError","id":2258466191,"comment":"Secure value error","arguments":[{"name":"type","type":"SecureValueType","comment":"Type of element which has the issue"},{"name":"hash","type":"bytes","comment":"Hash"},{"name":"text","type":"string","comment":"Error message"}]},{"kind":"class","name":"secureValueErrorTranslationFile","type":"SecureValueError","id":2702460784,"comment":"Represents an issue with one of the files that constitute the translation of a document. The error is considered resolved when the file changes.","arguments":[{"name":"type","type":"SecureValueType","comment":"One of {@link secureValueTypePersonalDetails}, {@link secureValueTypePassport}, {@link secureValueTypeDriverLicense}, {@link secureValueTypeIdentityCard}, {@link secureValueTypeInternalPassport}, {@link secureValueTypeUtilityBill}, {@link secureValueTypeBankStatement}, {@link secureValueTypeRentalAgreement}, {@link secureValueTypePassportRegistration}, {@link secureValueTypeTemporaryRegistration}"},{"name":"file_hash","type":"bytes"},{"name":"text","type":"string","comment":"Error message"}]},{"kind":"class","name":"secureValueErrorTranslationFiles","type":"SecureValueError","id":579341128,"comment":"Represents an issue with the translated version of a document. The error is considered resolved when a file with the document translation changes.","arguments":[{"name":"type","type":"SecureValueType","comment":"One of {@link secureValueTypePersonalDetails}, {@link secureValueTypePassport}, {@link secureValueTypeDriverLicense}, {@link secureValueTypeIdentityCard}, {@link secureValueTypeInternalPassport}, {@link secureValueTypeUtilityBill}, {@link secureValueTypeBankStatement}, {@link secureValueTypeRentalAgreement}, {@link secureValueTypePassportRegistration}, {@link secureValueTypeTemporaryRegistration}"},{"name":"file_hash","type":"Vector"},{"name":"text","type":"string","comment":"Error message"}]},{"kind":"class","name":"secureCredentialsEncrypted","type":"SecureCredentialsEncrypted","id":871426631,"comment":"Encrypted credentials required to decrypt telegram passport data.","arguments":[{"name":"data","type":"bytes","comment":"Encrypted JSON-serialized data with unique user's payload, data hashes and secrets required for EncryptedPassportElement decryption and authentication, as described in decrypting data »"},{"name":"hash","type":"bytes","comment":"Data hash for data authentication as described in decrypting data »"},{"name":"secret","type":"bytes","comment":"Secret, encrypted with the bot's public RSA key, required for data decryption as described in decrypting data »"}]},{"kind":"class","name":"account.authorizationForm","type":"account.AuthorizationForm","id":2905480408,"comment":"Telegram Passport authorization form","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"required_types","type":"Vector"},{"name":"values","type":"Vector","comment":"Already submitted Telegram Passport documents"},{"name":"errors","type":"Vector","comment":"Telegram Passport errors"},{"name":"users","type":"Vector","comment":"Info about the bot to which the form will be submitted"},{"name":"privacy_policy_url","type":"string","predicate":"flags.0"}]},{"kind":"class","name":"account.sentEmailCode","type":"account.SentEmailCode","id":2166326607,"comment":"The sent email code","arguments":[{"name":"email_pattern","type":"string"},{"name":"length","type":"int","comment":"The length of the verification code"}]},{"kind":"class","name":"help.deepLinkInfoEmpty","type":"help.DeepLinkInfo","id":1722786150,"comment":"Deep link info empty","arguments":[]},{"kind":"class","name":"help.deepLinkInfo","type":"help.DeepLinkInfo","id":1783556146,"comment":"Deep linking info","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"update_app","type":"true","predicate":"flags.0"},{"name":"message","type":"string","comment":"Message to show to the user"},{"name":"entities","type":"Vector","predicate":"flags.1","comment":"Message entities for styled text"}]},{"kind":"class","name":"savedPhoneContact","type":"SavedContact","id":289586518,"comment":"Saved contact","arguments":[{"name":"phone","type":"string","comment":"Phone number"},{"name":"first_name","type":"string"},{"name":"last_name","type":"string"},{"name":"date","type":"int","comment":"Date added"}]},{"kind":"class","name":"account.takeout","type":"account.Takeout","id":1304052993,"comment":"Takout info","arguments":[{"name":"id","type":"long","comment":"Takeout ID"}]},{"kind":"class","name":"passwordKdfAlgoUnknown","type":"PasswordKdfAlgo","id":3562713238,"comment":"Unknown KDF (most likely, the client is outdated and does not support the specified KDF algorithm)","arguments":[]},{"kind":"class","name":"passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow","type":"PasswordKdfAlgo","id":982592842,"comment":"This key derivation algorithm defines that SRP 2FA login must be used","arguments":[{"name":"salt1","type":"bytes","comment":"One of two salts used by the derivation function (see SRP 2FA login)"},{"name":"salt2","type":"bytes","comment":"One of two salts used by the derivation function (see SRP 2FA login)"},{"name":"g","type":"int","comment":"Base (see SRP 2FA login)"},{"name":"p","type":"bytes","comment":"2048-bit modulus (see SRP 2FA login)"}]},{"kind":"class","name":"securePasswordKdfAlgoUnknown","type":"SecurePasswordKdfAlgo","id":4883767,"comment":"Unknown KDF algo (most likely the client has to be updated)","arguments":[]},{"kind":"class","name":"securePasswordKdfAlgoPBKDF2HMACSHA512iter100000","type":"SecurePasswordKdfAlgo","id":3153255840,"comment":"PBKDF2 with SHA512 and 100000 iterations KDF algo","arguments":[{"name":"salt","type":"bytes","comment":"Salt"}]},{"kind":"class","name":"securePasswordKdfAlgoSHA512","type":"SecurePasswordKdfAlgo","id":2252807570,"comment":"SHA512 KDF algo","arguments":[{"name":"salt","type":"bytes","comment":"Salt"}]},{"kind":"class","name":"secureSecretSettings","type":"SecureSecretSettings","id":354925740,"comment":"Secure settings","arguments":[{"name":"secure_algo","type":"SecurePasswordKdfAlgo"},{"name":"secure_secret","type":"bytes"},{"name":"secure_secret_id","type":"long"}]},{"kind":"class","name":"inputCheckPasswordEmpty","type":"InputCheckPasswordSRP","id":2558588504,"comment":"There is no password","arguments":[]},{"kind":"class","name":"inputCheckPasswordSRP","type":"InputCheckPasswordSRP","id":3531600002,"comment":"Constructor for checking the validity of a 2FA SRP password (see SRP)","arguments":[{"name":"srp_id","type":"long"},{"name":"A","type":"bytes","comment":"A parameter (see SRP)"},{"name":"M1","type":"bytes","comment":"M1 parameter (see SRP)"}]},{"kind":"class","name":"secureRequiredType","type":"SecureRequiredType","id":2191366618,"comment":"Required type","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"native_names","type":"true","predicate":"flags.0"},{"name":"selfie_required","type":"true","predicate":"flags.1"},{"name":"translation_required","type":"true","predicate":"flags.2"},{"name":"type","type":"SecureValueType","comment":"Secure value type"}]},{"kind":"class","name":"secureRequiredTypeOneOf","type":"SecureRequiredType","id":41187252,"comment":"One of","arguments":[{"name":"types","type":"Vector","comment":"Secure required value types"}]},{"kind":"class","name":"help.passportConfigNotModified","type":"help.PassportConfig","id":3216634967,"comment":"Password configuration not modified","arguments":[]},{"kind":"class","name":"help.passportConfig","type":"help.PassportConfig","id":2694370991,"comment":"Telegram passport configuration","arguments":[{"name":"hash","type":"int","comment":"Hash for pagination, for more info click here"},{"name":"countries_langs","type":"DataJSON"}]},{"kind":"class","name":"inputAppEvent","type":"InputAppEvent","id":488313413,"comment":"Event that occured in the application.","arguments":[{"name":"time","type":"double","comment":"Client's exact timestamp for the event"},{"name":"type","type":"string","comment":"Type of event"},{"name":"peer","type":"long","comment":"Arbitrary numeric value for more convenient selection of certain event types, or events referring to a certain object"},{"name":"data","type":"JSONValue","comment":"Details of the event"}]},{"kind":"class","name":"jsonObjectValue","type":"JSONObjectValue","id":3235781593,"comment":"JSON key: value pair","arguments":[{"name":"key","type":"string","comment":"Key"},{"name":"value","type":"JSONValue","comment":"Value"}]},{"kind":"class","name":"jsonNull","type":"JSONValue","id":1064139624,"comment":"null JSON value","arguments":[]},{"kind":"class","name":"jsonBool","type":"JSONValue","id":3342098026,"comment":"JSON boolean value","arguments":[{"name":"value","type":"Bool","comment":"Value"}]},{"kind":"class","name":"jsonNumber","type":"JSONValue","id":736157604,"comment":"JSON numeric value","arguments":[{"name":"value","type":"double","comment":"Value"}]},{"kind":"class","name":"jsonString","type":"JSONValue","id":3072226938,"comment":"JSON string","arguments":[{"name":"value","type":"string","comment":"Value"}]},{"kind":"class","name":"jsonArray","type":"JSONValue","id":4148447075,"comment":"JSON array","arguments":[{"name":"value","type":"Vector","comment":"JSON values"}]},{"kind":"class","name":"jsonObject","type":"JSONValue","id":2579616925,"comment":"JSON object value","arguments":[{"name":"value","type":"Vector","comment":"Values"}]},{"kind":"class","name":"pageTableCell","type":"PageTableCell","id":878078826,"comment":"Table cell","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"header","type":"true","predicate":"flags.0","comment":"Is this element part of the column header"},{"name":"align_center","type":"true","predicate":"flags.3"},{"name":"align_right","type":"true","predicate":"flags.4"},{"name":"valign_middle","type":"true","predicate":"flags.5"},{"name":"valign_bottom","type":"true","predicate":"flags.6"},{"name":"text","type":"RichText","predicate":"flags.7","comment":"Content"},{"name":"colspan","type":"int","predicate":"flags.1","comment":"For how many columns should this cell extend"},{"name":"rowspan","type":"int","predicate":"flags.2","comment":"For how many rows should this cell extend"}]},{"kind":"class","name":"pageTableRow","type":"PageTableRow","id":3770729957,"comment":"Table row","arguments":[{"name":"cells","type":"Vector","comment":"Table cells"}]},{"kind":"class","name":"pageCaption","type":"PageCaption","id":1869903447,"comment":"Page caption","arguments":[{"name":"text","type":"RichText","comment":"Caption"},{"name":"credit","type":"RichText","comment":"Credits"}]},{"kind":"class","name":"pageListItemText","type":"PageListItem","id":3106911949,"comment":"List item","arguments":[{"name":"text","type":"RichText","comment":"Text"}]},{"kind":"class","name":"pageListItemBlocks","type":"PageListItem","id":635466748,"comment":"List item","arguments":[{"name":"blocks","type":"Vector","comment":"Blocks"}]},{"kind":"class","name":"pageListOrderedItemText","type":"PageListOrderedItem","id":1577484359,"comment":"Ordered list of text items","arguments":[{"name":"num","type":"string","comment":"Number of element within ordered list"},{"name":"text","type":"RichText","comment":"Text"}]},{"kind":"class","name":"pageListOrderedItemBlocks","type":"PageListOrderedItem","id":2564655414,"comment":"Ordered list of IV blocks","arguments":[{"name":"num","type":"string","comment":"Number of element within ordered list"},{"name":"blocks","type":"Vector","comment":"Item contents"}]},{"kind":"class","name":"pageRelatedArticle","type":"PageRelatedArticle","id":3012615176,"comment":"Related article","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"url","type":"string","comment":"URL of article"},{"name":"webpage_id","type":"long"},{"name":"title","type":"string","predicate":"flags.0","comment":"Title"},{"name":"description","type":"string","predicate":"flags.1","comment":"Description"},{"name":"photo_id","type":"long","predicate":"flags.2"},{"name":"author","type":"string","predicate":"flags.3","comment":"Author name"},{"name":"published_date","type":"int","predicate":"flags.4"}]},{"kind":"class","name":"page","type":"Page","id":2556788493,"comment":"Instant view page","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"part","type":"true","predicate":"flags.0","comment":"Indicates that not full page preview is available to the client and it will need to fetch full Instant View from the server using {@link messages.getWebPagePreview}."},{"name":"rtl","type":"true","predicate":"flags.1","comment":"Whether the page contains RTL text"},{"name":"v2","type":"true","predicate":"flags.2","comment":"Whether this is an IV v2 page"},{"name":"url","type":"string","comment":"Original page HTTP URL"},{"name":"blocks","type":"Vector","comment":"Page elements (like with HTML elements, only as TL constructors)"},{"name":"photos","type":"Vector","comment":"Photos in page"},{"name":"documents","type":"Vector","comment":"Media in page"},{"name":"views","type":"int","predicate":"flags.3","comment":"Viewcount"}]},{"kind":"class","name":"help.supportName","type":"help.SupportName","id":2349199817,"comment":"Localized name for telegram support","arguments":[{"name":"name","type":"string","comment":"Localized name"}]},{"kind":"class","name":"help.userInfoEmpty","type":"help.UserInfo","id":4088278765,"comment":"Internal use","arguments":[]},{"kind":"class","name":"help.userInfo","type":"help.UserInfo","id":32192344,"comment":"Internal use","arguments":[{"name":"message","type":"string","comment":"Info"},{"name":"entities","type":"Vector","comment":"Message entities for styled text"},{"name":"author","type":"string","comment":"Author"},{"name":"date","type":"int","comment":"Date"}]},{"kind":"class","name":"pollAnswer","type":"PollAnswer","id":1823064809,"comment":"A possible answer of a poll","arguments":[{"name":"text","type":"string","comment":"Textual representation of the answer"},{"name":"option","type":"bytes","comment":"The param that has to be passed to {@link messages.sendVote}."}]},{"kind":"class","name":"poll","type":"Poll","id":2262925665,"comment":"Poll","arguments":[{"name":"id","type":"long","comment":"ID of the poll"},{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"closed","type":"true","predicate":"flags.0","comment":"Whether the poll is closed and doesn't accept any more answers"},{"name":"public_voters","type":"true","predicate":"flags.1"},{"name":"multiple_choice","type":"true","predicate":"flags.2"},{"name":"quiz","type":"true","predicate":"flags.3","comment":"Whether this is a quiz (with wrong and correct answers, results shown in the return type)"},{"name":"question","type":"string","comment":"The question of the poll"},{"name":"answers","type":"Vector","comment":"The possible answers, vote using {@link messages.sendVote}."},{"name":"close_period","type":"int","predicate":"flags.4"},{"name":"close_date","type":"int","predicate":"flags.5"}]},{"kind":"class","name":"pollAnswerVoters","type":"PollAnswerVoters","id":997055186,"comment":"A poll answer, and how users voted on it","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"chosen","type":"true","predicate":"flags.0","comment":"Whether we have chosen this answer"},{"name":"correct","type":"true","predicate":"flags.1","comment":"For quizes, whether the option we have chosen is correct"},{"name":"option","type":"bytes","comment":"The param that has to be passed to {@link messages.sendVote}."},{"name":"voters","type":"int","comment":"How many users voted for this option"}]},{"kind":"class","name":"pollResults","type":"PollResults","id":3703058083,"comment":"Results of poll","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"min","type":"true","predicate":"flags.0","comment":"Similar to min objects, used for poll constructors that are the same for all users so they don't have option chosen by the current user (you can use {@link messages.getPollResults} to get the full poll results)."},{"name":"results","type":"Vector","predicate":"flags.1","comment":"Poll results"},{"name":"total_voters","type":"int","predicate":"flags.2"},{"name":"recent_voters","type":"vector","predicate":"flags.3"},{"name":"solution","type":"string","predicate":"flags.4","comment":"Explanation of quiz solution"},{"name":"solution_entities","type":"Vector","predicate":"flags.4"}]},{"kind":"class","name":"chatOnlines","type":"ChatOnlines","id":4030849616,"comment":"Number of online users in a chat","arguments":[{"name":"onlines","type":"int","comment":"Number of online users"}]},{"kind":"class","name":"statsURL","type":"StatsURL","id":1202287072,"comment":"URL with chat statistics","arguments":[{"name":"url","type":"string","comment":"Chat statistics"}]},{"kind":"class","name":"chatAdminRights","type":"ChatAdminRights","id":1605510357,"comment":"Represents the rights of an admin in a channel/supergroup.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"change_info","type":"true","predicate":"flags.0"},{"name":"post_messages","type":"true","predicate":"flags.1"},{"name":"edit_messages","type":"true","predicate":"flags.2"},{"name":"delete_messages","type":"true","predicate":"flags.3"},{"name":"ban_users","type":"true","predicate":"flags.4"},{"name":"invite_users","type":"true","predicate":"flags.5"},{"name":"pin_messages","type":"true","predicate":"flags.7"},{"name":"add_admins","type":"true","predicate":"flags.9"},{"name":"anonymous","type":"true","predicate":"flags.10","comment":"Whether this admin is anonymous"},{"name":"manage_call","type":"true","predicate":"flags.11"},{"name":"other","type":"true","predicate":"flags.12","comment":"Set this flag if none of the other flags are set, but you stil want the user to be an admin."}]},{"kind":"class","name":"chatBannedRights","type":"ChatBannedRights","id":2668758040,"comment":"Represents the rights of a normal user in a supergroup/channel/chat. In this case, the flags are inverted: if set, a flag does not allow a user to do X.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"view_messages","type":"true","predicate":"flags.0"},{"name":"send_messages","type":"true","predicate":"flags.1"},{"name":"send_media","type":"true","predicate":"flags.2"},{"name":"send_stickers","type":"true","predicate":"flags.3"},{"name":"send_gifs","type":"true","predicate":"flags.4"},{"name":"send_games","type":"true","predicate":"flags.5"},{"name":"send_inline","type":"true","predicate":"flags.6"},{"name":"embed_links","type":"true","predicate":"flags.7"},{"name":"send_polls","type":"true","predicate":"flags.8"},{"name":"change_info","type":"true","predicate":"flags.10"},{"name":"invite_users","type":"true","predicate":"flags.15"},{"name":"pin_messages","type":"true","predicate":"flags.17"},{"name":"until_date","type":"int"}]},{"kind":"class","name":"inputWallPaper","type":"InputWallPaper","id":3861952889,"comment":"Wallpaper","arguments":[{"name":"id","type":"long","comment":"Wallpaper ID"},{"name":"access_hash","type":"long"}]},{"kind":"class","name":"inputWallPaperSlug","type":"InputWallPaper","id":1913199744,"comment":"Wallpaper by slug (a unique ID)","arguments":[{"name":"slug","type":"string","comment":"Unique wallpaper ID"}]},{"kind":"class","name":"inputWallPaperNoFile","type":"InputWallPaper","id":2524595758,"comment":"Wallpaper with no file access hash, used for example when deleting (unsave=true) wallpapers using {@link account.saveWallPaper}, specifying just the wallpaper ID.","arguments":[{"name":"id","type":"long","comment":"Wallpaper ID"}]},{"kind":"class","name":"account.wallPapersNotModified","type":"account.WallPapers","id":471437699,"comment":"No new wallpapers were found","arguments":[]},{"kind":"class","name":"account.wallPapers","type":"account.WallPapers","id":3452142988,"comment":"Installed wallpapers","arguments":[{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"},{"name":"wallpapers","type":"Vector","comment":"Wallpapers"}]},{"kind":"class","name":"codeSettings","type":"CodeSettings","id":3737042563,"comment":"Example implementations: telegram for android, tdlib.\n\nSettings used by telegram servers for sending the confirm code.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"allow_flashcall","type":"true","predicate":"flags.0"},{"name":"current_number","type":"true","predicate":"flags.1"},{"name":"allow_app_hash","type":"true","predicate":"flags.4"}]},{"kind":"class","name":"wallPaperSettings","type":"WallPaperSettings","id":499236004,"comment":"Wallpaper settings","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"blur","type":"true","predicate":"flags.1","comment":"If set, the wallpaper must be downscaled to fit in 450x450 square and then box-blurred with radius 12"},{"name":"motion","type":"true","predicate":"flags.2","comment":"If set, the background needs to be slightly moved when device is rotated"},{"name":"background_color","type":"int","predicate":"flags.0"},{"name":"second_background_color","type":"int","predicate":"flags.4"},{"name":"third_background_color","type":"int","predicate":"flags.5"},{"name":"fourth_background_color","type":"int","predicate":"flags.6"},{"name":"intensity","type":"int","predicate":"flags.3","comment":"Intensity of the pattern when it is shown above the main background color, 0-100"},{"name":"rotation","type":"int","predicate":"flags.4","comment":"Clockwise rotation angle of the gradient, in degrees; 0-359. Should be always divisible by 45"}]},{"kind":"class","name":"autoDownloadSettings","type":"AutoDownloadSettings","id":3762434803,"comment":"Autodownload settings","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"disabled","type":"true","predicate":"flags.0","comment":"Disable automatic media downloads?"},{"name":"video_preload_large","type":"true","predicate":"flags.1"},{"name":"audio_preload_next","type":"true","predicate":"flags.2"},{"name":"phonecalls_less_data","type":"true","predicate":"flags.3"},{"name":"photo_size_max","type":"int"},{"name":"video_size_max","type":"int"},{"name":"file_size_max","type":"int"},{"name":"video_upload_maxbitrate","type":"int"}]},{"kind":"class","name":"account.autoDownloadSettings","type":"account.AutoDownloadSettings","id":1674235686,"comment":"Media autodownload settings","arguments":[{"name":"low","type":"AutoDownloadSettings","comment":"Low data usage preset"},{"name":"medium","type":"AutoDownloadSettings","comment":"Medium data usage preset"},{"name":"high","type":"AutoDownloadSettings","comment":"High data usage preset"}]},{"kind":"class","name":"emojiKeyword","type":"EmojiKeyword","id":3585325561,"comment":"Emoji keyword","arguments":[{"name":"keyword","type":"string","comment":"Keyword"},{"name":"emoticons","type":"Vector","comment":"Emojis associated to keyword"}]},{"kind":"class","name":"emojiKeywordDeleted","type":"EmojiKeyword","id":594408994,"comment":"Deleted emoji keyword","arguments":[{"name":"keyword","type":"string","comment":"Keyword"},{"name":"emoticons","type":"Vector","comment":"Emojis that were associated to keyword"}]},{"kind":"class","name":"emojiKeywordsDifference","type":"EmojiKeywordsDifference","id":1556570557,"comment":"Changes to emoji keywords","arguments":[{"name":"lang_code","type":"string"},{"name":"from_version","type":"int"},{"name":"version","type":"int","comment":"Current version of emoji keyword list"},{"name":"keywords","type":"Vector","comment":"Emojis associated to keywords"}]},{"kind":"class","name":"emojiURL","type":"EmojiURL","id":2775937949,"comment":"An HTTP URL which can be used to automatically log in into translation platform and suggest new emoji replacements. The URL will be valid for 30 seconds after generation","arguments":[{"name":"url","type":"string","comment":"An HTTP URL which can be used to automatically log in into translation platform and suggest new emoji replacements. The URL will be valid for 30 seconds after generation"}]},{"kind":"class","name":"emojiLanguage","type":"EmojiLanguage","id":3019592545,"comment":"Emoji language","arguments":[{"name":"lang_code","type":"string"}]},{"kind":"class","name":"folder","type":"Folder","id":4283715173,"comment":"Folder","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"autofill_new_broadcasts","type":"true","predicate":"flags.0"},{"name":"autofill_public_groups","type":"true","predicate":"flags.1"},{"name":"autofill_new_correspondents","type":"true","predicate":"flags.2"},{"name":"id","type":"int","comment":"Folder ID"},{"name":"title","type":"string","comment":"Folder title"},{"name":"photo","type":"ChatPhoto","predicate":"flags.3","comment":"Folder picture"}]},{"kind":"class","name":"inputFolderPeer","type":"InputFolderPeer","id":4224893590,"comment":"Peer in a folder","arguments":[{"name":"peer","type":"InputPeer","comment":"Peer"},{"name":"folder_id","type":"int"}]},{"kind":"class","name":"folderPeer","type":"FolderPeer","id":3921323624,"comment":"Peer in a folder","arguments":[{"name":"peer","type":"Peer","comment":"Folder peer info"},{"name":"folder_id","type":"int"}]},{"kind":"class","name":"messages.searchCounter","type":"messages.SearchCounter","id":3896830975,"comment":"Indicates how many results would be found by a {@link messages.search} call with the same parameters","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"inexact","type":"true","predicate":"flags.1","comment":"If set, the results may be inexact"},{"name":"filter","type":"MessagesFilter","comment":"Provided message filter"},{"name":"count","type":"int","comment":"Number of results that were found server-side"}]},{"kind":"class","name":"urlAuthResultRequest","type":"UrlAuthResult","id":2463316494,"comment":"Details about the authorization request, for more info click here »","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"request_write_access","type":"true","predicate":"flags.0"},{"name":"bot","type":"User","comment":"Username of a bot, which will be used for user authorization. If not specified, the current bot's username will be assumed. The url's domain must be the same as the domain linked with the bot. See Linking your domain to the bot for more details."},{"name":"domain","type":"string","comment":"The domain name of the website on which the user will log in."}]},{"kind":"class","name":"urlAuthResultAccepted","type":"UrlAuthResult","id":2408320590,"comment":"Details about an accepted authorization request, for more info click here »","arguments":[{"name":"url","type":"string","comment":"The URL name of the website on which the user has logged in."}]},{"kind":"class","name":"urlAuthResultDefault","type":"UrlAuthResult","id":2849430303,"comment":"Details about an accepted authorization request, for more info click here »","arguments":[]},{"kind":"class","name":"channelLocationEmpty","type":"ChannelLocation","id":3216354699,"comment":"No location (normal supergroup)","arguments":[]},{"kind":"class","name":"channelLocation","type":"ChannelLocation","id":547062491,"comment":"Geographical location of supergroup (geogroups)","arguments":[{"name":"geo_point","type":"GeoPoint"},{"name":"address","type":"string","comment":"Textual description of the address"}]},{"kind":"class","name":"peerLocated","type":"PeerLocated","id":3393592157,"comment":"Peer geolocated nearby","arguments":[{"name":"peer","type":"Peer","comment":"Peer"},{"name":"expires","type":"int","comment":"Validity period of current data"},{"name":"distance","type":"int","comment":"Distance from the peer in meters"}]},{"kind":"class","name":"peerSelfLocated","type":"PeerLocated","id":4176226379,"comment":"Current peer","arguments":[{"name":"expires","type":"int","comment":"Expiry of geolocation info for current peer"}]},{"kind":"class","name":"restrictionReason","type":"RestrictionReason","id":3497176244,"comment":"Contains the reason why access to a certain object must be restricted. Clients are supposed to deny access to the channel if the platform field is equal to all or to the current platform (ios, android, wp, etc.). Platforms can be concatenated (ios-android, ios-wp), unknown platforms are to be ignored. The text is the error message that should be shown to the user.\n\nRestriction reason.","arguments":[{"name":"platform","type":"string","comment":"Platform identifier (ios, android, wp, all, etc.), can be concatenated with a dash as separator (android-ios, ios-wp, etc)"},{"name":"reason","type":"string","comment":"Restriction reason (porno, terms, etc.)"},{"name":"text","type":"string","comment":"Error message to be shown to the user"}]},{"kind":"class","name":"inputTheme","type":"InputTheme","id":1012306921,"comment":"Theme","arguments":[{"name":"id","type":"long","comment":"ID"},{"name":"access_hash","type":"long"}]},{"kind":"class","name":"inputThemeSlug","type":"InputTheme","id":4119399921,"comment":"Theme by theme ID","arguments":[{"name":"slug","type":"string","comment":"Unique theme ID"}]},{"kind":"class","name":"theme","type":"Theme","id":2685298646,"comment":"Theme","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"creator","type":"true","predicate":"flags.0","comment":"Whether the current user is the creator of this theme"},{"name":"default","type":"true","predicate":"flags.1","comment":"Whether this is the default theme"},{"name":"for_chat","type":"true","predicate":"flags.5"},{"name":"id","type":"long","comment":"Theme ID"},{"name":"access_hash","type":"long"},{"name":"slug","type":"string","comment":"Unique theme ID"},{"name":"title","type":"string","comment":"Theme name"},{"name":"document","type":"Document","predicate":"flags.2","comment":"Theme"},{"name":"settings","type":"Vector","predicate":"flags.3","comment":"Theme settings"},{"name":"emoticon","type":"string","predicate":"flags.6"},{"name":"installs_count","type":"int","predicate":"flags.4"}]},{"kind":"class","name":"account.themesNotModified","type":"account.Themes","id":4095653410,"comment":"No new themes were installed","arguments":[]},{"kind":"class","name":"account.themes","type":"account.Themes","id":2587724909,"comment":"Installed themes","arguments":[{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"},{"name":"themes","type":"Vector","comment":"Themes"}]},{"kind":"class","name":"auth.loginToken","type":"auth.LoginToken","id":1654593920,"comment":"Login token (for QR code login)","arguments":[{"name":"expires","type":"int","comment":"Expiry date of QR code"},{"name":"token","type":"bytes","comment":"Token to render in QR code"}]},{"kind":"class","name":"auth.loginTokenMigrateTo","type":"auth.LoginToken","id":110008598,"comment":"Repeat the query to the specified DC","arguments":[{"name":"dc_id","type":"int"},{"name":"token","type":"bytes","comment":"Token to use for login"}]},{"kind":"class","name":"auth.loginTokenSuccess","type":"auth.LoginToken","id":957176926,"comment":"Login via token (QR code) succeded!","arguments":[{"name":"authorization","type":"auth.Authorization","comment":"Authorization info"}]},{"kind":"class","name":"account.contentSettings","type":"account.ContentSettings","id":1474462241,"comment":"Sensitive content settings","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"sensitive_enabled","type":"true","predicate":"flags.0"},{"name":"sensitive_can_change","type":"true","predicate":"flags.1"}]},{"kind":"class","name":"messages.inactiveChats","type":"messages.InactiveChats","id":2837970629,"comment":"Inactive chat list","arguments":[{"name":"dates","type":"Vector","comment":"When was the chat last active"},{"name":"chats","type":"Vector","comment":"Chat list"},{"name":"users","type":"Vector","comment":"Users mentioned in the chat list"}]},{"kind":"class","name":"baseThemeClassic","type":"BaseTheme","id":3282117730,"comment":"Classic theme","arguments":[]},{"kind":"class","name":"baseThemeDay","type":"BaseTheme","id":4225242760,"comment":"Day theme","arguments":[]},{"kind":"class","name":"baseThemeNight","type":"BaseTheme","id":3081969320,"comment":"Night theme","arguments":[]},{"kind":"class","name":"baseThemeTinted","type":"BaseTheme","id":1834973166,"comment":"Tinted theme","arguments":[]},{"kind":"class","name":"baseThemeArctic","type":"BaseTheme","id":1527845466,"comment":"Arctic theme","arguments":[]},{"kind":"class","name":"inputThemeSettings","type":"InputThemeSettings","id":2413711439,"comment":"Theme settings","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"message_colors_animated","type":"true","predicate":"flags.2"},{"name":"base_theme","type":"BaseTheme"},{"name":"accent_color","type":"int"},{"name":"outbox_accent_color","type":"int","predicate":"flags.3"},{"name":"message_colors","type":"Vector","predicate":"flags.0"},{"name":"wallpaper","type":"InputWallPaper","predicate":"flags.1","comment":"Wallpaper"},{"name":"wallpaper_settings","type":"WallPaperSettings","predicate":"flags.1"}]},{"kind":"class","name":"themeSettings","type":"ThemeSettings","id":4200117972,"comment":"Theme settings","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"message_colors_animated","type":"true","predicate":"flags.2"},{"name":"base_theme","type":"BaseTheme"},{"name":"accent_color","type":"int"},{"name":"outbox_accent_color","type":"int","predicate":"flags.3"},{"name":"message_colors","type":"Vector","predicate":"flags.0"},{"name":"wallpaper","type":"WallPaper","predicate":"flags.1","comment":"Wallpaper"}]},{"kind":"class","name":"webPageAttributeTheme","type":"WebPageAttribute","id":1421174295,"comment":"Page theme","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"documents","type":"Vector","predicate":"flags.0","comment":"Theme files"},{"name":"settings","type":"ThemeSettings","predicate":"flags.1","comment":"Theme settings"}]},{"kind":"class","name":"messageUserVote","type":"MessageUserVote","id":886196148,"comment":"How a user voted in a poll","arguments":[{"name":"user_id","type":"int53"},{"name":"option","type":"bytes","comment":"The option chosen by the user"},{"name":"date","type":"int","comment":"When did the user cast the vote"}]},{"kind":"class","name":"messageUserVoteInputOption","type":"MessageUserVote","id":1017491692,"comment":"How a user voted in a poll (reduced constructor, returned if an option was provided to {@link messages.getPollVotes})","arguments":[{"name":"user_id","type":"int53"},{"name":"date","type":"int","comment":"When did the user cast the vote"}]},{"kind":"class","name":"messageUserVoteMultiple","type":"MessageUserVote","id":2003431412,"comment":"How a user voted in a multiple-choice poll","arguments":[{"name":"user_id","type":"int53"},{"name":"options","type":"Vector","comment":"Options chosen by the user"},{"name":"date","type":"int","comment":"When did the user cast their votes"}]},{"kind":"class","name":"messages.votesList","type":"messages.VotesList","id":136574537,"comment":"How users voted in a poll","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"count","type":"int","comment":"Total number of votes for all options (or only for the chosen option, if provided to {@link messages.getPollVotes})"},{"name":"votes","type":"Vector","comment":"Vote info for each user"},{"name":"users","type":"Vector","comment":"Info about users that voted in the poll"},{"name":"next_offset","type":"string","predicate":"flags.0"}]},{"kind":"class","name":"bankCardOpenUrl","type":"BankCardOpenUrl","id":4117234314,"comment":"Credit card info URL provided by the bank","arguments":[{"name":"url","type":"string","comment":"Info URL"},{"name":"name","type":"string","comment":"Bank name"}]},{"kind":"class","name":"payments.bankCardData","type":"payments.BankCardData","id":1042605427,"comment":"Credit card info, provided by the card's bank(s)","arguments":[{"name":"title","type":"string","comment":"Credit card title"},{"name":"open_urls","type":"Vector"}]},{"kind":"class","name":"dialogFilter","type":"DialogFilter","id":1949890536,"comment":"Dialog filter AKA folder","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"contacts","type":"true","predicate":"flags.0","comment":"Whether to include all contacts in this folder"},{"name":"non_contacts","type":"true","predicate":"flags.1"},{"name":"groups","type":"true","predicate":"flags.2","comment":"Whether to include all groups in this folder"},{"name":"broadcasts","type":"true","predicate":"flags.3","comment":"Whether to include all channels in this folder"},{"name":"bots","type":"true","predicate":"flags.4","comment":"Whether to include all bots in this folder"},{"name":"exclude_muted","type":"true","predicate":"flags.11"},{"name":"exclude_read","type":"true","predicate":"flags.12"},{"name":"exclude_archived","type":"true","predicate":"flags.13"},{"name":"id","type":"int","comment":"Folder ID"},{"name":"title","type":"string","comment":"Folder name"},{"name":"emoticon","type":"string","predicate":"flags.25","comment":"Folder emoticon"},{"name":"pinned_peers","type":"Vector"},{"name":"include_peers","type":"Vector"},{"name":"exclude_peers","type":"Vector"}]},{"kind":"class","name":"dialogFilterSuggested","type":"DialogFilterSuggested","id":2004110666,"comment":"Suggested folders","arguments":[{"name":"filter","type":"DialogFilter","comment":"Folder info"},{"name":"description","type":"string","comment":"Folder description"}]},{"kind":"class","name":"statsDateRangeDays","type":"StatsDateRangeDays","id":3057118639,"comment":"Channel statistics date range","arguments":[{"name":"min_date","type":"int"},{"name":"max_date","type":"int"}]},{"kind":"class","name":"statsAbsValueAndPrev","type":"StatsAbsValueAndPrev","id":3410210014,"comment":"Statistics value couple; initial and final value for period of time currently in consideration","arguments":[{"name":"current","type":"double","comment":"Current value"},{"name":"previous","type":"double","comment":"Previous value"}]},{"kind":"class","name":"statsPercentValue","type":"StatsPercentValue","id":3419287520,"comment":"Channel statistics percentage.\nCompute the percentage simply by doing part * total / 100","arguments":[{"name":"part","type":"double","comment":"Partial value"},{"name":"total","type":"double","comment":"Total value"}]},{"kind":"class","name":"statsGraphAsync","type":"StatsGraph","id":1244130093,"comment":"This channel statistics graph must be generated asynchronously using {@link stats.loadAsyncGraph} to reduce server load","arguments":[{"name":"token","type":"string","comment":"Token to use for fetching the async graph"}]},{"kind":"class","name":"statsGraphError","type":"StatsGraph","id":3202127906,"comment":"An error occurred while generating the statistics graph","arguments":[{"name":"error","type":"string","comment":"The error"}]},{"kind":"class","name":"statsGraph","type":"StatsGraph","id":2393138358,"comment":"Channel statistics graph","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"json","type":"DataJSON","comment":"Statistics data"},{"name":"zoom_token","type":"string","predicate":"flags.0"}]},{"kind":"class","name":"messageInteractionCounters","type":"MessageInteractionCounters","id":2907687357,"comment":"Message interaction counters","arguments":[{"name":"msg_id","type":"int"},{"name":"views","type":"int","comment":"Views"},{"name":"forwards","type":"int","comment":"Number of times this message was forwarded"}]},{"kind":"class","name":"stats.broadcastStats","type":"stats.BroadcastStats","id":3187114900,"comment":"Channel statistics.","arguments":[{"name":"period","type":"StatsDateRangeDays","comment":"Period in consideration"},{"name":"followers","type":"StatsAbsValueAndPrev","comment":"Follower count change for period in consideration"},{"name":"views_per_post","type":"StatsAbsValueAndPrev"},{"name":"shares_per_post","type":"StatsAbsValueAndPrev"},{"name":"enabled_notifications","type":"StatsPercentValue"},{"name":"growth_graph","type":"StatsGraph"},{"name":"followers_graph","type":"StatsGraph"},{"name":"mute_graph","type":"StatsGraph"},{"name":"top_hours_graph","type":"StatsGraph"},{"name":"interactions_graph","type":"StatsGraph"},{"name":"iv_interactions_graph","type":"StatsGraph"},{"name":"views_by_source_graph","type":"StatsGraph"},{"name":"new_followers_by_source_graph","type":"StatsGraph"},{"name":"languages_graph","type":"StatsGraph"},{"name":"recent_message_interactions","type":"Vector"}]},{"kind":"class","name":"help.promoDataEmpty","type":"help.PromoData","id":2566302837,"comment":"No PSA/MTProxy info is available","arguments":[{"name":"expires","type":"int","comment":"Re-fetch PSA/MTProxy info after the specified number of seconds"}]},{"kind":"class","name":"help.promoData","type":"help.PromoData","id":2352576831,"comment":"MTProxy/Public Service Announcement information","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"proxy","type":"true","predicate":"flags.0","comment":"MTProxy-related channel"},{"name":"expires","type":"int","comment":"Expiry of PSA/MTProxy info"},{"name":"peer","type":"Peer","comment":"MTProxy/PSA peer"},{"name":"chats","type":"Vector","comment":"Chat info"},{"name":"users","type":"Vector","comment":"User info"},{"name":"psa_type","type":"string","predicate":"flags.1"},{"name":"psa_message","type":"string","predicate":"flags.2"}]},{"kind":"class","name":"videoSize","type":"VideoSize","id":3727929492,"comment":"Animated profile picture in MPEG4 format","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"type","type":"string","comment":"u for animated profile pictures, and v for trimmed and downscaled video previews"},{"name":"w","type":"int","comment":"Video width"},{"name":"h","type":"int","comment":"Video height"},{"name":"size","type":"int","comment":"File size"},{"name":"video_start_ts","type":"double","predicate":"flags.0"}]},{"kind":"class","name":"statsGroupTopPoster","type":"StatsGroupTopPoster","id":2634330011,"comment":"Information about an active user in a supergroup","arguments":[{"name":"user_id","type":"int53"},{"name":"messages","type":"int","comment":"Number of messages for statistics period in consideration"},{"name":"avg_chars","type":"int"}]},{"kind":"class","name":"statsGroupTopAdmin","type":"StatsGroupTopAdmin","id":3612888199,"comment":"Information about an active admin in a supergroup","arguments":[{"name":"user_id","type":"int53"},{"name":"deleted","type":"int","comment":"Number of deleted messages for statistics period in consideration"},{"name":"kicked","type":"int","comment":"Number of kicked users for statistics period in consideration"},{"name":"banned","type":"int","comment":"Number of banned users for statistics period in consideration"}]},{"kind":"class","name":"statsGroupTopInviter","type":"StatsGroupTopInviter","id":1398765469,"comment":"Information about an active supergroup inviter","arguments":[{"name":"user_id","type":"int53"},{"name":"invitations","type":"int","comment":"Number of invitations for statistics period in consideration"}]},{"kind":"class","name":"stats.megagroupStats","type":"stats.MegagroupStats","id":4018141462,"comment":"Supergroup statistics","arguments":[{"name":"period","type":"StatsDateRangeDays","comment":"Period in consideration"},{"name":"members","type":"StatsAbsValueAndPrev","comment":"Member count change for period in consideration"},{"name":"messages","type":"StatsAbsValueAndPrev","comment":"Message number change for period in consideration"},{"name":"viewers","type":"StatsAbsValueAndPrev","comment":"Number of users that viewed messages, for range in consideration"},{"name":"posters","type":"StatsAbsValueAndPrev","comment":"Number of users that posted messages, for range in consideration"},{"name":"growth_graph","type":"StatsGraph"},{"name":"members_graph","type":"StatsGraph"},{"name":"new_members_by_source_graph","type":"StatsGraph"},{"name":"languages_graph","type":"StatsGraph"},{"name":"messages_graph","type":"StatsGraph"},{"name":"actions_graph","type":"StatsGraph"},{"name":"top_hours_graph","type":"StatsGraph"},{"name":"weekdays_graph","type":"StatsGraph"},{"name":"top_posters","type":"Vector"},{"name":"top_admins","type":"Vector"},{"name":"top_inviters","type":"Vector"},{"name":"users","type":"Vector","comment":"Info about users mentioned in statistics"}]},{"kind":"class","name":"globalPrivacySettings","type":"GlobalPrivacySettings","id":3198350372,"comment":"Global privacy settings","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"archive_and_mute_new_noncontact_peers","type":"Bool","predicate":"flags.0"}]},{"kind":"class","name":"help.countryCode","type":"help.CountryCode","id":1107543535,"comment":"Country code and phone number pattern of a specific country","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"country_code","type":"string"},{"name":"prefixes","type":"Vector","predicate":"flags.0","comment":"Possible phone prefixes"},{"name":"patterns","type":"Vector","predicate":"flags.1","comment":"Phone patterns: for example, XXX XXX XXX"}]},{"kind":"class","name":"help.country","type":"help.Country","id":3280440867,"comment":"Name, ISO code, localized name and phone codes/patterns of a specific country","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"hidden","type":"true","predicate":"flags.0","comment":"Whether this country should not be shown in the list"},{"name":"iso2","type":"string","comment":"ISO code of country"},{"name":"default_name","type":"string"},{"name":"name","type":"string","predicate":"flags.1","comment":"Name of the country in the user's language, if different from the original name"},{"name":"country_codes","type":"Vector"}]},{"kind":"class","name":"help.countriesListNotModified","type":"help.CountriesList","id":2479628082,"comment":"The country list has not changed","arguments":[]},{"kind":"class","name":"help.countriesList","type":"help.CountriesList","id":2278585758,"comment":"Name, ISO code, localized name and phone codes/patterns of all available countries","arguments":[{"name":"countries","type":"Vector","comment":"Name, ISO code, localized name and phone codes/patterns of all available countries"},{"name":"hash","type":"int","comment":"Hash for pagination, for more info click here"}]},{"kind":"class","name":"messageViews","type":"MessageViews","id":1163625789,"comment":"View, forward counter + info about replies of a specific message","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"views","type":"int","predicate":"flags.0","comment":"Viewcount of message"},{"name":"forwards","type":"int","predicate":"flags.1","comment":"Forward count of message"},{"name":"replies","type":"MessageReplies","predicate":"flags.2","comment":"Reply and thread information of message"}]},{"kind":"class","name":"messages.messageViews","type":"messages.MessageViews","id":3066361155,"comment":"View, forward counter + info about replies","arguments":[{"name":"views","type":"Vector","comment":"View, forward counter + info about replies"},{"name":"chats","type":"Vector","comment":"Chats mentioned in constructor"},{"name":"users","type":"Vector","comment":"Users mentioned in constructor"}]},{"kind":"class","name":"messages.discussionMessage","type":"messages.DiscussionMessage","id":2788431746,"comment":"Information about a message thread","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"messages","type":"Vector","comment":"Discussion messages"},{"name":"max_id","type":"int","predicate":"flags.0"},{"name":"read_inbox_max_id","type":"int","predicate":"flags.1"},{"name":"read_outbox_max_id","type":"int","predicate":"flags.2"},{"name":"unread_count","type":"int"},{"name":"chats","type":"Vector","comment":"Chats mentioned in constructor"},{"name":"users","type":"Vector","comment":"Users mentioned in constructor"}]},{"kind":"class","name":"messageReplyHeader","type":"MessageReplyHeader","id":2799007587,"comment":"Message replies and thread information","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"reply_to_msg_id","type":"int"},{"name":"reply_to_peer_id","type":"Peer","predicate":"flags.0"},{"name":"reply_to_top_id","type":"int","predicate":"flags.1"}]},{"kind":"class","name":"messageReplies","type":"MessageReplies","id":2211844034,"comment":"Info about the comment section of a channel post, or a simple message thread","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"comments","type":"true","predicate":"flags.0","comment":"Whether this constructor contains information about the comment section of a channel post, or a simple message thread"},{"name":"replies","type":"int","comment":"Contains the total number of replies in this thread or comment section."},{"name":"replies_pts","type":"int"},{"name":"recent_repliers","type":"Vector","predicate":"flags.1"},{"name":"channel_id","type":"int53","predicate":"flags.0"},{"name":"max_id","type":"int","predicate":"flags.2"},{"name":"read_max_id","type":"int","predicate":"flags.3"}]},{"kind":"class","name":"peerBlocked","type":"PeerBlocked","id":3908927508,"comment":"Information about a blocked peer","arguments":[{"name":"peer_id","type":"Peer"},{"name":"date","type":"int","comment":"When was the peer blocked"}]},{"kind":"class","name":"stats.messageStats","type":"stats.MessageStats","id":2308567701,"comment":"Message statistics","arguments":[{"name":"views_graph","type":"StatsGraph"}]},{"kind":"class","name":"groupCallDiscarded","type":"GroupCall","id":2004925620,"comment":"An ended group call","arguments":[{"name":"id","type":"long","comment":"Group call ID"},{"name":"access_hash","type":"long"},{"name":"duration","type":"int","comment":"Group call duration"}]},{"kind":"class","name":"groupCall","type":"GroupCall","id":3583468812,"comment":"Info about a group call or livestream","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"join_muted","type":"true","predicate":"flags.1"},{"name":"can_change_join_muted","type":"true","predicate":"flags.2"},{"name":"join_date_asc","type":"true","predicate":"flags.6"},{"name":"schedule_start_subscribed","type":"true","predicate":"flags.8"},{"name":"can_start_video","type":"true","predicate":"flags.9"},{"name":"record_video_active","type":"true","predicate":"flags.11"},{"name":"id","type":"long","comment":"Group call ID"},{"name":"access_hash","type":"long"},{"name":"participants_count","type":"int"},{"name":"title","type":"string","predicate":"flags.3","comment":"Group call title"},{"name":"stream_dc_id","type":"int","predicate":"flags.4"},{"name":"record_start_date","type":"int","predicate":"flags.5"},{"name":"schedule_date","type":"int","predicate":"flags.7"},{"name":"unmuted_video_count","type":"int","predicate":"flags.10"},{"name":"unmuted_video_limit","type":"int"},{"name":"version","type":"int","comment":"Version"}]},{"kind":"class","name":"inputGroupCall","type":"InputGroupCall","id":3635053583,"comment":"Points to a specific group call","arguments":[{"name":"id","type":"long","comment":"Group call ID"},{"name":"access_hash","type":"long"}]},{"kind":"class","name":"groupCallParticipant","type":"GroupCallParticipant","id":3953538814,"comment":"Info about a group call participant","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"muted","type":"true","predicate":"flags.0","comment":"Whether the participant is muted"},{"name":"left","type":"true","predicate":"flags.1","comment":"Whether the participant has left"},{"name":"can_self_unmute","type":"true","predicate":"flags.2"},{"name":"just_joined","type":"true","predicate":"flags.4"},{"name":"versioned","type":"true","predicate":"flags.5","comment":"If set, and {@link updateGroupCallParticipants}.version < locally stored call.version, info about this participant should be ignored. If (...), and {@link updateGroupCallParticipants}.version > call.version+1, the participant list should be refetched using {@link phone.getGroupParticipants}."},{"name":"min","type":"true","predicate":"flags.8","comment":"If not set, the volume and muted_by_you fields can be safely used to overwrite locally cached information; otherwise, volume will contain valid information only if volume_by_admin is set both in the cache and in the received constructor."},{"name":"muted_by_you","type":"true","predicate":"flags.9"},{"name":"volume_by_admin","type":"true","predicate":"flags.10"},{"name":"self","type":"true","predicate":"flags.12","comment":"Whether this participant is the current user"},{"name":"video_joined","type":"true","predicate":"flags.15"},{"name":"peer","type":"Peer","comment":"Peer information"},{"name":"date","type":"int","comment":"When did this participant join the group call"},{"name":"active_date","type":"int","predicate":"flags.3"},{"name":"source","type":"int","comment":"Source ID"},{"name":"volume","type":"int","predicate":"flags.7","comment":"Volume, if not set the volume is set to 100%."},{"name":"about","type":"string","predicate":"flags.11","comment":"Info about this participant"},{"name":"raise_hand_rating","type":"long","predicate":"flags.13"},{"name":"video","type":"GroupCallParticipantVideo","predicate":"flags.6","comment":"Info about the video stream the participant is currently broadcasting"},{"name":"presentation","type":"GroupCallParticipantVideo","predicate":"flags.14","comment":"Info about the screen sharing stream the participant is currently broadcasting"}]},{"kind":"class","name":"phone.groupCall","type":"phone.GroupCall","id":2658302637,"comment":"Contains info about a group call, and partial info about its participants.","arguments":[{"name":"call","type":"GroupCall","comment":"Info about the group call"},{"name":"participants","type":"Vector","comment":"A partial list of participants."},{"name":"participants_next_offset","type":"string"},{"name":"chats","type":"Vector","comment":"Chats mentioned in the participants vector"},{"name":"users","type":"Vector","comment":"Users mentioned in the participants vector"}]},{"kind":"class","name":"phone.groupParticipants","type":"phone.GroupParticipants","id":4101460406,"comment":"Info about the participants of a group call or livestream","arguments":[{"name":"count","type":"int","comment":"Number of participants"},{"name":"participants","type":"Vector","comment":"List of participants"},{"name":"next_offset","type":"string"},{"name":"chats","type":"Vector","comment":"Mentioned chats"},{"name":"users","type":"Vector","comment":"Mentioned users"},{"name":"version","type":"int","comment":"Version info"}]},{"kind":"class","name":"inlineQueryPeerTypeSameBotPM","type":"InlineQueryPeerType","id":813821341,"comment":"The inline query was sent in a private chat with the bot itself","arguments":[]},{"kind":"class","name":"inlineQueryPeerTypePM","type":"InlineQueryPeerType","id":2201751468,"comment":"The inline query was sent in a private chat","arguments":[]},{"kind":"class","name":"inlineQueryPeerTypeChat","type":"InlineQueryPeerType","id":3613836554,"comment":"The inline query was sent in a chat","arguments":[]},{"kind":"class","name":"inlineQueryPeerTypeMegagroup","type":"InlineQueryPeerType","id":1589952067,"comment":"The inline query was sent in a supergroup","arguments":[]},{"kind":"class","name":"inlineQueryPeerTypeBroadcast","type":"InlineQueryPeerType","id":1664413338,"comment":"The inline query was sent in a channel","arguments":[]},{"kind":"class","name":"messages.historyImport","type":"messages.HistoryImport","id":375566091,"comment":"ID of a specific chat import session, click here for more info ».","arguments":[{"name":"id","type":"long","comment":"History import ID"}]},{"kind":"class","name":"messages.historyImportParsed","type":"messages.HistoryImportParsed","id":1578088377,"comment":"Contains information about a chat export file generated by a foreign chat app, click here for more info.\nIf neither the pm or group flags are set, the specified chat export was generated from a chat of unknown type.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"pm","type":"true","predicate":"flags.0","comment":"The chat export file was generated from a private chat."},{"name":"group","type":"true","predicate":"flags.1","comment":"The chat export file was generated from a group chat."},{"name":"title","type":"string","predicate":"flags.2","comment":"Title of the chat."}]},{"kind":"class","name":"messages.affectedFoundMessages","type":"messages.AffectedFoundMessages","id":4019011180,"comment":"Messages found and affected by changes","arguments":[{"name":"pts","type":"int","comment":"Event count after generation"},{"name":"pts_count","type":"int"},{"name":"offset","type":"int","comment":"If bigger than zero, the request must be repeated to remove more messages"},{"name":"messages","type":"Vector","comment":"Affected message IDs"}]},{"kind":"class","name":"chatInviteImporter","type":"ChatInviteImporter","id":2354765785,"comment":"When and which user joined the chat using a chat invite","arguments":[{"name":"flags","type":"#"},{"name":"requested","type":"true","predicate":"flags.0"},{"name":"user_id","type":"int53"},{"name":"date","type":"int","comment":"When did the user join"},{"name":"about","type":"string","predicate":"flags.2"},{"name":"approved_by","type":"long","predicate":"flags.1"}]},{"kind":"class","name":"messages.exportedChatInvites","type":"messages.ExportedChatInvites","id":3183881676,"comment":"Info about chat invites exported by a certain admin.","arguments":[{"name":"count","type":"int","comment":"Number of invites exported by the admin"},{"name":"invites","type":"Vector","comment":"Exported invites"},{"name":"users","type":"Vector","comment":"Info about the admin"}]},{"kind":"class","name":"messages.exportedChatInvite","type":"messages.ExportedChatInvite","id":410107472,"comment":"Info about a chat invite","arguments":[{"name":"invite","type":"ExportedChatInvite","comment":"Info about the chat invite"},{"name":"users","type":"Vector","comment":"Mentioned users"}]},{"kind":"class","name":"messages.exportedChatInviteReplaced","type":"messages.ExportedChatInvite","id":572915951,"comment":"The specified chat invite was replaced with another one","arguments":[{"name":"invite","type":"ExportedChatInvite","comment":"The replaced chat invite"},{"name":"new_invite","type":"ExportedChatInvite"},{"name":"users","type":"Vector","comment":"Mentioned users"}]},{"kind":"class","name":"messages.chatInviteImporters","type":"messages.ChatInviteImporters","id":2176233482,"comment":"Info about the users that joined the chat using a specific chat invite","arguments":[{"name":"count","type":"int","comment":"Number of users that joined"},{"name":"importers","type":"Vector","comment":"The users that joined"},{"name":"users","type":"Vector","comment":"The users that joined"}]},{"kind":"class","name":"chatAdminWithInvites","type":"ChatAdminWithInvites","id":4075613987,"comment":"Info about chat invites generated by admins.","arguments":[{"name":"admin_id","type":"int53"},{"name":"invites_count","type":"int"},{"name":"revoked_invites_count","type":"int"}]},{"kind":"class","name":"messages.chatAdminsWithInvites","type":"messages.ChatAdminsWithInvites","id":3063640791,"comment":"Info about chat invites generated by admins.","arguments":[{"name":"admins","type":"Vector","comment":"Info about chat invites generated by admins."},{"name":"users","type":"Vector","comment":"Mentioned users"}]},{"kind":"class","name":"messages.checkedHistoryImportPeer","type":"messages.CheckedHistoryImportPeer","id":2723014423,"comment":"Contains a confirmation text to be shown to the user, upon importing chat history, click here for more info ».","arguments":[{"name":"confirm_text","type":"string"}]},{"kind":"class","name":"phone.joinAsPeers","type":"phone.JoinAsPeers","id":2951045695,"comment":"A list of peers that can be used to join a group call, presenting yourself as a specific user/channel.","arguments":[{"name":"peers","type":"Vector","comment":"Peers"},{"name":"chats","type":"Vector","comment":"Chats mentioned in the peers vector"},{"name":"users","type":"Vector","comment":"Users mentioned in the peers vector"}]},{"kind":"class","name":"phone.exportedGroupCallInvite","type":"phone.ExportedGroupCallInvite","id":541839704,"comment":"An invite to a group call or livestream","arguments":[{"name":"link","type":"string","comment":"Invite link"}]},{"kind":"class","name":"groupCallParticipantVideoSourceGroup","type":"GroupCallParticipantVideoSourceGroup","id":3702593719,"comment":"Describes a group of video synchronization source identifiers","arguments":[{"name":"semantics","type":"string","comment":"SDP semantics"},{"name":"sources","type":"Vector","comment":"Source IDs"}]},{"kind":"class","name":"groupCallParticipantVideo","type":"GroupCallParticipantVideo","id":1735736008,"comment":"Info about a video stream","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"paused","type":"true","predicate":"flags.0","comment":"Whether the stream is currently paused"},{"name":"endpoint","type":"string","comment":"Endpoint"},{"name":"source_groups","type":"Vector"},{"name":"audio_source","type":"int","predicate":"flags.1"}]},{"kind":"class","name":"stickers.suggestedShortName","type":"stickers.SuggestedShortName","id":2248056895,"comment":"A suggested short name for a stickerpack","arguments":[{"name":"short_name","type":"string"}]},{"kind":"class","name":"botCommandScopeDefault","type":"BotCommandScope","id":795652779,"comment":"The commands will be valid in all dialogs","arguments":[]},{"kind":"class","name":"botCommandScopeUsers","type":"BotCommandScope","id":1011811544,"comment":"The specified bot commands will only be valid in all private chats with users.","arguments":[]},{"kind":"class","name":"botCommandScopeChats","type":"BotCommandScope","id":1877059713,"comment":"The specified bot commands will be valid in all groups and supergroups.","arguments":[]},{"kind":"class","name":"botCommandScopeChatAdmins","type":"BotCommandScope","id":3114950762,"comment":"The specified bot commands will be valid only for chat administrators, in all groups and supergroups.","arguments":[]},{"kind":"class","name":"botCommandScopePeer","type":"BotCommandScope","id":3684534653,"comment":"The specified bot commands will be valid only in a specific dialog.","arguments":[{"name":"peer","type":"InputPeer","comment":"The dialog"}]},{"kind":"class","name":"botCommandScopePeerAdmins","type":"BotCommandScope","id":1071145937,"comment":"The specified bot commands will be valid for all admins of the specified group or supergroup.","arguments":[{"name":"peer","type":"InputPeer","comment":"The chat"}]},{"kind":"class","name":"botCommandScopePeerUser","type":"BotCommandScope","id":169026035,"comment":"The specified bot commands will be valid only for a specific user in the specified group or supergroup.","arguments":[{"name":"peer","type":"InputPeer","comment":"The chat"},{"name":"user_id","type":"InputUser"}]},{"kind":"class","name":"account.resetPasswordFailedWait","type":"account.ResetPasswordResult","id":3816265825,"comment":"You recently requested a password reset that was canceled, please wait until the specified date before requesting another reset.","arguments":[{"name":"retry_date","type":"int"}]},{"kind":"class","name":"account.resetPasswordRequestedWait","type":"account.ResetPasswordResult","id":3924819069,"comment":"You successfully requested a password reset, please wait until the specified date before finalizing the reset.","arguments":[{"name":"until_date","type":"int"}]},{"kind":"class","name":"account.resetPasswordOk","type":"account.ResetPasswordResult","id":3911636542,"comment":"The 2FA password was reset successfully.","arguments":[]},{"kind":"class","name":"sponsoredMessage","type":"SponsoredMessage","id":3511804314,"comment":"A sponsored message","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"random_id","type":"bytes"},{"name":"from_id","type":"Peer"},{"name":"channel_post","type":"int","predicate":"flags.2"},{"name":"start_param","type":"string","predicate":"flags.0"},{"name":"message","type":"string","comment":"Sponsored message"},{"name":"entities","type":"Vector","predicate":"flags.1","comment":"Message entities for styled text"}]},{"kind":"class","name":"messages.sponsoredMessages","type":"messages.SponsoredMessages","id":1705297877,"comment":"A set of sponsored messages associated to a channel","arguments":[{"name":"messages","type":"Vector","comment":"Sponsored messages"},{"name":"chats","type":"Vector","comment":"Chats mentioned in the sponsored messages"},{"name":"users","type":"Vector","comment":"Users mentioned in the sponsored messages"}]},{"kind":"class","name":"searchResultsCalendarPeriod","type":"SearchResultsCalendarPeriod","id":3383776159,"arguments":[{"name":"date","type":"int"},{"name":"min_msg_id","type":"int"},{"name":"max_msg_id","type":"int"},{"name":"count","type":"int"}]},{"kind":"class","name":"messages.searchResultsCalendar","type":"messages.SearchResultsCalendar","id":343859772,"arguments":[{"name":"flags","type":"#"},{"name":"inexact","type":"true","predicate":"flags.0"},{"name":"count","type":"int"},{"name":"min_date","type":"int"},{"name":"min_msg_id","type":"int"},{"name":"offset_id_offset","type":"int","predicate":"flags.1"},{"name":"periods","type":"Vector"},{"name":"messages","type":"Vector"},{"name":"chats","type":"Vector"},{"name":"users","type":"Vector"}]},{"kind":"class","name":"searchResultPosition","type":"SearchResultsPosition","id":2137295719,"arguments":[{"name":"msg_id","type":"int"},{"name":"date","type":"int"},{"name":"offset","type":"int"}]},{"kind":"class","name":"messages.searchResultsPositions","type":"messages.SearchResultsPositions","id":1404185519,"arguments":[{"name":"count","type":"int"},{"name":"positions","type":"Vector"}]},{"kind":"class","name":"chatTheme","id":3976944691,"type":"ChatTheme","arguments":[{"name":"emoticon","type":"string","comment":"Emoji, identifying this specific chat theme"},{"name":"theme","type":"Theme","comment":"Theme"},{"name":"dark_theme","type":"Theme"}],"comment":"A chat theme"},{"kind":"class","name":"account.chatThemesNotModified","id":3759268292,"type":"account.ChatThemes","arguments":[],"comment":"The available chat themes were not modified"},{"kind":"class","name":"account.chatThemes","id":4266442429,"type":"account.ChatThemes","arguments":[{"name":"hash","type":"int","comment":"Hash for pagination, for more info click here"},{"name":"themes","type":"Vector","comment":"Available chat themes"}],"comment":"Available chat themes"},{"kind":"class","name":"dummyUpdate","id":1461556981,"type":"Update","arguments":[{"name":"pts","type":"int"},{"name":"pts_count","type":"int"},{"name":"channel_id","type":"int53"}]},{"kind":"class","name":"updateMessageReactions","id":357013699,"type":"Update","arguments":[{"name":"peer","type":"Peer","comment":"Peer"},{"name":"msg_id","type":"int"},{"name":"reactions","type":"MessageReactions","comment":"Reactions"}],"comment":"New message reactions are available"},{"kind":"class","name":"messageReactions","id":3095012561,"type":"MessageReactions","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"min","type":"true","predicate":"flags.0","comment":"Similar to min objects, used for message reaction constructors that are the same for all users so they don't have the reactions sent by the current user (you can use {@link messages.getMessagesReactions} to get the full reaction info)."},{"name":"results","type":"Vector","comment":"Reactions"}],"comment":"Message reactions"},{"kind":"class","name":"reactionCount","id":1873957073,"type":"ReactionCount","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"chosen","type":"true","predicate":"flags.0","comment":"Whether the current user sent this reaction"},{"name":"reaction","type":"string","comment":"Reaction (a UTF8 emoji)"},{"name":"count","type":"int","comment":"NUmber of users that reacted with this emoji"}],"comment":"Reactions"},{"kind":"class","name":"messageReactionsList","id":3819856136,"type":"MessageReactionsList","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"count","type":"int","comment":"Total number of reactions"},{"name":"reactions","type":"Vector","comment":"Reactions"},{"name":"users","type":"Vector","comment":"Users that reacted like this"},{"name":"next_offset","type":"string","predicate":"flags.0"}]},{"kind":"class","name":"messageUserReaction","id":3530022076,"type":"MessageUserReaction","arguments":[{"name":"user_id","type":"int"},{"name":"reaction","type":"string","comment":"Reaction (UTF8 emoji)"}],"comment":"Message reaction"},{"kind":"method","name":"test.useError","id":4000689921,"type":"Error","arguments":[]},{"kind":"method","name":"test.useConfigSimple","id":4189565501,"type":"help.ConfigSimple","arguments":[]},{"kind":"method","name":"invokeAfterMsg","type":"X","id":3416209197,"comment":"Invokes a query after successfull completion of one of the previous queries.","generics":[{"name":"X","type":"Type"}],"arguments":[{"name":"msg_id","type":"long"},{"name":"query","type":"!X","comment":"The query itself"}],"available":"both"},{"kind":"method","name":"invokeAfterMsgs","type":"X","id":1036301552,"comment":"Invokes a query after a successfull completion of previous queries","generics":[{"name":"X","type":"Type"}],"arguments":[{"name":"msg_ids","type":"Vector"},{"name":"query","type":"!X","comment":"The query itself"}],"available":"both"},{"kind":"method","name":"initConnection","type":"X","id":3251461801,"comment":"Initialize connection","generics":[{"name":"X","type":"Type"}],"arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"api_id","type":"int"},{"name":"device_model","type":"string"},{"name":"system_version","type":"string"},{"name":"app_version","type":"string"},{"name":"system_lang_code","type":"string"},{"name":"lang_pack","type":"string"},{"name":"lang_code","type":"string"},{"name":"proxy","type":"InputClientProxy","predicate":"flags.0","comment":"Info about an MTProto proxy"},{"name":"params","type":"JSONValue","predicate":"flags.1","comment":"Additional initConnection parameters.
For now, only the tz_offset field is supported, for specifying timezone offset in seconds."},{"name":"query","type":"!X","comment":"The query itself"}],"throws":[{"code":400,"name":"CONNECTION_LAYER_INVALID","comment":"Layer invalid."}],"available":"both"},{"kind":"method","name":"invokeWithLayer","type":"X","id":3667594509,"comment":"Invoke the specified query using the specified API layer","generics":[{"name":"X","type":"Type"}],"arguments":[{"name":"layer","type":"int","comment":"The layer to use"},{"name":"query","type":"!X","comment":"The query"}],"throws":[{"code":400,"name":"AUTH_BYTES_INVALID","comment":"The provided authorization is invalid."},{"code":400,"name":"CDN_METHOD_INVALID","comment":"You can't call this method in a CDN DC."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"CONNECTION_API_ID_INVALID","comment":"The provided API id is invalid."},{"code":400,"name":"CONNECTION_NOT_INITED","comment":"Connection not initialized."},{"code":400,"name":"INPUT_LAYER_INVALID","comment":"The provided layer is invalid."},{"code":400,"name":"INVITE_HASH_EXPIRED","comment":"The invite link has expired."}],"available":"both"},{"kind":"method","name":"invokeWithoutUpdates","type":"X","id":3214170551,"comment":"Invoke a request without subscribing the used connection for updates (this is enabled by default for file queries).","generics":[{"name":"X","type":"Type"}],"arguments":[{"name":"query","type":"!X","comment":"The query"}],"available":"both"},{"kind":"method","name":"invokeWithMessagesRange","type":"X","id":911373810,"comment":"Invoke with the given message range","generics":[{"name":"X","type":"Type"}],"arguments":[{"name":"range","type":"MessageRange","comment":"Message range"},{"name":"query","type":"!X","comment":"Query"}],"available":"both"},{"kind":"method","name":"invokeWithTakeout","type":"X","id":2896821550,"comment":"Invoke a method within a takeout session","generics":[{"name":"X","type":"Type"}],"arguments":[{"name":"takeout_id","type":"long"},{"name":"query","type":"!X","comment":"Query"}],"available":"both"},{"kind":"method","name":"auth.sendCode","type":"auth.SentCode","id":2792825935,"comment":"Send the verification code for login","arguments":[{"name":"phone_number","type":"string"},{"name":"api_id","type":"int"},{"name":"api_hash","type":"string"},{"name":"settings","type":"CodeSettings","comment":"Settings for the code type to send"}],"throws":[{"code":400,"name":"API_ID_INVALID","comment":"API ID invalid."},{"code":400,"name":"API_ID_PUBLISHED_FLOOD","comment":"This API id was published somewhere, you can't use it now."},{"code":401,"name":"AUTH_KEY_PERM_EMPTY","comment":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":303,"name":"NETWORK_MIGRATE_X","comment":"Repeat the query to data-center X."},{"code":303,"name":"PHONE_MIGRATE_X","comment":"Repeat the query to data-center X."},{"code":400,"name":"PHONE_NUMBER_APP_SIGNUP_FORBIDDEN","comment":"You can't sign up using this app."},{"code":400,"name":"PHONE_NUMBER_BANNED","comment":"The provided phone number is banned from telegram."},{"code":400,"name":"PHONE_NUMBER_FLOOD","comment":"You asked for the code too many times."},{"code":400,"name":"PHONE_NUMBER_INVALID","comment":"Invalid phone number."},{"code":406,"name":"PHONE_PASSWORD_FLOOD","comment":"You have tried logging in too many times."},{"code":400,"name":"PHONE_PASSWORD_PROTECTED","comment":"This phone is password protected."},{"code":400,"name":"SMS_CODE_CREATE_FAILED","comment":"An error occurred while creating the SMS code."}],"available":"user"},{"kind":"method","name":"auth.signUp","type":"auth.Authorization","id":2163139623,"comment":"Registers a validated phone number in the system.","arguments":[{"name":"phone_number","type":"string"},{"name":"phone_code_hash","type":"string"},{"name":"first_name","type":"string"},{"name":"last_name","type":"string"}],"throws":[{"code":400,"name":"FIRSTNAME_INVALID","comment":"Invalid first name."},{"code":400,"name":"LASTNAME_INVALID","comment":"Invalid last name."},{"code":400,"name":"PHONE_CODE_EMPTY","comment":"phone_code from a SMS is empty."},{"code":400,"name":"PHONE_CODE_EXPIRED","comment":"SMS expired."},{"code":400,"name":"PHONE_CODE_INVALID","comment":"Invalid SMS code was sent."},{"code":400,"name":"PHONE_NUMBER_FLOOD","comment":"You asked for the code too many times."},{"code":400,"name":"PHONE_NUMBER_INVALID","comment":"Invalid phone number."},{"code":400,"name":"PHONE_NUMBER_OCCUPIED","comment":"The phone number is already in use."}],"available":"user"},{"kind":"method","name":"auth.signIn","type":"auth.Authorization","id":3168081281,"comment":"Signs in a user with a validated phone number.","arguments":[{"name":"phone_number","type":"string"},{"name":"phone_code_hash","type":"string"},{"name":"phone_code","type":"string"}],"throws":[{"code":400,"name":"PHONE_CODE_EMPTY","comment":"phone_code from the SMS is empty."},{"code":400,"name":"PHONE_CODE_EXPIRED","comment":"SMS expired."},{"code":400,"name":"PHONE_CODE_INVALID","comment":"Invalid SMS code was sent."},{"code":400,"name":"PHONE_NUMBER_INVALID","comment":"Invalid phone number."},{"code":400,"name":"PHONE_NUMBER_UNOCCUPIED","comment":"The code is valid but no user with the given number is registered."}],"available":"user"},{"kind":"method","name":"auth.logOut","type":"Bool","id":1461180992,"comment":"Logs out the user.","arguments":[],"available":"both"},{"kind":"method","name":"auth.resetAuthorizations","type":"Bool","id":2678787354,"comment":"After calling this method it is necessary to reregister the current device using the method {@link account.registerDevice}\n\nTerminates all user's authorized sessions except for the current one.","arguments":[],"throws":[{"code":406,"name":"FRESH_RESET_AUTHORISATION_FORBIDDEN","comment":"You can't logout other sessions if less than 24 hours have passed since you logged on the current session."}],"available":"user"},{"kind":"method","name":"auth.exportAuthorization","type":"auth.ExportedAuthorization","id":3854565325,"comment":"Returns data for copying authorization to another data-centre.","arguments":[{"name":"dc_id","type":"int"}],"throws":[{"code":400,"name":"DC_ID_INVALID","comment":"The provided DC ID is invalid."}],"available":"both"},{"kind":"method","name":"auth.importAuthorization","type":"auth.Authorization","id":2776268205,"comment":"Logs in a user using a key transmitted from their native data-centre.","arguments":[{"name":"id","type":"long","comment":"User ID"},{"name":"bytes","type":"bytes","comment":"Authorization key"}],"throws":[{"code":400,"name":"AUTH_BYTES_INVALID","comment":"The provided authorization is invalid."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"both"},{"kind":"method","name":"auth.bindTempAuthKey","type":"Bool","id":3453233669,"comment":"For more information, see Perfect Forward Secrecy.\n\nBinds a temporary authorization key temp_auth_key_id to the permanent authorization key perm_auth_key_id. Each permanent key may only be bound to one temporary key at a time, binding a new temporary key overwrites the previous one.","arguments":[{"name":"perm_auth_key_id","type":"long"},{"name":"nonce","type":"long","comment":"Random long from Binding message contents"},{"name":"expires_at","type":"int"},{"name":"encrypted_message","type":"bytes"}],"throws":[{"code":400,"name":"ENCRYPTED_MESSAGE_INVALID","comment":"Encrypted message is incorrect."},{"code":400,"name":"TEMP_AUTH_KEY_ALREADY_BOUND","comment":"The passed temporary key is already bound to another perm_auth_key_id."},{"code":400,"name":"TEMP_AUTH_KEY_EMPTY","comment":"The request was not performed with a temporary authorization key."}],"available":"both"},{"kind":"method","name":"auth.importBotAuthorization","type":"auth.Authorization","id":1738800940,"comment":"Login as a bot","arguments":[{"name":"flags","type":"int","comment":"Reserved for future use"},{"name":"api_id","type":"int"},{"name":"api_hash","type":"string"},{"name":"bot_auth_token","type":"string"}],"throws":[{"code":400,"name":"ACCESS_TOKEN_EXPIRED","comment":"Bot token expired."},{"code":400,"name":"ACCESS_TOKEN_INVALID","comment":"The provided token is not valid."},{"code":400,"name":"API_ID_INVALID","comment":"The api_id/api_hash combination is invalid."},{"code":400,"name":"API_ID_PUBLISHED_FLOOD","comment":"This API id was published somewhere, you can't use it now."},{"code":401,"name":"AUTH_KEY_INVALID","comment":"Auth key invalid."}],"available":"both"},{"kind":"method","name":"auth.checkPassword","type":"auth.Authorization","id":3515567382,"comment":"Try logging to an account protected by a 2FA password.","arguments":[{"name":"password","type":"InputCheckPasswordSRP","comment":"The account's password (see SRP)"}],"throws":[{"code":400,"name":"PASSWORD_HASH_INVALID","comment":"The provided password isn't valid."},{"code":400,"name":"SRP_ID_INVALID","comment":"Invalid SRP ID provided."},{"code":400,"name":"SRP_PASSWORD_CHANGED","comment":"Password has changed."}],"available":"user"},{"kind":"method","name":"auth.requestPasswordRecovery","type":"auth.PasswordRecovery","id":3633822822,"comment":"Request recovery code of a 2FA password, only for accounts with a recovery email configured.","arguments":[],"throws":[{"code":400,"name":"PASSWORD_EMPTY","comment":"The provided password is empty."},{"code":400,"name":"PASSWORD_RECOVERY_NA","comment":"No email was set, can't recover password via email."}],"available":"user"},{"kind":"method","name":"auth.recoverPassword","type":"auth.Authorization","id":923364464,"comment":"Reset the 2FA password using the recovery code sent using {@link auth.requestPasswordRecovery}.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"code","type":"string","comment":"Code received via email"},{"name":"new_settings","type":"account.PasswordInputSettings","predicate":"flags.0"}],"throws":[{"code":400,"name":"CODE_EMPTY","comment":"The provided code is empty."},{"code":400,"name":"NEW_SETTINGS_INVALID","comment":"The new settings are invalid."}],"available":"user"},{"kind":"method","name":"auth.resendCode","type":"auth.SentCode","id":1056025023,"comment":"Resend the login code via another medium, the phone code type is determined by the return value of the previous auth.sendCode/auth.resendCode: see login for more info.","arguments":[{"name":"phone_number","type":"string"},{"name":"phone_code_hash","type":"string"}],"throws":[{"code":400,"name":"PHONE_CODE_EXPIRED","comment":"The phone code you provided has expired, this may happen if it was sent to any chat on telegram (if the code is sent through a telegram chat (not the official account) to avoid it append or prepend to the code some chars)."},{"code":400,"name":"PHONE_CODE_HASH_EMPTY","comment":"phone_code_hash is missing."},{"code":400,"name":"PHONE_NUMBER_INVALID","comment":"The phone number is invalid."},{"code":406,"name":"SEND_CODE_UNAVAILABLE","comment":"Returned when all available options for this type of number were already used (e.g. flash-call, then SMS, then this error might be returned to trigger a second resend)."}],"available":"user"},{"kind":"method","name":"auth.cancelCode","type":"Bool","id":520357240,"comment":"Cancel the login verification code","arguments":[{"name":"phone_number","type":"string"},{"name":"phone_code_hash","type":"string"}],"throws":[{"code":400,"name":"PHONE_CODE_EXPIRED","comment":"The phone code you provided has expired, this may happen if it was sent to any chat on telegram (if the code is sent through a telegram chat (not the official account) to avoid it append or prepend to the code some chars)."},{"code":400,"name":"PHONE_NUMBER_INVALID","comment":"The phone number is invalid."}],"available":"user"},{"kind":"method","name":"auth.dropTempAuthKeys","type":"Bool","id":2387124616,"comment":"Delete all temporary authorization keys except for the ones specified","arguments":[{"name":"except_auth_keys","type":"Vector"}],"available":"both"},{"kind":"method","name":"auth.exportLoginToken","type":"auth.LoginToken","id":3084944894,"comment":"For more info, see login via QR code.\n\nGenerate a login token, for login via QR code.\nThe generated login token should be encoded using base64url, then shown as a tg://login?token=base64encodedtoken URL in the QR code.","arguments":[{"name":"api_id","type":"int"},{"name":"api_hash","type":"string"},{"name":"except_ids","type":"Vector"}],"throws":[{"code":400,"name":"API_ID_INVALID","comment":"API ID invalid."}],"available":"user"},{"kind":"method","name":"auth.importLoginToken","type":"auth.LoginToken","id":2511101156,"comment":"For more info, see login via QR code.\n\nLogin using a redirected login token, generated in case of DC mismatch during QR code login.","arguments":[{"name":"token","type":"bytes","comment":"Login token"}],"throws":[{"code":400,"name":"AUTH_TOKEN_ALREADY_ACCEPTED","comment":"The specified auth token was already accepted."},{"code":400,"name":"AUTH_TOKEN_EXPIRED","comment":"The authorization token has expired."},{"code":400,"name":"AUTH_TOKEN_INVALID","comment":"The specified auth token is invalid."},{"code":400,"name":"AUTH_TOKEN_INVALIDX","comment":"The specified auth token is invalid."}],"available":"user"},{"kind":"method","name":"auth.acceptLoginToken","type":"Authorization","id":3902057805,"comment":"For more info, see login via QR code.\n\nReturns info about the new session.\n\nAccept QR code login token, logging in the app that generated it.","arguments":[{"name":"token","type":"bytes","comment":"Login token embedded in QR code, for more info, see login via QR code."}],"throws":[{"code":400,"name":"AUTH_TOKEN_INVALIDX","comment":"The specified auth token is invalid."}],"available":"user"},{"kind":"method","name":"auth.checkRecoveryPassword","type":"Bool","id":221691769,"comment":"Check if the 2FA recovery code sent using {@link auth.requestPasswordRecovery} is valid, before passing it to {@link auth.recoverPassword}.","arguments":[{"name":"code","type":"string","comment":"Code received via email"}],"throws":[{"code":400,"name":"PASSWORD_RECOVERY_EXPIRED","comment":"The recovery code has expired."}],"available":"user"},{"kind":"method","name":"account.registerDevice","type":"Bool","id":3968205178,"comment":"Register device to receive PUSH notifications","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"no_muted","type":"true","predicate":"flags.0"},{"name":"token_type","type":"int"},{"name":"token","type":"string","comment":"Device token"},{"name":"app_sandbox","type":"Bool"},{"name":"secret","type":"bytes","comment":"For FCM and APNS VoIP, optional encryption key used to encrypt push notifications"},{"name":"other_uids","type":"vector"}],"throws":[{"code":400,"name":"TOKEN_INVALID","comment":"The provided token is invalid."},{"code":400,"name":"WEBPUSH_AUTH_INVALID","comment":"The specified web push authentication secret is invalid."},{"code":400,"name":"WEBPUSH_KEY_INVALID","comment":"The specified web push elliptic curve Diffie-Hellman public key is invalid."},{"code":400,"name":"WEBPUSH_TOKEN_INVALID","comment":"The specified web push token is invalid."}],"available":"user"},{"kind":"method","name":"account.unregisterDevice","type":"Bool","id":1779249670,"comment":"Deletes a device by its token, stops sending PUSH-notifications to it.","arguments":[{"name":"token_type","type":"int"},{"name":"token","type":"string","comment":"Device token"},{"name":"other_uids","type":"vector"}],"throws":[{"code":400,"name":"TOKEN_INVALID","comment":"The provided token is invalid."}],"available":"user"},{"kind":"method","name":"account.updateNotifySettings","type":"Bool","id":2227067795,"comment":"Edits notification settings from a given user/group, from all users/all groups.","arguments":[{"name":"peer","type":"InputNotifyPeer","comment":"Notification source"},{"name":"settings","type":"InputPeerNotifySettings","comment":"Notification settings"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"SETTINGS_INVALID","comment":"Invalid settings were provided."}],"available":"user"},{"kind":"method","name":"account.getNotifySettings","type":"PeerNotifySettings","id":313765169,"comment":"Gets current notification settings for a given user/group, from all users/all groups.","arguments":[{"name":"peer","type":"InputNotifyPeer","comment":"Notification source"}],"throws":[{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"account.resetNotifySettings","type":"Bool","id":3682473799,"comment":"Resets all notification settings from users and groups.","arguments":[],"available":"user"},{"kind":"method","name":"account.updateProfile","type":"User","id":2018596725,"comment":"Updates user profile.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"first_name","type":"string","predicate":"flags.0"},{"name":"last_name","type":"string","predicate":"flags.1"},{"name":"about","type":"string","predicate":"flags.2","comment":"New bio"}],"throws":[{"code":400,"name":"ABOUT_TOO_LONG","comment":"About string too long."},{"code":400,"name":"FIRSTNAME_INVALID","comment":"The first name is invalid."}],"available":"user"},{"kind":"method","name":"account.updateStatus","type":"Bool","id":1713919532,"comment":"Updates online user status.","arguments":[{"name":"offline","type":"Bool","comment":"If {@link boolTrue} is transmitted, user status will change to {@link userStatusOffline}."}],"available":"user"},{"kind":"method","name":"account.getWallPapers","type":"account.WallPapers","id":127302966,"comment":"Returns a list of available wallpapers.","arguments":[{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"}],"available":"user"},{"kind":"method","name":"account.reportPeer","type":"Bool","id":3317316998,"comment":"Report a peer for violation of telegram's Terms of Service","arguments":[{"name":"peer","type":"InputPeer","comment":"The peer to report"},{"name":"reason","type":"ReportReason","comment":"The reason why this peer is being reported"},{"name":"message","type":"string","comment":"Comment for report moderation"}],"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"account.checkUsername","type":"Bool","id":655677548,"comment":"Validates a username and checks availability.","arguments":[{"name":"username","type":"string","comment":"username
Accepted characters: A-z (case-insensitive), 0-9 and underscores.
Length: 5-32 characters."}],"throws":[{"code":400,"name":"USERNAME_INVALID","comment":"Unacceptable username."}],"available":"user"},{"kind":"method","name":"account.updateUsername","type":"User","id":1040964988,"comment":"Changes username for the current user.","arguments":[{"name":"username","type":"string","comment":"username or empty string if username is to be removed
Accepted characters: a-z (case-insensitive), 0-9 and underscores.
Length: 5-32 characters."}],"throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","comment":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"USERNAME_INVALID","comment":"Unacceptable username."},{"code":400,"name":"USERNAME_NOT_MODIFIED","comment":"Username is not different from the current username."},{"code":400,"name":"USERNAME_OCCUPIED","comment":"Username is taken."}],"available":"user"},{"kind":"method","name":"account.getPrivacy","type":"account.PrivacyRules","id":3671837008,"comment":"Get privacy settings of current account","arguments":[{"name":"key","type":"InputPrivacyKey","comment":"Peer category whose privacy settings should be fetched"}],"throws":[{"code":400,"name":"PRIVACY_KEY_INVALID","comment":"The privacy key is invalid."}],"available":"user"},{"kind":"method","name":"account.setPrivacy","type":"account.PrivacyRules","id":3388480744,"comment":"Change privacy settings of current account","arguments":[{"name":"key","type":"InputPrivacyKey","comment":"Peers to which the privacy rules apply"},{"name":"rules","type":"Vector","comment":"New privacy rules"}],"throws":[{"code":400,"name":"PRIVACY_KEY_INVALID","comment":"The privacy key is invalid."},{"code":400,"name":"PRIVACY_TOO_LONG","comment":"Too many privacy rules were specified, the current limit is 1000."},{"code":400,"name":"PRIVACY_VALUE_INVALID","comment":"The specified privacy rule combination is invalid."}],"available":"user"},{"kind":"method","name":"account.deleteAccount","type":"Bool","id":1099779595,"comment":"Delete the user's account from the telegram servers. Can be used, for example, to delete the account of a user that provided the login code, but forgot the 2FA password and no recovery method is configured.","arguments":[{"name":"reason","type":"string","comment":"Why is the account being deleted, can be empty"}],"throws":[{"code":420,"name":"2FA_CONFIRM_WAIT_X","comment":"Since this account is active and protected by a 2FA password, we will delete it in 1 week for security purposes. You can cancel this process at any time, you'll be able to reset your account in X seconds."}],"available":"user"},{"kind":"method","name":"account.getAccountTTL","type":"AccountDaysTTL","id":150761757,"comment":"Get days to live of account","arguments":[],"available":"user"},{"kind":"method","name":"account.setAccountTTL","type":"Bool","id":608323678,"comment":"Set account self-destruction period","arguments":[{"name":"ttl","type":"AccountDaysTTL","comment":"Time to live in days"}],"throws":[{"code":400,"name":"TTL_DAYS_INVALID","comment":"The provided TTL is invalid."}],"available":"user"},{"kind":"method","name":"account.sendChangePhoneCode","type":"auth.SentCode","id":2186758885,"comment":"Verify a new phone number to associate to the current account","arguments":[{"name":"phone_number","type":"string"},{"name":"settings","type":"CodeSettings","comment":"Phone code settings"}],"throws":[{"code":406,"name":"FRESH_CHANGE_PHONE_FORBIDDEN","comment":"You can't change phone number right after logging in, please wait at least 24 hours."},{"code":400,"name":"PHONE_NUMBER_BANNED","comment":"The provided phone number is banned from telegram."},{"code":400,"name":"PHONE_NUMBER_INVALID","comment":"The phone number is invalid."},{"code":400,"name":"PHONE_NUMBER_OCCUPIED","comment":"The phone number is already in use."}],"available":"user"},{"kind":"method","name":"account.changePhone","type":"User","id":1891839707,"comment":"Change the phone number of the current account","arguments":[{"name":"phone_number","type":"string"},{"name":"phone_code_hash","type":"string"},{"name":"phone_code","type":"string"}],"throws":[{"code":400,"name":"PHONE_CODE_EMPTY","comment":"phone_code is missing."},{"code":400,"name":"PHONE_NUMBER_INVALID","comment":"The phone number is invalid."},{"code":400,"name":"PHONE_NUMBER_OCCUPIED","comment":"The phone number is already in use."}],"available":"user"},{"kind":"method","name":"account.updateDeviceLocked","type":"Bool","id":954152242,"comment":"When client-side passcode lock feature is enabled, will not show message texts in incoming PUSH notifications.","arguments":[{"name":"period","type":"int","comment":"Inactivity period after which to start hiding message texts in PUSH notifications."}],"available":"user"},{"kind":"method","name":"account.getAuthorizations","type":"account.Authorizations","id":3810574680,"comment":"Get logged-in sessions","arguments":[],"available":"user"},{"kind":"method","name":"account.resetAuthorization","type":"Bool","id":3749180348,"comment":"Log out an active authorized session by its hash","arguments":[{"name":"hash","type":"long","comment":"Session hash"}],"throws":[{"code":406,"name":"FRESH_RESET_AUTHORISATION_FORBIDDEN","comment":"You can't logout other sessions if less than 24 hours have passed since you logged on the current session."},{"code":400,"name":"HASH_INVALID","comment":"The provided hash is invalid."}],"available":"user"},{"kind":"method","name":"account.getPassword","type":"account.Password","id":1418342645,"comment":"Obtain configuration for two-factor authorization with password","arguments":[],"available":"user"},{"kind":"method","name":"account.getPasswordSettings","type":"account.PasswordSettings","id":2631199481,"comment":"Get private info associated to the password info (recovery email, telegram passport info & so on)","arguments":[{"name":"password","type":"InputCheckPasswordSRP","comment":"The password (see SRP)"}],"throws":[{"code":400,"name":"PASSWORD_HASH_INVALID","comment":"The provided password hash is invalid."}],"available":"user"},{"kind":"method","name":"account.updatePasswordSettings","type":"Bool","id":2778402863,"comment":"Set a new 2FA password","arguments":[{"name":"password","type":"InputCheckPasswordSRP","comment":"The old password (see SRP)"},{"name":"new_settings","type":"account.PasswordInputSettings"}],"throws":[{"code":400,"name":"EMAIL_UNCONFIRMED","comment":"Email unconfirmed."},{"code":400,"name":"EMAIL_UNCONFIRMED_X","comment":"The provided email isn't confirmed, X is the length of the verification code that was just sent to the email: use {@link account.verifyEmail} to enter the received verification code and enable the recovery email."},{"code":400,"name":"NEW_SALT_INVALID","comment":"The new salt is invalid."},{"code":400,"name":"NEW_SETTINGS_INVALID","comment":"The new password settings are invalid."},{"code":400,"name":"PASSWORD_HASH_INVALID","comment":"The old password hash is invalid."},{"code":400,"name":"SRP_ID_INVALID","comment":"Invalid SRP ID provided."},{"code":400,"name":"SRP_PASSWORD_CHANGED","comment":"Password has changed."}],"available":"user"},{"kind":"method","name":"account.sendConfirmPhoneCode","type":"auth.SentCode","id":457157256,"comment":"Send confirmation code to cancel account deletion, for more info click here »","arguments":[{"name":"hash","type":"string","comment":"The hash from the service notification, for more info click here »"},{"name":"settings","type":"CodeSettings","comment":"Phone code settings"}],"throws":[{"code":400,"name":"HASH_INVALID","comment":"The provided hash is invalid."}],"available":"user"},{"kind":"method","name":"account.confirmPhone","type":"Bool","id":1596029123,"comment":"Confirm a phone number to cancel account deletion, for more info click here »","arguments":[{"name":"phone_code_hash","type":"string"},{"name":"phone_code","type":"string"}],"throws":[{"code":400,"name":"CODE_HASH_INVALID","comment":"Code hash invalid."},{"code":400,"name":"PHONE_CODE_EMPTY","comment":"phone_code is missing."}],"available":"user"},{"kind":"method","name":"account.getTmpPassword","type":"account.TmpPassword","id":1151208273,"comment":"Get temporary payment password","arguments":[{"name":"password","type":"InputCheckPasswordSRP","comment":"SRP password parameters"},{"name":"period","type":"int","comment":"Time during which the temporary password will be valid, in seconds; should be between 60 and 86400"}],"throws":[{"code":400,"name":"PASSWORD_HASH_INVALID","comment":"The provided password hash is invalid."},{"code":400,"name":"TMP_PASSWORD_DISABLED","comment":"The temporary password is disabled."}],"available":"user"},{"kind":"method","name":"account.getWebAuthorizations","type":"account.WebAuthorizations","id":405695855,"comment":"Get web login widget authorizations","arguments":[],"available":"user"},{"kind":"method","name":"account.resetWebAuthorization","type":"Bool","id":755087855,"comment":"Log out an active web telegram login session","arguments":[{"name":"hash","type":"long","comment":"{@link webAuthorization} hash"}],"throws":[{"code":400,"name":"HASH_INVALID","comment":"The provided hash is invalid."}],"available":"user"},{"kind":"method","name":"account.resetWebAuthorizations","type":"Bool","id":1747789204,"comment":"Reset all active web telegram login sessions","arguments":[],"available":"user"},{"kind":"method","name":"account.getAllSecureValues","type":"Vector","id":2995305597,"comment":"Get all saved Telegram Passport documents, for more info see the passport docs »","arguments":[],"available":"user"},{"kind":"method","name":"account.getSecureValue","type":"Vector","id":1936088002,"comment":"Get saved Telegram Passport document, for more info see the passport docs »","arguments":[{"name":"types","type":"Vector","comment":"Requested value types"}],"available":"user"},{"kind":"method","name":"account.saveSecureValue","type":"SecureValue","id":2308956957,"comment":"Securely save Telegram Passport document, for more info see the passport docs »","arguments":[{"name":"value","type":"InputSecureValue","comment":"Secure value, for more info see the passport docs »"},{"name":"secure_secret_id","type":"long"}],"throws":[{"code":400,"name":"PASSWORD_REQUIRED","comment":"A 2FA password must be configured to use Telegram Passport."}],"available":"user"},{"kind":"method","name":"account.deleteSecureValue","type":"Bool","id":3095444555,"comment":"Delete stored Telegram Passport documents, for more info see the passport docs »","arguments":[{"name":"types","type":"Vector","comment":"Document types to delete"}],"available":"user"},{"kind":"method","name":"account.getAuthorizationForm","type":"account.AuthorizationForm","id":2838059386,"comment":"Returns a Telegram Passport authorization form for sharing data with a service","arguments":[{"name":"bot_id","type":"int53"},{"name":"scope","type":"string","comment":"Telegram Passport element types requested by the service"},{"name":"public_key","type":"string"}],"throws":[{"code":400,"name":"PUBLIC_KEY_REQUIRED","comment":"A public key is required."}],"available":"user"},{"kind":"method","name":"account.acceptAuthorization","type":"Bool","id":4092415091,"comment":"Sends a Telegram Passport authorization form, effectively sharing data with the service","arguments":[{"name":"bot_id","type":"int53"},{"name":"scope","type":"string","comment":"Telegram Passport element types requested by the service"},{"name":"public_key","type":"string"},{"name":"value_hashes","type":"Vector"},{"name":"credentials","type":"SecureCredentialsEncrypted","comment":"Encrypted values"}],"available":"user"},{"kind":"method","name":"account.sendVerifyPhoneCode","type":"auth.SentCode","id":2778945273,"comment":"Send the verification phone code for telegram passport.","arguments":[{"name":"phone_number","type":"string"},{"name":"settings","type":"CodeSettings","comment":"Phone code settings"}],"throws":[{"code":400,"name":"PHONE_NUMBER_INVALID","comment":"The phone number is invalid."}],"available":"user"},{"kind":"method","name":"account.verifyPhone","type":"Bool","id":1305716726,"comment":"Verify a phone number for telegram passport.","arguments":[{"name":"phone_number","type":"string"},{"name":"phone_code_hash","type":"string"},{"name":"phone_code","type":"string"}],"throws":[{"code":400,"name":"PHONE_CODE_EMPTY","comment":"phone_code is missing."},{"code":400,"name":"PHONE_CODE_EXPIRED","comment":"The phone code you provided has expired, this may happen if it was sent to any chat on telegram (if the code is sent through a telegram chat (not the official account) to avoid it append or prepend to the code some chars)."},{"code":400,"name":"PHONE_NUMBER_INVALID","comment":"The phone number is invalid."}],"available":"user"},{"kind":"method","name":"account.sendVerifyEmailCode","type":"account.SentEmailCode","id":1880182943,"comment":"Send the verification email code for telegram passport.","arguments":[{"name":"email","type":"string","comment":"The email where to send the code"}],"throws":[{"code":400,"name":"EMAIL_INVALID","comment":"The specified email is invalid."}],"available":"user"},{"kind":"method","name":"account.verifyEmail","type":"Bool","id":3971627483,"comment":"Verify an email address for telegram passport.","arguments":[{"name":"email","type":"string","comment":"The email to verify"},{"name":"code","type":"string","comment":"The verification code that was received"}],"throws":[{"code":400,"name":"EMAIL_INVALID","comment":"The specified email is invalid."},{"code":400,"name":"EMAIL_VERIFY_EXPIRED","comment":"The verification email has expired."}],"available":"user"},{"kind":"method","name":"account.initTakeoutSession","type":"account.Takeout","id":4032514052,"comment":"Initialize account takeout session","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"contacts","type":"true","predicate":"flags.0","comment":"Whether to export contacts"},{"name":"message_users","type":"true","predicate":"flags.1"},{"name":"message_chats","type":"true","predicate":"flags.2"},{"name":"message_megagroups","type":"true","predicate":"flags.3"},{"name":"message_channels","type":"true","predicate":"flags.4"},{"name":"files","type":"true","predicate":"flags.5","comment":"Whether to export files"},{"name":"file_max_size","type":"int","predicate":"flags.5"}],"throws":[{"code":420,"name":"TAKEOUT_INIT_DELAY_X","comment":"Wait X seconds before initing takeout."}],"available":"user"},{"kind":"method","name":"account.finishTakeoutSession","type":"Bool","id":489050862,"comment":"Finish account takeout session","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"success","type":"true","predicate":"flags.0","comment":"Data exported successfully"}],"throws":[{"code":403,"name":"TAKEOUT_REQUIRED","comment":"A takeout session has to be initialized, first."}],"available":"user"},{"kind":"method","name":"account.confirmPasswordEmail","type":"Bool","id":2413762848,"comment":"Verify an email to use as 2FA recovery method.","arguments":[{"name":"code","type":"string","comment":"The phone code that was received after setting a recovery email"}],"throws":[{"code":400,"name":"CODE_INVALID","comment":"Code invalid."},{"code":400,"name":"EMAIL_HASH_EXPIRED","comment":"Email hash expired."}],"available":"user"},{"kind":"method","name":"account.resendPasswordEmail","type":"Bool","id":2055154197,"comment":"Resend the code to verify an email to use as 2FA recovery method.","arguments":[],"available":"user"},{"kind":"method","name":"account.cancelPasswordEmail","type":"Bool","id":3251361206,"comment":"Cancel the code that was sent to verify an email to use as 2FA recovery method.","arguments":[],"available":"user"},{"kind":"method","name":"account.getContactSignUpNotification","type":"Bool","id":2668087080,"comment":"Whether the user will receive notifications when contacts sign up","arguments":[],"available":"user"},{"kind":"method","name":"account.setContactSignUpNotification","type":"Bool","id":3488890721,"comment":"Toggle contact sign up notifications","arguments":[{"name":"silent","type":"Bool","comment":"Whether to disable contact sign up notifications"}],"available":"user"},{"kind":"method","name":"account.getNotifyExceptions","type":"Updates","id":1398240377,"comment":"Returns list of chats with non-default notification settings","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"compare_sound","type":"true","predicate":"flags.1"},{"name":"peer","type":"InputNotifyPeer","predicate":"flags.0","comment":"If specified, only chats of the specified category will be returned"}],"available":"user"},{"kind":"method","name":"account.getWallPaper","type":"WallPaper","id":4237155306,"comment":"Get info about a certain wallpaper","arguments":[{"name":"wallpaper","type":"InputWallPaper","comment":"The wallpaper to get info about"}],"throws":[{"code":400,"name":"WALLPAPER_INVALID","comment":"The specified wallpaper is invalid."}],"available":"user"},{"kind":"method","name":"account.uploadWallPaper","type":"WallPaper","id":3716494945,"comment":"Create and upload a new wallpaper","arguments":[{"name":"file","type":"InputFile","comment":"The JPG/PNG wallpaper"},{"name":"mime_type","type":"string"},{"name":"settings","type":"WallPaperSettings","comment":"Wallpaper settings"}],"throws":[{"code":400,"name":"WALLPAPER_FILE_INVALID","comment":"The specified wallpaper file is invalid."},{"code":400,"name":"WALLPAPER_MIME_INVALID","comment":"The specified wallpaper MIME type is invalid."}],"available":"user"},{"kind":"method","name":"account.saveWallPaper","type":"Bool","id":1817860919,"comment":"Install/uninstall wallpaper","arguments":[{"name":"wallpaper","type":"InputWallPaper","comment":"Wallpaper to save"},{"name":"unsave","type":"Bool","comment":"Uninstall wallpaper?"},{"name":"settings","type":"WallPaperSettings","comment":"Wallpaper settings"}],"throws":[{"code":400,"name":"WALLPAPER_INVALID","comment":"The specified wallpaper is invalid."}],"available":"user"},{"kind":"method","name":"account.installWallPaper","type":"Bool","id":4276967273,"comment":"Install wallpaper","arguments":[{"name":"wallpaper","type":"InputWallPaper","comment":"Wallpaper to install"},{"name":"settings","type":"WallPaperSettings","comment":"Wallpaper settings"}],"throws":[{"code":400,"name":"WALLPAPER_INVALID","comment":"The specified wallpaper is invalid."}],"available":"user"},{"kind":"method","name":"account.resetWallPapers","type":"Bool","id":3141244932,"comment":"Delete installed wallpapers","arguments":[],"available":"user"},{"kind":"method","name":"account.getAutoDownloadSettings","type":"account.AutoDownloadSettings","id":1457130303,"comment":"Get media autodownload settings","arguments":[],"available":"user"},{"kind":"method","name":"account.saveAutoDownloadSettings","type":"Bool","id":1995661875,"comment":"Change media autodownload settings","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"low","type":"true","predicate":"flags.0","comment":"Whether to save settings in the low data usage preset"},{"name":"high","type":"true","predicate":"flags.1","comment":"Whether to save settings in the high data usage preset"},{"name":"settings","type":"AutoDownloadSettings","comment":"Media autodownload settings"}],"available":"user"},{"kind":"method","name":"account.uploadTheme","type":"Document","id":473805619,"comment":"Upload theme","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"file","type":"InputFile","comment":"Theme file uploaded as described in files »"},{"name":"thumb","type":"InputFile","predicate":"flags.0","comment":"Thumbnail"},{"name":"file_name","type":"string"},{"name":"mime_type","type":"string"}],"throws":[{"code":400,"name":"THEME_FILE_INVALID","comment":"Invalid theme file provided."}],"available":"user"},{"kind":"method","name":"account.createTheme","type":"Theme","id":1697530880,"comment":"Create a theme","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"slug","type":"string","comment":"Unique theme ID"},{"name":"title","type":"string","comment":"Theme name"},{"name":"document","type":"InputDocument","predicate":"flags.2","comment":"Theme file"},{"name":"settings","type":"Vector","predicate":"flags.3","comment":"Theme settings"}],"throws":[{"code":400,"name":"THEME_MIME_INVALID","comment":"The theme's MIME type is invalid."}],"available":"user"},{"kind":"method","name":"account.updateTheme","type":"Theme","id":737414348,"comment":"Update theme","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"format","type":"string","comment":"Theme format, a string that identifies the theming engines supported by the client"},{"name":"theme","type":"InputTheme","comment":"Theme to update"},{"name":"slug","type":"string","predicate":"flags.0","comment":"Unique theme ID"},{"name":"title","type":"string","predicate":"flags.1","comment":"Theme name"},{"name":"document","type":"InputDocument","predicate":"flags.2","comment":"Theme file"},{"name":"settings","type":"Vector","predicate":"flags.3","comment":"Theme settings"}],"throws":[{"code":400,"name":"THEME_INVALID","comment":"Invalid theme provided."}],"available":"user"},{"kind":"method","name":"account.saveTheme","type":"Bool","id":4065792108,"comment":"Save a theme","arguments":[{"name":"theme","type":"InputTheme","comment":"Theme to save"},{"name":"unsave","type":"Bool","comment":"Unsave"}],"available":"user"},{"kind":"method","name":"account.installTheme","type":"Bool","id":3341269819,"comment":"Install a theme","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"dark","type":"true","predicate":"flags.0","comment":"Whether to install the dark version"},{"name":"theme","type":"InputTheme","predicate":"flags.1","comment":"Theme to install"},{"name":"format","type":"string","predicate":"flags.2","comment":"Theme format, a string that identifies the theming engines supported by the client"},{"name":"base_theme","type":"BaseTheme","predicate":"flags.3"}],"available":"user"},{"kind":"method","name":"account.getTheme","type":"Theme","id":2375906347,"comment":"Get theme information","arguments":[{"name":"format","type":"string","comment":"Theme format, a string that identifies the theming engines supported by the client"},{"name":"theme","type":"InputTheme","comment":"Theme"},{"name":"document_id","type":"long"}],"throws":[{"code":400,"name":"THEME_FORMAT_INVALID","comment":"Invalid theme format provided."},{"code":400,"name":"THEME_INVALID","comment":"Invalid theme provided."}],"available":"user"},{"kind":"method","name":"account.getThemes","type":"account.Themes","id":1913054296,"comment":"Get installed themes","arguments":[{"name":"format","type":"string","comment":"Theme format, a string that identifies the theming engines supported by the client"},{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"}],"available":"user"},{"kind":"method","name":"account.setContentSettings","type":"Bool","id":3044323691,"comment":"Set sensitive content settings (for viewing or hiding NSFW content)","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"sensitive_enabled","type":"true","predicate":"flags.0"}],"throws":[{"code":403,"name":"SENSITIVE_CHANGE_FORBIDDEN","comment":"You can't change your sensitive content settings."}],"available":"user"},{"kind":"method","name":"account.getContentSettings","type":"account.ContentSettings","id":2342210990,"comment":"Get sensitive content settings","arguments":[],"available":"user"},{"kind":"method","name":"account.getMultiWallPapers","type":"Vector","id":1705865692,"comment":"Get info about multiple wallpapers","arguments":[{"name":"wallpapers","type":"Vector","comment":"Wallpapers to fetch info about"}],"available":"user"},{"kind":"method","name":"account.getGlobalPrivacySettings","type":"GlobalPrivacySettings","id":3945483510,"comment":"Get global privacy settings","arguments":[],"available":"user"},{"kind":"method","name":"account.setGlobalPrivacySettings","type":"GlobalPrivacySettings","id":517647042,"comment":"Set global privacy settings","arguments":[{"name":"settings","type":"GlobalPrivacySettings","comment":"Global privacy settings"}],"throws":[{"code":400,"name":"AUTOARCHIVE_NOT_AVAILABLE","comment":"The autoarchive setting is not available at this time: please check the value of the autoarchive_setting_available field in client config » before calling this method."}],"available":"user"},{"kind":"method","name":"account.reportProfilePhoto","type":"Bool","id":4203529973,"comment":"Report a profile photo of a dialog","arguments":[{"name":"peer","type":"InputPeer","comment":"The dialog"},{"name":"photo_id","type":"InputPhoto"},{"name":"reason","type":"ReportReason","comment":"Report reason"},{"name":"message","type":"string","comment":"Comment for report moderation"}],"available":"user"},{"kind":"method","name":"account.resetPassword","type":"account.ResetPasswordResult","id":2466827803,"comment":"Initiate a 2FA password reset: can only be used if the user is already logged-in, see here for more info »","arguments":[],"available":"user"},{"kind":"method","name":"account.declinePasswordReset","type":"Bool","id":1284770294,"comment":"Abort a pending 2FA password reset, see here for more info »","arguments":[],"throws":[{"code":400,"name":"RESET_REQUEST_MISSING","comment":"No password reset is in progress."}],"available":"user"},{"kind":"method","name":"account.getChatThemes","type":"account.Themes","id":3594051209,"comment":"Get all available chat themes","arguments":[{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"}],"available":"user"},{"kind":"method","name":"users.getUsers","type":"Vector","id":227648840,"comment":"Returns basic user info according to their identifiers.","arguments":[{"name":"id","type":"Vector","comment":"List of user identifiers"}],"throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","comment":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CONNECTION_NOT_INITED","comment":"Connection not initialized."},{"code":400,"name":"INPUT_LAYER_INVALID","comment":"The provided layer is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"both"},{"kind":"method","name":"users.getFullUser","type":"UserFull","id":3392185777,"comment":"Returns extended user info by ID.","arguments":[{"name":"id","type":"InputUser","comment":"User ID"}],"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"both"},{"kind":"method","name":"users.setSecureValueErrors","type":"Bool","id":2429064373,"comment":"Use this if the data submitted by the user doesn't satisfy the standards your service requires for any reason. For example, if a birthday date seems invalid, a submitted document is blurry, a scan shows evidence of tampering, etc. Supply some details in the error message to make sure the user knows how to correct the issues.\n\nNotify the user that the sent passport data contains some errors The user will not be able to re-submit their Passport data to you until the errors are fixed (the contents of the field for which you returned the error must change).","arguments":[{"name":"id","type":"InputUser","comment":"The user"},{"name":"errors","type":"Vector","comment":"Errors"}],"throws":[{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"both"},{"kind":"method","name":"contacts.getContactIDs","type":"Vector","id":2061264541,"comment":"Get contact by telegram IDs","arguments":[{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"}],"available":"user"},{"kind":"method","name":"contacts.getStatuses","type":"Vector","id":3299038190,"comment":"Returns the list of contact statuses.","arguments":[],"available":"user"},{"kind":"method","name":"contacts.getContacts","type":"contacts.Contacts","id":1574346258,"comment":"Returns the current user's contact list.","arguments":[{"name":"hash","type":"long","comment":"If there already is a full contact list on the client, a hash of a the list of contact IDs in ascending order may be passed in this parameter. If the contact set was not changed, {@link contacts.contactsNotModified} will be returned."}],"available":"user"},{"kind":"method","name":"contacts.importContacts","type":"contacts.ImportedContacts","id":746589157,"comment":"Use {@link contacts.addContact} to add Telegram contacts without actually using their phone number.\n\nImports contacts: saves a full list on the server, adds already registered contacts to the contact list, returns added contacts and their info.","arguments":[{"name":"contacts","type":"Vector","comment":"List of contacts to import"}],"available":"user"},{"kind":"method","name":"contacts.deleteContacts","type":"Updates","id":157945344,"comment":"Deletes several contacts from the list.","arguments":[{"name":"id","type":"Vector","comment":"User ID list"}],"available":"user"},{"kind":"method","name":"contacts.deleteByPhones","type":"Bool","id":269745566,"comment":"Delete contacts by phone number","arguments":[{"name":"phones","type":"Vector","comment":"Phone numbers"}],"available":"user"},{"kind":"method","name":"contacts.block","type":"Bool","id":1758204945,"comment":"Adds the user to the blacklist.","arguments":[{"name":"id","type":"InputPeer","comment":"User ID"}],"throws":[{"code":400,"name":"CONTACT_ID_INVALID","comment":"The provided contact ID is invalid."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"contacts.unblock","type":"Bool","id":3198573904,"comment":"Deletes the user from the blacklist.","arguments":[{"name":"id","type":"InputPeer","comment":"User ID"}],"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CONTACT_ID_INVALID","comment":"The provided contact ID is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"contacts.getBlocked","type":"contacts.Blocked","id":4118557967,"comment":"Returns the list of blocked users.","arguments":[{"name":"offset","type":"int","comment":"The number of list elements to be skipped"},{"name":"limit","type":"int","comment":"The number of list elements to be returned"}],"available":"user"},{"kind":"method","name":"contacts.search","type":"contacts.Found","id":301470424,"comment":"Returns users found by username substring.","arguments":[{"name":"q","type":"string","comment":"Target substring"},{"name":"limit","type":"int","comment":"Maximum number of users to be returned"}],"throws":[{"code":400,"name":"QUERY_TOO_SHORT","comment":"The query string is too short."},{"code":400,"name":"SEARCH_QUERY_EMPTY","comment":"The search query is empty."}],"available":"user"},{"kind":"method","name":"contacts.resolveUsername","type":"contacts.ResolvedPeer","id":4181511075,"comment":"Resolve a @username to get peer info","arguments":[{"name":"username","type":"string","comment":"@username to resolve"}],"throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","comment":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"CONNECTION_LAYER_INVALID","comment":"Layer invalid."},{"code":400,"name":"USERNAME_INVALID","comment":"The provided username is not valid."},{"code":400,"name":"USERNAME_NOT_OCCUPIED","comment":"The provided username is not occupied."}],"available":"both"},{"kind":"method","name":"contacts.getTopPeers","type":"contacts.TopPeers","id":2536798390,"comment":"Get most used peers","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"correspondents","type":"true","predicate":"flags.0","comment":"Users we've chatted most frequently with"},{"name":"bots_pm","type":"true","predicate":"flags.1"},{"name":"bots_inline","type":"true","predicate":"flags.2"},{"name":"phone_calls","type":"true","predicate":"flags.3"},{"name":"forward_users","type":"true","predicate":"flags.4"},{"name":"forward_chats","type":"true","predicate":"flags.5"},{"name":"groups","type":"true","predicate":"flags.10","comment":"Often-opened groups and supergroups"},{"name":"channels","type":"true","predicate":"flags.15","comment":"Most frequently visited channels"},{"name":"offset","type":"int","comment":"Offset for pagination"},{"name":"limit","type":"int","comment":"Maximum number of results to return, see pagination"},{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"}],"throws":[{"code":400,"name":"TYPES_EMPTY","comment":"No top peer type was provided."}],"available":"user"},{"kind":"method","name":"contacts.resetTopPeerRating","type":"Bool","id":451113900,"comment":"Reset rating of top peer","arguments":[{"name":"category","type":"TopPeerCategory","comment":"Top peer category"},{"name":"peer","type":"InputPeer","comment":"Peer whose rating should be reset"}],"throws":[{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"contacts.resetSaved","type":"Bool","id":2274703345,"comment":"Delete saved contacts","arguments":[],"available":"user"},{"kind":"method","name":"contacts.getSaved","type":"Vector","id":2196890527,"comment":"Get all contacts","arguments":[],"throws":[{"code":403,"name":"TAKEOUT_REQUIRED","comment":"A takeout session has to be initialized, first."}],"available":"user"},{"kind":"method","name":"contacts.toggleTopPeers","type":"Bool","id":2232729050,"comment":"Enable/disable top peers","arguments":[{"name":"enabled","type":"Bool","comment":"Enable/disable"}],"available":"user"},{"kind":"method","name":"contacts.addContact","type":"Updates","id":3908330448,"comment":"Use {@link contacts.importContacts} to add contacts by phone number, without knowing their Telegram ID.\n\nAdd an existing telegram user as contact.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"add_phone_privacy_exception","type":"true","predicate":"flags.0"},{"name":"id","type":"InputUser","comment":"Telegram ID of the other user"},{"name":"first_name","type":"string"},{"name":"last_name","type":"string"},{"name":"phone","type":"string","comment":"User's phone number"}],"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CONTACT_ID_INVALID","comment":"The provided contact ID is invalid."},{"code":400,"name":"CONTACT_NAME_EMPTY","comment":"Contact name empty."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"user"},{"kind":"method","name":"contacts.acceptContact","type":"Updates","id":4164002319,"comment":"If the {@link peerSettings} of a new user allow us to add them as contact, add that user as contact","arguments":[{"name":"id","type":"InputUser","comment":"The user to add as contact"}],"throws":[{"code":400,"name":"CONTACT_ADD_MISSING","comment":"Contact to add is missing."},{"code":400,"name":"CONTACT_ID_INVALID","comment":"The provided contact ID is invalid."},{"code":400,"name":"CONTACT_REQ_MISSING","comment":"Missing contact request."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"user"},{"kind":"method","name":"contacts.getLocated","type":"Updates","id":3544759364,"comment":"Get contacts near you","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"background","type":"true","predicate":"flags.1","comment":"While the geolocation of the current user is public, clients should update it in the background every half-an-hour or so, while setting this flag.
Do this only if the new location is more than 1 KM away from the previous one, or if the previous location is unknown."},{"name":"geo_point","type":"InputGeoPoint"},{"name":"self_expires","type":"int","predicate":"flags.0"}],"throws":[{"code":400,"name":"GEO_POINT_INVALID","comment":"Invalid geoposition provided."},{"code":406,"name":"USERPIC_PRIVACY_REQUIRED","comment":"You need to disable privacy settings for your profile picture in order to make your geolocation public."},{"code":406,"name":"USERPIC_UPLOAD_REQUIRED","comment":"You must have a profile picture to publish your geolocation."}],"available":"user"},{"kind":"method","name":"contacts.blockFromReplies","type":"Updates","id":698914348,"comment":"Stop getting notifications about thread replies of a certain user in @replies","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"delete_message","type":"true","predicate":"flags.0"},{"name":"delete_history","type":"true","predicate":"flags.1"},{"name":"report_spam","type":"true","predicate":"flags.2"},{"name":"msg_id","type":"int"}],"available":"user"},{"kind":"method","name":"messages.getMessages","type":"messages.Messages","id":1673946374,"comment":"Returns the list of messages by their IDs.","arguments":[{"name":"id","type":"Vector","comment":"Message ID list"}],"available":"both"},{"kind":"method","name":"messages.getDialogs","type":"messages.Dialogs","id":2700397391,"comment":"Returns the current user dialog list.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"exclude_pinned","type":"true","predicate":"flags.0"},{"name":"folder_id","type":"int","predicate":"flags.1"},{"name":"offset_date","type":"int"},{"name":"offset_id","type":"int"},{"name":"offset_peer","type":"InputPeer"},{"name":"limit","type":"int","comment":"Number of list elements to be returned"},{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"}],"throws":[{"code":400,"name":"FOLDER_ID_INVALID","comment":"Invalid folder ID."},{"code":400,"name":"OFFSET_PEER_ID_INVALID","comment":"The provided offset peer is invalid."}],"available":"user"},{"kind":"method","name":"messages.getHistory","type":"messages.Messages","id":1143203525,"comment":"Gets back the conversation history with one interlocutor / within a chat","arguments":[{"name":"peer","type":"InputPeer","comment":"Target peer"},{"name":"offset_id","type":"int"},{"name":"offset_date","type":"int"},{"name":"add_offset","type":"int"},{"name":"limit","type":"int","comment":"Number of results to return"},{"name":"max_id","type":"int"},{"name":"min_id","type":"int"},{"name":"hash","type":"long","comment":"Result hash"}],"throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","comment":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.search","type":"messages.Messages","id":2700978018,"comment":"Gets back found messages","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"peer","type":"InputPeer","comment":"User or chat, histories with which are searched, or {@link inputPeerEmpty} constructor for global search"},{"name":"q","type":"string","comment":"Text search request"},{"name":"from_id","type":"InputPeer","predicate":"flags.0"},{"name":"top_msg_id","type":"int","predicate":"flags.1"},{"name":"filter","type":"MessagesFilter","comment":"Filter to return only specified message types"},{"name":"min_date","type":"int"},{"name":"max_date","type":"int"},{"name":"offset_id","type":"int"},{"name":"add_offset","type":"int"},{"name":"limit","type":"int","comment":"Number of results to return"},{"name":"max_id","type":"int"},{"name":"min_id","type":"int"},{"name":"hash","type":"long","comment":"Hash"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"FROM_PEER_INVALID","comment":"The specified from_id is invalid."},{"code":400,"name":"INPUT_FILTER_INVALID","comment":"The specified filter is invalid."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"PEER_ID_NOT_SUPPORTED","comment":"The provided peer ID is not supported."},{"code":400,"name":"SEARCH_QUERY_EMPTY","comment":"The search query is empty."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"user"},{"kind":"method","name":"messages.readHistory","type":"messages.AffectedMessages","id":238054714,"comment":"Marks message history as read.","arguments":[{"name":"peer","type":"InputPeer","comment":"Target user or group"},{"name":"max_id","type":"int"}],"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.deleteHistory","type":"messages.AffectedHistory","id":2962199082,"comment":"Deletes communication history.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"just_clear","type":"true","predicate":"flags.0"},{"name":"revoke","type":"true","predicate":"flags.1","comment":"Whether to delete the message history for all chat participants"},{"name":"peer","type":"InputPeer","comment":"User or chat, communication history of which will be deleted"},{"name":"max_id","type":"int"},{"name":"min_date","type":"int","predicate":"flags.2"},{"name":"max_date","type":"int","predicate":"flags.3"}],"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.deleteMessages","type":"messages.AffectedMessages","id":3851326930,"comment":"Deletes messages by their identifiers.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"revoke","type":"true","predicate":"flags.0","comment":"Whether to delete messages for all participants of the chat"},{"name":"id","type":"Vector","comment":"Message ID list"}],"throws":[{"code":403,"name":"MESSAGE_DELETE_FORBIDDEN","comment":"You can't delete one of the messages you tried to delete, most likely because it is a service message."}],"available":"both"},{"kind":"method","name":"messages.receivedMessages","type":"Vector","id":94983360,"comment":"Confirms receipt of messages by a client, cancels PUSH-notification sending.","arguments":[{"name":"max_id","type":"int"}],"available":"user"},{"kind":"method","name":"messages.setTyping","type":"Bool","id":1486110434,"comment":"Sends a current user typing event (see SendMessageAction for all event types) to a conversation partner or group.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"peer","type":"InputPeer","comment":"Target user or group"},{"name":"top_msg_id","type":"int","predicate":"flags.0"},{"name":"action","type":"SendMessageAction","comment":"Type of action
Parameter added in Layer 17."}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","comment":"You're banned from sending messages in supergroups/channels."},{"code":400,"name":"USER_IS_BLOCKED","comment":"You were blocked by this user."},{"code":400,"name":"USER_IS_BOT","comment":"Bots can't send messages to other bots."}],"available":"both"},{"kind":"method","name":"messages.sendMessage","type":"Updates","id":1376532592,"comment":"Sends a message to a chat","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"no_webpage","type":"true","predicate":"flags.1"},{"name":"silent","type":"true","predicate":"flags.5","comment":"Send this message silently (no notifications for the receivers)"},{"name":"background","type":"true","predicate":"flags.6","comment":"Send this message as background message"},{"name":"clear_draft","type":"true","predicate":"flags.7"},{"name":"peer","type":"InputPeer","comment":"The destination where the message will be sent"},{"name":"reply_to_msg_id","type":"int","predicate":"flags.0"},{"name":"message","type":"string","comment":"The message"},{"name":"random_id","type":"long"},{"name":"reply_markup","type":"ReplyMarkup","predicate":"flags.2"},{"name":"entities","type":"Vector","predicate":"flags.3","comment":"Message entities for sending styled text"},{"name":"schedule_date","type":"int","predicate":"flags.10"}],"throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","comment":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"BOT_DOMAIN_INVALID","comment":"Bot domain invalid."},{"code":400,"name":"BOT_INVALID","comment":"This is not a valid bot."},{"code":400,"name":"BUTTON_DATA_INVALID","comment":"The data of one or more of the buttons you provided is invalid."},{"code":400,"name":"BUTTON_TYPE_INVALID","comment":"The type of one or more of the buttons you provided is invalid."},{"code":400,"name":"BUTTON_URL_INVALID","comment":"Button URL invalid."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"CHAT_RESTRICTED","comment":"You can't send messages in this chat, you were restricted."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"ENCRYPTION_DECLINED","comment":"The secret chat was declined."},{"code":400,"name":"ENTITIES_TOO_LONG","comment":"You provided too many styled message entities."},{"code":400,"name":"ENTITY_MENTION_USER_INVALID","comment":"You mentioned an invalid user."},{"code":400,"name":"FROM_MESSAGE_BOT_DISABLED","comment":"Bots can't use fromMessage min constructors."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MESSAGE_EMPTY","comment":"The provided message is empty."},{"code":400,"name":"MESSAGE_TOO_LONG","comment":"The provided message is too long."},{"code":400,"name":"MSG_ID_INVALID","comment":"Provided reply_to_msg_id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"PINNED_DIALOGS_TOO_MUCH","comment":"Too many pinned dialogs."},{"code":400,"name":"POLL_OPTION_INVALID","comment":"Invalid poll option provided."},{"code":400,"name":"REPLY_MARKUP_INVALID","comment":"The provided reply markup is invalid."},{"code":400,"name":"REPLY_MARKUP_TOO_LONG","comment":"The specified reply_markup is too long."},{"code":400,"name":"SCHEDULE_BOT_NOT_ALLOWED","comment":"Bots cannot schedule messages."},{"code":400,"name":"SCHEDULE_DATE_TOO_LATE","comment":"You can't schedule a message this far in the future."},{"code":400,"name":"SCHEDULE_STATUS_PRIVATE","comment":"Can't schedule until user is online, if the user's last seen timestamp is hidden by their privacy settings."},{"code":400,"name":"SCHEDULE_TOO_MUCH","comment":"There are too many scheduled messages."},{"code":420,"name":"SLOWMODE_WAIT_X","comment":"Slowmode is enabled in this chat: you must wait for the specified number of seconds before sending another message to the chat."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","comment":"You're banned from sending messages in supergroups/channels."},{"code":400,"name":"USER_IS_BLOCKED","comment":"You were blocked by this user."},{"code":400,"name":"USER_IS_BOT","comment":"Bots can't send messages to other bots."},{"code":400,"name":"YOU_BLOCKED_USER","comment":"You blocked this user."}],"available":"both"},{"kind":"method","name":"messages.sendMedia","type":"Updates","id":881978281,"comment":"Send a media","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"silent","type":"true","predicate":"flags.5","comment":"Send message silently (no notification should be triggered)"},{"name":"background","type":"true","predicate":"flags.6","comment":"Send message in background"},{"name":"clear_draft","type":"true","predicate":"flags.7"},{"name":"peer","type":"InputPeer","comment":"Destination"},{"name":"reply_to_msg_id","type":"int","predicate":"flags.0"},{"name":"media","type":"InputMedia","comment":"Attached media"},{"name":"message","type":"string","comment":"Caption"},{"name":"random_id","type":"long"},{"name":"reply_markup","type":"ReplyMarkup","predicate":"flags.2"},{"name":"entities","type":"Vector","predicate":"flags.3","comment":"Message entities for styled text"},{"name":"schedule_date","type":"int","predicate":"flags.10"}],"throws":[{"code":400,"name":"BOT_PAYMENTS_DISABLED","comment":"Please enable bot payments in botfather before calling this method."},{"code":400,"name":"BOT_POLLS_DISABLED","comment":" "},{"code":400,"name":"BROADCAST_PUBLIC_VOTERS_FORBIDDEN","comment":"You can't forward polls with public voters."},{"code":400,"name":"BUTTON_DATA_INVALID","comment":"The data of one or more of the buttons you provided is invalid."},{"code":400,"name":"BUTTON_TYPE_INVALID","comment":"The type of one or more of the buttons you provided is invalid."},{"code":400,"name":"BUTTON_URL_INVALID","comment":"Button URL invalid."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_RESTRICTED","comment":"You can't send messages in this chat, you were restricted."},{"code":403,"name":"CHAT_SEND_GIFS_FORBIDDEN","comment":"You can't send gifs in this chat."},{"code":403,"name":"CHAT_SEND_MEDIA_FORBIDDEN","comment":"You can't send media in this chat."},{"code":403,"name":"CHAT_SEND_POLL_FORBIDDEN","comment":"You can't send polls in this chat."},{"code":403,"name":"CHAT_SEND_STICKERS_FORBIDDEN","comment":"You can't send stickers in this chat."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"CURRENCY_TOTAL_AMOUNT_INVALID","comment":"The total amount of all prices is invalid."},{"code":400,"name":"EMOTICON_INVALID","comment":"The specified emoji is invalid."},{"code":400,"name":"EXTERNAL_URL_INVALID","comment":"External URL invalid."},{"code":400,"name":"FILE_PARTS_INVALID","comment":"The number of file parts is invalid."},{"code":400,"name":"FILE_PART_LENGTH_INVALID","comment":"The length of a file part is invalid."},{"code":400,"name":"FILE_REFERENCE_EMPTY","comment":"An empty file reference was specified."},{"code":400,"name":"FILE_REFERENCE_EXPIRED","comment":"File reference expired, it must be refetched as described in https://core.telegram.org/api/file_reference."},{"code":400,"name":"GAME_BOT_INVALID","comment":"Bots can't send another bot's game."},{"code":400,"name":"IMAGE_PROCESS_FAILED","comment":"Failure while processing image."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MD5_CHECKSUM_INVALID","comment":"The MD5 checksums do not match."},{"code":400,"name":"MEDIA_CAPTION_TOO_LONG","comment":"The caption is too long."},{"code":400,"name":"MEDIA_EMPTY","comment":"The provided media object is invalid."},{"code":400,"name":"MEDIA_INVALID","comment":"Media invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PAYMENT_PROVIDER_INVALID","comment":"The specified payment provider is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"PHOTO_EXT_INVALID","comment":"The extension of the photo is invalid."},{"code":400,"name":"PHOTO_INVALID_DIMENSIONS","comment":"The photo dimensions are invalid."},{"code":400,"name":"PHOTO_SAVE_FILE_INVALID","comment":"Internal issues, try again later."},{"code":400,"name":"POLL_ANSWERS_INVALID","comment":"Invalid poll answers were provided."},{"code":400,"name":"POLL_ANSWER_INVALID","comment":"One of the poll answers is not acceptable."},{"code":400,"name":"POLL_OPTION_DUPLICATE","comment":"Duplicate poll options provided."},{"code":400,"name":"POLL_OPTION_INVALID","comment":"Invalid poll option provided."},{"code":400,"name":"POLL_QUESTION_INVALID","comment":"One of the poll questions is not acceptable."},{"code":400,"name":"QUIZ_CORRECT_ANSWERS_EMPTY","comment":"No correct quiz answer was specified."},{"code":400,"name":"QUIZ_CORRECT_ANSWERS_TOO_MUCH","comment":"You specified too many correct answers in a quiz, quizes can only have one right answer!"},{"code":400,"name":"QUIZ_CORRECT_ANSWER_INVALID","comment":"An invalid value was provided to the correct_answers field."},{"code":400,"name":"QUIZ_MULTIPLE_INVALID","comment":"Quizes can't have the multiple_choice flag set!"},{"code":400,"name":"REPLY_MARKUP_BUY_EMPTY","comment":"Reply markup for buy button empty."},{"code":400,"name":"REPLY_MARKUP_INVALID","comment":"The provided reply markup is invalid."},{"code":400,"name":"SCHEDULE_BOT_NOT_ALLOWED","comment":"Bots cannot schedule messages."},{"code":400,"name":"SCHEDULE_DATE_TOO_LATE","comment":"You can't schedule a message this far in the future."},{"code":400,"name":"SCHEDULE_TOO_MUCH","comment":"There are too many scheduled messages."},{"code":420,"name":"SLOWMODE_WAIT_X","comment":"Slowmode is enabled in this chat: you must wait for the specified number of seconds before sending another message to the chat."},{"code":400,"name":"TTL_MEDIA_INVALID","comment":"Invalid media Time To Live was provided."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","comment":"You're banned from sending messages in supergroups/channels."},{"code":400,"name":"USER_IS_BLOCKED","comment":"You were blocked by this user."},{"code":400,"name":"USER_IS_BOT","comment":"Bots can't send messages to other bots."},{"code":400,"name":"VIDEO_CONTENT_TYPE_INVALID","comment":"The video's content type is invalid."},{"code":400,"name":"WEBPAGE_CURL_FAILED","comment":"Failure while fetching the webpage with cURL."},{"code":400,"name":"WEBPAGE_MEDIA_EMPTY","comment":"Webpage media empty."},{"code":400,"name":"YOU_BLOCKED_USER","comment":"You blocked this user."}],"available":"both"},{"kind":"method","name":"messages.forwardMessages","type":"Updates","id":3657360910,"comment":"Forwards messages by their IDs.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"silent","type":"true","predicate":"flags.5","comment":"Whether to send messages silently (no notification will be triggered on the destination clients)"},{"name":"background","type":"true","predicate":"flags.6","comment":"Whether to send the message in background"},{"name":"with_my_score","type":"true","predicate":"flags.8"},{"name":"drop_author","type":"true","predicate":"flags.11"},{"name":"drop_media_captions","type":"true","predicate":"flags.12"},{"name":"from_peer","type":"InputPeer"},{"name":"id","type":"Vector","comment":"IDs of messages"},{"name":"random_id","type":"Vector"},{"name":"to_peer","type":"InputPeer"},{"name":"schedule_date","type":"int","predicate":"flags.10"}],"throws":[{"code":400,"name":"BROADCAST_PUBLIC_VOTERS_FORBIDDEN","comment":"You can't forward polls with public voters."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"CHAT_RESTRICTED","comment":"You can't send messages in this chat, you were restricted."},{"code":403,"name":"CHAT_SEND_GAME_FORBIDDEN","comment":"You can't send a game to this chat."},{"code":403,"name":"CHAT_SEND_GIFS_FORBIDDEN","comment":"You can't send gifs in this chat."},{"code":403,"name":"CHAT_SEND_MEDIA_FORBIDDEN","comment":"You can't send media in this chat."},{"code":403,"name":"CHAT_SEND_POLL_FORBIDDEN","comment":"You can't send polls in this chat."},{"code":403,"name":"CHAT_SEND_STICKERS_FORBIDDEN","comment":"You can't send stickers in this chat."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"GROUPED_MEDIA_INVALID","comment":"Invalid grouped media."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MEDIA_EMPTY","comment":"The provided media object is invalid."},{"code":400,"name":"MESSAGE_IDS_EMPTY","comment":"No message ids were provided."},{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":420,"name":"P0NY_FLOODWAIT","comment":" "},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"RANDOM_ID_INVALID","comment":"A provided random ID is invalid."},{"code":400,"name":"SCHEDULE_DATE_TOO_LATE","comment":"You can't schedule a message this far in the future."},{"code":400,"name":"SCHEDULE_TOO_MUCH","comment":"There are too many scheduled messages."},{"code":400,"name":"SLOWMODE_MULTI_MSGS_DISABLED","comment":"Slowmode is enabled, you cannot forward multiple messages to this group."},{"code":420,"name":"SLOWMODE_WAIT_X","comment":"Slowmode is enabled in this chat: you must wait for the specified number of seconds before sending another message to the chat."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","comment":"You're banned from sending messages in supergroups/channels."},{"code":400,"name":"USER_IS_BLOCKED","comment":"You were blocked by this user."},{"code":400,"name":"USER_IS_BOT","comment":"Bots can't send messages to other bots."},{"code":400,"name":"YOU_BLOCKED_USER","comment":"You blocked this user."}],"available":"both"},{"kind":"method","name":"messages.reportSpam","type":"Bool","id":3474297563,"comment":"Report a new incoming chat for spam, if the {@link peerSettings} of the chat allow us to do that","arguments":[{"name":"peer","type":"InputPeer","comment":"Peer to report"}],"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.getPeerSettings","type":"PeerSettings","id":913498268,"comment":"Get peer settings","arguments":[{"name":"peer","type":"InputPeer","comment":"The peer"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.report","type":"Bool","id":2303961934,"comment":"Report a message in a chat for violation of telegram's Terms of Service","arguments":[{"name":"peer","type":"InputPeer","comment":"Peer"},{"name":"id","type":"Vector","comment":"IDs of messages to report"},{"name":"reason","type":"ReportReason","comment":"Why are these messages being reported"},{"name":"message","type":"string","comment":"Comment for report moderation"}],"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.getChats","type":"messages.Chats","id":1240027791,"comment":"Returns chat basic info on their IDs.","arguments":[{"name":"id","type":"vector","comment":"List of chat IDs"}],"throws":[{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"both"},{"kind":"method","name":"messages.getFullChat","type":"messages.ChatFull","id":2930772788,"comment":"Returns full chat info according to its ID.","arguments":[{"name":"chat_id","type":"int53"}],"throws":[{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"both"},{"kind":"method","name":"messages.editChatTitle","type":"Updates","id":1937260541,"comment":"Chanages chat name and sends a service message on it.","arguments":[{"name":"chat_id","type":"int53"},{"name":"title","type":"string","comment":"New chat name, different from the old one"}],"throws":[{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":400,"name":"CHAT_TITLE_EMPTY","comment":"No chat title provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"both"},{"kind":"method","name":"messages.editChatPhoto","type":"Updates","id":903730804,"comment":"Changes chat photo and sends a service message on it","arguments":[{"name":"chat_id","type":"int53"},{"name":"photo","type":"InputChatPhoto","comment":"Photo to be set"}],"throws":[{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"PHOTO_CROP_SIZE_SMALL","comment":"Photo is too small."},{"code":400,"name":"PHOTO_EXT_INVALID","comment":"The extension of the photo is invalid."},{"code":400,"name":"PHOTO_INVALID","comment":"Photo invalid."}],"available":"both"},{"kind":"method","name":"messages.addChatUser","type":"Updates","id":4064760803,"comment":"Adds a user to a chat and sends a service message on it.","arguments":[{"name":"chat_id","type":"int53"},{"name":"user_id","type":"InputUser"},{"name":"fwd_limit","type":"int"}],"throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"USERS_TOO_MUCH","comment":"The maximum number of users has been exceeded (to create a chat, for example)."},{"code":400,"name":"USER_ALREADY_PARTICIPANT","comment":"The user is already in the group."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."},{"code":400,"name":"USER_IS_BLOCKED","comment":"You were blocked by this user."},{"code":403,"name":"USER_NOT_MUTUAL_CONTACT","comment":"The provided user is not a mutual contact."},{"code":403,"name":"USER_PRIVACY_RESTRICTED","comment":"The user's privacy settings do not allow you to do this."},{"code":400,"name":"YOU_BLOCKED_USER","comment":"You blocked this user."}],"available":"user"},{"kind":"method","name":"messages.deleteChatUser","type":"Updates","id":2719505579,"comment":"Deletes a user from a chat and sends a service message on it.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"revoke_history","type":"true","predicate":"flags.0"},{"name":"chat_id","type":"int53"},{"name":"user_id","type":"InputUser"}],"throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."},{"code":400,"name":"USER_NOT_PARTICIPANT","comment":"You're not a member of this supergroup/channel."}],"available":"both"},{"kind":"method","name":"messages.createChat","type":"Updates","id":164303470,"comment":"Creates a new chat.","arguments":[{"name":"users","type":"Vector","comment":"List of user IDs to be invited"},{"name":"title","type":"string","comment":"Chat name"}],"throws":[{"code":400,"name":"CHAT_INVALID","comment":"Invalid chat."},{"code":400,"name":"CHAT_TITLE_EMPTY","comment":"No chat title provided."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"USERS_TOO_FEW","comment":"Not enough users (to create a chat, for example)."},{"code":403,"name":"USER_RESTRICTED","comment":"You're spamreported, you can't create channels or chats."}],"available":"user"},{"kind":"method","name":"messages.getDhConfig","type":"messages.DhConfig","id":651135312,"comment":"Returns configuration parameters for Diffie-Hellman key generation. Can also return a random sequence of bytes of required length.","arguments":[{"name":"version","type":"int","comment":"Value of the version parameter from {@link messages.dhConfig}, avialable at the client"},{"name":"random_length","type":"int"}],"throws":[{"code":400,"name":"RANDOM_LENGTH_INVALID","comment":"Random length invalid."}],"available":"user"},{"kind":"method","name":"messages.requestEncryption","type":"EncryptedChat","id":4132286275,"comment":"Sends a request to start a secret chat to the user.","arguments":[{"name":"user_id","type":"InputUser"},{"name":"random_id","type":"int"},{"name":"g_a","type":"bytes"}],"throws":[{"code":400,"name":"DH_G_A_INVALID","comment":"g_a invalid."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"user"},{"kind":"method","name":"messages.acceptEncryption","type":"EncryptedChat","id":1035731989,"comment":"Confirms creation of a secret chat","arguments":[{"name":"peer","type":"InputEncryptedChat","comment":"Secret chat ID"},{"name":"g_b","type":"bytes"},{"name":"key_fingerprint","type":"long"}],"throws":[{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"ENCRYPTION_ALREADY_ACCEPTED","comment":"Secret chat already accepted."},{"code":400,"name":"ENCRYPTION_ALREADY_DECLINED","comment":"The secret chat was already declined."}],"available":"user"},{"kind":"method","name":"messages.discardEncryption","type":"Bool","id":4086541984,"comment":"Cancels a request for creation and/or delete info on secret chat.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"delete_history","type":"true","predicate":"flags.0"},{"name":"chat_id","type":"int"}],"throws":[{"code":400,"name":"CHAT_ID_EMPTY","comment":"The provided chat ID is empty."},{"code":400,"name":"ENCRYPTION_ALREADY_DECLINED","comment":"The secret chat was already declined."},{"code":400,"name":"ENCRYPTION_ID_INVALID","comment":"The provided secret chat ID is invalid."}],"available":"user"},{"kind":"method","name":"messages.setEncryptedTyping","type":"Bool","id":2031374829,"comment":"Send typing event by the current user to a secret chat.","arguments":[{"name":"peer","type":"InputEncryptedChat","comment":"Secret chat ID"},{"name":"typing","type":"Bool","comment":"Typing.
Possible values:
{@link boolTrue}, if the user started typing and more than 5 seconds have passed since the last request
{@link boolFalse}, if the user stopped typing"}],"throws":[{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."}],"available":"user"},{"kind":"method","name":"messages.readEncryptedHistory","type":"Bool","id":2135648522,"comment":"Marks message history within a secret chat as read.","arguments":[{"name":"peer","type":"InputEncryptedChat","comment":"Secret chat ID"},{"name":"max_date","type":"int"}],"throws":[{"code":400,"name":"MSG_WAIT_FAILED","comment":"A waiting call returned an error."}],"available":"user"},{"kind":"method","name":"messages.sendEncrypted","type":"messages.SentEncryptedMessage","id":1157265941,"comment":"Sends a text message to a secret chat.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"silent","type":"true","predicate":"flags.0","comment":"Send encrypted message without a notification"},{"name":"peer","type":"InputEncryptedChat","comment":"Secret chat ID"},{"name":"random_id","type":"long"},{"name":"data","type":"bytes","comment":"TL-serialization of DecryptedMessage type, encrypted with a key that was created during chat initialization"}],"throws":[{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"DATA_INVALID","comment":"Encrypted data invalid."},{"code":400,"name":"ENCRYPTION_DECLINED","comment":"The secret chat was declined."},{"code":400,"name":"MSG_WAIT_FAILED","comment":"A waiting call returned an error."},{"code":403,"name":"USER_IS_BLOCKED","comment":"You were blocked by this user."}],"available":"user"},{"kind":"method","name":"messages.sendEncryptedFile","type":"messages.SentEncryptedMessage","id":1431914525,"comment":"Sends a message with a file attachment to a secret chat","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"silent","type":"true","predicate":"flags.0","comment":"Whether to send the file without triggering a notification"},{"name":"peer","type":"InputEncryptedChat","comment":"Secret chat ID"},{"name":"random_id","type":"long"},{"name":"data","type":"bytes","comment":"TL-serialization of DecryptedMessage type, encrypted with a key generated during chat initialization"},{"name":"file","type":"InputEncryptedFile","comment":"File attachment for the secret chat"}],"throws":[{"code":400,"name":"DATA_TOO_LONG","comment":"Data too long."},{"code":400,"name":"ENCRYPTION_DECLINED","comment":"The secret chat was declined."},{"code":400,"name":"FILE_EMTPY","comment":"An empty file was provided."},{"code":400,"name":"MD5_CHECKSUM_INVALID","comment":"The MD5 checksums do not match."},{"code":400,"name":"MSG_WAIT_FAILED","comment":"A waiting call returned an error."}],"available":"user"},{"kind":"method","name":"messages.sendEncryptedService","type":"messages.SentEncryptedMessage","id":852769188,"comment":"Sends a service message to a secret chat.","arguments":[{"name":"peer","type":"InputEncryptedChat","comment":"Secret chat ID"},{"name":"random_id","type":"long"},{"name":"data","type":"bytes","comment":"TL-serialization of DecryptedMessage type, encrypted with a key generated during chat initialization"}],"throws":[{"code":400,"name":"DATA_INVALID","comment":"Encrypted data invalid."},{"code":400,"name":"ENCRYPTION_DECLINED","comment":"The secret chat was declined."},{"code":400,"name":"ENCRYPTION_ID_INVALID","comment":"The provided secret chat ID is invalid."},{"code":400,"name":"MSG_WAIT_FAILED","comment":"A waiting call returned an error."},{"code":403,"name":"USER_DELETED","comment":"You can't send this secret message because the other participant deleted their account."},{"code":403,"name":"USER_IS_BLOCKED","comment":"You were blocked by this user."}],"available":"user"},{"kind":"method","name":"messages.receivedQueue","type":"Vector","id":1436924774,"comment":"Confirms receipt of messages in a secret chat by client, cancels push notifications.","arguments":[{"name":"max_qts","type":"int"}],"throws":[{"code":400,"name":"MAX_QTS_INVALID","comment":"The specified max_qts is invalid."},{"code":400,"name":"MSG_WAIT_FAILED","comment":"A waiting call returned an error."}],"available":"user"},{"kind":"method","name":"messages.reportEncryptedSpam","type":"Bool","id":1259113487,"comment":"Report a secret chat for spam","arguments":[{"name":"peer","type":"InputEncryptedChat","comment":"The secret chat to report"}],"throws":[{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."}],"available":"user"},{"kind":"method","name":"messages.readMessageContents","type":"messages.AffectedMessages","id":916930423,"comment":"Notifies the sender about the recipient having listened a voice message or watched a video.","arguments":[{"name":"id","type":"Vector","comment":"Message ID list"}],"available":"user"},{"kind":"method","name":"messages.getStickers","type":"messages.Stickers","id":3584414625,"comment":"Get stickers by emoji","arguments":[{"name":"emoticon","type":"string","comment":"The emoji"},{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"}],"throws":[{"code":400,"name":"EMOTICON_EMPTY","comment":"The emoji is empty."}],"available":"user"},{"kind":"method","name":"messages.getAllStickers","type":"messages.AllStickers","id":3097534888,"comment":"Get all installed stickers","arguments":[{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"}],"available":"user"},{"kind":"method","name":"messages.getWebPagePreview","type":"MessageMedia","id":2338894028,"comment":"Get preview of webpage","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"message","type":"string","comment":"Message from which to extract the preview"},{"name":"entities","type":"Vector","predicate":"flags.3","comment":"Message entities for styled text"}],"throws":[{"code":400,"name":"MESSAGE_EMPTY","comment":"The provided message is empty."}],"available":"user"},{"kind":"method","name":"messages.exportChatInvite","type":"ExportedChatInvite","id":2687296981,"comment":"Export an invite link for a chat","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"legacy_revoke_permanent","type":"true","predicate":"flags.2"},{"name":"request_needed","type":"true","predicate":"flags.3"},{"name":"peer","type":"InputPeer","comment":"Chat"},{"name":"expire_date","type":"int","predicate":"flags.0"},{"name":"usage_limit","type":"int","predicate":"flags.1"},{"name":"title","type":"string","predicate":"flags.4"}],"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"EXPIRE_DATE_INVALID","comment":"The specified expiration date is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"USAGE_LIMIT_INVALID","comment":"The specified usage limit is invalid."}],"available":"both"},{"kind":"method","name":"messages.checkChatInvite","type":"ChatInvite","id":1051570619,"comment":"Check the validity of a chat invite link and get basic info about it","arguments":[{"name":"hash","type":"string","comment":"Invite hash in t.me/joinchat/hash"}],"throws":[{"code":400,"name":"INVITE_HASH_EMPTY","comment":"The invite hash is empty."},{"code":400,"name":"INVITE_HASH_EXPIRED","comment":"The invite link has expired."},{"code":400,"name":"INVITE_HASH_INVALID","comment":"The invite hash is invalid."}],"available":"user"},{"kind":"method","name":"messages.importChatInvite","type":"Updates","id":1817183516,"comment":"Import a chat invite and join a private chat/supergroup/channel","arguments":[{"name":"hash","type":"string","comment":"hash from t.me/joinchat/hash"}],"throws":[{"code":400,"name":"CHANNELS_TOO_MUCH","comment":"You have joined too many channels/supergroups."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_INVALID","comment":"Invalid chat."},{"code":400,"name":"INVITE_HASH_EMPTY","comment":"The invite hash is empty."},{"code":400,"name":"INVITE_HASH_EXPIRED","comment":"The invite link has expired."},{"code":400,"name":"INVITE_HASH_INVALID","comment":"The invite hash is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"USERS_TOO_MUCH","comment":"The maximum number of users has been exceeded (to create a chat, for example)."},{"code":400,"name":"USER_ALREADY_PARTICIPANT","comment":"The user is already in the group."},{"code":400,"name":"USER_CHANNELS_TOO_MUCH","comment":"One of the users you tried to add is already in too many channels/supergroups."}],"available":"user"},{"kind":"method","name":"messages.getStickerSet","type":"messages.StickerSet","id":639215886,"comment":"Get info about a stickerset","arguments":[{"name":"stickerset","type":"InputStickerSet","comment":"Stickerset"}],"throws":[{"code":400,"name":"EMOTICON_STICKERPACK_MISSING","comment":" "},{"code":400,"name":"STICKERSET_INVALID","comment":"The provided sticker set is invalid."}],"available":"both"},{"kind":"method","name":"messages.installStickerSet","type":"messages.StickerSetInstallResult","id":3348096096,"comment":"Install a stickerset","arguments":[{"name":"stickerset","type":"InputStickerSet","comment":"Stickerset to install"},{"name":"archived","type":"Bool","comment":"Whether to archive stickerset"}],"throws":[{"code":400,"name":"STICKERSET_INVALID","comment":"The provided sticker set is invalid."}],"available":"user"},{"kind":"method","name":"messages.uninstallStickerSet","type":"Bool","id":4184757726,"comment":"Uninstall a stickerset","arguments":[{"name":"stickerset","type":"InputStickerSet","comment":"The stickerset to uninstall"}],"throws":[{"code":400,"name":"STICKERSET_INVALID","comment":"The provided sticker set is invalid."}],"available":"user"},{"kind":"method","name":"messages.startBot","type":"Updates","id":3873403768,"comment":"Start a conversation with a bot using a deep linking parameter","arguments":[{"name":"bot","type":"InputUser","comment":"The bot"},{"name":"peer","type":"InputPeer","comment":"The chat where to start the bot, can be the bot's private chat or a group"},{"name":"random_id","type":"long"},{"name":"start_param","type":"string"}],"throws":[{"code":400,"name":"BOT_INVALID","comment":"This is not a valid bot."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"START_PARAM_EMPTY","comment":"The start parameter is empty."},{"code":400,"name":"START_PARAM_INVALID","comment":"Start parameter invalid."},{"code":400,"name":"START_PARAM_TOO_LONG","comment":"Start parameter is too long."}],"available":"user"},{"kind":"method","name":"messages.getMessagesViews","type":"messages.MessageViews","id":1468322785,"comment":"Get and increase the view counter of a message sent or forwarded from a channel","arguments":[{"name":"peer","type":"InputPeer","comment":"Peer where the message was found"},{"name":"id","type":"Vector","comment":"ID of message"},{"name":"increment","type":"Bool","comment":"Whether to mark the message as viewed and increment the view counter"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.editChatAdmin","type":"Bool","id":2824589762,"comment":"Make a user admin in a legacy group.","arguments":[{"name":"chat_id","type":"int53"},{"name":"user_id","type":"InputUser"},{"name":"is_admin","type":"Bool"}],"throws":[{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."},{"code":400,"name":"USER_NOT_PARTICIPANT","comment":"You're not a member of this supergroup/channel."}],"available":"user"},{"kind":"method","name":"messages.migrateChat","type":"Updates","id":2726777625,"comment":"Turn a legacy group into a supergroup","arguments":[{"name":"chat_id","type":"int53"}],"throws":[{"code":400,"name":"CHANNELS_TOO_MUCH","comment":"You have joined too many channels/supergroups."},{"code":403,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.searchGlobal","type":"messages.Messages","id":1271290010,"comment":"Search for messages and peers globally","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"folder_id","type":"int","predicate":"flags.0"},{"name":"q","type":"string","comment":"Query"},{"name":"filter","type":"MessagesFilter","comment":"Global search filter"},{"name":"min_date","type":"int"},{"name":"max_date","type":"int"},{"name":"offset_rate","type":"int"},{"name":"offset_peer","type":"InputPeer"},{"name":"offset_id","type":"int"},{"name":"limit","type":"int","comment":"Offsets for pagination, for more info click here"}],"throws":[{"code":400,"name":"FOLDER_ID_INVALID","comment":"Invalid folder ID."},{"code":400,"name":"SEARCH_QUERY_EMPTY","comment":"The search query is empty."}],"available":"user"},{"kind":"method","name":"messages.reorderStickerSets","type":"Bool","id":2016638777,"comment":"Reorder installed stickersets","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"masks","type":"true","predicate":"flags.0","comment":"Reorder mask stickersets"},{"name":"order","type":"Vector","comment":"New stickerset order by stickerset IDs"}],"available":"user"},{"kind":"method","name":"messages.getDocumentByHash","type":"Document","id":864953444,"comment":"Get a document by its SHA256 hash, mainly used for gifs","arguments":[{"name":"sha256","type":"bytes","comment":"SHA256 of file"},{"name":"size","type":"int","comment":"Size of the file in bytes"},{"name":"mime_type","type":"string"}],"throws":[{"code":400,"name":"SHA256_HASH_INVALID","comment":"The provided SHA256 hash is invalid."}],"available":"both"},{"kind":"method","name":"messages.getSavedGifs","type":"messages.SavedGifs","id":1559270965,"comment":"Get saved GIFs","arguments":[{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"}],"available":"user"},{"kind":"method","name":"messages.saveGif","type":"Bool","id":846868683,"comment":"Add GIF to saved gifs list","arguments":[{"name":"id","type":"InputDocument","comment":"GIF to save"},{"name":"unsave","type":"Bool","comment":"Whether to remove GIF from saved gifs list"}],"throws":[{"code":400,"name":"GIF_ID_INVALID","comment":"The provided GIF ID is invalid."}],"available":"user"},{"kind":"method","name":"messages.getInlineBotResults","type":"messages.BotResults","id":1364105629,"comment":"Query an inline bot","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"bot","type":"InputUser","comment":"The bot to query"},{"name":"peer","type":"InputPeer","comment":"The currently opened chat"},{"name":"geo_point","type":"InputGeoPoint","predicate":"flags.0"},{"name":"query","type":"string","comment":"The query"},{"name":"offset","type":"string","comment":"The offset within the results, will be passed directly as-is to the bot."}],"throws":[{"code":400,"name":"BOT_INLINE_DISABLED","comment":"This bot can't be used in inline mode."},{"code":400,"name":"BOT_INVALID","comment":"This is not a valid bot."},{"code":400,"name":"BOT_RESPONSE_TIMEOUT","comment":"A timeout occurred while fetching data from the bot."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":-503,"name":"Timeout","comment":"Timeout while fetching data."}],"available":"user"},{"kind":"method","name":"messages.setInlineBotResults","type":"Bool","id":3948847622,"comment":"Answer an inline query, for bots only","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"gallery","type":"true","predicate":"flags.0","comment":"Set this flag if the results are composed of media files"},{"name":"private","type":"true","predicate":"flags.1","comment":"Set this flag if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query"},{"name":"query_id","type":"long"},{"name":"results","type":"Vector","comment":"Vector of results for the inline query"},{"name":"cache_time","type":"int"},{"name":"next_offset","type":"string","predicate":"flags.2"},{"name":"switch_pm","type":"InlineBotSwitchPM","predicate":"flags.3"}],"throws":[{"code":400,"name":"ARTICLE_TITLE_EMPTY","comment":"The title of the article is empty."},{"code":400,"name":"AUDIO_CONTENT_URL_EMPTY","comment":"The remote URL specified in the content field is empty."},{"code":400,"name":"AUDIO_TITLE_EMPTY","comment":"An empty audio title was provided."},{"code":400,"name":"BUTTON_DATA_INVALID","comment":"The data of one or more of the buttons you provided is invalid."},{"code":400,"name":"BUTTON_TYPE_INVALID","comment":"The type of one or more of the buttons you provided is invalid."},{"code":400,"name":"BUTTON_URL_INVALID","comment":"Button URL invalid."},{"code":400,"name":"DOCUMENT_INVALID","comment":"The specified document is invalid."},{"code":400,"name":"FILE_CONTENT_TYPE_INVALID","comment":"File content-type is invalid."},{"code":400,"name":"FILE_TITLE_EMPTY","comment":"An empty file title was specified."},{"code":400,"name":"GIF_CONTENT_TYPE_INVALID","comment":"GIF content-type invalid."},{"code":400,"name":"MESSAGE_EMPTY","comment":"The provided message is empty."},{"code":400,"name":"MESSAGE_TOO_LONG","comment":"The provided message is too long."},{"code":400,"name":"NEXT_OFFSET_INVALID","comment":"The specified offset is longer than 64 bytes."},{"code":400,"name":"PHOTO_CONTENT_TYPE_INVALID","comment":"Photo mime-type invalid."},{"code":400,"name":"PHOTO_CONTENT_URL_EMPTY","comment":"Photo URL invalid."},{"code":400,"name":"PHOTO_INVALID","comment":"Photo invalid."},{"code":400,"name":"PHOTO_THUMB_URL_EMPTY","comment":"Photo thumbnail URL is empty."},{"code":400,"name":"QUERY_ID_INVALID","comment":"The query ID is invalid."},{"code":400,"name":"REPLY_MARKUP_INVALID","comment":"The provided reply markup is invalid."},{"code":400,"name":"RESULTS_TOO_MUCH","comment":"Too many results were provided."},{"code":400,"name":"RESULT_ID_DUPLICATE","comment":"You provided a duplicate result ID."},{"code":400,"name":"RESULT_TYPE_INVALID","comment":"Result type invalid."},{"code":400,"name":"SEND_MESSAGE_MEDIA_INVALID","comment":"Invalid media provided."},{"code":400,"name":"SEND_MESSAGE_TYPE_INVALID","comment":"The message type is invalid."},{"code":400,"name":"START_PARAM_INVALID","comment":"Start parameter invalid."},{"code":400,"name":"STICKER_DOCUMENT_INVALID","comment":"The specified sticker document is invalid."},{"code":403,"name":"USER_BOT_INVALID","comment":"This method can only be called by a bot."},{"code":400,"name":"VIDEO_TITLE_EMPTY","comment":"The specified video title is empty."},{"code":400,"name":"WEBDOCUMENT_INVALID","comment":"Invalid webdocument URL provided."},{"code":400,"name":"WEBDOCUMENT_MIME_INVALID","comment":"Invalid webdocument mime type provided."},{"code":400,"name":"WEBDOCUMENT_SIZE_TOO_BIG","comment":"Webdocument is too big!"},{"code":400,"name":"WEBDOCUMENT_URL_INVALID","comment":"The specified webdocument URL is invalid."}],"available":"bot"},{"kind":"method","name":"messages.sendInlineBotResult","type":"Updates","id":570955184,"comment":"Send a result obtained using {@link messages.getInlineBotResults}.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"silent","type":"true","predicate":"flags.5","comment":"Whether to send the message silently (no notification will be triggered on the other client)"},{"name":"background","type":"true","predicate":"flags.6","comment":"Whether to send the message in background"},{"name":"clear_draft","type":"true","predicate":"flags.7"},{"name":"hide_via","type":"true","predicate":"flags.11"},{"name":"peer","type":"InputPeer","comment":"Destination"},{"name":"reply_to_msg_id","type":"int","predicate":"flags.0"},{"name":"random_id","type":"long"},{"name":"query_id","type":"long"},{"name":"id","type":"string","comment":"Result ID from {@link messages.getInlineBotResults}"},{"name":"schedule_date","type":"int","predicate":"flags.10"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_RESTRICTED","comment":"You can't send messages in this chat, you were restricted."},{"code":403,"name":"CHAT_SEND_GAME_FORBIDDEN","comment":"You can't send a game to this chat."},{"code":403,"name":"CHAT_SEND_GIFS_FORBIDDEN","comment":"You can't send gifs in this chat."},{"code":403,"name":"CHAT_SEND_INLINE_FORBIDDEN","comment":"You can't send inline messages in this group."},{"code":403,"name":"CHAT_SEND_MEDIA_FORBIDDEN","comment":"You can't send media in this chat."},{"code":403,"name":"CHAT_SEND_STICKERS_FORBIDDEN","comment":"You can't send stickers in this chat."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"INLINE_RESULT_EXPIRED","comment":"The inline query expired."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MEDIA_EMPTY","comment":"The provided media object is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"QUERY_ID_EMPTY","comment":"The query ID is empty."},{"code":400,"name":"RESULT_ID_EMPTY","comment":"Result ID empty."},{"code":400,"name":"SCHEDULE_DATE_TOO_LATE","comment":"You can't schedule a message this far in the future."},{"code":400,"name":"SCHEDULE_TOO_MUCH","comment":"There are too many scheduled messages."},{"code":420,"name":"SLOWMODE_WAIT_X","comment":"Slowmode is enabled in this chat: you must wait for the specified number of seconds before sending another message to the chat."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","comment":"You're banned from sending messages in supergroups/channels."},{"code":400,"name":"WEBPAGE_CURL_FAILED","comment":"Failure while fetching the webpage with cURL."},{"code":400,"name":"WEBPAGE_MEDIA_EMPTY","comment":"Webpage media empty."},{"code":400,"name":"YOU_BLOCKED_USER","comment":"You blocked this user."}],"available":"user"},{"kind":"method","name":"messages.getMessageEditData","type":"messages.MessageEditData","id":4255550774,"comment":"Find out if a media message's caption can be edited","arguments":[{"name":"peer","type":"InputPeer","comment":"Peer where the media was sent"},{"name":"id","type":"int","comment":"ID of message"}],"throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":403,"name":"MESSAGE_AUTHOR_REQUIRED","comment":"Message author required."},{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.editMessage","type":"Updates","id":1224152952,"comment":"Edit message","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"no_webpage","type":"true","predicate":"flags.1"},{"name":"peer","type":"InputPeer","comment":"Where was the message sent"},{"name":"id","type":"int","comment":"ID of the message to edit"},{"name":"message","type":"string","predicate":"flags.11","comment":"New message"},{"name":"media","type":"InputMedia","predicate":"flags.14","comment":"New attached media"},{"name":"reply_markup","type":"ReplyMarkup","predicate":"flags.2"},{"name":"entities","type":"Vector","predicate":"flags.3","comment":"Message entities for styled text"},{"name":"schedule_date","type":"int","predicate":"flags.15"}],"throws":[{"code":400,"name":"BUTTON_DATA_INVALID","comment":"The data of one or more of the buttons you provided is invalid."},{"code":400,"name":"BUTTON_TYPE_INVALID","comment":"The type of one or more of the buttons you provided is invalid."},{"code":400,"name":"BUTTON_URL_INVALID","comment":"Button URL invalid."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"ENTITIES_TOO_LONG","comment":"You provided too many styled message entities."},{"code":403,"name":"INLINE_BOT_REQUIRED","comment":"Only the inline bot can edit message."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MEDIA_CAPTION_TOO_LONG","comment":"The caption is too long."},{"code":400,"name":"MEDIA_GROUPED_INVALID","comment":"You tried to send media of different types in an album."},{"code":400,"name":"MEDIA_NEW_INVALID","comment":"The new media is invalid."},{"code":400,"name":"MEDIA_PREV_INVALID","comment":"Previous media invalid."},{"code":403,"name":"MESSAGE_AUTHOR_REQUIRED","comment":"Message author required."},{"code":400,"name":"MESSAGE_EDIT_TIME_EXPIRED","comment":"You can't edit this message anymore, too much time has passed since its creation."},{"code":400,"name":"MESSAGE_EMPTY","comment":"The provided message is empty."},{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"MESSAGE_NOT_MODIFIED","comment":"The message text has not changed."},{"code":400,"name":"MESSAGE_TOO_LONG","comment":"The provided message is too long."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"REPLY_MARKUP_INVALID","comment":"The provided reply markup is invalid."},{"code":400,"name":"SCHEDULE_DATE_INVALID","comment":"Invalid schedule date provided."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","comment":"You're banned from sending messages in supergroups/channels."}],"available":"both"},{"kind":"method","name":"messages.editInlineBotMessage","type":"Bool","id":2203418042,"comment":"Edit an inline bot message","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"no_webpage","type":"true","predicate":"flags.1"},{"name":"id","type":"InputBotInlineMessageID","comment":"Sent inline message ID"},{"name":"message","type":"string","predicate":"flags.11","comment":"Message"},{"name":"media","type":"InputMedia","predicate":"flags.14","comment":"Media"},{"name":"reply_markup","type":"ReplyMarkup","predicate":"flags.2"},{"name":"entities","type":"Vector","predicate":"flags.3","comment":"Message entities for styled text"}],"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"MESSAGE_NOT_MODIFIED","comment":"The message text has not changed."}],"available":"both"},{"kind":"method","name":"messages.getBotCallbackAnswer","type":"messages.BotCallbackAnswer","id":2470627847,"comment":"Press an inline callback button and get a callback answer from the bot","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"game","type":"true","predicate":"flags.1","comment":"Whether this is a \"play game\" button"},{"name":"peer","type":"InputPeer","comment":"Where was the inline keyboard sent"},{"name":"msg_id","type":"int"},{"name":"data","type":"bytes","predicate":"flags.0","comment":"Callback data"},{"name":"password","type":"InputCheckPasswordSRP","predicate":"flags.2","comment":"For buttons {@link keyboardButtonCallback}, the SRP payload generated using SRP."}],"throws":[{"code":400,"name":"BOT_RESPONSE_TIMEOUT","comment":"A timeout occurred while fetching data from the bot."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"DATA_INVALID","comment":"Encrypted data invalid."},{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":-503,"name":"Timeout","comment":"Timeout while fetching data."}],"available":"user"},{"kind":"method","name":"messages.setBotCallbackAnswer","type":"Bool","id":3582923530,"comment":"Set the callback answer to a user button press (bots only)","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"alert","type":"true","predicate":"flags.1","comment":"Whether to show the message as a popup instead of a toast notification"},{"name":"query_id","type":"long"},{"name":"message","type":"string","predicate":"flags.0","comment":"Popup to show"},{"name":"url","type":"string","predicate":"flags.2","comment":"URL to open"},{"name":"cache_time","type":"int"}],"throws":[{"code":400,"name":"MESSAGE_TOO_LONG","comment":"The provided message is too long."},{"code":400,"name":"QUERY_ID_INVALID","comment":"The query ID is invalid."},{"code":400,"name":"URL_INVALID","comment":"Invalid URL provided."}],"available":"both"},{"kind":"method","name":"messages.getPeerDialogs","type":"messages.PeerDialogs","id":3832593661,"comment":"Get dialog info of specified peers","arguments":[{"name":"peers","type":"Vector","comment":"Peers"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.saveDraft","type":"Bool","id":3157909835,"comment":"Save a message draft associated to a chat.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"no_webpage","type":"true","predicate":"flags.1"},{"name":"reply_to_msg_id","type":"int","predicate":"flags.0"},{"name":"peer","type":"InputPeer","comment":"Destination of the message that should be sent"},{"name":"message","type":"string","comment":"The draft"},{"name":"entities","type":"Vector","predicate":"flags.3","comment":"Message entities for styled text"}],"throws":[{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.getAllDrafts","type":"Updates","id":1782549861,"comment":"Save get all message drafts.","arguments":[],"available":"user"},{"kind":"method","name":"messages.getFeaturedStickers","type":"messages.FeaturedStickers","id":1685588756,"comment":"Get featured stickers","arguments":[{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"}],"available":"user"},{"kind":"method","name":"messages.readFeaturedStickers","type":"Bool","id":1527873830,"comment":"Mark new featured stickers as read","arguments":[{"name":"id","type":"Vector","comment":"IDs of stickersets to mark as read"}],"available":"user"},{"kind":"method","name":"messages.getRecentStickers","type":"messages.RecentStickers","id":2645114939,"comment":"Get recent stickers","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"attached","type":"true","predicate":"flags.0","comment":"Get stickers recently attached to photo or video files"},{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"}],"available":"user"},{"kind":"method","name":"messages.saveRecentSticker","type":"Bool","id":958863608,"comment":"Add/remove sticker from recent stickers list","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"attached","type":"true","predicate":"flags.0","comment":"Whether to add/remove stickers recently attached to photo or video files"},{"name":"id","type":"InputDocument","comment":"Sticker"},{"name":"unsave","type":"Bool","comment":"Whether to save or unsave the sticker"}],"throws":[{"code":400,"name":"STICKER_ID_INVALID","comment":"The provided sticker ID is invalid."}],"available":"user"},{"kind":"method","name":"messages.clearRecentStickers","type":"Bool","id":2308530221,"comment":"Clear recent stickers","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"attached","type":"true","predicate":"flags.0","comment":"Set this flag to clear the list of stickers recently attached to photo or video files"}],"available":"user"},{"kind":"method","name":"messages.getArchivedStickers","type":"messages.ArchivedStickers","id":1475442322,"comment":"Get all archived stickers","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"masks","type":"true","predicate":"flags.0","comment":"Get mask stickers"},{"name":"offset_id","type":"long"},{"name":"limit","type":"int","comment":"Maximum number of results to return, see pagination"}],"available":"user"},{"kind":"method","name":"messages.getMaskStickers","type":"messages.AllStickers","id":1678738104,"comment":"Get installed mask stickers","arguments":[{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"}],"available":"user"},{"kind":"method","name":"messages.getAttachedStickers","type":"Vector","id":3428542412,"comment":"Get stickers attached to a photo or video","arguments":[{"name":"media","type":"InputStickeredMedia","comment":"Stickered media"}],"available":"user"},{"kind":"method","name":"messages.setGameScore","type":"Updates","id":2398678208,"comment":"Use this method to set the score of the specified user in a game sent as a normal message (bots only).","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"edit_message","type":"true","predicate":"flags.0"},{"name":"force","type":"true","predicate":"flags.1","comment":"Set this flag if the high score is allowed to decrease. This can be useful when fixing mistakes or banning cheaters"},{"name":"peer","type":"InputPeer","comment":"Unique identifier of target chat"},{"name":"id","type":"int","comment":"Identifier of the sent message"},{"name":"user_id","type":"InputUser"},{"name":"score","type":"int","comment":"New score"}],"throws":[{"code":400,"name":"BOT_SCORE_NOT_MODIFIED","comment":"The score wasn't modified."},{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"USER_BOT_REQUIRED","comment":"This method can only be called by a bot."}],"available":"bot"},{"kind":"method","name":"messages.setInlineGameScore","type":"Bool","id":363700068,"comment":"Use this method to set the score of the specified user in a game sent as an inline message (bots only).","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"edit_message","type":"true","predicate":"flags.0"},{"name":"force","type":"true","predicate":"flags.1","comment":"Set this flag if the high score is allowed to decrease. This can be useful when fixing mistakes or banning cheaters"},{"name":"id","type":"InputBotInlineMessageID","comment":"ID of the inline message"},{"name":"user_id","type":"InputUser"},{"name":"score","type":"int","comment":"New score"}],"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"USER_BOT_REQUIRED","comment":"This method can only be called by a bot."}],"available":"bot"},{"kind":"method","name":"messages.getGameHighScores","type":"messages.HighScores","id":3894568093,"comment":"Get highscores of a game","arguments":[{"name":"peer","type":"InputPeer","comment":"Where was the game sent"},{"name":"id","type":"int","comment":"ID of message with game media attachment"},{"name":"user_id","type":"InputUser"}],"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"USER_BOT_REQUIRED","comment":"This method can only be called by a bot."}],"available":"bot"},{"kind":"method","name":"messages.getInlineGameHighScores","type":"messages.HighScores","id":258170395,"comment":"Get highscores of a game sent using an inline bot","arguments":[{"name":"id","type":"InputBotInlineMessageID","comment":"ID of inline message"},{"name":"user_id","type":"InputUser"}],"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"USER_BOT_REQUIRED","comment":"This method can only be called by a bot."}],"available":"bot"},{"kind":"method","name":"messages.getCommonChats","type":"messages.Chats","id":3826032900,"comment":"Get chats in common with a user","arguments":[{"name":"user_id","type":"InputUser"},{"name":"max_id","type":"int53"},{"name":"limit","type":"int","comment":"Maximum number of results to return, see pagination"}],"throws":[{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"user"},{"kind":"method","name":"messages.getAllChats","type":"messages.Chats","id":2271179966,"comment":"Get all chats, channels and supergroups","arguments":[{"name":"except_ids","type":"vector"}],"available":"user"},{"kind":"method","name":"messages.getWebPage","type":"WebPage","id":852135825,"comment":"Get instant view page","arguments":[{"name":"url","type":"string","comment":"URL of IV page to fetch"},{"name":"hash","type":"int","comment":"Hash for pagination, for more info click here"}],"throws":[{"code":400,"name":"WC_CONVERT_URL_INVALID","comment":"WC convert URL invalid."}],"available":"user"},{"kind":"method","name":"messages.toggleDialogPin","type":"Bool","id":2805064279,"comment":"Pin/unpin a dialog","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"pinned","type":"true","predicate":"flags.0","comment":"Whether to pin or unpin the dialog"},{"name":"peer","type":"InputDialogPeer","comment":"The dialog to pin"}],"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"PINNED_DIALOGS_TOO_MUCH","comment":"Too many pinned dialogs."}],"available":"user"},{"kind":"method","name":"messages.reorderPinnedDialogs","type":"Bool","id":991616823,"comment":"Reorder pinned dialogs","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"force","type":"true","predicate":"flags.0","comment":"If set, dialogs pinned server-side but not present in the order field will be unpinned."},{"name":"folder_id","type":"int"},{"name":"order","type":"Vector","comment":"New dialog order"}],"throws":[{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.getPinnedDialogs","type":"messages.PeerDialogs","id":3602468338,"comment":"Get pinned dialogs","arguments":[{"name":"folder_id","type":"int"}],"throws":[{"code":400,"name":"FOLDER_ID_INVALID","comment":"Invalid folder ID."}],"available":"user"},{"kind":"method","name":"messages.setBotShippingResults","type":"Bool","id":3858133754,"comment":"If you sent an invoice requesting a shipping address and the parameter is_flexible was specified, the bot will receive an {@link updateBotShippingQuery} update. Use this method to reply to shipping queries.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"query_id","type":"long"},{"name":"error","type":"string","predicate":"flags.0","comment":"Error message in human readable form that explains why it is impossible to complete the order (e.g. \"Sorry, delivery to your desired address is unavailable'). Telegram will display this message to the user."},{"name":"shipping_options","type":"Vector","predicate":"flags.1"}],"throws":[{"code":400,"name":"QUERY_ID_INVALID","comment":"The query ID is invalid."}],"available":"both"},{"kind":"method","name":"messages.setBotPrecheckoutResults","type":"Bool","id":163765653,"comment":"Once the user has confirmed their payment and shipping details, the bot receives an {@link updateBotPrecheckoutQuery} update.\nUse this method to respond to such pre-checkout queries.\nNote: Telegram must receive an answer within 10 seconds after the pre-checkout query was sent.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"success","type":"true","predicate":"flags.1","comment":"Set this flag if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order, otherwise do not set it, and set the error field, instead"},{"name":"query_id","type":"long"},{"name":"error","type":"string","predicate":"flags.0","comment":"Required if the success isn't set. Error message in human readable form that explains the reason for failure to proceed with the checkout (e.g. \"Sorry, somebody just bought the last of our amazing black T-shirts while you were busy filling out your payment details. Please choose a different color or garment!\"). Telegram will display this message to the user."}],"throws":[{"code":400,"name":"ERROR_TEXT_EMPTY","comment":"The provided error message is empty."}],"available":"both"},{"kind":"method","name":"messages.uploadMedia","type":"MessageMedia","id":1369162417,"comment":"Upload a file and associate it to a chat (without actually sending it to the chat)","arguments":[{"name":"peer","type":"InputPeer","comment":"The chat, can be an {@link inputPeerEmpty} for bots"},{"name":"media","type":"InputMedia","comment":"File uploaded in chunks as described in files »"}],"throws":[{"code":400,"name":"BOT_MISSING","comment":"This method can only be run by a bot."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_RESTRICTED","comment":"You can't send messages in this chat, you were restricted."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"FILE_PARTS_INVALID","comment":"The number of file parts is invalid."},{"code":400,"name":"IMAGE_PROCESS_FAILED","comment":"Failure while processing image."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MEDIA_INVALID","comment":"Media invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"PHOTO_EXT_INVALID","comment":"The extension of the photo is invalid."},{"code":400,"name":"PHOTO_INVALID_DIMENSIONS","comment":"The photo dimensions are invalid."},{"code":400,"name":"PHOTO_SAVE_FILE_INVALID","comment":"Internal issues, try again later."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","comment":"You're banned from sending messages in supergroups/channels."},{"code":400,"name":"WEBPAGE_CURL_FAILED","comment":"Failure while fetching the webpage with cURL."}],"available":"both"},{"kind":"method","name":"messages.sendScreenshotNotification","type":"Updates","id":3380473888,"comment":"Notify the other user in a private chat that a screenshot of the chat was taken","arguments":[{"name":"peer","type":"InputPeer","comment":"Other user"},{"name":"reply_to_msg_id","type":"int"},{"name":"random_id","type":"long"}],"throws":[{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"YOU_BLOCKED_USER","comment":"You blocked this user."}],"available":"user"},{"kind":"method","name":"messages.getFavedStickers","type":"messages.FavedStickers","id":82946729,"comment":"Get faved stickers","arguments":[{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"}],"available":"user"},{"kind":"method","name":"messages.faveSticker","type":"Bool","id":3120547163,"comment":"Mark a sticker as favorite","arguments":[{"name":"id","type":"InputDocument","comment":"Sticker to mark as favorite"},{"name":"unfave","type":"Bool","comment":"Unfavorite"}],"throws":[{"code":400,"name":"STICKER_ID_INVALID","comment":"The provided sticker ID is invalid."}],"available":"user"},{"kind":"method","name":"messages.getUnreadMentions","type":"messages.Messages","id":1180140658,"comment":"Get unread messages where we were mentioned","arguments":[{"name":"peer","type":"InputPeer","comment":"Peer where to look for mentions"},{"name":"offset_id","type":"int"},{"name":"add_offset","type":"int"},{"name":"limit","type":"int","comment":"Maximum number of results to return, see pagination"},{"name":"max_id","type":"int"},{"name":"min_id","type":"int"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.readMentions","type":"messages.AffectedHistory","id":251759059,"comment":"Mark mentions as read","arguments":[{"name":"peer","type":"InputPeer","comment":"Dialog"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.getRecentLocations","type":"messages.Messages","id":1881817312,"comment":"Get live location history of a certain user","arguments":[{"name":"peer","type":"InputPeer","comment":"User"},{"name":"limit","type":"int","comment":"Maximum number of results to return, see pagination"},{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"}],"available":"user"},{"kind":"method","name":"messages.sendMultiMedia","type":"Updates","id":3422621899,"comment":"Send an album or grouped media","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"silent","type":"true","predicate":"flags.5","comment":"Whether to send the album silently (no notification triggered)"},{"name":"background","type":"true","predicate":"flags.6","comment":"Send in background?"},{"name":"clear_draft","type":"true","predicate":"flags.7"},{"name":"peer","type":"InputPeer","comment":"The destination chat"},{"name":"reply_to_msg_id","type":"int","predicate":"flags.0"},{"name":"multi_media","type":"Vector"},{"name":"schedule_date","type":"int","predicate":"flags.10"}],"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"MEDIA_CAPTION_TOO_LONG","comment":"The caption is too long."},{"code":400,"name":"MEDIA_EMPTY","comment":"The provided media object is invalid."},{"code":400,"name":"MEDIA_INVALID","comment":"Media invalid."},{"code":400,"name":"MULTI_MEDIA_TOO_LONG","comment":"Too many media files for album."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"RANDOM_ID_EMPTY","comment":"Random ID empty."},{"code":400,"name":"SCHEDULE_DATE_TOO_LATE","comment":"You can't schedule a message this far in the future."},{"code":400,"name":"SCHEDULE_TOO_MUCH","comment":"There are too many scheduled messages."},{"code":420,"name":"SLOWMODE_WAIT_X","comment":"Slowmode is enabled in this chat: wait X seconds before sending another message to this chat."}],"available":"both"},{"kind":"method","name":"messages.uploadEncryptedFile","type":"EncryptedFile","id":1347929239,"comment":"Upload encrypted file and associate it to a secret chat","arguments":[{"name":"peer","type":"InputEncryptedChat","comment":"The secret chat to associate the file to"},{"name":"file","type":"InputEncryptedFile","comment":"The file"}],"available":"user"},{"kind":"method","name":"messages.searchStickerSets","type":"messages.FoundStickerSets","id":896555914,"comment":"Search for stickersets","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"exclude_featured","type":"true","predicate":"flags.0"},{"name":"q","type":"string","comment":"Query string"},{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"}],"available":"user"},{"kind":"method","name":"messages.getSplitRanges","type":"Vector","id":486505992,"comment":"Get message ranges for saving the user's chat history","arguments":[],"available":"user"},{"kind":"method","name":"messages.markDialogUnread","type":"Bool","id":3263617423,"comment":"Manually mark dialog as unread","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"unread","type":"true","predicate":"flags.0","comment":"Mark as unread/read"},{"name":"peer","type":"InputDialogPeer","comment":"Dialog"}],"available":"user"},{"kind":"method","name":"messages.getDialogUnreadMarks","type":"Vector","id":585256482,"comment":"Get dialogs manually marked as unread","arguments":[],"available":"user"},{"kind":"method","name":"messages.clearAllDrafts","type":"Bool","id":2119757468,"comment":"Clear all drafts.","arguments":[],"available":"user"},{"kind":"method","name":"messages.updatePinnedMessage","type":"Updates","id":3534419948,"comment":"Pin a message","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"silent","type":"true","predicate":"flags.0","comment":"Pin the message silently, without triggering a notification"},{"name":"unpin","type":"true","predicate":"flags.1","comment":"Whether the message should unpinned or pinned"},{"name":"pm_oneside","type":"true","predicate":"flags.2"},{"name":"peer","type":"InputPeer","comment":"The peer where to pin the message"},{"name":"id","type":"int","comment":"The message to pin or unpin"}],"throws":[{"code":400,"name":"BOT_ONESIDE_NOT_AVAIL","comment":"Bots can't pin messages in PM just for themselves."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"PIN_RESTRICTED","comment":"You can't pin messages."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","comment":"You're banned from sending messages in supergroups/channels."}],"available":"both"},{"kind":"method","name":"messages.sendVote","type":"Updates","id":2327879442,"comment":"Vote in a {@link poll}","arguments":[{"name":"peer","type":"InputPeer","comment":"The chat where the poll was sent"},{"name":"msg_id","type":"int"},{"name":"options","type":"Vector","comment":"The options that were chosen"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"MESSAGE_POLL_CLOSED","comment":"Poll closed."},{"code":400,"name":"OPTIONS_TOO_MUCH","comment":"Too many options provided."},{"code":400,"name":"OPTION_INVALID","comment":"Invalid option selected."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"REVOTE_NOT_ALLOWED","comment":"You cannot change your vote."}],"available":"user"},{"kind":"method","name":"messages.getPollResults","type":"Updates","id":1941660731,"comment":"Get poll results","arguments":[{"name":"peer","type":"InputPeer","comment":"Peer where the poll was found"},{"name":"msg_id","type":"int"}],"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."}],"available":"user"},{"kind":"method","name":"messages.getOnlines","type":"ChatOnlines","id":1848369232,"comment":"Get count of online users in a chat","arguments":[{"name":"peer","type":"InputPeer","comment":"The chat"}],"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.editChatAbout","type":"Bool","id":3740665751,"comment":"Edit the description of a group/supergroup/channel.","arguments":[{"name":"peer","type":"InputPeer","comment":"The group/supergroup/channel."},{"name":"about","type":"string","comment":"The new description"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ABOUT_NOT_MODIFIED","comment":"About text has not changed."},{"code":400,"name":"CHAT_ABOUT_TOO_LONG","comment":"Chat about too long."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"both"},{"kind":"method","name":"messages.editChatDefaultBannedRights","type":"Updates","id":2777049921,"comment":"Edit the default banned rights of a channel/supergroup/group.","arguments":[{"name":"peer","type":"InputPeer","comment":"The peer"},{"name":"banned_rights","type":"ChatBannedRights"}],"throws":[{"code":400,"name":"BANNED_RIGHTS_INVALID","comment":"You provided some invalid flags in the banned rights."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"UNTIL_DATE_INVALID","comment":"Invalid until date provided."}],"available":"both"},{"kind":"method","name":"messages.getEmojiKeywords","type":"EmojiKeywordsDifference","id":899735650,"comment":"Get localized emoji keywords","arguments":[{"name":"lang_code","type":"string"}],"available":"user"},{"kind":"method","name":"messages.getEmojiKeywordsDifference","type":"EmojiKeywordsDifference","id":352892591,"comment":"Get changed emoji keywords","arguments":[{"name":"lang_code","type":"string"},{"name":"from_version","type":"int"}],"available":"user"},{"kind":"method","name":"messages.getEmojiKeywordsLanguages","type":"Vector","id":1318675378,"comment":"Get info about an emoji keyword localization","arguments":[{"name":"lang_codes","type":"Vector"}],"available":"user"},{"kind":"method","name":"messages.getEmojiURL","type":"EmojiURL","id":3585149990,"comment":"Returns an HTTP URL which can be used to automatically log in into translation platform and suggest new emoji replacements. The URL will be valid for 30 seconds after generation","arguments":[{"name":"lang_code","type":"string"}],"available":"user"},{"kind":"method","name":"messages.getSearchCounters","type":"Vector","id":1932455680,"comment":"Get the number of results that would be found by a {@link messages.search} call with the same parameters","arguments":[{"name":"peer","type":"InputPeer","comment":"Peer where to search"},{"name":"filters","type":"Vector","comment":"Search filters"}],"throws":[{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.requestUrlAuth","type":"UrlAuthResult","id":428848198,"comment":"Get more info about a Seamless Telegram Login authorization request, for more info click here »","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"peer","type":"InputPeer","predicate":"flags.1","comment":"Peer where the message is located"},{"name":"msg_id","type":"int","predicate":"flags.1"},{"name":"button_id","type":"int","predicate":"flags.1"},{"name":"url","type":"string","predicate":"flags.2","comment":"URL used for link URL authorization, click here for more info »"}],"available":"user"},{"kind":"method","name":"messages.acceptUrlAuth","type":"UrlAuthResult","id":2972479781,"comment":"Use this to accept a Seamless Telegram Login authorization request, for more info click here »","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"write_allowed","type":"true","predicate":"flags.0"},{"name":"peer","type":"InputPeer","predicate":"flags.1","comment":"The location of the message"},{"name":"msg_id","type":"int","predicate":"flags.1"},{"name":"button_id","type":"int","predicate":"flags.1"},{"name":"url","type":"string","predicate":"flags.2","comment":"URL used for link URL authorization, click here for more info »"}],"available":"user"},{"kind":"method","name":"messages.hidePeerSettingsBar","type":"Bool","id":1336717624,"comment":"Should be called after the user hides the report spam/add as contact bar of a new chat, effectively prevents the user from executing the actions specified in the {@link peerSettings}.","arguments":[{"name":"peer","type":"InputPeer","comment":"Peer"}],"available":"user"},{"kind":"method","name":"messages.getScheduledHistory","type":"messages.Messages","id":4111889931,"comment":"Get scheduled messages","arguments":[{"name":"peer","type":"InputPeer","comment":"Peer"},{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"}],"throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.getScheduledMessages","type":"messages.Messages","id":3183150180,"comment":"Get scheduled messages","arguments":[{"name":"peer","type":"InputPeer","comment":"Peer"},{"name":"id","type":"Vector","comment":"IDs of scheduled messages"}],"throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.sendScheduledMessages","type":"Updates","id":3174597898,"comment":"Send scheduled messages right away","arguments":[{"name":"peer","type":"InputPeer","comment":"Peer"},{"name":"id","type":"Vector","comment":"Scheduled message IDs"}],"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.deleteScheduledMessages","type":"Updates","id":1504586518,"comment":"Delete scheduled messages","arguments":[{"name":"peer","type":"InputPeer","comment":"Peer"},{"name":"id","type":"Vector","comment":"Scheduled message IDs"}],"available":"user"},{"kind":"method","name":"messages.getPollVotes","type":"messages.VotesList","id":3094231054,"comment":"Get poll results for non-anonymous polls","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"peer","type":"InputPeer","comment":"Chat where the poll was sent"},{"name":"id","type":"int","comment":"Message ID"},{"name":"option","type":"bytes","predicate":"flags.0","comment":"Get only results for the specified poll option"},{"name":"offset","type":"string","predicate":"flags.1","comment":"Offset for results, taken from the next_offset field of {@link messages.votesList}, initially an empty string.
Note: if no more results are available, the method call will return an empty next_offset; thus, avoid providing the next_offset returned in {@link messages.votesList} if it is empty, to avoid an infinite loop."},{"name":"limit","type":"int","comment":"Number of results to return"}],"throws":[{"code":403,"name":"BROADCAST_FORBIDDEN","comment":"Participants of polls in channels should stay anonymous."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":403,"name":"POLL_VOTE_REQUIRED","comment":"Cast a vote in the poll before calling this method."}],"available":"user"},{"kind":"method","name":"messages.toggleStickerSets","type":"Bool","id":3037016042,"comment":"Apply changes to multiple stickersets","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"uninstall","type":"true","predicate":"flags.0","comment":"Uninstall the specified stickersets"},{"name":"archive","type":"true","predicate":"flags.1","comment":"Archive the specified stickersets"},{"name":"unarchive","type":"true","predicate":"flags.2","comment":"Unarchive the specified stickersets"},{"name":"stickersets","type":"Vector","comment":"Stickersets to act upon"}],"available":"user"},{"kind":"method","name":"messages.getDialogFilters","type":"Vector","id":4053719405,"comment":"Get folders","arguments":[],"available":"user"},{"kind":"method","name":"messages.getSuggestedDialogFilters","type":"Vector","id":2728186924,"comment":"Get suggested folders","arguments":[],"available":"user"},{"kind":"method","name":"messages.updateDialogFilter","type":"Bool","id":450142282,"comment":"Update folder","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"id","type":"int","comment":"Folder ID"},{"name":"filter","type":"DialogFilter","predicate":"flags.0","comment":"Folder info"}],"throws":[{"code":400,"name":"FILTER_ID_INVALID","comment":"The specified filter ID is invalid."},{"code":400,"name":"FILTER_INCLUDE_EMPTY","comment":"The include_peers vector of the filter is empty."},{"code":400,"name":"FILTER_TITLE_EMPTY","comment":"The title field of the filter is empty."}],"available":"user"},{"kind":"method","name":"messages.updateDialogFiltersOrder","type":"Bool","id":3311649252,"comment":"Reorder folders","arguments":[{"name":"order","type":"Vector","comment":"New folder order"}],"available":"user"},{"kind":"method","name":"messages.getOldFeaturedStickers","type":"messages.FeaturedStickers","id":2127598753,"comment":"Method for fetching previously featured stickers","arguments":[{"name":"offset","type":"int","comment":"Offset"},{"name":"limit","type":"int","comment":"Maximum number of results to return, see pagination"},{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"}],"available":"user"},{"kind":"method","name":"messages.getReplies","type":"messages.Messages","id":584962828,"comment":"Get messages in a reply thread","arguments":[{"name":"peer","type":"InputPeer","comment":"Peer"},{"name":"msg_id","type":"int"},{"name":"offset_id","type":"int"},{"name":"offset_date","type":"int"},{"name":"add_offset","type":"int"},{"name":"limit","type":"int","comment":"Maximum number of results to return, see pagination"},{"name":"max_id","type":"int"},{"name":"min_id","type":"int"},{"name":"hash","type":"long","comment":"Hash for pagination, for more info click here"}],"throws":[{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.getDiscussionMessage","type":"messages.DiscussionMessage","id":1147761405,"comment":"Get discussion message from the associated discussion group of a channel to show it on top of the comment section, without actually joining the group","arguments":[{"name":"peer","type":"InputPeer","comment":"Channel ID"},{"name":"msg_id","type":"int"}],"throws":[{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.readDiscussion","type":"Bool","id":4147227124,"comment":"Mark a thread as read","arguments":[{"name":"peer","type":"InputPeer","comment":"Group ID"},{"name":"msg_id","type":"int"},{"name":"read_max_id","type":"int"}],"throws":[{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.unpinAllMessages","type":"messages.AffectedHistory","id":4029004939,"comment":"Unpin all pinned messages","arguments":[{"name":"peer","type":"InputPeer","comment":"Chat where to unpin"}],"available":"both"},{"kind":"method","name":"messages.deleteChat","type":"Bool","id":1540419152,"comment":"Delete a chat","arguments":[{"name":"chat_id","type":"int53"}],"throws":[{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},{"kind":"method","name":"messages.deletePhoneCallHistory","type":"messages.AffectedFoundMessages","id":4190888969,"comment":"Delete the entire phone call history.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"revoke","type":"true","predicate":"flags.0","comment":"Whether to remove phone call history for participants as well"}],"available":"user"},{"kind":"method","name":"messages.checkHistoryImport","type":"messages.HistoryImportParsed","id":1140726259,"comment":"Obtains information about a chat export file, generated by a foreign chat app, click here for more info about imported chats ».","arguments":[{"name":"import_head","type":"string"}],"available":"user"},{"kind":"method","name":"messages.initHistoryImport","type":"messages.HistoryImport","id":873008187,"comment":"Import chat history from a foreign chat app into a specific Telegram chat, click here for more info about imported chats ».","arguments":[{"name":"peer","type":"InputPeer","comment":"The Telegram chat where the history should be imported."},{"name":"file","type":"InputFile","comment":"File with messages to import."},{"name":"media_count","type":"int"}],"throws":[{"code":400,"name":"IMPORT_FILE_INVALID","comment":"The specified chat export file is invalid."},{"code":400,"name":"IMPORT_FORMAT_UNRECOGNIZED","comment":"The specified chat export file was exported from an unsupported chat app."},{"code":406,"name":"PREVIOUS_CHAT_IMPORT_ACTIVE_WAIT_5MIN","comment":"Import for this chat is already in progress, wait 5 minutes before starting a new one."}],"available":"user"},{"kind":"method","name":"messages.uploadImportedMedia","type":"MessageMedia","id":713433234,"comment":"Upload a media file associated with an imported chat, click here for more info ».","arguments":[{"name":"peer","type":"InputPeer","comment":"The Telegram chat where the media will be imported"},{"name":"import_id","type":"long"},{"name":"file_name","type":"string"},{"name":"media","type":"InputMedia","comment":"Media metadata"}],"available":"user"},{"kind":"method","name":"messages.startHistoryImport","type":"Bool","id":3023958852,"comment":"Complete the history import process, importing all messages into the chat.\nTo be called only after initializing the import with {@link messages.initHistoryImport} and uploading all files using {@link messages.uploadImportedMedia}.","arguments":[{"name":"peer","type":"InputPeer","comment":"The Telegram chat where the messages should be imported, click here for more info »"},{"name":"import_id","type":"long"}],"throws":[{"code":400,"name":"IMPORT_ID_INVALID","comment":"The specified import ID is invalid."}],"available":"user"},{"kind":"method","name":"messages.getExportedChatInvites","type":"messages.ExportedChatInvites","id":2729812982,"comment":"Get info about the chat invites of a specific chat","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"revoked","type":"true","predicate":"flags.3","comment":"Whether to fetch revoked chat invites"},{"name":"peer","type":"InputPeer","comment":"Chat"},{"name":"admin_id","type":"InputUser"},{"name":"offset_date","type":"int","predicate":"flags.2"},{"name":"offset_link","type":"string","predicate":"flags.2"},{"name":"limit","type":"int","comment":"Maximum number of results to return, see pagination"}],"available":"user"},{"kind":"method","name":"messages.getExportedChatInvite","type":"messages.ExportedChatInvite","id":1937010524,"comment":"Get info about a chat invite","arguments":[{"name":"peer","type":"InputPeer","comment":"Chat"},{"name":"link","type":"string","comment":"Invite link"}],"available":"user"},{"kind":"method","name":"messages.editExportedChatInvite","type":"messages.ExportedChatInvite","id":3184144245,"comment":"Edit an exported chat invite","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"revoked","type":"true","predicate":"flags.2","comment":"Whether to revoke the chat invite"},{"name":"peer","type":"InputPeer","comment":"Chat"},{"name":"link","type":"string","comment":"Invite link"},{"name":"expire_date","type":"int","predicate":"flags.0"},{"name":"usage_limit","type":"int","predicate":"flags.1"},{"name":"request_needed","type":"Bool","predicate":"flags.3"},{"name":"title","type":"string","predicate":"flags.4"}],"throws":[{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"both"},{"kind":"method","name":"messages.deleteRevokedExportedChatInvites","type":"Bool","id":1452833749,"comment":"Delete all revoked chat invites","arguments":[{"name":"peer","type":"InputPeer","comment":"Chat"},{"name":"admin_id","type":"InputUser"}],"available":"user"},{"kind":"method","name":"messages.deleteExportedChatInvite","type":"Bool","id":3563365419,"comment":"Delete a chat invite","arguments":[{"name":"peer","type":"InputPeer","comment":"Peer"},{"name":"link","type":"string","comment":"Invite link"}],"available":"user"},{"kind":"method","name":"messages.getAdminsWithInvites","type":"messages.ChatAdminsWithInvites","id":958457583,"comment":"Get info about chat invites generated by admins.","arguments":[{"name":"peer","type":"InputPeer","comment":"Chat"}],"available":"user"},{"kind":"method","name":"messages.getChatInviteImporters","type":"messages.ChatInviteImporters","id":3741637966,"comment":"Get info about the users that joined the chat using a specific chat invite","arguments":[{"name":"flags","type":"#"},{"name":"requested","type":"true","predicate":"flags.0"},{"name":"peer","type":"InputPeer","comment":"Chat"},{"name":"link","type":"string","predicate":"flags.1","comment":"Invite link"},{"name":"q","type":"string","predicate":"flags.2"},{"name":"offset_date","type":"int"},{"name":"offset_user","type":"InputUser"},{"name":"limit","type":"int","comment":"Maximum number of results to return, see pagination"}],"available":"user"},{"kind":"method","name":"messages.setHistoryTTL","type":"Updates","id":3087949796,"comment":"Set maximum Time-To-Live of all messages in the specified chat","arguments":[{"name":"peer","type":"InputPeer","comment":"The dialog"},{"name":"period","type":"int","comment":"Automatically delete all messages sent in the chat after this many seconds"}],"throws":[{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":400,"name":"TTL_PERIOD_INVALID","comment":"The specified TTL period is invalid."}],"available":"user"},{"kind":"method","name":"messages.checkHistoryImportPeer","type":"messages.CheckedHistoryImportPeer","id":1573261059,"comment":"If the check succeeds, and no RPC errors are returned, a messages.CheckedHistoryImportPeer constructor will be returned, with a confirmation text to be shown to the user, before actually initializing the import.\n\nCheck whether chat history exported from another chat app can be imported into a specific Telegram chat, click here for more info ».","arguments":[{"name":"peer","type":"InputPeer","comment":"The chat where we want to import history »."}],"throws":[{"code":400,"name":"USER_NOT_MUTUAL_CONTACT","comment":"The provided user is not a mutual contact."}],"available":"user"},{"kind":"method","name":"messages.setChatTheme","type":"Updates","id":3862683967,"comment":"Change the chat theme of a certain chat","arguments":[{"name":"peer","type":"InputPeer","comment":"Private chat where to change theme"},{"name":"emoticon","type":"string","comment":"Emoji, identifying a specific chat theme; a list of chat themes can be fetched using {@link account.getChatThemes}"}],"throws":[{"code":400,"name":"EMOJI_INVALID","comment":"The specified theme emoji is valid."},{"code":400,"name":"EMOJI_NOT_MODIFIED","comment":"The theme wasn't changed."}],"available":"user"},{"kind":"method","name":"messages.getMessageReadParticipants","type":"Vector","id":745510839,"comment":"Get which users read a specific message: only available for groups and supergroups with less than chat_read_mark_size_threshold members, read receipts will be stored for chat_read_mark_expire_period seconds after the message was sent, see client configuration for more info ».","arguments":[{"name":"peer","type":"InputPeer","comment":"Dialog"},{"name":"msg_id","type":"int"}],"throws":[{"code":400,"name":"CHAT_TOO_BIG","comment":"This method is not available for groups with more than chat_read_mark_size_threshold members, see client configuration »."}],"available":"user"},{"kind":"method","name":"messages.getSearchResultsCalendar","type":"messages.SearchResultsCalendar","id":1240514025,"arguments":[{"name":"peer","type":"InputPeer"},{"name":"filter","type":"MessagesFilter"},{"name":"offset_id","type":"int"},{"name":"offset_date","type":"int"}]},{"kind":"method","name":"messages.getSearchResultsPositions","type":"messages.SearchResultsPositions","id":1855292323,"arguments":[{"name":"peer","type":"InputPeer"},{"name":"filter","type":"MessagesFilter"},{"name":"offset_id","type":"int"},{"name":"limit","type":"int"}]},{"kind":"method","name":"messages.hideChatJoinRequest","type":"Updates","id":2145904661,"arguments":[{"name":"flags","type":"#"},{"name":"approved","type":"true","predicate":"flags.0"},{"name":"peer","type":"InputPeer"},{"name":"user_id","type":"InputUser"}]},{"kind":"method","name":"updates.getState","type":"updates.State","id":3990128682,"comment":"Returns a current state of updates.","arguments":[],"available":"both"},{"kind":"method","name":"updates.getDifference","type":"updates.Difference","id":630429265,"comment":"Get new updates.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"pts","type":"int","comment":"PTS, see updates."},{"name":"pts_total_limit","type":"int","predicate":"flags.0"},{"name":"date","type":"int","comment":"date, see updates."},{"name":"qts","type":"int","comment":"QTS, see updates."}],"throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","comment":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"CDN_METHOD_INVALID","comment":"You can't call this method in a CDN DC."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"DATE_EMPTY","comment":"Date empty."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PERSISTENT_TIMESTAMP_EMPTY","comment":"Persistent timestamp empty."},{"code":400,"name":"PERSISTENT_TIMESTAMP_INVALID","comment":"Persistent timestamp invalid."}],"available":"both"},{"kind":"method","name":"updates.getChannelDifference","type":"updates.ChannelDifference","id":51854712,"comment":"Returns the difference between the current state of updates of a certain channel and transmitted.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"force","type":"true","predicate":"flags.0","comment":"Set to true to skip some possibly unneeded updates and reduce server-side load"},{"name":"channel","type":"InputChannel","comment":"The channel"},{"name":"filter","type":"ChannelMessagesFilter","comment":"Messsage filter"},{"name":"pts","type":"int","comment":"Persistent timestamp (see updates)"},{"name":"limit","type":"int","comment":"How many updates to fetch, max 100000
Ordinary (non-bot) users are supposed to pass 10-100"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":403,"name":"CHANNEL_PUBLIC_GROUP_NA","comment":"channel/supergroup not available."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"FROM_MESSAGE_BOT_DISABLED","comment":"Bots can't use fromMessage min constructors."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PERSISTENT_TIMESTAMP_EMPTY","comment":"Persistent timestamp empty."},{"code":400,"name":"PERSISTENT_TIMESTAMP_INVALID","comment":"Persistent timestamp invalid."},{"code":400,"name":"PINNED_DIALOGS_TOO_MUCH","comment":"Too many pinned dialogs."},{"code":400,"name":"RANGES_INVALID","comment":"Invalid range provided."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","comment":"You're banned from sending messages in supergroups/channels."}],"available":"both"},{"kind":"method","name":"photos.updateProfilePhoto","type":"photos.Photo","id":1926525996,"comment":"Installs a previously uploaded photo as a profile photo.","arguments":[{"name":"id","type":"InputPhoto","comment":"Input photo"}],"throws":[{"code":400,"name":"ALBUM_PHOTOS_TOO_MANY","comment":"Too many."},{"code":400,"name":"FILE_PARTS_INVALID","comment":"The number of file parts is invalid."},{"code":400,"name":"IMAGE_PROCESS_FAILED","comment":"Failure while processing image."},{"code":400,"name":"LOCATION_INVALID","comment":"The provided location is invalid."},{"code":400,"name":"PHOTO_CROP_SIZE_SMALL","comment":"Photo is too small."},{"code":400,"name":"PHOTO_EXT_INVALID","comment":"The extension of the photo is invalid."},{"code":400,"name":"PHOTO_ID_INVALID","comment":"Photo ID invalid."}],"available":"user"},{"kind":"method","name":"photos.uploadProfilePhoto","type":"photos.Photo","id":2314407785,"comment":"Updates current user profile photo.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"file","type":"InputFile","predicate":"flags.0","comment":"File saved in parts by means of {@link upload.saveFilePart} method"},{"name":"video","type":"InputFile","predicate":"flags.1","comment":"Animated profile picture video"},{"name":"video_start_ts","type":"double","predicate":"flags.2"}],"throws":[{"code":400,"name":"ALBUM_PHOTOS_TOO_MANY","comment":"Too many ."},{"code":400,"name":"FILE_PARTS_INVALID","comment":"The number of file parts is invalid."},{"code":400,"name":"IMAGE_PROCESS_FAILED","comment":"Failure while processing image."},{"code":400,"name":"PHOTO_CROP_FILE_MISSING","comment":"Photo crop file missing."},{"code":400,"name":"PHOTO_CROP_SIZE_SMALL","comment":"Photo is too small."},{"code":400,"name":"PHOTO_EXT_INVALID","comment":"The extension of the photo is invalid."},{"code":400,"name":"PHOTO_FILE_MISSING","comment":"Profile photo file missing."},{"code":400,"name":"VIDEO_FILE_INVALID","comment":"The specified video file is invalid."}],"available":"user"},{"kind":"method","name":"photos.deletePhotos","type":"Vector","id":2278522671,"comment":"Deletes profile photos.","arguments":[{"name":"id","type":"Vector","comment":"Input photos to delete"}],"available":"user"},{"kind":"method","name":"photos.getUserPhotos","type":"photos.Photos","id":2446144168,"comment":"Returns the list of user photos.","arguments":[{"name":"user_id","type":"InputUser"},{"name":"offset","type":"int","comment":"Number of list elements to be skipped"},{"name":"max_id","type":"long"},{"name":"limit","type":"int","comment":"Number of list elements to be returned"}],"throws":[{"code":400,"name":"MAX_ID_INVALID","comment":"The provided max ID is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"both"},{"kind":"method","name":"upload.saveFilePart","type":"Bool","id":3003426337,"comment":"Saves a part of file for futher sending to one of the methods.","arguments":[{"name":"file_id","type":"long"},{"name":"file_part","type":"int"},{"name":"bytes","type":"bytes","comment":"Binary data, contend of a part"}],"throws":[{"code":400,"name":"FILE_PART_EMPTY","comment":"The provided file part is empty."},{"code":400,"name":"FILE_PART_INVALID","comment":"The file part number is invalid."}],"available":"both"},{"kind":"method","name":"upload.getFile","type":"upload.File","id":2975505148,"comment":"Returns content of a whole file or its part.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"precise","type":"true","predicate":"flags.0","comment":"Disable some checks on limit and offset values, useful for example to stream videos by keyframes"},{"name":"cdn_supported","type":"true","predicate":"flags.1"},{"name":"location","type":"InputFileLocation","comment":"File location"},{"name":"offset","type":"int","comment":"Number of bytes to be skipped"},{"name":"limit","type":"int","comment":"Number of bytes to be returned"}],"throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","comment":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":406,"name":"FILEREF_UPGRADE_NEEDED","comment":"The client has to be updated in order to support file references."},{"code":400,"name":"FILE_ID_INVALID","comment":"The provided file id is invalid."},{"code":400,"name":"FILE_REFERENCE_*","comment":"The file reference expired, it must be refreshed."},{"code":400,"name":"FILE_REFERENCE_EXPIRED","comment":"File reference expired, it must be refetched as described in https://core.telegram.org/api/file_reference."},{"code":400,"name":"LIMIT_INVALID","comment":"The provided limit is invalid."},{"code":400,"name":"LOCATION_INVALID","comment":"The provided location is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"OFFSET_INVALID","comment":"The provided offset is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"both"},{"kind":"method","name":"upload.saveBigFilePart","type":"Bool","id":3732629309,"comment":"Saves a part of a large file (over 10Mb in size) to be later passed to one of the methods.","arguments":[{"name":"file_id","type":"long"},{"name":"file_part","type":"int"},{"name":"file_total_parts","type":"int"},{"name":"bytes","type":"bytes","comment":"Binary data, part contents"}],"throws":[{"code":400,"name":"FILE_PARTS_INVALID","comment":"The number of file parts is invalid."},{"code":400,"name":"FILE_PART_EMPTY","comment":"The provided file part is empty."},{"code":400,"name":"FILE_PART_INVALID","comment":"The file part number is invalid."},{"code":400,"name":"FILE_PART_SIZE_CHANGED","comment":"Provided file part size has changed."},{"code":400,"name":"FILE_PART_SIZE_INVALID","comment":"The provided file part size is invalid."},{"code":400,"name":"FILE_PART_TOO_BIG","comment":"The uploaded file part is too big."}],"available":"both"},{"kind":"method","name":"upload.getWebFile","type":"upload.WebFile","id":619086221,"comment":"Returns content of an HTTP file or a part, by proxying the request through telegram.","arguments":[{"name":"location","type":"InputWebFileLocation","comment":"The file to download"},{"name":"offset","type":"int","comment":"Number of bytes to be skipped"},{"name":"limit","type":"int","comment":"Number of bytes to be returned"}],"throws":[{"code":400,"name":"LOCATION_INVALID","comment":"The provided location is invalid."}],"available":"user"},{"kind":"method","name":"upload.getCdnFile","type":"upload.CdnFile","id":536919235,"comment":"Download a CDN file.","arguments":[{"name":"file_token","type":"bytes"},{"name":"offset","type":"int","comment":"Offset of chunk to download"},{"name":"limit","type":"int","comment":"Length of chunk to download"}],"available":"user"},{"kind":"method","name":"upload.reuploadCdnFile","type":"Vector","id":2603046056,"comment":"Request a reupload of a certain file to a CDN DC.","arguments":[{"name":"file_token","type":"bytes"},{"name":"request_token","type":"bytes"}],"throws":[{"code":400,"name":"RSA_DECRYPT_FAILED","comment":"Internal RSA decryption failed."}],"available":"both"},{"kind":"method","name":"upload.getCdnFileHashes","type":"Vector","id":1302676017,"comment":"Get SHA256 hashes for verifying downloaded CDN files","arguments":[{"name":"file_token","type":"bytes"},{"name":"offset","type":"int","comment":"Offset from which to start getting hashes"}],"throws":[{"code":400,"name":"CDN_METHOD_INVALID","comment":"You can't call this method in a CDN DC."},{"code":400,"name":"RSA_DECRYPT_FAILED","comment":"Internal RSA decryption failed."}],"available":"both"},{"kind":"method","name":"upload.getFileHashes","type":"Vector","id":3338819889,"comment":"Get SHA256 hashes for verifying downloaded files","arguments":[{"name":"location","type":"InputFileLocation","comment":"File"},{"name":"offset","type":"int","comment":"Offset from which to get file hashes"}],"throws":[{"code":400,"name":"LOCATION_INVALID","comment":"The provided location is invalid."}],"available":"both"},{"kind":"method","name":"help.getConfig","type":"Config","id":3304659051,"comment":"Returns current configuration, including data center configuration.","arguments":[],"throws":[{"code":400,"name":"CONNECTION_API_ID_INVALID","comment":"The provided API id is invalid."},{"code":400,"name":"CONNECTION_APP_VERSION_EMPTY","comment":"App version is empty."},{"code":400,"name":"CONNECTION_LAYER_INVALID","comment":"Layer invalid."},{"code":400,"name":"CONNECTION_NOT_INITED","comment":"Connection not initialized."},{"code":400,"name":"DATA_INVALID","comment":"Encrypted data invalid."},{"code":400,"name":"INPUT_LAYER_INVALID","comment":"The provided layer is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"USERNAME_INVALID","comment":"The provided username is not valid."},{"code":403,"name":"USER_PRIVACY_RESTRICTED","comment":"The user's privacy settings do not allow you to do this."}],"available":"both"},{"kind":"method","name":"help.getNearestDc","type":"NearestDc","id":531836966,"comment":"Returns info on data centre nearest to the user.","arguments":[],"available":"user"},{"kind":"method","name":"help.getAppUpdate","type":"help.AppUpdate","id":1378703997,"comment":"Returns information on update availability for the current application.","arguments":[{"name":"source","type":"string","comment":"Source"}],"available":"user"},{"kind":"method","name":"help.getInviteText","type":"help.InviteText","id":1295590211,"comment":"Returns localized text of a text message with an invitation.","arguments":[],"available":"user"},{"kind":"method","name":"help.getSupport","type":"help.Support","id":2631862477,"comment":"Returns the support user for the 'ask a question' feature.","arguments":[],"available":"user"},{"kind":"method","name":"help.getAppChangelog","type":"Updates","id":2417028975,"comment":"Get changelog of current app.\nTypically, an {@link updates} constructor will be returned, containing one or more {@link updateServiceNotification} updates with app-specific changelogs.","arguments":[{"name":"prev_app_version","type":"string"}],"available":"user"},{"kind":"method","name":"help.setBotUpdatesStatus","type":"Bool","id":3961704397,"comment":"Informs the server about the number of pending bot updates if they haven't been processed for a long time; for bots only","arguments":[{"name":"pending_updates_count","type":"int"},{"name":"message","type":"string","comment":"Error message, if present"}],"available":"bot"},{"kind":"method","name":"help.getCdnConfig","type":"CdnConfig","id":1375900482,"comment":"Get configuration for CDN file downloads.","arguments":[],"throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","comment":"The temporary auth key must be binded to the permanent auth key to use these methods."}],"available":"both"},{"kind":"method","name":"help.getRecentMeUrls","type":"help.RecentMeUrls","id":1036054804,"comment":"Get recently used t.me links","arguments":[{"name":"referer","type":"string","comment":"Referer"}],"available":"user"},{"kind":"method","name":"help.getTermsOfServiceUpdate","type":"help.TermsOfServiceUpdate","id":749019089,"comment":"Look for updates of telegram's terms of service","arguments":[],"available":"user"},{"kind":"method","name":"help.acceptTermsOfService","type":"Bool","id":4000511898,"comment":"Accept the new terms of service","arguments":[{"name":"id","type":"DataJSON","comment":"ID of terms of service"}],"available":"user"},{"kind":"method","name":"help.getDeepLinkInfo","type":"help.DeepLinkInfo","id":1072547679,"comment":"Get info about a t.me link","arguments":[{"name":"path","type":"string","comment":"Path in t.me/path"}],"available":"user"},{"kind":"method","name":"help.getAppConfig","type":"JSONValue","id":2559656208,"comment":"Get app-specific configuration, see client configuration for more info on the result.","arguments":[],"available":"user"},{"kind":"method","name":"help.saveAppLog","type":"Bool","id":1862465352,"comment":"Saves logs of application on the server.","arguments":[{"name":"events","type":"Vector","comment":"List of input events"}],"available":"user"},{"kind":"method","name":"help.getPassportConfig","type":"help.PassportConfig","id":3328290056,"comment":"Get passport configuration","arguments":[{"name":"hash","type":"int","comment":"Hash for pagination, for more info click here"}],"available":"user"},{"kind":"method","name":"help.getSupportName","type":"help.SupportName","id":3546343212,"comment":"Get localized name of the telegram support user","arguments":[],"throws":[{"code":403,"name":"USER_INVALID","comment":"Invalid user provided."}],"available":"user"},{"kind":"method","name":"help.getUserInfo","type":"help.UserInfo","id":59377875,"comment":"Internal use","arguments":[{"name":"user_id","type":"InputUser"}],"throws":[{"code":403,"name":"USER_INVALID","comment":"Invalid user provided."}],"available":"user"},{"kind":"method","name":"help.editUserInfo","type":"help.UserInfo","id":1723407216,"comment":"Internal use","arguments":[{"name":"user_id","type":"InputUser"},{"name":"message","type":"string","comment":"Message"},{"name":"entities","type":"Vector","comment":"Message entities for styled text"}],"throws":[{"code":400,"name":"USER_INVALID","comment":"Invalid user provided."}],"available":"user"},{"kind":"method","name":"help.getPromoData","type":"help.PromoData","id":3231151137,"comment":"Get MTProxy/Public Service Announcement information","arguments":[],"available":"user"},{"kind":"method","name":"help.hidePromoData","type":"Bool","id":505748629,"comment":"Hide MTProxy/Public Service Announcement information","arguments":[{"name":"peer","type":"InputPeer","comment":"Peer to hide"}],"available":"user"},{"kind":"method","name":"help.dismissSuggestion","type":"Bool","id":4111317665,"comment":"Dismiss a suggestion, see here for more info ».","arguments":[{"name":"peer","type":"InputPeer","comment":"In the case of pending suggestions in {@link channelFull}, the channel ID."},{"name":"suggestion","type":"string","comment":"Suggestion, see here for more info »."}],"available":"user"},{"kind":"method","name":"help.getCountriesList","type":"help.CountriesList","id":1935116200,"comment":"Get name, ISO code, localized name and phone codes/patterns of all available countries","arguments":[{"name":"lang_code","type":"string"},{"name":"hash","type":"int","comment":"Hash for pagination, for more info click here"}],"available":"user"},{"kind":"method","name":"channels.readHistory","type":"Bool","id":3423619383,"comment":"Mark channel/supergroup history as read","arguments":[{"name":"channel","type":"InputChannel","comment":"Channel/supergroup"},{"name":"max_id","type":"int"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"user"},{"kind":"method","name":"channels.deleteMessages","type":"messages.AffectedMessages","id":2227305806,"comment":"Delete messages in a channel/supergroup","arguments":[{"name":"channel","type":"InputChannel","comment":"Channel/supergroup"},{"name":"id","type":"Vector","comment":"IDs of messages to delete"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":403,"name":"MESSAGE_DELETE_FORBIDDEN","comment":"You can't delete one of the messages you tried to delete, most likely because it is a service message."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"both"},{"kind":"method","name":"channels.deleteUserHistory","type":"messages.AffectedHistory","id":3507345179,"comment":"Delete all messages sent by a certain user in a supergroup","arguments":[{"name":"channel","type":"InputChannel","comment":"Supergroup"},{"name":"user_id","type":"InputUser"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"user"},{"kind":"method","name":"channels.reportSpam","type":"Bool","id":4261967888,"comment":"Reports some messages from a user in a supergroup as spam; requires administrator rights in the supergroup","arguments":[{"name":"channel","type":"InputChannel","comment":"Supergroup"},{"name":"user_id","type":"InputUser"},{"name":"id","type":"Vector","comment":"IDs of spam messages"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"user"},{"kind":"method","name":"channels.getMessages","type":"messages.Messages","id":2911672867,"comment":"Get channel/supergroup messages","arguments":[{"name":"channel","type":"InputChannel","comment":"Channel/supergroup"},{"name":"id","type":"Vector","comment":"IDs of messages to get"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MESSAGE_IDS_EMPTY","comment":"No message ids were provided."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"both"},{"kind":"method","name":"channels.getParticipants","type":"channels.ChannelParticipants","id":2010044880,"comment":"Get the participants of a supergroup/channel","arguments":[{"name":"channel","type":"InputChannel","comment":"Channel"},{"name":"filter","type":"ChannelParticipantsFilter","comment":"Which participant types to fetch"},{"name":"offset","type":"int","comment":"Offset"},{"name":"limit","type":"int","comment":"Limit"},{"name":"hash","type":"long","comment":"Hash"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."}],"available":"both"},{"kind":"method","name":"channels.getParticipant","type":"channels.ChannelParticipant","id":2695589062,"comment":"Get info about a channel/supergroup participant","arguments":[{"name":"channel","type":"InputChannel","comment":"Channel/supergroup"},{"name":"participant","type":"InputPeer","comment":"Participant to get info about"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PARTICIPANT_ID_INVALID","comment":"The specified participant ID is invalid."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."},{"code":400,"name":"USER_NOT_PARTICIPANT","comment":"You're not a member of this supergroup/channel."}],"available":"both"},{"kind":"method","name":"channels.getChannels","type":"messages.Chats","id":176122811,"comment":"Get info about channels/supergroups","arguments":[{"name":"id","type":"Vector","comment":"IDs of channels/supergroups to get info about"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"both"},{"kind":"method","name":"channels.getFullChannel","type":"messages.ChatFull","id":141781513,"comment":"Get full info about a channel","arguments":[{"name":"channel","type":"InputChannel","comment":"The channel to get info about"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":403,"name":"CHANNEL_PUBLIC_GROUP_NA","comment":"channel/supergroup not available."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"both"},{"kind":"method","name":"channels.createChannel","type":"Updates","id":1029681423,"comment":"Create a supergroup/channel.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"broadcast","type":"true","predicate":"flags.0","comment":"Whether to create a channel"},{"name":"megagroup","type":"true","predicate":"flags.1","comment":"Whether to create a supergroup"},{"name":"for_import","type":"true","predicate":"flags.3"},{"name":"title","type":"string","comment":"Channel title"},{"name":"about","type":"string","comment":"Channel description"},{"name":"geo_point","type":"InputGeoPoint","predicate":"flags.2"},{"name":"address","type":"string","predicate":"flags.2","comment":"Geogroup address"}],"throws":[{"code":400,"name":"CHANNELS_ADMIN_LOCATED_TOO_MUCH","comment":"The user has reached the limit of public geogroups."},{"code":400,"name":"CHANNELS_TOO_MUCH","comment":"You have joined too many channels/supergroups."},{"code":400,"name":"CHAT_ABOUT_TOO_LONG","comment":"Chat about too long."},{"code":400,"name":"CHAT_TITLE_EMPTY","comment":"No chat title provided."},{"code":403,"name":"USER_RESTRICTED","comment":"You're spamreported, you can't create channels or chats."}],"available":"user"},{"kind":"method","name":"channels.editAdmin","type":"Updates","id":3543959810,"comment":"Modify the admin rights of a user in a supergroup/channel.","arguments":[{"name":"channel","type":"InputChannel","comment":"The supergroup/channel."},{"name":"user_id","type":"InputUser"},{"name":"admin_rights","type":"ChatAdminRights"},{"name":"rank","type":"string","comment":"Indicates the role (rank) of the admin in the group: just an arbitrary string"}],"throws":[{"code":400,"name":"ADMINS_TOO_MUCH","comment":"There are too many admins."},{"code":400,"name":"ADMIN_RANK_EMOJI_NOT_ALLOWED","comment":"An admin rank cannot contain emojis."},{"code":400,"name":"ADMIN_RANK_INVALID","comment":"The specified admin rank is invalid."},{"code":400,"name":"BOTS_TOO_MUCH","comment":"There are too many bots in this chat/channel."},{"code":400,"name":"BOT_CHANNELS_NA","comment":"Bots can't edit admin privileges."},{"code":400,"name":"BOT_GROUPS_BLOCKED","comment":"This bot can't be added to groups."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":403,"name":"CHAT_ADMIN_INVITE_REQUIRED","comment":"You do not have the rights to do this."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":406,"name":"FRESH_CHANGE_ADMINS_FORBIDDEN","comment":"You were just elected admin, you can't add or modify other admins yet."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":403,"name":"RIGHT_FORBIDDEN","comment":"Your admin rights do not allow you to do this."},{"code":400,"name":"USERS_TOO_MUCH","comment":"The maximum number of users has been exceeded (to create a chat, for example)."},{"code":400,"name":"USER_BLOCKED","comment":"User blocked."},{"code":403,"name":"USER_CHANNELS_TOO_MUCH","comment":"One of the users you tried to add is already in too many channels/supergroups."},{"code":400,"name":"USER_CREATOR","comment":"You can't leave this channel, because you're its creator."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."},{"code":400,"name":"USER_NOT_MUTUAL_CONTACT","comment":"The provided user is not a mutual contact."},{"code":403,"name":"USER_PRIVACY_RESTRICTED","comment":"The user's privacy settings do not allow you to do this."},{"code":403,"name":"USER_RESTRICTED","comment":"You're spamreported, you can't create channels or chats."}],"available":"both"},{"kind":"method","name":"channels.editTitle","type":"Updates","id":1450044624,"comment":"Edit the name of a channel/supergroup","arguments":[{"name":"channel","type":"InputChannel","comment":"Channel/supergroup"},{"name":"title","type":"string","comment":"New name"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":400,"name":"CHAT_TITLE_EMPTY","comment":"No chat title provided."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."}],"available":"both"},{"kind":"method","name":"channels.editPhoto","type":"Updates","id":4046346185,"comment":"Change the photo of a channel/supergroup","arguments":[{"name":"channel","type":"InputChannel","comment":"Channel/supergroup whose photo should be edited"},{"name":"photo","type":"InputChatPhoto","comment":"New photo"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"FILE_REFERENCE_INVALID","comment":"The specified file reference is invalid."},{"code":400,"name":"PHOTO_CROP_SIZE_SMALL","comment":"Photo is too small."},{"code":400,"name":"PHOTO_EXT_INVALID","comment":"The extension of the photo is invalid."},{"code":400,"name":"PHOTO_INVALID","comment":"Photo invalid."}],"available":"both"},{"kind":"method","name":"channels.checkUsername","type":"Bool","id":283557164,"comment":"Check if a username is free and can be assigned to a channel/supergroup","arguments":[{"name":"channel","type":"InputChannel","comment":"The channel/supergroup that will assigned the specified username"},{"name":"username","type":"string","comment":"The username to check"}],"throws":[{"code":400,"name":"CHANNELS_ADMIN_PUBLIC_TOO_MUCH","comment":"You're admin of too many public channels, make some channels private to change the username of this channel."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"USERNAME_INVALID","comment":"The provided username is not valid."}],"available":"user"},{"kind":"method","name":"channels.updateUsername","type":"Bool","id":890549214,"comment":"Change the username of a supergroup/channel","arguments":[{"name":"channel","type":"InputChannel","comment":"Channel"},{"name":"username","type":"string","comment":"New username"}],"throws":[{"code":400,"name":"CHANNELS_ADMIN_PUBLIC_TOO_MUCH","comment":"You're admin of too many public channels, make some channels private to change the username of this channel."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"USERNAME_INVALID","comment":"The provided username is not valid."},{"code":400,"name":"USERNAME_NOT_MODIFIED","comment":"The username was not modified."},{"code":400,"name":"USERNAME_OCCUPIED","comment":"The provided username is already occupied."}],"available":"user"},{"kind":"method","name":"channels.joinChannel","type":"Updates","id":615851205,"comment":"Join a channel/supergroup","arguments":[{"name":"channel","type":"InputChannel","comment":"Channel/supergroup to join"}],"throws":[{"code":400,"name":"CHANNELS_TOO_MUCH","comment":"You have joined too many channels/supergroups."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_INVALID","comment":"Invalid chat."},{"code":400,"name":"INVITE_HASH_EMPTY","comment":"The invite hash is empty."},{"code":400,"name":"INVITE_HASH_EXPIRED","comment":"The invite link has expired."},{"code":400,"name":"INVITE_HASH_INVALID","comment":"The invite hash is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"USERS_TOO_MUCH","comment":"The maximum number of users has been exceeded (to create a chat, for example)."},{"code":400,"name":"USER_ALREADY_PARTICIPANT","comment":"The user is already in the group."},{"code":400,"name":"USER_CHANNELS_TOO_MUCH","comment":"One of the users you tried to add is already in too many channels/supergroups."}],"available":"user"},{"kind":"method","name":"channels.leaveChannel","type":"Updates","id":4164332181,"comment":"Leave a channel/supergroup","arguments":[{"name":"channel","type":"InputChannel","comment":"Channel/supergroup to leave"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":403,"name":"CHANNEL_PUBLIC_GROUP_NA","comment":"channel/supergroup not available."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"USER_CREATOR","comment":"You can't leave this channel, because you're its creator."},{"code":400,"name":"USER_NOT_PARTICIPANT","comment":"You're not a member of this supergroup/channel."}],"available":"both"},{"kind":"method","name":"channels.inviteToChannel","type":"Updates","id":429865580,"comment":"Invite users to a channel/supergroup","arguments":[{"name":"channel","type":"InputChannel","comment":"Channel/supergroup"},{"name":"users","type":"Vector","comment":"Users to invite"}],"throws":[{"code":400,"name":"BOTS_TOO_MUCH","comment":"There are too many bots in this chat/channel."},{"code":400,"name":"BOT_GROUPS_BLOCKED","comment":"This bot can't be added to groups."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_INVALID","comment":"Invalid chat."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"USERS_TOO_MUCH","comment":"The maximum number of users has been exceeded (to create a chat, for example)."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","comment":"You're banned from sending messages in supergroups/channels."},{"code":400,"name":"USER_BLOCKED","comment":"User blocked."},{"code":400,"name":"USER_BOT","comment":"Bots can only be admins in channels."},{"code":403,"name":"USER_CHANNELS_TOO_MUCH","comment":"One of the users you tried to add is already in too many channels/supergroups."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."},{"code":400,"name":"USER_KICKED","comment":"This user was kicked from this supergroup/channel."},{"code":400,"name":"USER_NOT_MUTUAL_CONTACT","comment":"The provided user is not a mutual contact."},{"code":403,"name":"USER_PRIVACY_RESTRICTED","comment":"The user's privacy settings do not allow you to do this."}],"available":"user"},{"kind":"method","name":"channels.deleteChannel","type":"Updates","id":3222347747,"comment":"Delete a channel/supergroup","arguments":[{"name":"channel","type":"InputChannel","comment":"Channel/supergroup to delete"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHANNEL_TOO_LARGE","comment":"Channel is too large to be deleted; this error is issued when trying to delete channels with more than 1000 members (subject to change)."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."}],"available":"user"},{"kind":"method","name":"channels.exportMessageLink","type":"ExportedMessageLink","id":3862932971,"comment":"Get link and embed info of a message in a channel/supergroup","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"grouped","type":"true","predicate":"flags.0","comment":"Whether to include other grouped media (for albums)"},{"name":"thread","type":"true","predicate":"flags.1","comment":"Whether to also include a thread ID, if available, inside of the link"},{"name":"channel","type":"InputChannel","comment":"Channel"},{"name":"id","type":"int","comment":"Message ID"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"user"},{"kind":"method","name":"channels.toggleSignatures","type":"Updates","id":527021574,"comment":"Enable/disable message signatures in channels","arguments":[{"name":"channel","type":"InputChannel","comment":"Channel"},{"name":"enabled","type":"Bool","comment":"Value"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."}],"available":"user"},{"kind":"method","name":"channels.getAdminedPublicChannels","type":"messages.Chats","id":4172297903,"comment":"Get channels/supergroups/geogroups we're admin in. Usually called when the user exceeds the {@link config} for owned public channels/supergroups/geogroups, and the user is given the choice to remove one of their channels/supergroups/geogroups.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"by_location","type":"true","predicate":"flags.0"},{"name":"check_limit","type":"true","predicate":"flags.1"}],"throws":[{"code":400,"name":"CHANNELS_ADMIN_LOCATED_TOO_MUCH","comment":"Returned if both the check_limit and the by_location flags are set and the user has reached the limit of public geogroups."},{"code":400,"name":"CHANNELS_ADMIN_PUBLIC_TOO_MUCH","comment":"Returned if the check_limit flag is set and the user has reached the limit of public channels/supergroups."}],"available":"user"},{"kind":"method","name":"channels.editBanned","type":"Updates","id":2531708289,"comment":"Ban/unban/kick a user in a supergroup/channel.","arguments":[{"name":"channel","type":"InputChannel","comment":"The supergroup/channel."},{"name":"participant","type":"InputPeer","comment":"Participant to ban"},{"name":"banned_rights","type":"ChatBannedRights"}],"throws":[{"code":400,"name":"CHANNEL_ADD_INVALID","comment":"Internal error."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PARTICIPANT_ID_INVALID","comment":"The specified participant ID is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"PINNED_DIALOGS_TOO_MUCH","comment":"Too many pinned dialogs."},{"code":400,"name":"USER_ADMIN_INVALID","comment":"You're not an admin."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"both"},{"kind":"method","name":"channels.getAdminLog","type":"channels.AdminLogResults","id":870184064,"comment":"Get the admin log of a channel/supergroup","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"channel","type":"InputChannel","comment":"Channel"},{"name":"q","type":"string","comment":"Search query, can be empty"},{"name":"events_filter","type":"ChannelAdminLogEventsFilter","predicate":"flags.0"},{"name":"admins","type":"Vector","predicate":"flags.1","comment":"Only show events from these admins"},{"name":"max_id","type":"long"},{"name":"min_id","type":"long"},{"name":"limit","type":"int","comment":"Maximum number of results to return, see pagination"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"user"},{"kind":"method","name":"channels.setStickers","type":"Bool","id":3935085817,"comment":"Associate a stickerset to the supergroup","arguments":[{"name":"channel","type":"InputChannel","comment":"Supergroup"},{"name":"stickerset","type":"InputStickerSet","comment":"The stickerset to associate"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"PARTICIPANTS_TOO_FEW","comment":"Not enough participants."},{"code":406,"name":"STICKERSET_OWNER_ANONYMOUS","comment":"Provided stickerset can't be installed as group stickerset to prevent admin deanonymisation."}],"available":"both"},{"kind":"method","name":"channels.readMessageContents","type":"Bool","id":3937786936,"comment":"Mark channel/supergroup message contents as read","arguments":[{"name":"channel","type":"InputChannel","comment":"Channel/supergroup"},{"name":"id","type":"Vector","comment":"IDs of messages whose contents should be marked as read"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"user"},{"kind":"method","name":"channels.deleteHistory","type":"Bool","id":2939592002,"comment":"Delete the history of a supergroup","arguments":[{"name":"channel","type":"InputChannel","comment":"Supergroup whose history must be deleted"},{"name":"max_id","type":"int"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."}],"available":"user"},{"kind":"method","name":"channels.togglePreHistoryHidden","type":"Updates","id":3938171212,"comment":"Hide/unhide message history for new channel/supergroup users","arguments":[{"name":"channel","type":"InputChannel","comment":"Channel/supergroup"},{"name":"enabled","type":"Bool","comment":"Hide/unhide"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"CHAT_LINK_EXISTS","comment":"The chat is public, you can't hide the history to new users."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."}],"available":"user"},{"kind":"method","name":"channels.getLeftChannels","type":"messages.Chats","id":2202135744,"comment":"Get a list of channels/supergroups we left","arguments":[{"name":"offset","type":"int","comment":"Offset for pagination"}],"throws":[{"code":403,"name":"TAKEOUT_REQUIRED","comment":"A takeout session has to be initialized, first."}],"available":"user"},{"kind":"method","name":"channels.getGroupsForDiscussion","type":"messages.Chats","id":4124758904,"comment":"Returned legacy group chats must be first upgraded to supergroups before they can be set as a discussion group.\nTo set a returned supergroup as a discussion group, access to its old messages must be enabled using {@link channels.togglePreHistoryHidden}, first.\n\nGet all groups that can be used as discussion groups.","arguments":[],"available":"user"},{"kind":"method","name":"channels.setDiscussionGroup","type":"Bool","id":1079520178,"comment":"Associate a group to a channel as discussion group for that channel","arguments":[{"name":"broadcast","type":"InputChannel","comment":"Channel"},{"name":"group","type":"InputChannel","comment":"Discussion group to associate to the channel"}],"throws":[{"code":400,"name":"BROADCAST_ID_INVALID","comment":"Broadcast ID invalid."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"LINK_NOT_MODIFIED","comment":"Discussion link not modified."},{"code":400,"name":"MEGAGROUP_ID_INVALID","comment":"Invalid supergroup ID."},{"code":400,"name":"MEGAGROUP_PREHISTORY_HIDDEN","comment":"Group with hidden history for new members can't be set as discussion groups."}],"available":"user"},{"kind":"method","name":"channels.editCreator","type":"Updates","id":2402864415,"comment":"Transfer channel ownership","arguments":[{"name":"channel","type":"InputChannel","comment":"Channel"},{"name":"user_id","type":"InputUser"},{"name":"password","type":"InputCheckPasswordSRP","comment":"2FA password of account"}],"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"PASSWORD_HASH_INVALID","comment":"The provided password hash is invalid."},{"code":400,"name":"PASSWORD_MISSING","comment":"You must enable 2FA in order to transfer ownership of a channel."},{"code":400,"name":"PASSWORD_TOO_FRESH_X","comment":"The password was modified less than 24 hours ago, try again in X seconds."},{"code":400,"name":"SESSION_TOO_FRESH_X","comment":"This session was created less than 24 hours ago, try again in X seconds."},{"code":400,"name":"SRP_ID_INVALID","comment":"Invalid SRP ID provided."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"user"},{"kind":"method","name":"channels.editLocation","type":"Bool","id":1491484525,"comment":"Edit location of geo group","arguments":[{"name":"channel","type":"InputChannel","comment":"Geogroup"},{"name":"geo_point","type":"InputGeoPoint"},{"name":"address","type":"string","comment":"Address string"}],"throws":[{"code":400,"name":"MEGAGROUP_REQUIRED","comment":"You can only use this method on a supergroup."}],"available":"user"},{"kind":"method","name":"channels.toggleSlowMode","type":"Updates","id":3990134512,"comment":"Toggle supergroup slow mode: if enabled, users will only be able to send one message every seconds seconds","arguments":[{"name":"channel","type":"InputChannel","comment":"The supergroup"},{"name":"seconds","type":"int","comment":"Users will only be able to send one message every seconds seconds, 0 to disable the limitation"}],"throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":400,"name":"SECONDS_INVALID","comment":"Invalid duration provided."}],"available":"user"},{"kind":"method","name":"channels.getInactiveChannels","type":"messages.InactiveChats","id":300429806,"comment":"Get inactive channels and supergroups","arguments":[],"available":"user"},{"kind":"method","name":"channels.convertToGigagroup","type":"Updates","id":187239529,"comment":"Convert a supergroup to a gigagroup, when requested by channel suggestions.","arguments":[{"name":"channel","type":"InputChannel","comment":"The supergroup to convert"}],"throws":[{"code":400,"name":"PARTICIPANTS_TOO_FEW","comment":"Not enough participants."}],"available":"user"},{"kind":"method","name":"channels.viewSponsoredMessage","type":"Bool","id":3199130516,"comment":"Mark a specific sponsored message as read","arguments":[{"name":"channel","type":"InputChannel","comment":"Peer"},{"name":"random_id","type":"bytes"}],"throws":[{"code":400,"name":"UNKNOWN_ERROR","comment":"Internal error."}],"available":"user"},{"kind":"method","name":"channels.getSponsoredMessages","type":"messages.SponsoredMessages","id":3961589695,"comment":"Get a list of sponsored messages","arguments":[{"name":"channel","type":"InputChannel","comment":"Peer"}],"available":"user"},{"kind":"method","name":"bots.sendCustomRequest","type":"DataJSON","id":2854709741,"comment":"Sends a custom request; for bots only","arguments":[{"name":"custom_method","type":"string"},{"name":"params","type":"DataJSON","comment":"JSON-serialized method parameters"}],"throws":[{"code":400,"name":"METHOD_INVALID","comment":"The specified method is invalid."},{"code":400,"name":"USER_BOT_INVALID","comment":"This method can only be called by a bot."}],"available":"bot"},{"kind":"method","name":"bots.answerWebhookJSONQuery","type":"Bool","id":3860938573,"comment":"Answers a custom query; for bots only","arguments":[{"name":"query_id","type":"long"},{"name":"data","type":"DataJSON","comment":"JSON-serialized answer to the query"}],"throws":[{"code":400,"name":"QUERY_ID_INVALID","comment":"The query ID is invalid."},{"code":400,"name":"USER_BOT_INVALID","comment":"This method can only be called by a bot."}],"available":"bot"},{"kind":"method","name":"bots.setBotCommands","type":"Bool","id":85399130,"comment":"Set bot command list","arguments":[{"name":"scope","type":"BotCommandScope","comment":"Command scope"},{"name":"lang_code","type":"string"},{"name":"commands","type":"Vector","comment":"Bot commands"}],"throws":[{"code":400,"name":"BOT_COMMAND_DESCRIPTION_INVALID","comment":"The specified command description is invalid."},{"code":400,"name":"BOT_COMMAND_INVALID","comment":"The specified command is invalid."},{"code":400,"name":"LANG_CODE_INVALID","comment":"The specified language code is invalid."}],"available":"both"},{"kind":"method","name":"bots.resetBotCommands","type":"Bool","id":1032708345,"comment":"Clear bot commands for the specified bot scope and language code","arguments":[{"name":"scope","type":"BotCommandScope","comment":"Command scope"},{"name":"lang_code","type":"string"}],"available":"both"},{"kind":"method","name":"bots.getBotCommands","type":"Vector","id":3813412310,"comment":"Obtain a list of bot commands for the specified bot scope and language code","arguments":[{"name":"scope","type":"BotCommandScope","comment":"Command scope"},{"name":"lang_code","type":"string"}],"available":"both"},{"kind":"method","name":"payments.getPaymentForm","type":"payments.PaymentForm","id":2318613645,"comment":"Get a payment form","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"peer","type":"InputPeer","comment":"The peer where the payment form was sent"},{"name":"msg_id","type":"int"},{"name":"theme_params","type":"DataJSON","predicate":"flags.0"}],"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."}],"available":"user"},{"kind":"method","name":"payments.getPaymentReceipt","type":"payments.PaymentReceipt","id":611897804,"comment":"Get payment receipt","arguments":[{"name":"peer","type":"InputPeer","comment":"The peer where the payment receipt was sent"},{"name":"msg_id","type":"int"}],"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."}],"available":"user"},{"kind":"method","name":"payments.validateRequestedInfo","type":"payments.ValidatedRequestedInfo","id":3675271536,"comment":"Submit requested order information for validation","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"save","type":"true","predicate":"flags.0","comment":"Save order information to re-use it for future orders"},{"name":"peer","type":"InputPeer","comment":"Peer where the payment form was sent"},{"name":"msg_id","type":"int"},{"name":"info","type":"PaymentRequestedInfo","comment":"Requested order information"}],"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."}],"available":"user"},{"kind":"method","name":"payments.sendPaymentForm","type":"payments.PaymentResult","id":818134173,"comment":"Send compiled payment form","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"form_id","type":"long"},{"name":"peer","type":"InputPeer","comment":"The peer where the payment form was sent"},{"name":"msg_id","type":"int"},{"name":"requested_info_id","type":"string","predicate":"flags.0"},{"name":"shipping_option_id","type":"string","predicate":"flags.1"},{"name":"credentials","type":"InputPaymentCredentials","comment":"Payment credentials"},{"name":"tip_amount","type":"long","predicate":"flags.2"}],"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."}],"available":"user"},{"kind":"method","name":"payments.getSavedInfo","type":"payments.SavedInfo","id":578650699,"comment":"Get saved payment information","arguments":[],"available":"user"},{"kind":"method","name":"payments.clearSavedInfo","type":"Bool","id":3627905217,"comment":"Clear saved payment information","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"credentials","type":"true","predicate":"flags.0","comment":"Remove saved payment credentials"},{"name":"info","type":"true","predicate":"flags.1","comment":"Clear the last order settings saved by the user"}],"available":"user"},{"kind":"method","name":"payments.getBankCardData","type":"payments.BankCardData","id":779736953,"comment":"Get info about a credit card","arguments":[{"name":"number","type":"string","comment":"Credit card number"}],"throws":[{"code":400,"name":"BANK_CARD_NUMBER_INVALID","comment":"The specified card number is invalid."}],"available":"user"},{"kind":"method","name":"stickers.createStickerSet","type":"messages.StickerSet","id":2418125671,"comment":"Create a stickerset, bots only.","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"masks","type":"true","predicate":"flags.0","comment":"Whether this is a mask stickerset"},{"name":"animated","type":"true","predicate":"flags.1","comment":"Whether this is an animated stickerset"},{"name":"user_id","type":"InputUser"},{"name":"title","type":"string","comment":"Stickerset name, 1-64 chars"},{"name":"short_name","type":"string"},{"name":"thumb","type":"InputDocument","predicate":"flags.2","comment":"Thumbnail"},{"name":"stickers","type":"Vector","comment":"Stickers"},{"name":"software","type":"string","predicate":"flags.3","comment":"Used when importing stickers using the sticker import SDKs, specifies the name of the software that created the stickers"}],"throws":[{"code":400,"name":"BOT_MISSING","comment":"This method can only be run by a bot."},{"code":400,"name":"PACK_SHORT_NAME_INVALID","comment":"Short pack name invalid."},{"code":400,"name":"PACK_SHORT_NAME_OCCUPIED","comment":"A stickerpack with this name already exists."},{"code":400,"name":"PACK_TITLE_INVALID","comment":"The stickerpack title is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"SHORTNAME_OCCUPY_FAILED","comment":"An internal error occurred."},{"code":400,"name":"STICKERS_EMPTY","comment":"No sticker provided."},{"code":400,"name":"STICKER_EMOJI_INVALID","comment":"Sticker emoji invalid."},{"code":400,"name":"STICKER_FILE_INVALID","comment":"Sticker file invalid."},{"code":400,"name":"STICKER_PNG_DIMENSIONS","comment":"Sticker png dimensions invalid."},{"code":400,"name":"STICKER_PNG_NOPNG","comment":"One of the specified stickers is not a valid PNG file."},{"code":400,"name":"STICKER_TGS_NODOC","comment":"Incorrect document type for sticker."},{"code":400,"name":"STICKER_TGS_NOTGS","comment":"Invalid TGS sticker provided."},{"code":400,"name":"STICKER_THUMB_PNG_NOPNG","comment":"Incorrect stickerset thumb file provided, PNG / WEBP expected."},{"code":400,"name":"STICKER_THUMB_TGS_NOTGS","comment":"Incorrect stickerset TGS thumb file provided."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"both"},{"kind":"method","name":"stickers.removeStickerFromSet","type":"messages.StickerSet","id":4151709521,"comment":"Remove a sticker from the set where it belongs, bots only. The sticker set must have been created by the bot.","arguments":[{"name":"sticker","type":"InputDocument","comment":"The sticker to remove"}],"throws":[{"code":400,"name":"BOT_MISSING","comment":"This method can only be run by a bot."},{"code":400,"name":"STICKER_INVALID","comment":"The provided sticker is invalid."}],"available":"both"},{"kind":"method","name":"stickers.changeStickerPosition","type":"messages.StickerSet","id":4290172106,"comment":"Changes the absolute position of a sticker in the set to which it belongs; for bots only. The sticker set must have been created by the bot","arguments":[{"name":"sticker","type":"InputDocument","comment":"The sticker"},{"name":"position","type":"int","comment":"The new position of the sticker, zero-based"}],"throws":[{"code":400,"name":"BOT_MISSING","comment":"This method can only be run by a bot."},{"code":400,"name":"STICKER_INVALID","comment":"The provided sticker is invalid."}],"available":"both"},{"kind":"method","name":"stickers.addStickerToSet","type":"messages.StickerSet","id":2253651646,"comment":"Add a sticker to a stickerset, bots only. The sticker set must have been created by the bot.","arguments":[{"name":"stickerset","type":"InputStickerSet","comment":"The stickerset"},{"name":"sticker","type":"InputStickerSetItem","comment":"The sticker"}],"throws":[{"code":400,"name":"BOT_MISSING","comment":"This method can only be run by a bot."},{"code":400,"name":"STICKERSET_INVALID","comment":"The provided sticker set is invalid."},{"code":400,"name":"STICKER_PNG_NOPNG","comment":"One of the specified stickers is not a valid PNG file."},{"code":400,"name":"STICKER_TGS_NOTGS","comment":"Invalid TGS sticker provided."}],"available":"both"},{"kind":"method","name":"stickers.setStickerSetThumb","type":"messages.StickerSet","id":2587250224,"comment":"Set stickerset thumbnail","arguments":[{"name":"stickerset","type":"InputStickerSet","comment":"Stickerset"},{"name":"thumb","type":"InputDocument","comment":"Thumbnail"}],"throws":[{"code":400,"name":"STICKERSET_INVALID","comment":"The provided sticker set is invalid."},{"code":400,"name":"STICKER_THUMB_PNG_NOPNG","comment":"Incorrect stickerset thumb file provided, PNG / WEBP expected."},{"code":400,"name":"STICKER_THUMB_TGS_NOTGS","comment":"Incorrect stickerset TGS thumb file provided."}],"available":"both"},{"kind":"method","name":"stickers.checkShortName","type":"Bool","id":676017721,"comment":"Check whether the given short name is available","arguments":[{"name":"short_name","type":"string"}],"throws":[{"code":400,"name":"SHORT_NAME_INVALID","comment":"The specified short name is invalid."},{"code":400,"name":"SHORT_NAME_OCCUPIED","comment":"The specified short name is already in use."}],"available":"user"},{"kind":"method","name":"stickers.suggestShortName","type":"stickers.SuggestedShortName","id":1303364867,"comment":"Suggests a short name for a given stickerpack name","arguments":[{"name":"title","type":"string","comment":"Sticker pack name"}],"throws":[{"code":400,"name":"TITLE_INVALID","comment":"The specified stickerpack title is invalid."}],"available":"user"},{"kind":"method","name":"phone.getCallConfig","type":"DataJSON","id":1430593449,"comment":"Get phone call configuration to be passed to libtgvoip's shared config","arguments":[],"available":"user"},{"kind":"method","name":"phone.requestCall","type":"phone.PhoneCall","id":1124046573,"comment":"Start a telegram phone call","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"video","type":"true","predicate":"flags.0","comment":"Whether to start a video call"},{"name":"user_id","type":"InputUser"},{"name":"random_id","type":"int"},{"name":"g_a_hash","type":"bytes"},{"name":"protocol","type":"PhoneCallProtocol","comment":"Phone call settings"}],"throws":[{"code":400,"name":"CALL_PROTOCOL_FLAGS_INVALID","comment":"Call protocol flags invalid."},{"code":400,"name":"PARTICIPANT_VERSION_OUTDATED","comment":"The other participant does not use an up to date telegram client with support for calls."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."},{"code":403,"name":"USER_IS_BLOCKED","comment":"You were blocked by this user."},{"code":403,"name":"USER_PRIVACY_RESTRICTED","comment":"The user's privacy settings do not allow you to do this."}],"available":"user"},{"kind":"method","name":"phone.acceptCall","type":"phone.PhoneCall","id":1003664544,"comment":"Accept incoming call","arguments":[{"name":"peer","type":"InputPhoneCall","comment":"The call to accept"},{"name":"g_b","type":"bytes"},{"name":"protocol","type":"PhoneCallProtocol","comment":"Phone call settings"}],"throws":[{"code":400,"name":"CALL_ALREADY_ACCEPTED","comment":"The call was already accepted."},{"code":400,"name":"CALL_ALREADY_DECLINED","comment":"The call was already declined."},{"code":400,"name":"CALL_PEER_INVALID","comment":"The provided call peer object is invalid."},{"code":400,"name":"CALL_PROTOCOL_FLAGS_INVALID","comment":"Call protocol flags invalid."}],"available":"user"},{"kind":"method","name":"phone.confirmCall","type":"phone.PhoneCall","id":788404002,"comment":"Complete phone call E2E encryption key exchange »","arguments":[{"name":"peer","type":"InputPhoneCall","comment":"The phone call"},{"name":"g_a","type":"bytes"},{"name":"key_fingerprint","type":"long"},{"name":"protocol","type":"PhoneCallProtocol","comment":"Phone call settings"}],"throws":[{"code":400,"name":"CALL_ALREADY_DECLINED","comment":"The call was already declined."},{"code":400,"name":"CALL_PEER_INVALID","comment":"The provided call peer object is invalid."}],"available":"user"},{"kind":"method","name":"phone.receivedCall","type":"Bool","id":399855457,"comment":"Optional: notify the server that the user is currently busy in a call: this will automatically refuse all incoming phone calls until the current phone call is ended.","arguments":[{"name":"peer","type":"InputPhoneCall","comment":"The phone call we're currently in"}],"throws":[{"code":400,"name":"CALL_ALREADY_DECLINED","comment":"The call was already declined."},{"code":400,"name":"CALL_PEER_INVALID","comment":"The provided call peer object is invalid."}],"available":"user"},{"kind":"method","name":"phone.discardCall","type":"Updates","id":2999697856,"comment":"Refuse or end running call","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"video","type":"true","predicate":"flags.0","comment":"Whether this is a video call"},{"name":"peer","type":"InputPhoneCall","comment":"The phone call"},{"name":"duration","type":"int","comment":"Call duration"},{"name":"reason","type":"PhoneCallDiscardReason","comment":"Why was the call discarded"},{"name":"connection_id","type":"long"}],"throws":[{"code":400,"name":"CALL_ALREADY_ACCEPTED","comment":"The call was already accepted."},{"code":400,"name":"CALL_PEER_INVALID","comment":"The provided call peer object is invalid."}],"available":"user"},{"kind":"method","name":"phone.setCallRating","type":"Updates","id":1508562471,"comment":"Rate a call","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"user_initiative","type":"true","predicate":"flags.0"},{"name":"peer","type":"InputPhoneCall","comment":"The call to rate"},{"name":"rating","type":"int","comment":"Rating in 1-5 stars"},{"name":"comment","type":"string","comment":"An additional comment"}],"throws":[{"code":400,"name":"CALL_PEER_INVALID","comment":"The provided call peer object is invalid."}],"available":"user"},{"kind":"method","name":"phone.saveCallDebug","type":"Bool","id":662363518,"comment":"Send phone call debug data to server","arguments":[{"name":"peer","type":"InputPhoneCall","comment":"Phone call"},{"name":"debug","type":"DataJSON","comment":"Debug statistics obtained from libtgvoip"}],"throws":[{"code":400,"name":"CALL_PEER_INVALID","comment":"The provided call peer object is invalid."},{"code":400,"name":"DATA_JSON_INVALID","comment":"The provided JSON data is invalid."}],"available":"user"},{"kind":"method","name":"phone.sendSignalingData","type":"Bool","id":4286223235,"comment":"Send VoIP signaling data","arguments":[{"name":"peer","type":"InputPhoneCall","comment":"Phone call"},{"name":"data","type":"bytes","comment":"Signaling payload"}],"available":"user"},{"kind":"method","name":"phone.createGroupCall","type":"Updates","id":1221445336,"comment":"Create a group call or livestream","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"peer","type":"InputPeer","comment":"Associate the group call or livestream to the provided group/supergroup/channel"},{"name":"random_id","type":"int"},{"name":"title","type":"string","predicate":"flags.0","comment":"Call title"},{"name":"schedule_date","type":"int","predicate":"flags.1"}],"throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"SCHEDULE_DATE_INVALID","comment":"Invalid schedule date provided."}],"available":"user"},{"kind":"method","name":"phone.joinGroupCall","type":"Updates","id":2972909435,"comment":"Join a group call","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"muted","type":"true","predicate":"flags.0","comment":"If set, the user will be muted by default upon joining."},{"name":"video_stopped","type":"true","predicate":"flags.2"},{"name":"call","type":"InputGroupCall","comment":"The group call"},{"name":"join_as","type":"InputPeer"},{"name":"invite_hash","type":"string","predicate":"flags.1"},{"name":"params","type":"DataJSON","comment":"WebRTC parameters"}],"throws":[{"code":400,"name":"GROUPCALL_SSRC_DUPLICATE_MUCH","comment":"The app needs to retry joining the group call with a new SSRC value."}],"available":"user"},{"kind":"method","name":"phone.leaveGroupCall","type":"Updates","id":1342404601,"comment":"Leave a group call","arguments":[{"name":"call","type":"InputGroupCall","comment":"The group call"},{"name":"source","type":"int","comment":"Your source ID"}],"available":"user"},{"kind":"method","name":"phone.inviteToGroupCall","type":"Updates","id":2067345760,"comment":"Invite a set of users to a group call.","arguments":[{"name":"call","type":"InputGroupCall","comment":"The group call"},{"name":"users","type":"Vector","comment":"The users to invite."}],"throws":[{"code":403,"name":"GROUPCALL_FORBIDDEN","comment":"The group call has already ended."}],"available":"user"},{"kind":"method","name":"phone.discardGroupCall","type":"Updates","id":2054648117,"comment":"Terminate a group call","arguments":[{"name":"call","type":"InputGroupCall","comment":"The group call to terminate"}],"available":"user"},{"kind":"method","name":"phone.toggleGroupCallSettings","type":"Updates","id":1958458429,"comment":"Change group call settings","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"reset_invite_hash","type":"true","predicate":"flags.1"},{"name":"call","type":"InputGroupCall","comment":"Group call"},{"name":"join_muted","type":"Bool","predicate":"flags.0"}],"throws":[{"code":400,"name":"GROUPCALL_NOT_MODIFIED","comment":"Group call settings weren't modified."}],"available":"user"},{"kind":"method","name":"phone.getGroupCall","type":"phone.GroupCall","id":68699611,"comment":"Get info about a group call","arguments":[{"name":"call","type":"InputGroupCall","comment":"The group call"},{"name":"limit","type":"int","comment":"Maximum number of results to return, see pagination"}],"available":"user"},{"kind":"method","name":"phone.getGroupParticipants","type":"phone.GroupParticipants","id":3310934187,"comment":"Get group call participants","arguments":[{"name":"call","type":"InputGroupCall","comment":"Group call"},{"name":"ids","type":"Vector","comment":"If specified, will fetch group participant info about the specified peers"},{"name":"sources","type":"Vector","comment":"If specified, will fetch group participant info about the specified WebRTC source IDs"},{"name":"offset","type":"string","comment":"Offset for results, taken from the next_offset field of {@link phone.groupParticipants}, initially an empty string.
Note: if no more results are available, the method call will return an empty next_offset; thus, avoid providing the next_offset returned in {@link phone.groupParticipants} if it is empty, to avoid an infinite loop."},{"name":"limit","type":"int","comment":"Maximum number of results to return, see pagination"}],"available":"user"},{"kind":"method","name":"phone.checkGroupCall","type":"Vector","id":3046963575,"comment":"Check whether the group call Server Forwarding Unit is currently receiving the streams with the specified WebRTC source IDs","arguments":[{"name":"call","type":"InputGroupCall","comment":"Group call"},{"name":"sources","type":"Vector","comment":"Source IDs"}],"available":"user"},{"kind":"method","name":"phone.toggleGroupCallRecord","type":"Updates","id":4045981448,"comment":"Start or stop recording a group call: the recorded audio and video streams will be automatically sent to Saved messages (the chat with ourselves).","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"start","type":"true","predicate":"flags.0","comment":"Whether to start or stop recording"},{"name":"video","type":"true","predicate":"flags.2","comment":"Whether to also record video streams"},{"name":"call","type":"InputGroupCall","comment":"The group call or livestream"},{"name":"title","type":"string","predicate":"flags.1","comment":"Recording title"},{"name":"video_portrait","type":"Bool","predicate":"flags.2"}],"available":"user"},{"kind":"method","name":"phone.editGroupCallParticipant","type":"Updates","id":2770811583,"comment":"Note: flags.N?Bool parameters can have three possible values:\n\nEdit information about a given group call participant","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"call","type":"InputGroupCall","comment":"The group call"},{"name":"participant","type":"InputPeer","comment":"The group call participant (can also be the user itself)"},{"name":"muted","type":"Bool","predicate":"flags.0","comment":"Whether to mute or unmute the specified participant"},{"name":"volume","type":"int","predicate":"flags.1","comment":"New volume"},{"name":"raise_hand","type":"Bool","predicate":"flags.2"},{"name":"video_stopped","type":"Bool","predicate":"flags.3"},{"name":"video_paused","type":"Bool","predicate":"flags.4"},{"name":"presentation_paused","type":"Bool","predicate":"flags.5"}],"throws":[{"code":400,"name":"USER_VOLUME_INVALID","comment":"The specified user volume is invalid."}],"available":"user"},{"kind":"method","name":"phone.editGroupCallTitle","type":"Updates","id":480685066,"comment":"Edit the title of a group call or livestream","arguments":[{"name":"call","type":"InputGroupCall","comment":"Group call"},{"name":"title","type":"string","comment":"New title"}],"available":"user"},{"kind":"method","name":"phone.getGroupCallJoinAs","type":"phone.JoinAsPeers","id":4017889594,"comment":"Get a list of peers that can be used to join a group call, presenting yourself as a specific user/channel.","arguments":[{"name":"peer","type":"InputPeer","comment":"The dialog whose group call or livestream we're trying to join"}],"available":"user"},{"kind":"method","name":"phone.exportGroupCallInvite","type":"phone.ExportedGroupCallInvite","id":3869926527,"comment":"Get an invite link for a group call or livestream","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"can_self_unmute","type":"true","predicate":"flags.0"},{"name":"call","type":"InputGroupCall","comment":"The group call"}],"available":"user"},{"kind":"method","name":"phone.toggleGroupCallStartSubscription","type":"Updates","id":563885286,"comment":"Subscribe or unsubscribe to a scheduled group call","arguments":[{"name":"call","type":"InputGroupCall","comment":"Scheduled group call"},{"name":"subscribed","type":"Bool","comment":"Enable or disable subscription"}],"available":"user"},{"kind":"method","name":"phone.startScheduledGroupCall","type":"Updates","id":1451287362,"comment":"Start a scheduled group call.","arguments":[{"name":"call","type":"InputGroupCall","comment":"The scheduled group call"}],"available":"user"},{"kind":"method","name":"phone.saveDefaultGroupCallJoinAs","type":"Bool","id":1465786252,"comment":"Set the default peer that will be used to join a group call in a specific dialog.","arguments":[{"name":"peer","type":"InputPeer","comment":"The dialog"},{"name":"join_as","type":"InputPeer"}],"available":"user"},{"kind":"method","name":"phone.joinGroupCallPresentation","type":"Updates","id":3421137860,"comment":"Start screen sharing in a call","arguments":[{"name":"call","type":"InputGroupCall","comment":"The group call"},{"name":"params","type":"DataJSON","comment":"WebRTC parameters"}],"throws":[{"code":403,"name":"PARTICIPANT_JOIN_MISSING","comment":"Trying to enable a presentation, when the user hasn't joined the Video Chat with {@link phone.joinGroupCall}."}],"available":"user"},{"kind":"method","name":"phone.leaveGroupCallPresentation","type":"Updates","id":475058500,"comment":"Stop screen sharing in a group call","arguments":[{"name":"call","type":"InputGroupCall","comment":"The group call"}],"available":"user"},{"kind":"method","name":"langpack.getLangPack","type":"LangPackDifference","id":4075959050,"comment":"Get localization pack strings","arguments":[{"name":"lang_pack","type":"string"},{"name":"lang_code","type":"string"}],"throws":[{"code":400,"name":"LANG_PACK_INVALID","comment":"The provided language pack is invalid."}],"available":"user"},{"kind":"method","name":"langpack.getStrings","type":"Vector","id":4025104387,"comment":"Get strings from a language pack","arguments":[{"name":"lang_pack","type":"string"},{"name":"lang_code","type":"string"},{"name":"keys","type":"Vector","comment":"Strings to get"}],"throws":[{"code":400,"name":"LANG_PACK_INVALID","comment":"The provided language pack is invalid."}],"available":"user"},{"kind":"method","name":"langpack.getDifference","type":"LangPackDifference","id":3449309861,"comment":"Get new strings in languagepack","arguments":[{"name":"lang_pack","type":"string"},{"name":"lang_code","type":"string"},{"name":"from_version","type":"int"}],"throws":[{"code":400,"name":"LANG_PACK_INVALID","comment":"The provided language pack is invalid."}],"available":"user"},{"kind":"method","name":"langpack.getLanguages","type":"Vector","id":1120311183,"comment":"Get information about all languages in a localization pack","arguments":[{"name":"lang_pack","type":"string"}],"throws":[{"code":400,"name":"LANG_PACK_INVALID","comment":"The provided language pack is invalid."}],"available":"user"},{"kind":"method","name":"langpack.getLanguage","type":"LangPackLanguage","id":1784243458,"comment":"Get information about a language in a localization pack","arguments":[{"name":"lang_pack","type":"string"},{"name":"lang_code","type":"string"}],"available":"user"},{"kind":"method","name":"folders.editPeerFolders","type":"Updates","id":1749536939,"comment":"Edit peers in peer folder","arguments":[{"name":"folder_peers","type":"Vector"}],"throws":[{"code":400,"name":"FOLDER_ID_INVALID","comment":"Invalid folder ID."}],"available":"user"},{"kind":"method","name":"folders.deleteFolder","type":"Updates","id":472471681,"comment":"Delete a peer folder","arguments":[{"name":"folder_id","type":"int"}],"throws":[{"code":400,"name":"FOLDER_ID_EMPTY","comment":"An empty folder ID was specified."}],"available":"user"},{"kind":"method","name":"stats.getBroadcastStats","type":"stats.BroadcastStats","id":2873246746,"comment":"Get channel statistics","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"dark","type":"true","predicate":"flags.0","comment":"Whether to enable dark theme for graph colors"},{"name":"channel","type":"InputChannel","comment":"The channel"}],"throws":[{"code":400,"name":"BROADCAST_REQUIRED","comment":"This method can only be called on a channel, please use stats.getMegagroupStats for supergroups."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."}],"available":"user"},{"kind":"method","name":"stats.loadAsyncGraph","type":"StatsGraph","id":1646092192,"comment":"Load channel statistics graph asynchronously","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"token","type":"string","comment":"Graph token from {@link statsGraphAsync} constructor"},{"name":"x","type":"long","predicate":"flags.0","comment":"Zoom value, if required"}],"throws":[{"code":400,"name":"GRAPH_EXPIRED_RELOAD","comment":"This graph has expired, please obtain a new graph token."},{"code":400,"name":"GRAPH_INVALID_RELOAD","comment":"Invalid graph token provided, please reload the stats and provide the updated token."},{"code":400,"name":"GRAPH_OUTDATED_RELOAD","comment":"The graph is outdated, please get a new async token using stats.getBroadcastStats."}],"available":"user"},{"kind":"method","name":"stats.getMegagroupStats","type":"stats.MegagroupStats","id":3705636359,"comment":"Get supergroup statistics","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"dark","type":"true","predicate":"flags.0","comment":"Whether to enable dark theme for graph colors"},{"name":"channel","type":"InputChannel","comment":"Supergroup ID"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"MEGAGROUP_REQUIRED","comment":"You can only use this method on a supergroup."}],"available":"user"},{"kind":"method","name":"stats.getMessagePublicForwards","type":"messages.Messages","id":1445996571,"comment":"Obtains a list of messages, indicating to which other public channels was a channel message forwarded.\nWill return a list of {@link message} with peer_id equal to the public channel to which this message was forwarded.","arguments":[{"name":"channel","type":"InputChannel","comment":"Source channel"},{"name":"msg_id","type":"int"},{"name":"offset_rate","type":"int"},{"name":"offset_peer","type":"InputPeer"},{"name":"offset_id","type":"int"},{"name":"limit","type":"int","comment":"Maximum number of results to return, see pagination"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."}],"available":"user"},{"kind":"method","name":"stats.getMessageStats","type":"stats.MessageStats","id":3068175349,"comment":"Get message statistics","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"dark","type":"true","predicate":"flags.0","comment":"Whether to enable dark theme for graph colors"},{"name":"channel","type":"InputChannel","comment":"Channel ID"},{"name":"msg_id","type":"int"}],"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."}],"available":"user"},{"kind":"method","name":"messages.sendReaction","id":627641572,"type":"Updates","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"peer","type":"InputPeer","comment":"Peer"},{"name":"msg_id","type":"int"},{"name":"reaction","type":"string","predicate":"flags.0","comment":"Reaction (a UTF8 emoji)"}],"comment":"Send reaction to message","throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"REACTION_EMPTY","comment":"Empty reaction provided."}],"available":"both"},{"kind":"method","name":"messages.getMessagesReactions","id":2344259814,"type":"Updates","arguments":[{"name":"peer","type":"InputPeer","comment":"Peer"},{"name":"id","type":"Vector","comment":"Message IDs"}],"comment":"Get message reactions","available":"both"},{"kind":"method","name":"messages.getMessageReactionsList","id":363935594,"type":"MessageReactionsList","arguments":[{"name":"flags","type":"#","comment":"Flags, see TL conditional fields"},{"name":"peer","type":"InputPeer","comment":"Peer"},{"name":"id","type":"int","comment":"Message ID"},{"name":"reaction","type":"string","predicate":"flags.0","comment":"Get only reactions of this type (UTF8 emoji)"},{"name":"offset","type":"string","predicate":"flags.1","comment":"Offset (typically taken from the next_offset field of the returned MessageReactionsList)"},{"name":"limit","type":"int","comment":"Maximum number of results to return, see pagination"}],"comment":"Get full message reaction list","available":"both"}],"u":{"Error":"An object containing a query error.","InputFileLocation":"Defines the location of a file for download.","InputPeer":"Peer","InputUser":"Defines a user for subsequent interaction.","InputContact":"Object defines a contact from the user's phonebook.","InputFile":"Defines a file uploaded by the client.","InputMedia":"Defines media content of a message.","InputChatPhoto":"Defines a new group profile photo.","InputGeoPoint":"Defines a GeoPoint.","InputPhoto":"Defines a photo for further interaction.","Peer":"Chat partner or group.","storage.FileType":"Object describes the file type.","User":"Object defines a user.","UserProfilePhoto":"Object contains info on the user's profile photo.","UserStatus":"User online status","Chat":"Object defines a group.","ChatFull":"Object containing detailed group info","ChatParticipant":"Details of a group member.","ChatParticipants":"Object contains info on group members.","ChatPhoto":"Object defines a group profile photo.","Message":"Object describing a message.","MessageMedia":"Media","MessageAction":"Object describing actions connected to a service message.","Dialog":"Chat info.","Photo":"Object describes a photo.","PhotoSize":"Location of a certain size of a picture","GeoPoint":"Object defines a GeoPoint.","auth.SentCode":"Contains info on a confirmation code message sent via SMS, phone call or Telegram.","auth.Authorization":"Oject contains info on user authorization.","auth.ExportedAuthorization":"Exported authorization","InputNotifyPeer":"Object defines the set of users and/or groups that generate notifications.","InputPeerNotifySettings":"Notifications settings.","PeerNotifySettings":"Notification settings.","PeerSettings":"Peer settings","WallPaper":"Object contains info on a wallpaper.","ReportReason":"Report reason","UserFull":"Object contains extended user info.","Contact":"A contact of the current user.","ImportedContact":"Object contains info on a successfully imported contact.","ContactStatus":"Contact status: online / offline.","contacts.Contacts":"Info on the current user's contact list.","contacts.ImportedContacts":"Object contains info on succesfully imported contacts.","contacts.Blocked":"Info on users from the current user's black list.","messages.Dialogs":"Object contains a list of chats with messages and auxiliary data.","messages.Messages":"Object contains infor on list of messages with auxiliary data.","messages.Chats":"Object contains list of chats with auxiliary data.","messages.ChatFull":"Object contains extended info on chat with auxiliary data.","messages.AffectedHistory":"Object contains info on affected part of communication history with the user or in a chat.","MessagesFilter":"Object describes message filter.","Update":"Object contains info on events occured.","updates.State":"Object contains info on state for further updates.","updates.Difference":"Occurred changes.","Updates":"Object which is perceived by the client without a call on its part when an event occurs.","photos.Photos":"Object contains list of photos with auxiliary data.","photos.Photo":"Photo with auxiliary data.","upload.File":"Contains info on file.","DcOption":"Information for connection to data centre.","Config":"Object contains info on API configuring parameters.","NearestDc":"Object contains info on nearest data centre.","help.AppUpdate":"Contains info on app update availability.","help.InviteText":"Object contains info on the text of a message with an invitation.","EncryptedChat":"Object contains info on an encrypted chat.","InputEncryptedChat":"Object sets an encrypted chat ID.","EncryptedFile":"Seta an encrypted file.","InputEncryptedFile":"Object sets encrypted file for attachment","EncryptedMessage":"Object contains encrypted message.","messages.DhConfig":"Contains info on cofiguring parameters for key generation by Diffie-Hellman protocol.","messages.SentEncryptedMessage":"Contains info on message sent to an encrypted chat.","InputDocument":"Defines a document for subsequent interaction.","Document":"A document.","help.Support":"Info about the support user, relevant to the current user.","NotifyPeer":"Object defines the set of users and/or groups that generate notifications.","SendMessageAction":"User actions. Use this to provide users with detailed info about their chat partners' actions: typing or sending attachments of all kinds.","contacts.Found":"Object contains info on users found by name substring and auxiliary data.","InputPrivacyKey":"Privacy key","PrivacyKey":"Privacy key","InputPrivacyRule":"Privacy rule","PrivacyRule":"Privacy rule","account.PrivacyRules":"Privacy rules","AccountDaysTTL":"Time-to-live of current account","DocumentAttribute":"Various possible attributes of a document (used to define if it's a sticker, a GIF, a video, a mask sticker, an image, an audio, and so on)","messages.Stickers":"Stickers","StickerPack":"Stickerpack","messages.AllStickers":"All stickers","messages.AffectedMessages":"Messages affected by changes","WebPage":"Instant View webpage preview","Authorization":"Represents a logged-in session","account.Authorizations":"Logged-in sessions","account.Password":"Configuration for two-factor authorization","account.PasswordSettings":"Private info associated to the password info (recovery email, telegram passport info & so on)","account.PasswordInputSettings":"Constructor for setting up a new 2FA SRP password","auth.PasswordRecovery":"Recovery info of a 2FA password, only for accounts with a recovery email configured.","ReceivedNotifyMessage":"Confirmation of message receipt","ExportedChatInvite":"Exported chat invite","ChatInvite":"Chat invite","InputStickerSet":"Represents a stickerset","StickerSet":"Represents a stickerset (stickerpack)","messages.StickerSet":"Stickerset","BotCommand":"Describes a bot command that can be used in a chat","BotInfo":"Info about bots (available bot commands, etc)","KeyboardButton":"Bot or inline keyboard buttons","KeyboardButtonRow":"Bot or inline keyboard rows","ReplyMarkup":"Reply markup for bot and inline keyboards","MessageEntity":"Message entities, representing styled text in a message","InputChannel":"Represents a channel","contacts.ResolvedPeer":"Peer returned after resolving a @username","MessageRange":"Indicates a range of chat messages","updates.ChannelDifference":"Contains the difference (new messages) between our local channel state and the remote state","ChannelMessagesFilter":"Filter for fetching only certain types of channel messages","ChannelParticipant":"Channel participant","ChannelParticipantsFilter":"Filter for fetching channel participants","channels.ChannelParticipants":"Channel/supergroup participants","channels.ChannelParticipant":"Channel participant","help.TermsOfService":"Contains info about the latest telegram Terms Of Service.","messages.SavedGifs":"Saved GIFs","InputBotInlineMessage":"Represents a sent inline message from the perspective of a bot","InputBotInlineResult":"Inline bot result","BotInlineMessage":"Inline message","BotInlineResult":"Results of an inline query","messages.BotResults":"Result of a query to an inline bot","ExportedMessageLink":"HTTP link and embed info of channel message","MessageFwdHeader":"Info about a forwarded message","auth.CodeType":"Type of verification code that will be sent next if you call the resendCode method","auth.SentCodeType":"Type of the verification code that was sent","messages.BotCallbackAnswer":"Callback answer of bot","messages.MessageEditData":"Message edit data for media","InputBotInlineMessageID":"Represents a sent inline message from the perspective of a bot","InlineBotSwitchPM":"The bot requested the user to message them in private","messages.PeerDialogs":"List of dialogs","TopPeer":"Top peer","TopPeerCategory":"Top peer category","TopPeerCategoryPeers":"Top peers by top peer category","contacts.TopPeers":"Top peers","DraftMessage":"Represents a message draft.","messages.FeaturedStickers":"Featured stickers","messages.RecentStickers":"Recent stickers","messages.ArchivedStickers":"Archived stickers","messages.StickerSetInstallResult":"Result of stickerset installation process","StickerSetCovered":"Stickerset, with a specific sticker as preview","MaskCoords":"Mask coordinates (if this is a mask sticker, attached to a photo)","InputStickeredMedia":"Represents a media with attached stickers","Game":"Indicates an already sent game","InputGame":"A game to send","HighScore":"Game high score","messages.HighScores":"High scores (in games)","RichText":"Rich text","PageBlock":"Represents an instant view page element","PhoneCallDiscardReason":"Why was the phone call discarded?","DataJSON":"Represent a JSON-encoded object","LabeledPrice":"Labeled pricetag","Invoice":"Invoice","PaymentCharge":"Charged payment","PostAddress":"Shipping address","PaymentRequestedInfo":"Requested payment info","PaymentSavedCredentials":"Saved payment credentials","WebDocument":"Remote document","InputWebDocument":"Specifies a document that will have to be downloaded from the URL by the telegram servers","InputWebFileLocation":"Location of remote file","upload.WebFile":"Remote file","payments.PaymentForm":"Payment form","payments.ValidatedRequestedInfo":"Validated requested info","payments.PaymentResult":"Payment result","payments.PaymentReceipt":"Payment receipt","payments.SavedInfo":"Saved payment info","InputPaymentCredentials":"Payment credentials","account.TmpPassword":"Temporary password","ShippingOption":"Shipping options","InputStickerSetItem":"Sticker","InputPhoneCall":"Phone call","PhoneCall":"Phone call","PhoneConnection":"Phone call connection","PhoneCallProtocol":"Phone call protocol","phone.PhoneCall":"Phone call","upload.CdnFile":"Represents the download status of a CDN file","CdnPublicKey":"Public key to use only during handshakes to CDN DCs.","CdnConfig":"Configuration for CDN file downloads.","LangPackString":"Language pack string","LangPackDifference":"Language pack changes","LangPackLanguage":"Language pack language","ChannelAdminLogEventAction":"Channel admin log event","ChannelAdminLogEvent":"An event in a channel admin log","channels.AdminLogResults":"Admin log events","ChannelAdminLogEventsFilter":"Filter for fetching events in the channel admin log","PopularContact":"Popular contact","messages.FavedStickers":"Favorited stickers","RecentMeUrl":"Recent t.me urls","help.RecentMeUrls":"Recent t.me URLs","InputSingleMedia":"A single media in an album or grouped media sent with {@link messages.sendMultiMedia}.","WebAuthorization":"Web authorization","account.WebAuthorizations":"Web authorizations","InputMessage":"A message","InputDialogPeer":"Peer, or all peers in a certain folder","DialogPeer":"Peer, or all peers in a folder","messages.FoundStickerSets":"Found stickersets","FileHash":"Hash of an uploaded file, to be checked for validity after download","InputClientProxy":"Info about an MTProxy used to connect.","help.TermsOfServiceUpdate":"Update of Telegram's terms of service","InputSecureFile":"Secure passport file, for more info see the passport docs »","SecureFile":"Secure passport file, for more info see the passport docs »","SecureData":"Secure passport data, for more info see the passport docs »","SecurePlainData":"Plaintext verified passport data.","SecureValueType":"Secure value type","SecureValue":"Secure tgpassport value","InputSecureValue":"Secure value, for more info see the passport docs »","SecureValueHash":"Secure value hash","SecureValueError":"Secure value error","SecureCredentialsEncrypted":"Encrypted secure credentials","account.AuthorizationForm":"Authorization form","account.SentEmailCode":"The email code that was sent","help.DeepLinkInfo":"Contains information about a tg:// deep link","SavedContact":"Saved contact","account.Takeout":"Takeout info","PasswordKdfAlgo":"Key derivation function to use when generating the password hash for SRP two-factor authorization","SecurePasswordKdfAlgo":"KDF algorithm to use for computing telegram passport hash","SecureSecretSettings":"Telegram passport settings","InputCheckPasswordSRP":"Constructors for checking the validity of a 2FA SRP password","SecureRequiredType":"Required secure file type","help.PassportConfig":"Telegram passport configuration","InputAppEvent":"Object contains info about an event that occured in the application.","JSONObjectValue":"JSON key: value pair","JSONValue":"JSON value","PageTableCell":"Represents a table in an instant view table","PageTableRow":"Table row","PageCaption":"Page caption","PageListItem":"Item in block list","PageListOrderedItem":"Represents an instant view ordered list","PageRelatedArticle":"Related articles","Page":"Instant view page","help.SupportName":"Get localized name for support user","help.UserInfo":"User info","PollAnswer":"Indicates a possible answer to a poll.","Poll":"Indicates a poll message","PollAnswerVoters":"How users voted on a certain poll answer","PollResults":"Results of poll","ChatOnlines":"Number of online users in a chat","StatsURL":"URL with chat statistics","ChatAdminRights":"Represents the rights of an admin in a channel/supergroup.","ChatBannedRights":"Represents the rights of a normal user in a supergroup/channel/chat.","InputWallPaper":"Wallpaper","account.WallPapers":"Wallpapers","CodeSettings":"Settings for the code type to send","WallPaperSettings":"Wallpaper settings","AutoDownloadSettings":"Media autodownload settings","account.AutoDownloadSettings":"Media autodownload settings","EmojiKeyword":"Emoji keyword","EmojiKeywordsDifference":"New emoji keywords","EmojiURL":"Emoji URL","EmojiLanguage":"Emoji language","Folder":"A folder","InputFolderPeer":"Peer in a folder","FolderPeer":"Peer associated to folder","messages.SearchCounter":"Number of results that would be returned by a search","UrlAuthResult":"URL authorization result","ChannelLocation":"Geographical location of supergroup (geogroups)","PeerLocated":"Geolocated peer","RestrictionReason":"Restriction reason","InputTheme":"Cloud theme","Theme":"Cloud theme","account.Themes":"Installed themes","auth.LoginToken":"Login token (for QR code login)","account.ContentSettings":"Sensitive content settings","messages.InactiveChats":"Inactive chat list","BaseTheme":"Basic theme settings","InputThemeSettings":"Theme settings","ThemeSettings":"Theme settings","WebPageAttribute":"Webpage attributes","MessageUserVote":"How a user voted in a poll","messages.VotesList":"How users voted in a poll","BankCardOpenUrl":"Credit card info URL provided by the bank","payments.BankCardData":"Credit card info, provided by the card's bank(s)","DialogFilter":"Dialog filter (folders)","DialogFilterSuggested":"Suggested dialog filters (folders)","StatsDateRangeDays":"Channel statistics date range","StatsAbsValueAndPrev":"Channel statistics value pair","StatsPercentValue":"Channel statistics percentage","StatsGraph":"Channel statistics graph","MessageInteractionCounters":"Message interaction counters","stats.BroadcastStats":"Channel statistics","help.PromoData":"Info about pinned MTProxy or Public Service Announcement peers.","VideoSize":"Represents an animated video thumbnail","StatsGroupTopPoster":"Most active user in a supergroup","StatsGroupTopAdmin":"Most active admin in a supergroup","StatsGroupTopInviter":"Most active inviter in a supergroup","stats.MegagroupStats":"Supergroup statistics","GlobalPrivacySettings":"Global privacy settings","help.CountryCode":"Country code and phone number pattern of a specific country","help.Country":"Name, ISO code, localized name and phone codes/patterns of a specific country","help.CountriesList":"Name, ISO code, localized name and phone codes/patterns of all available countries","MessageViews":"View, forward counter + info about replies of a specific message","messages.MessageViews":"View, forward counter + info about replies","messages.DiscussionMessage":"Info about a message thread","MessageReplyHeader":"Reply information","MessageReplies":"Info about post comments (for channels) or message replies (for groups)","PeerBlocked":"Info about a blocked user","stats.MessageStats":"Message statistics","GroupCall":"A group call","InputGroupCall":"Indicates a group call","GroupCallParticipant":"Info about a group call participant","phone.GroupCall":"Contains info about a group call, and partial info about its participants.","phone.GroupParticipants":"Info about the participants of a group call or livestream","InlineQueryPeerType":"Type of the chat from which the inline query was sent.","messages.HistoryImport":"Identifier of a history import session, click here for more info ».","messages.HistoryImportParsed":"Contains information about a chat export file, generated by a foreign chat app.","messages.AffectedFoundMessages":"Messages found and affected by changes","ChatInviteImporter":"When and which user joined the chat using a chat invite","messages.ExportedChatInvites":"Info about chat invites exported by a certain admin.","messages.ExportedChatInvite":"Contains info about a chat invite, and eventually a pointer to the newest chat invite.","messages.ChatInviteImporters":"List of users that imported a chat invitation link.","ChatAdminWithInvites":"Info about chat invites generated by admins.","messages.ChatAdminsWithInvites":"Info about chat invites generated by admins.","messages.CheckedHistoryImportPeer":"Contains a confirmation text to be shown to the user, upon importing chat history, click here for more info ».","phone.JoinAsPeers":"A list of peers that can be used to join a group call, presenting yourself as a specific user/channel.","phone.ExportedGroupCallInvite":"An exported group call invitation.","GroupCallParticipantVideoSourceGroup":"Describes a group of video synchronization source identifiers","GroupCallParticipantVideo":"Info about a video stream","stickers.SuggestedShortName":"A suggested short name for the specified stickerpack","BotCommandScope":"Represents a scope where the bot commands, specified using {@link bots.setBotCommands} will be valid.","account.ResetPasswordResult":"Result of an {@link account.resetPassword} request.","SponsoredMessage":"A sponsored message","messages.SponsoredMessages":"A set of sponsored messages associated to a channel","ChatTheme":"A chat theme","account.ChatThemes":"Available chat themes","MessageReactions":"Message reactions","ReactionCount":"Number of users that reacted with a certain emoji","MessageReactionsList":"List of message reactions","MessageUserReaction":"Message reaction"}} \ No newline at end of file diff --git a/packages/tl/binary/reader.d.ts b/packages/tl/binary/reader.d.ts index e6ddc6db..78239886 100644 --- a/packages/tl/binary/reader.d.ts +++ b/packages/tl/binary/reader.d.ts @@ -1,80 +1,2 @@ -import { BigInteger } from 'big-integer' - -/** - * Interface describing binary reader compatible with @mtcute/tl - * generated binary readers - */ -export interface ITlBinaryReader { - /** Read 32-bit signed integer from the source */ - int32(): number - - /** Read 32-bit unsigned integer from the source */ - uint32(): number - - /** Read 64-bit (un-)signed integer from the source */ - long(unsigned?: boolean): BigInteger - - /** Read 64-bit usigned integer from the source */ - ulong(): BigInteger - - /** Read 64-bit integer as a byte array */ - rawLong(): Buffer - - /** Read 32-bit floating point value */ - float(): number - - /** Read 64-bit floating point value */ - double(): number - - /** - * Read TL-encoded boolean - * - `0xbc799737` = false - * - `0x997275b5` = true - */ - boolean(): boolean - - /** Read 128-bit integer as a byte array */ - int128(): Buffer - - /** Read 256-bit integer as a byte array */ - int256(): Buffer - - /** Read TL-encoded byte array (see [TL Base Types](https://core.telegram.org/mtproto/serialize#base-types)) */ - bytes(): Buffer - - /** Read TL-encoded string (see [TL Base Types](https://core.telegram.org/mtproto/serialize#base-types)) */ - string(): string - - /** Read TL object */ - object(): any - - /** Read {@link bytes | this.bytes()} and gunzip it */ - gzip(): any - - /** - * Read a TL-encoded array of items. - * - * @param reader Function used for reading - * @param bare Whether the vector is bare (i.e. vector ID is not present) - */ - vector(reader?: TlBinaryReaderFunction, bare?: boolean): unknown[] -} - -export type TlBinaryReaderFunction = (this: ITlBinaryReader) => unknown - -/** - * Mapping of TL object IDs to reader functions. - * - * Note that these types will never be present in the map - * and must be parsed manually inside `.object()`: - * - `0x1cb5c415` aka `vector` - * - `0x3072cfa1` aka `gzip_packed` - * - `0xbc799737` aka `boolFalse` - * - `0x997275b5` aka `boolTrue` - * - `0x3fedd339` aka `true` - * - `0x56730bcc` aka `null` - */ -export type TlReaderMap = Record - -declare const __tlReaderMap: TlReaderMap +declare const __tlReaderMap: Record any> export default __tlReaderMap diff --git a/packages/tl/binary/rsa-keys.js b/packages/tl/binary/rsa-keys.js index 0351c10a..2c253878 100644 --- a/packages/tl/binary/rsa-keys.js +++ b/packages/tl/binary/rsa-keys.js @@ -1,62 +1,4 @@ -// This file was auto-generated. Do not edit. -'use strict' -Object.defineProperty(exports, '__esModule', { value: true }) - -exports.default = { - '0bc35f3509f7b7a5': { - modulus: - 'aeec36c8ffc109cb099624685b97815415657bd76d8c9c3e398103d7ad16c9bba6f525ed0412d7ae2c2de2b44e77d72cbf4b7438709a4e646a05c43427c7f184debf72947519680e651500890c6832796dd11f772c25ff8f576755afe055b0a3752c696eb7d8da0d8be1faf38c9bdd97ce0a77d3916230c4032167100edd0f9e7a3a9b602d04367b689536af0d64b613ccba7962939d3b57682beb6dae5b608130b2e52aca78ba023cf6ce806b1dc49c72cf928a7199d22e3d7ac84e47bc9427d0236945d10dbd15177bab413fbf0edfda09f014c7a7da088dde9759702ca760af2b8e4e97cc055c617bd74c3d97008635b98dc4d621b4891da9fb0473047927', - exponent: '010001', - fingerprint: '0bc35f3509f7b7a5', - old: false, - }, - '15ae5fa8b5529542': { - modulus: - 'bdf2c77d81f6afd47bd30f29ac76e55adfe70e487e5e48297e5a9055c9c07d2b93b4ed3994d3eca5098bf18d978d54f8b7c713eb10247607e69af9ef44f38e28f8b439f257a11572945cc0406fe3f37bb92b79112db69eedf2dc71584a661638ea5becb9e23585074b80d57d9f5710dd30d2da940e0ada2f1b878397dc1a72b5ce2531b6f7dd158e09c828d03450ca0ff8a174deacebcaa22dde84ef66ad370f259d18af806638012da0ca4a70baa83d9c158f3552bc9158e69bf332a45809e1c36905a5caa12348dd57941a482131be7b2355a5f4635374f3bd3ddf5ff925bf4809ee27c1e67d9120c5fe08a9de458b1b4a3c5d0a428437f2beca81f4e2d5ff', - exponent: '010001', - fingerprint: '15ae5fa8b5529542', - old: false, - }, - aeae98e13cd7f94f: { - modulus: - 'b3f762b739be98f343eb1921cf0148cfa27ff7af02b6471213fed9daa0098976e667750324f1abcea4c31e43b7d11f1579133f2b3d9fe27474e462058884e5e1b123be9cbbc6a443b2925c08520e7325e6f1a6d50e117eb61ea49d2534c8bb4d2ae4153fabe832b9edf4c5755fdd8b19940b81d1d96cf433d19e6a22968a85dc80f0312f596bd2530c1cfb28b5fe019ac9bc25cd9c2a5d8a0f3a1c0c79bcca524d315b5e21b5c26b46babe3d75d06d1cd33329ec782a0f22891ed1db42a1d6c0dea431428bc4d7aabdcf3e0eb6fda4e23eb7733e7727e9a1915580796c55188d2596d2665ad1182ba7abf15aaa5a8b779ea996317a20ae044b820bff35b6e8a1', - exponent: '010001', - fingerprint: 'aeae98e13cd7f94f', - old: false, - }, - '5a181b2235057d98': { - modulus: - 'be6a71558ee577ff03023cfa17aab4e6c86383cff8a7ad38edb9fafe6f323f2d5106cbc8cafb83b869cffd1ccf121cd743d509e589e68765c96601e813dc5b9dfc4be415c7a6526132d0035ca33d6d6075d4f535122a1cdfe017041f1088d1419f65c8e5490ee613e16dbf662698c0f54870f0475fa893fc41eb55b08ff1ac211bc045ded31be27d12c96d8d3cfc6a7ae8aa50bf2ee0f30ed507cc2581e3dec56de94f5dc0a7abee0be990b893f2887bd2c6310a1e0a9e3e38bd34fded2541508dc102a9c9b4c95effd9dd2dfe96c29be647d6c69d66ca500843cfaed6e440196f1dbe0e2e22163c61ca48c79116fa77216726749a976a1c4b0944b5121e8c01', - exponent: '010001', - fingerprint: '5a181b2235057d98', - old: false, - }, - c3b42b026ce86b21: { - modulus: - 'c150023e2f70db7985ded064759cfecf0af328e69a41daf4d6f01b538135a6f91f8f8b2a0ec9ba9720ce352efcf6c5680ffc424bd634864902de0b4bd6d49f4e580230e3ae97d95c8b19442b3c0a10d8f5633fecedd6926a7f6dab0ddb7d457f9ea81b8465fcd6fffeed114011df91c059caedaf97625f6c96ecc74725556934ef781d866b34f011fce4d835a090196e9a5f0e4449af7eb697ddb9076494ca5f81104a305b6dd27665722c46b60e5df680fb16b210607ef217652e60236c255f6a28315f4083a96791d7214bf64c1df4fd0db1944fb26a2a57031b32eee64ad15a8ba68885cde74a5bfc920f6abf59ba5c75506373e7130f9042da922179251f', - exponent: '010001', - fingerprint: 'c3b42b026ce86b21', - old: true, - }, - '9a996a1db11c729b': { - modulus: - 'c6aeda78b02a251db4b6441031f467fa871faed32526c436524b1fb3b5dca28efb8c089dd1b46d92c895993d87108254951c5f001a0f055f3063dcd14d431a300eb9e29517e359a1c9537e5e87ab1b116faecf5d17546ebc21db234d9d336a693efcb2b6fbcca1e7d1a0be414dca408a11609b9c4269a920b09fed1f9a1597be02761430f09e4bc48fcafbe289054c99dba51b6b5eb7d9c3a2ab4e490545b4676bd620e93804bcac93bf94f73f92c729ca899477ff17625ef14a934d51dc11d5f8650a3364586b3a52fcff2fedec8a8406cac4e751705a472e55707e3c8cd5594342b119c6c3293532d85dbe9271ed54a2fd18b4dc79c04a30951107d5639397', - exponent: '010001', - fingerprint: '9a996a1db11c729b', - old: true, - }, - b05b2a6f70cdea78: { - modulus: - 'b1066749655935f0a5936f517034c943bea7f3365a8931ae52c8bcb14856f004b83d26cf2839be0f22607470d67481771c1ce5ec31de16b20bbaa4ecd2f7d2ecf6b6356f27501c226984263edc046b89fb6d3981546b01d7bd34fedcfcc1058e2d494bda732ff813e50e1c6ae249890b225f82b22b1e55fcb063dc3c0e18e91c28d0c4aa627dec8353eee6038a95a4fd1ca984eb09f94aeb7a2220635a8ceb450ea7e61d915cdb4eecedaa083aa3801daf071855ec1fb38516cb6c2996d2d60c0ecbcfa57e4cf1fb0ed39b2f37e94ab4202ecf595e167b3ca62669a6da520859fb6d6c6203dfdfc79c75ec3ee97da8774b2da903e3435f2cd294670a75a526c1', - exponent: '010001', - fingerprint: 'b05b2a6f70cdea78', - old: true, - }, - '71e025b6c76033e3': { - modulus: - 'c2a8c55b4a62e2b78a19b91cf692bcdc4ba7c23fe4d06f194e2a0c30f6d9996f7d1a2bcc89bc1ac4333d44359a6c433252d1a8402d9970378b5912b75bc8cc3fa76710a025bcb9032df0b87d7607cc53b928712a174ea2a80a8176623588119d42ffce40205c6d72160860d8d80b22a8b8651907cf388effbef29cd7cf2b4eb8a872052da1351cfe7fec214ce48304ea472bd66329d60115b3420d08f6894b0410b6ab9450249967617670c932f7cbdb5d6fbcce1e492c595f483109999b2661fcdeec31b196429b7834c7211a93c6789d9ee601c18c39e521fda9d7264e61e518add6f0712d2d5228204b851e13c4f322e5c5431c3b7f31089668486aadc59f', - exponent: '010001', - fingerprint: '71e025b6c76033e3', - old: true, - }, -} +// This file is auto-generated. Do not edit. +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default=JSON.parse('{"b25898df208d2603":{"modulus":"c8c11d635691fac091dd9489aedced2932aa8a0bcefef05fa800892d9b52ed03200865c9e97211cb2ee6c7ae96d3fb0e15aeffd66019b44a08a240cfdd2868a85e1f54d6fa5deaa041f6941ddf302690d61dc476385c2fa655142353cb4e4b59f6e5b6584db76fe8b1370263246c010c93d011014113ebdf987d093f9d37c2be48352d69a1683f8f6e6c2167983c761e3ab169fde5daaa12123fa1beab621e4da5935e9c198f82f35eae583a99386d8110ea6bd1abb0f568759f62694419ea5f69847c43462abef858b4cb5edc84e7b9226cd7bd7e183aa974a712c079dde85b9dc063b8a5c08e8f859c0ee5dcd824c7807f20153361a7f63cfd2a433a1be7f5","exponent":"010001","fingerprint":"b25898df208d2603","old":false},"d09d1d85de64fd85":{"modulus":"e8bb3305c0b52c6cf2afdf7637313489e63e05268e5badb601af417786472e5f93b85438968e20e6729a301c0afc121bf7151f834436f7fda680847a66bf64accec78ee21c0b316f0edafe2f41908da7bd1f4a5107638eeb67040ace472a14f90d9f7c2b7def99688ba3073adb5750bb02964902a359fe745d8170e36876d4fd8a5d41b2a76cbff9a13267eb9580b2d06d10357448d20d9da2191cb5d8c93982961cdfdeda629e37f1fb09a0722027696032fe61ed663db7a37f6f263d370f69db53a0dc0a1748bdaaff6209d5645485e6e001d1953255757e4b8e42813347b11da6ab500fd0ace7e6dfa3736199ccaf9397ed0745a427dcfa6cd67bcb1acff3","exponent":"010001","fingerprint":"d09d1d85de64fd85","old":false},"0bc35f3509f7b7a5":{"modulus":"aeec36c8ffc109cb099624685b97815415657bd76d8c9c3e398103d7ad16c9bba6f525ed0412d7ae2c2de2b44e77d72cbf4b7438709a4e646a05c43427c7f184debf72947519680e651500890c6832796dd11f772c25ff8f576755afe055b0a3752c696eb7d8da0d8be1faf38c9bdd97ce0a77d3916230c4032167100edd0f9e7a3a9b602d04367b689536af0d64b613ccba7962939d3b57682beb6dae5b608130b2e52aca78ba023cf6ce806b1dc49c72cf928a7199d22e3d7ac84e47bc9427d0236945d10dbd15177bab413fbf0edfda09f014c7a7da088dde9759702ca760af2b8e4e97cc055c617bd74c3d97008635b98dc4d621b4891da9fb0473047927","exponent":"010001","fingerprint":"0bc35f3509f7b7a5","old":true},"15ae5fa8b5529542":{"modulus":"bdf2c77d81f6afd47bd30f29ac76e55adfe70e487e5e48297e5a9055c9c07d2b93b4ed3994d3eca5098bf18d978d54f8b7c713eb10247607e69af9ef44f38e28f8b439f257a11572945cc0406fe3f37bb92b79112db69eedf2dc71584a661638ea5becb9e23585074b80d57d9f5710dd30d2da940e0ada2f1b878397dc1a72b5ce2531b6f7dd158e09c828d03450ca0ff8a174deacebcaa22dde84ef66ad370f259d18af806638012da0ca4a70baa83d9c158f3552bc9158e69bf332a45809e1c36905a5caa12348dd57941a482131be7b2355a5f4635374f3bd3ddf5ff925bf4809ee27c1e67d9120c5fe08a9de458b1b4a3c5d0a428437f2beca81f4e2d5ff","exponent":"010001","fingerprint":"15ae5fa8b5529542","old":true},"aeae98e13cd7f94f":{"modulus":"b3f762b739be98f343eb1921cf0148cfa27ff7af02b6471213fed9daa0098976e667750324f1abcea4c31e43b7d11f1579133f2b3d9fe27474e462058884e5e1b123be9cbbc6a443b2925c08520e7325e6f1a6d50e117eb61ea49d2534c8bb4d2ae4153fabe832b9edf4c5755fdd8b19940b81d1d96cf433d19e6a22968a85dc80f0312f596bd2530c1cfb28b5fe019ac9bc25cd9c2a5d8a0f3a1c0c79bcca524d315b5e21b5c26b46babe3d75d06d1cd33329ec782a0f22891ed1db42a1d6c0dea431428bc4d7aabdcf3e0eb6fda4e23eb7733e7727e9a1915580796c55188d2596d2665ad1182ba7abf15aaa5a8b779ea996317a20ae044b820bff35b6e8a1","exponent":"010001","fingerprint":"aeae98e13cd7f94f","old":true},"5a181b2235057d98":{"modulus":"be6a71558ee577ff03023cfa17aab4e6c86383cff8a7ad38edb9fafe6f323f2d5106cbc8cafb83b869cffd1ccf121cd743d509e589e68765c96601e813dc5b9dfc4be415c7a6526132d0035ca33d6d6075d4f535122a1cdfe017041f1088d1419f65c8e5490ee613e16dbf662698c0f54870f0475fa893fc41eb55b08ff1ac211bc045ded31be27d12c96d8d3cfc6a7ae8aa50bf2ee0f30ed507cc2581e3dec56de94f5dc0a7abee0be990b893f2887bd2c6310a1e0a9e3e38bd34fded2541508dc102a9c9b4c95effd9dd2dfe96c29be647d6c69d66ca500843cfaed6e440196f1dbe0e2e22163c61ca48c79116fa77216726749a976a1c4b0944b5121e8c01","exponent":"010001","fingerprint":"5a181b2235057d98","old":true},"c3b42b026ce86b21":{"modulus":"c150023e2f70db7985ded064759cfecf0af328e69a41daf4d6f01b538135a6f91f8f8b2a0ec9ba9720ce352efcf6c5680ffc424bd634864902de0b4bd6d49f4e580230e3ae97d95c8b19442b3c0a10d8f5633fecedd6926a7f6dab0ddb7d457f9ea81b8465fcd6fffeed114011df91c059caedaf97625f6c96ecc74725556934ef781d866b34f011fce4d835a090196e9a5f0e4449af7eb697ddb9076494ca5f81104a305b6dd27665722c46b60e5df680fb16b210607ef217652e60236c255f6a28315f4083a96791d7214bf64c1df4fd0db1944fb26a2a57031b32eee64ad15a8ba68885cde74a5bfc920f6abf59ba5c75506373e7130f9042da922179251f","exponent":"010001","fingerprint":"c3b42b026ce86b21","old":true},"9a996a1db11c729b":{"modulus":"c6aeda78b02a251db4b6441031f467fa871faed32526c436524b1fb3b5dca28efb8c089dd1b46d92c895993d87108254951c5f001a0f055f3063dcd14d431a300eb9e29517e359a1c9537e5e87ab1b116faecf5d17546ebc21db234d9d336a693efcb2b6fbcca1e7d1a0be414dca408a11609b9c4269a920b09fed1f9a1597be02761430f09e4bc48fcafbe289054c99dba51b6b5eb7d9c3a2ab4e490545b4676bd620e93804bcac93bf94f73f92c729ca899477ff17625ef14a934d51dc11d5f8650a3364586b3a52fcff2fedec8a8406cac4e751705a472e55707e3c8cd5594342b119c6c3293532d85dbe9271ed54a2fd18b4dc79c04a30951107d5639397","exponent":"010001","fingerprint":"9a996a1db11c729b","old":true},"b05b2a6f70cdea78":{"modulus":"b1066749655935f0a5936f517034c943bea7f3365a8931ae52c8bcb14856f004b83d26cf2839be0f22607470d67481771c1ce5ec31de16b20bbaa4ecd2f7d2ecf6b6356f27501c226984263edc046b89fb6d3981546b01d7bd34fedcfcc1058e2d494bda732ff813e50e1c6ae249890b225f82b22b1e55fcb063dc3c0e18e91c28d0c4aa627dec8353eee6038a95a4fd1ca984eb09f94aeb7a2220635a8ceb450ea7e61d915cdb4eecedaa083aa3801daf071855ec1fb38516cb6c2996d2d60c0ecbcfa57e4cf1fb0ed39b2f37e94ab4202ecf595e167b3ca62669a6da520859fb6d6c6203dfdfc79c75ec3ee97da8774b2da903e3435f2cd294670a75a526c1","exponent":"010001","fingerprint":"b05b2a6f70cdea78","old":true},"71e025b6c76033e3":{"modulus":"c2a8c55b4a62e2b78a19b91cf692bcdc4ba7c23fe4d06f194e2a0c30f6d9996f7d1a2bcc89bc1ac4333d44359a6c433252d1a8402d9970378b5912b75bc8cc3fa76710a025bcb9032df0b87d7607cc53b928712a174ea2a80a8176623588119d42ffce40205c6d72160860d8d80b22a8b8651907cf388effbef29cd7cf2b4eb8a872052da1351cfe7fec214ce48304ea472bd66329d60115b3420d08f6894b0410b6ab9450249967617670c932f7cbdb5d6fbcce1e492c595f483109999b2661fcdeec31b196429b7834c7211a93c6789d9ee601c18c39e521fda9d7264e61e518add6f0712d2d5228204b851e13c4f322e5c5431c3b7f31089668486aadc59f","exponent":"010001","fingerprint":"71e025b6c76033e3","old":true}}'); \ No newline at end of file diff --git a/packages/tl/binary/writer.d.ts b/packages/tl/binary/writer.d.ts index 9895c2ed..ae055204 100644 --- a/packages/tl/binary/writer.d.ts +++ b/packages/tl/binary/writer.d.ts @@ -1,70 +1,2 @@ -import { BigInteger } from 'big-integer' - -/** - * Interface describing binary writer compatible with @mtcute/tl - * generated binary writers - */ -export interface ITlBinaryWriter { - /** Write signed 32-bit integer to the stream */ - int32(val: number): void - - /** Write unsigned 32-bit integer to the stream */ - uint32(val: number): void - - /** Write signed 64-bit integer to the stream */ - long(val: BigInteger): void - - /** Write signed 64-bit integer from buffer to the stream */ - rawLong(val: Buffer): void - - /** Write 32-bit floating point value to the stream */ - float(val: number): void - - /** Write 64-bit floating point value to the stream */ - double(val: number): void - - /** - * Write TL-encoded boolean to the stream - * - `0xbc799737` = false - * - `0x997275b5` = true - */ - boolean(val: boolean): void - - /** Write 128-bit integer as a buffer to the stream */ - int128(val: Buffer): void - - /** Write 256-bit integer as a buffer to the stream */ - int256(val: Buffer): void - - /** Write TL-encoded byte array to the stream (see [TL Base Types](https://core.telegram.org/mtproto/serialize#base-types)) */ - bytes(val: Buffer): void - - /** Write TL-encoded string to the stream (see [TL Base Types](https://core.telegram.org/mtproto/serialize#base-types))*/ - string(val: string): void - - /** Write TL object */ - object(obj: unknown, bare?: boolean): void - - /** - * Write an array of TL objects - * - * @param fn Writer function - * @param items Items to be written - * @param bare Whether the vector is bare (i.e. object ID should not be written) - */ - vector(fn: TlBinaryWriterFunction, items: unknown[], bare?: boolean): void -} - -export type TlBinaryWriterFunction = ( - this: ITlBinaryWriter, - obj: unknown, - bare?: boolean -) => void - -/** - * Mapping of TL object names to writer functions. - */ -export type TlWriterMap = Record - -declare const __tlWriterMap: TlWriterMap +declare const __tlWriterMap: Record void> export default __tlWriterMap diff --git a/packages/tl/scripts/_prepend.tl b/packages/tl/data/custom.tl similarity index 92% rename from packages/tl/scripts/_prepend.tl rename to packages/tl/data/custom.tl index 1b9c373a..0c46daba 100644 --- a/packages/tl/scripts/_prepend.tl +++ b/packages/tl/data/custom.tl @@ -3,6 +3,11 @@ // but seem to work // in case of conflict, type from main schema is preferred +// for internal use +---types--- + +dummyUpdate pts:int pts_count:int channel_id:int53 = Update; + // reactions // taken from official docs coz why not lol // ctor ids will be generated by the codegen diff --git a/packages/tl/data/descriptions.yaml b/packages/tl/data/descriptions.yaml new file mode 100644 index 00000000..2c667973 --- /dev/null +++ b/packages/tl/data/descriptions.yaml @@ -0,0 +1,36 @@ +# This file contains additional descriptions for TL objects, +# for example for objects/fields that don't have a description +# from Telegram documentation. +# +# By default, these descriptions are used as a fallback, +# but can also be used to overwrite original documentation. +# +# This file is licensed under MIT license. + +# Override TL objects' (classes/methods) description, or their arguments' descriptions. +# Type is defined by the first 2 symbols in key ("c_" for class, "m_" for method, "u_" for union) +objects: + +# Override arguments wherever they are, based on the filters +arguments: + gigagroup: Is this a broadcast group? + +# Replace any description, based on regex. Used to fix typos. +regex: + - regex: \bchanel\b + repl: channel + - regex: \bgeoposition\b + repl: geo position + - regex: \bgeogroup\b + repl: geo group + - regex: \bunixdate\b|Unix timestamp + repl: UNIX timestamp in seconds + - regex: \bstickerset\b + repl: stickerset + - regex: \bonly "foursquare" needs\b + repl: only "foursquare" and "gplaces" need + # gender-neutral pronouns + - regex: \bhis\b + repl: their + - regex: \bhim\b + repl: them diff --git a/packages/tl/data/int53-overrides.json b/packages/tl/data/int53-overrides.json new file mode 100644 index 00000000..53a9bf17 --- /dev/null +++ b/packages/tl/data/int53-overrides.json @@ -0,0 +1,144 @@ +{ + "What is this?": [ + "It is guaranteed that user/chat/channel ids fit in int53,", + "so we can safely replace `long` with `int53` there.", + "Note: this is a non-exhaustive list", + "When contributing, please maintain alphabetical key ordering" + ], + "class": { + "botInfo": ["user_id"], + "channel": ["id"], + "channelAdminLogEvent": ["user_id"], + "channelAdminLogEventActionChangeLinkedChat": ["prev_value", "new_value"], + "channelForbidden": ["id"], + "channelFull": ["id", "linked_chat_id"], + "channelParticipant": ["user_id"], + "channelParticipantAdmin": ["user_id", "inviter_id", "promoted_by"], + "channelParticipantBanned": ["kicked_by"], + "channelParticipantCreator": ["user_id"], + "channelParticipantSelf": ["user_id", "inviter_id"], + "chat": ["id"], + "chatAdminWithInvites": ["admin_id"], + "chatEmpty": ["id"], + "chatForbidden": ["id"], + "chatFull": ["id"], + "chatInviteExported": ["admin_id"], + "chatInviteImporter": ["user_id"], + "chatParticipant": ["user_id", "inviter_id"], + "chatParticipantAdmin": ["user_id", "inviter_id"], + "chatParticipantCreator": ["user_id"], + "chatParticipants": ["chat_id"], + "chatParticipantsForbidden": ["chat_id"], + "contact": ["user_id"], + "contactStatus": ["user_id"], + "encryptedChat": ["admin_id", "participant_id"], + "encryptedChatRequested": ["admin_id", "participant_id"], + "encryptedChatWaiting": ["admin_id", "participant_id"], + "highScore": ["user_id"], + "importedContact": ["user_id"], + "inputChannel": ["channel_id"], + "inputChannelFromMessage": ["channel_id"], + "inputPeerChannel": ["channel_id"], + "inputPeerChannelFromMessage": ["channel_id"], + "inputPeerChat": ["chat_id"], + "inputPeerUser": ["user_id"], + "inputPeerUserFromMessage": ["user_id"], + "inputPrivacyValueAllowChatParticipants": ["chats"], + "inputPrivacyValueDisallowChatParticipants": ["chats"], + "inputUser": ["user_id"], + "inputUserFromMessage": ["user_id"], + "message": ["via_bot_id"], + "messageActionChannelMigrateFrom": ["chat_id"], + "messageActionChatAddUser": ["users"], + "messageActionChatCreate": ["users"], + "messageActionChatDeleteUser": ["user_id"], + "messageActionChatJoinedByLink": ["inviter_id"], + "messageActionChatMigrateTo": ["channel_id"], + "messageActionInviteToGroupCall": ["users"], + "messageEntityMentionName": ["user_id"], + "messageMediaContact": ["user_id"], + "messageReplies": ["channel_id"], + "messageUserVote": ["user_id"], + "messageUserVoteInputOption": ["user_id"], + "messageUserVoteMultiple": ["user_id"], + "payments.paymentForm": ["bot_id"], + "payments.paymentReceipt": ["bot_id"], + "peerChannel": ["channel_id"], + "peerChat": ["chat_id"], + "peerUser": ["user_id"], + "phoneCall": ["admin_id", "participant_id"], + "phoneCallAccepted": ["admin_id", "participant_id"], + "phoneCallRequested": ["admin_id", "participant_id"], + "phoneCallWaiting": ["admin_id", "participant_id"], + "pollResults": ["recent_voters"], + "privacyValueAllowChatParticipants": ["chats"], + "privacyValueAllowUsers": ["users"], + "privacyValueDisallowChatParticipants": ["chats"], + "privacyValueDisallowUsers": ["users"], + "recentMeUrlChat": ["chat_id"], + "recentMeUrlUser": ["user_id"], + "statsGroupTopAdmin": ["user_id"], + "statsGroupTopInviter": ["user_id"], + "statsGroupTopPoster": ["user_id"], + "updateBotCallbackQuery": ["user_id"], + "updateBotCommands": ["bot_id"], + "updateBotInlineQuery": ["user_id"], + "updateBotInlineSend": ["user_id"], + "updateBotPrecheckoutQuery": ["user_id"], + "updateBotShippingQuery": ["user_id"], + "updateBotStopped": ["user_id"], + "updateChannel": ["channel_id"], + "updateChannelAvailableMessages": ["channel_id"], + "updateChannelMessageForwards": ["channel_id"], + "updateChannelMessageViews": ["channel_id"], + "updateChannelParticipant": ["channel_id", "actor_id", "user_id"], + "updateChannelReadMessagesContents": ["channel_id"], + "updateChannelTooLong": ["channel_id"], + "updateChannelUserTyping": ["channel_id"], + "updateChannelWebPage": ["channel_id"], + "updateChat": ["chat_id"], + "updateChatParticipant": ["chat_id", "actor_id", "user_id"], + "updateChatParticipantAdd": ["chat_id", "user_id", "inviter_id"], + "updateChatParticipantAdmin": ["chat_id", "user_id"], + "updateChatParticipantDelete": ["chat_id", "user_id"], + "updateChatUserTyping": ["chat_id"], + "updateDeleteChannelMessages": ["channel_id"], + "updateGroupCall": ["chat_id"], + "updateInlineBotCallbackQuery": ["user_id"], + "updateMessagePollVote": ["user_id"], + "updatePinnedChannelMessages": ["channel_id"], + "updateReadChannelDiscussionInbox": ["channel_id", "broadcast_id"], + "updateReadChannelDiscussionOutbox": ["channel_id"], + "updateReadChannelInbox": ["channel_id"], + "updateReadChannelOutbox": ["channel_id"], + "updateShortChatMessage": ["from_id", "chat_id", "via_bot_id"], + "updateShortMessage": ["user_id", "via_bot_id"], + "updateUserName": ["user_id"], + "updateUserPhone": ["user_id"], + "updateUserPhoto": ["user_id"], + "updateUserStatus": ["user_id"], + "updateUserTyping": ["user_id"], + "user": ["id"], + "userEmpty": ["id"], + "webAuthorization": ["bot_id"], + "_": "Dummy line teehee~" + }, + "method": { + "account.acceptAuthorization": ["bot_id"], + "account.getAuthorizationForm": ["bot_id"], + "account.registerDevice": ["other_uids"], + "account.unregisterDevice": ["other_uids"], + "messages.addChatUser": ["chat_id"], + "messages.deleteChat": ["chat_id"], + "messages.deleteChatUser": ["chat_id"], + "messages.editChatAdmin": ["chat_id"], + "messages.editChatPhoto": ["chat_id"], + "messages.editChatTitle": ["chat_id"], + "messages.getAllChats": ["except_ids"], + "messages.getChats": ["id"], + "messages.getCommonChats": ["max_id"], + "messages.getFullChat": ["chat_id"], + "messages.migrateChat": ["chat_id"], + "_": "Dummy line teehee~" + } +} diff --git a/packages/tl/descriptions.yaml b/packages/tl/descriptions.yaml deleted file mode 100644 index bb2edf7d..00000000 --- a/packages/tl/descriptions.yaml +++ /dev/null @@ -1,477 +0,0 @@ -# This file contains additional descriptions for TL objects, -# for example for objects/fields that don't have a description -# from Telegram documentation. -# -# By default, these descriptions are used as a fallback, -# but can also be used to overwrite original documentation. -# -# This file is licensed under MIT license. - -# Big thanks to Durov team for not providing complete docs like this in the first place, -# and instead spreading the docs across dozens of pages without proper formatting or anything. - -# Override TL objects' (classes/methods) description, or their arguments' descriptions. -# Type is defined by the first 2 symbols in key ("o_" for class/method, "u_" for union) -objects: - o_mt_reqPq: - desc: > - Request for the first step of Authorization key derivation process. - This method is deprecated, use {@link tl.mtproto.RawReqPqMultiRequest} instead. - The difference is that when using this, only one server key is returned, while - `req_pq_multi` will return multiple. - arguments: - nonce: Randomly generated number (32-bit) that will be used in the later steps - o_mt_reqPqMulti: - desc: > - Request for the first step of Authorization key derivation process. - arguments: - nonce: Randomly generated number that will be used in the later steps - o_mt_resPQ: - desc: Response for the first step of Authorization key derivation process - arguments: - nonce: Client nonce that was generated earlier - serverNonce: Random number generated by the server, used in the later steps - pq: > - Big endian representation of a natural number, which is a product - of two different odd prime numbers. Normally, this value is `<= 2^63-1`. - Client is expected to decompose this product to `p` and `q`. - serverPublicKeyFingerprints: | - List of public RSA key fingerprints, which are computed as follows: - - First, the modulus and the exponent are extracted from the key - - Then, the following TL type is written: `rsa_public_key n:string e:string = RSAPublicKey` - - This is a bare type, meaning there's no 4-byte type number before it - - `n` is the modulus, `e` is the exponent, encoded as big-endian - - Finally, SHA1 is computed, and its last 8 bytes are taken and parsed as LE long - (i.e. `parse_int64_le(sha1(rsa_public_key).slice(-8))`) - - Client is expected to choose out of those keys any single one that it has - embedded in itself and return one of them in the following request. - o_mt_reqDHParams: - desc: Request for the second step of Authorization key derivation process. - arguments: - nonce: Client nonce that was generated earlier - serverNonce: Server nonce that was received earlier - p: Big endian encoded first factor. Note - `p < q` - q: Big endian encoded second factor. Node - `p < q` - publicKeyFingerprint: Fingerprint of the RSA key that the client has chosen - encryptedData: | - Encrypted payload, obtained as follows: - - Let `newNonce` be a random 32-bit number - - Let `data` be a serialization of either {@link tl.mtproto.RawP_q_inner_data} - or {@link tl.mtproto.RawP_q_inner_data_temp} with the generated `newNonce` - - `dataWithHash = concat(sha1(data), data, random_bytes(235 - data.length))` - - `encryptedData = dataWithHash ^ key.exponent % key.modulus`, where `key` is the - server public key which was chosen - o_mt_p_q_inner_data: - desc: Inner data used for {@link tl.mtproto.RawReqDHParamsRequest} - arguments: - pq: Original product of `p` and `q` sent by the server - p: Big endian encoded first factor. Note - `p < q` - q: Big endian encoded second factor. Node - `p < q` - nonce: Client nonce that was generated earlier - serverNonce: Server nonce that was received earlier - newNonce: New client nonce generated - o_mt_p_q_inner_data_temp: - desc: > - Inner data used for {@link tl.mtproto.RawReqDHParamsRequest}. - Unlike {@link tl.mtproto.RawP_q_inner_data}, this is used to generate - a temporary key. - arguments: - pq: Original product of `p` and `q` sent by the server - p: Big endian encoded first factor. Note - `p < q` - q: Big endian encoded second factor. Node - `p < q` - nonce: Client nonce that was generated earlier - serverNonce: Server nonce that was received earlier - newNonce: New client nonce generated - expiresIn: > - Maximum number of seconds that this key will be valid for. - The server might discard the key earlier. - o_mt_server_DH_params_fail: - desc: PQ decomposition was incorrect, try again. - arguments: - nonce: Client nonce that was generated earlier - serverNonce: Server nonce that was received earlier - o_mt_server_DH_params_ok: - desc: PQ decomposition was correct, server-side variables for DH are returned - arguments: - nonce: Client nonce that was generated earlier - serverNonce: Server nonce that was received earlier - encryptedAnswer: | - Encrypted DH parameters, obtained as follows: - - Let `answer` be a serialization of {@link tl.mtproto.RawServer_DH_inner_data} - - `hash1 = sha1(concat(newNonce, serverNonce))` - - `hash2 = sha1(concat(serverNonce, newNonce))` - - `hash3 = sha1(concat(newNonce, newNonce))` - - `key = concat(hash1, hash2.slice(0, 12))` - - `iv = concat(hash2.slice(12, 20), hash3, newNonce.slice(0, 4))` - - `encryptedAnswer = aes256_ige_encrypt(answer, key, iv) - o_mt_server_DH_inner_data: - desc: > - Inner data that is returned in {@link tl.mtproto.RawServer_DH_params_ok}, - containing server-side variables for Diffie-Hellman exchange - arguments: - nonce: Client nonce that was generated earlier - serverNonce: Server nonce that was received earlier - g: '`g` number (generator) used for Diffie-Hellman' - dhPrime: '`p` prime number (modulus) used for Diffie-Hellman' - gA: '`gA` number (`gA = g ^ A % p`, where `A` is server secret) used for Diffie-Hellman' - serverTime: Server UNIX timestamp (in seconds) - o_mt_setClientDHParams: - desc: Request containing encrypted client-side variables for Diffie-Hellman exchange - arguments: - nonce: Client nonce that was generated earlier - serverNonce: Server nonce that was received earlier - encryptedData: | - Encrypted DH parameters, obtained as follows: - - Let `B` be a random 2048-bit (256 bytes) integer - - `gB = g ^ B % dhPrime` (Diffie-Hellman) - - Let `data` be a serialization of {@link tl.mtproto.RawClient_DH_inner_data} - - `dataWithHash = concat(sha1(data), data, padding))`, where `padding` is - 0-15 random bytes, such that `dataWithHash.length` is divisible by 16 - - `encryptedData = aes256_ige_encrypt(dataWithHash, key, iv)`, where `key` and `iv` - are the same as ones used in {@link tl.mtproto.RawServer_DH_params_ok} - o_mt_client_DH_inner_data: - desc: > - Inner data of {@link tl.mtproto.RawSetClientDHParamsRequest}, - containing client-side variables for Diffie-Hellman exchange - arguments: - nonce: Client nonce that was generated earlier - serverNonce: Server nonce that was received earlier - retryId: > - Retry ID. When requesting for the first time, `0`, - then last `authKeyAuxHash` is used - gB: '`gB` number (`gB = g ^ B % p`, where `B` is client secret) used for Diffie-Hellman' - с_mt_dh_gen_ok: - desc: > - DH exchange was successful, and auth key is `gA ^ b % dhPrime`, - and `authKeyAuxHash = sha1(authKey).slice(0, 8)` - arguments: - nonce: Client nonce that was generated earlier - serverNonce: Server nonce that was received earlier - newNonceHash1: > - Nonce hash, computed as follows: `sha1(concat([newNonce, [0x01], authKeyAuxHash])` - o_mt_dh_gen_retry: - desc: > - DH exchange need to be retried. Current auth key is `gA ^ b % dhPrime`, - and `authKeyAuxHash = sha1(authKey).slice(0, 8)`, but they will change. - - When this is received, you are expected to send - another {@link tl.mtproto.RawSetClientDHParamsRequest}. - arguments: - nonce: Client nonce that was generated earlier - serverNonce: Server nonce that was received earlier - newNonceHash2: > - Nonce hash, computed as follows: `sha1(concat([newNonce, [0x02], authKeyAuxHash])` - o_mt_dh_gen_fail: - desc: DH exchange failed. You should restart the entire authorization flow. - arguments: - nonce: Client nonce that was generated earlier - serverNonce: Server nonce that was received earlier - newNonceHash3: > - Nonce hash, computed as follows: `sha1(concat([newNonce, [0x03], authKeyAuxHash])` - o_mt_rpc_result: - desc: Result of an RPC call - arguments: - reqMsgId: Requesting message ID - result: Result of the call - o_mt_rpc_error: - desc: > - RPC call resulted in an error, information about that error. - Error is still a result, and thus it is sent as a `result` - of {@link tl.mtproto.RawRpc_result} - arguments: - errorCode: Numeric error code (like 404) - errorMessage: String error code - с_mt_rpc_answer_unknown: - description: Nothing is known about this RPC call - o_mt_rpc_answer_dropped_running: - desc: > - Response was canceled while the RPC query was being processed - (where the RPC query itself was still fully processed); in this case, - the same rpc_answer_dropped_running is also returned in response to - the original query, and both of these responses require - an acknowledgment from the client. - o_mt_rpc_answer_dropped: - desc: The RPC response was removed from the server’s outgoing queue - arguments: - msgId: Message ID of the RPC response - seqNo: Seq NO of the RPC response - bytes: Length in bytes of the RPC response - o_mt_future_salt: - desc: Information about a single future server salt - arguments: - validSince: UNIX time in seconds from when this salt will be used - validUntil: UNIX time in seconds until which this salt will be used - salt: The server salt itself - o_mt_future_salts: - desc: Information about future server salts - arguments: - reqMsgId: Requesting message ID - now: Current server UNIX timestamp in seconds - salts: List of future salts - o_mt_pong: - desc: Response to a {@link tl.mtproto.RawPingRequest} - arguments: - msgId: Message ID that contained `mt_ping` - pingId: Ping ID that was sent in `mt_ping` - o_mt_ping: - desc: Ping a server to test connection - arguments: - pingId: Random ping ID - o_mt_pingDelayDisconnect: - desc: > - Works like ping. In addition, after this is received, - the server starts a timer which will close the current connection - `disconnectDelay` seconds later unless it receives a new message - of the same type which automatically resets all previous timers. - - If the client sends these pings once every 60 seconds, for example, - it may set `disconnect_delay` equal to 75 seconds. - arguments: - pingId: Random ping ID - disconnectDelay: Disconnect delay in seconds - o_mt_destroy_session: - desc: > - Used by the client to notify the server that it may forget the - data from a different session belonging to the same user - (i.e. with the same `authKeyId`). - - The result of this being applied to the current session is undefined. - arguments: - sessionId: Old session ID - o_mt_destroy_session_ok: - desc: Session was succesfully destroyed - arguments: - sessionId: Old session ID - o_mt_destroy_session_none: - desc: Session was not destroyed because it does not exist - arguments: - sessionId: Old session ID - o_mt_new_session_created: - desc: > - The server notifies the client that a new session (from the server’s standpoint) - had to be created to handle a client message. If, after this, the server receives - a message with an even smaller `msg_id` within the same session, a similar - notification will be generated for this `msg_id` as well. - No such notifications are generated for high msg_id values. - - This notification must be acknowledged by the client. It is necessary, for instance, - for the client to understand that there is, in fact, a “gap” in the stream of notifications - received from the server (the user may have failed to receive - notifications during some period of time). - - Client should also resend all the messages that were sent before `firstMsgId` - arguments: - firstMsgId: First message ID that is known by the server to be from this session. - uniqueId: Random number generated by the server every time a session is (re-)created - serverSalt: Current server salt - o_mt_msg_container: - desc: A simple container that carries several messages - arguments: - messages: List of messages in the container - o_mt_message: - desc: A message in the container - arguments: - msgId: Original message ID - seqno: Original message seq No - bytes: Length of the message - body: Contents of the message - o_mt_msg_copy: - desc: A copy of a message - arguments: - origMessage: Original message - o_mt_gzip_packed: - desc: > - An object, which was gzipped. - - At the present time, it is supported in the body - of an RPC response (i.e., as result in `rpc_result`) - and generated by the server for a limited number - of high-level queries. In addition, in the future - it may be used to transmit non-service messages - (i. e. RPC queries) from client to server. - arguments: - packedData: Gzipped contents of the message - o_mt_msgs_ack: - desc: > - Receipt of virtually all messages (with the exception of some purely - service ones as well as the plain-text messages used in the protocol - for creating an authorization key) must be acknowledged. - - This requires the use of the this service message (not requiring an acknowledgment itself) - - A server usually acknowledges the receipt of a message - from a client (normally, an RPC query) using an RPC response. - If a response is a long time coming, a server may first send a - receipt acknowledgment, and somewhat later, the RPC response itself. - - A client normally acknowledges the receipt of a message - from a server (usually, an RPC response) by adding an acknowledgment - to the next RPC query if it is not transmitted too late (if it is - generated, say, 60-120 seconds following the receipt of a message - from the server). However, if for a long period of time there is no - reason to send messages to the server or if there is a large number of - unacknowledged messages from the server (say, over 16), - the client transmits a stand-alone acknowledgment. - arguments: - msgIds: IDs of messages to be acknowledged. Maximum 8192 IDs. - o_mt_bad_msg_notification: - desc: Used by the server to notify client that the sent message was incorrect. - arguments: - badMsgId: ID of the "bad" message - badMsgSeqno: Seq No of the "bad" message - errorCode: | - Error code. Known values: - - `16`: Message ID was too small. Most likely, client time is wrong, it would be - worthwhile to synchronize it using notification's message ID and re-send the - original message with the "correct" message ID or wrap it in a container - with a new message ID if the original message had waited too - long on the client to be transmitted. - - `17`: Message ID was too big. Similar to the previous case, - the client time has to be synchronized, and the message re-sent - - `18`: Incorrect two lower order msg_id bits. The server expects client - message ID to be divisible by 4. - - `19`: Container message ID is the same as the message ID of the previous message - - `20`: Message is too old, and it cannot be verified whether the server - has received a message with this ID or not - - `32`: Message seq No was too small (the server has already received a message - with a higher or same seq No) - - `33`: Message seq No was too big (the server has already received a message - with a lower or same seq No) - - `34`: An even seq No expected (not content-relevant), but odd received - - `35`: An odd seq No expected (content-relevant), but even received - - `48`: Incorrect server salt was used. In practice, `mt_bad_server_salt` is used instead. - - `64`: Incorrect container - o_mt_bad_server_salt: - desc: Used by the server to notify client that the sent message was incorrect. - arguments: - badMsgId: ID of the "bad" message - badMsgSeqno: Seq No of the "bad" message - errorCode: Always `48` - newServerSalt: New server salt to be used - o_mt_msg_resend_req: - desc: | - Explicit Request to Re-Send Messages - - The remote party immediately responds by re-sending - the requested messages, normally using the same connection - that was used to transmit the query. If at least one message - with requested ID does not exist or has already been forgotten, - or has been sent by the requesting party (known from parity), - `MsgsStateInfo` is returned for all messages requested as if the - `MsgResendReq` query had been a `MsgsStateReq` query as well. - arguments: - msgIds: IDs of the messages to be resent (up to 8192 IDs) - o_mt_msg_resend_ans_req: - desc: | - Explicit Request to Re-Send Answers - - The remote party immediately responds by re-sending *answers* - to the requested messages, normally using the same connection - that was used to transmit the query. `MsgsStateInfo` is returned - for all messages requested as if the `MsgResendReq` query had - been a `MsgsStateReq` query as well. - arguments: - msgIds: IDs of the messages answers to which should be resent (up to 8192 IDs) - o_mt_msgs_state_req: - desc: > - Request for Message Status Information. If either party has - not received information on the status of its outgoing - messages for a while, it may explicitly request it from the other party - arguments: - msgIds: IDs of the messages state of which should be sent (up to 8192 IDs) - o_mt_msgs_state_info: - desc: Informational Message regarding Status of Messages - arguments: - reqMsgId: Requesting message ID - info: | - Byte array containing exactly one byte for each message ID: - - `1`: nothing is known about the message (ID is too small, the other party may have forgotten it) - - `2`: message was not received (ID falls within the range of stored identifiers; - however, the other party has certainly not received a message like that) - - `3`: message not received (ID is too big; however, the other - party has certainly not received it yet) - - `4`: message received (note that this response is also at the same time a receipt acknowledgment) - - `+8`: message already acknowledged - - `+16`: message not requiring acknowledgment - - `+32`: RPC query contained in message being processed or processing already complete - - `+64`: content-related response to message already generated - - `+128`: other party knows for a fact that message is already received - o_mt_msgs_all_info: - desc: > - Voluntary Communication of Status of Messages. Either party may - voluntarily inform the other party of the status of the messages - transmitted by the other party. - arguments: - msgIds: Message IDs that the other party is being informed about - info: Byte array in the same format as {@link tl.mtproto.RawMsgs_state_info} - o_mt_msg_detailed_info: - desc: | - Extended Voluntary Communication of Status of One Message. - - Normally used by the server to respond to the receipt of - a duplicate message ID, especially if a response to the - message has already been generated and the response is large. - If the response is small, the server may re-send the answer - itself instead. This message can also be used as a notification - instead of resending a large message. - arguments: - msgId: Original message ID that this message is informing about - answerMsgId: Message ID that was the response to that message - bytes: Size of the answer message - status: Always `0`, but this may change in the future - o_mt_msg_new_detailed_info: - desc: > - Similar to {@link tl.mtproto.RawMsg_detailed_info}, but - used to notify about messages that were created on the server - not in response to an RPC query (e.g. updates) and were transmitted - to the client some time ago, but not acknowledged - arguments: - answerMsgId: ID of the message that was sent by the server - bytes: Size of the answer message - status: Always `0`, but this may change in the future - o_mt_rpcDropAnswer: - desc: > - Cancellation of an RPC query. - In certain situations, the client does not want to receive a - response to an already transmitted RPC query, for example because - the response turns out to be long and the client has decided to do - without it because of insufficient link capacity. Simply interrupting - the connection will not have any effect because the server would re-send - the missing response at the first opportunity. Therefore, the client needs - a way to cancel receipt of the RPC response message, actually acknowledging - its receipt prior to it being in fact received, which will settle the server - down and prevent it from re-sending the response. However, the client does - not know the RPC response's message ID prior to receiving the response; - the only thing it knows is the requesting message ID, i.e. the message ID - of the relevant RPC query. Therefore, this special query is used - arguments: - reqMsgId: ID of a message containing an RPC query to be cancelled - - -# Override arguments wherever they are, based on the filters -arguments: - - name: gigagroup - filters: - - type: "true" - desc: Is this a broadcast group? - -# Replace any description, based on regex. Used to fix typos. -regex: - - regex: \bchanel\b - repl: channel - - regex: \bgeoposition\b - repl: geo position - - regex: \bgeogroup\b - repl: geo group - - regex: \bunixdate\b|Unix timestamp - repl: UNIX timestamp in seconds - - regex: \bstickerset\b - repl: stickerset - - regex: \bonly "foursquare" needs\b - repl: only "foursquare" and "gplaces" need - # gender-neutral pronouns - - regex: \bhis\b - repl: their - - regex: \bhim\b - repl: them diff --git a/packages/tl/mtp-schema.json b/packages/tl/mtp-schema.json new file mode 100644 index 00000000..7fec7cda --- /dev/null +++ b/packages/tl/mtp-schema.json @@ -0,0 +1 @@ +[{"kind":"class","name":"mt_resPQ","id":85337187,"type":"ResPQ","arguments":[{"name":"nonce","type":"int128"},{"name":"server_nonce","type":"int128"},{"name":"pq","type":"bytes"},{"name":"server_public_key_fingerprints","type":"Vector"}]},{"kind":"class","name":"mt_p_q_inner_data_dc","id":2851430293,"type":"P_Q_inner_data","arguments":[{"name":"pq","type":"bytes"},{"name":"p","type":"bytes"},{"name":"q","type":"bytes"},{"name":"nonce","type":"int128"},{"name":"server_nonce","type":"int128"},{"name":"new_nonce","type":"int256"},{"name":"dc","type":"int"}]},{"kind":"class","name":"mt_p_q_inner_data_temp_dc","id":1459478408,"type":"P_Q_inner_data","arguments":[{"name":"pq","type":"bytes"},{"name":"p","type":"bytes"},{"name":"q","type":"bytes"},{"name":"nonce","type":"int128"},{"name":"server_nonce","type":"int128"},{"name":"new_nonce","type":"int256"},{"name":"dc","type":"int"},{"name":"expires_in","type":"int"}]},{"kind":"class","name":"mt_server_DH_params_ok","id":3504867164,"type":"Server_DH_Params","arguments":[{"name":"nonce","type":"int128"},{"name":"server_nonce","type":"int128"},{"name":"encrypted_answer","type":"bytes"}]},{"kind":"class","name":"mt_server_DH_inner_data","id":3045658042,"type":"Server_DH_inner_data","arguments":[{"name":"nonce","type":"int128"},{"name":"server_nonce","type":"int128"},{"name":"g","type":"int"},{"name":"dh_prime","type":"bytes"},{"name":"g_a","type":"bytes"},{"name":"server_time","type":"int"}]},{"kind":"class","name":"mt_client_DH_inner_data","id":1715713620,"type":"Client_DH_Inner_Data","arguments":[{"name":"nonce","type":"int128"},{"name":"server_nonce","type":"int128"},{"name":"retry_id","type":"long"},{"name":"g_b","type":"bytes"}]},{"kind":"class","name":"mt_dh_gen_ok","id":1003222836,"type":"Set_client_DH_params_answer","arguments":[{"name":"nonce","type":"int128"},{"name":"server_nonce","type":"int128"},{"name":"new_nonce_hash1","type":"int128"}]},{"kind":"class","name":"mt_dh_gen_retry","id":1188831161,"type":"Set_client_DH_params_answer","arguments":[{"name":"nonce","type":"int128"},{"name":"server_nonce","type":"int128"},{"name":"new_nonce_hash2","type":"int128"}]},{"kind":"class","name":"mt_dh_gen_fail","id":2795351554,"type":"Set_client_DH_params_answer","arguments":[{"name":"nonce","type":"int128"},{"name":"server_nonce","type":"int128"},{"name":"new_nonce_hash3","type":"int128"}]},{"kind":"class","name":"mt_bind_auth_key_inner","id":1973679973,"type":"BindAuthKeyInner","arguments":[{"name":"nonce","type":"long"},{"name":"temp_auth_key_id","type":"long"},{"name":"perm_auth_key_id","type":"long"},{"name":"temp_session_id","type":"long"},{"name":"expires_at","type":"int"}]},{"kind":"class","name":"mt_rpc_result","id":4082920705,"type":"RpcResult","arguments":[{"name":"req_msg_id","type":"long"},{"name":"result","type":"any"}]},{"kind":"class","name":"mt_rpc_error","id":558156313,"type":"RpcError","arguments":[{"name":"error_code","type":"int"},{"name":"error_message","type":"string"}]},{"kind":"class","name":"mt_rpc_answer_unknown","id":1579864942,"type":"RpcDropAnswer","arguments":[]},{"kind":"class","name":"mt_rpc_answer_dropped_running","id":3447252358,"type":"RpcDropAnswer","arguments":[]},{"kind":"class","name":"mt_rpc_answer_dropped","id":2755319991,"type":"RpcDropAnswer","arguments":[{"name":"msg_id","type":"long"},{"name":"seq_no","type":"int"},{"name":"bytes","type":"int"}]},{"kind":"class","name":"mt_future_salt","id":155834844,"type":"FutureSalt","arguments":[{"name":"valid_since","type":"int"},{"name":"valid_until","type":"int"},{"name":"salt","type":"long"}]},{"kind":"class","name":"mt_future_salts","id":2924480661,"type":"FutureSalts","arguments":[{"name":"req_msg_id","type":"long"},{"name":"now","type":"int"},{"name":"salts","type":"Vector"}]},{"kind":"class","name":"mt_pong","id":880243653,"type":"Pong","arguments":[{"name":"msg_id","type":"long"},{"name":"ping_id","type":"long"}]},{"kind":"class","name":"mt_destroy_session_ok","id":3793765884,"type":"DestroySessionRes","arguments":[{"name":"session_id","type":"long"}]},{"kind":"class","name":"mt_destroy_session_none","id":1658015945,"type":"DestroySessionRes","arguments":[{"name":"session_id","type":"long"}]},{"kind":"class","name":"mt_new_session_created","id":2663516424,"type":"NewSession","arguments":[{"name":"first_msg_id","type":"long"},{"name":"unique_id","type":"long"},{"name":"server_salt","type":"long"}]},{"kind":"class","name":"mt_msgs_ack","id":1658238041,"type":"MsgsAck","arguments":[{"name":"msg_ids","type":"Vector"}]},{"kind":"class","name":"mt_bad_msg_notification","id":2817521681,"type":"BadMsgNotification","arguments":[{"name":"bad_msg_id","type":"long"},{"name":"bad_msg_seqno","type":"int"},{"name":"error_code","type":"int"}]},{"kind":"class","name":"mt_bad_server_salt","id":3987424379,"type":"BadMsgNotification","arguments":[{"name":"bad_msg_id","type":"long"},{"name":"bad_msg_seqno","type":"int"},{"name":"error_code","type":"int"},{"name":"new_server_salt","type":"long"}]},{"kind":"class","name":"mt_msg_resend_req","id":2105940488,"type":"MsgResendReq","arguments":[{"name":"msg_ids","type":"Vector"}]},{"kind":"class","name":"mt_msgs_state_req","id":3664378706,"type":"MsgsStateReq","arguments":[{"name":"msg_ids","type":"Vector"}]},{"kind":"class","name":"mt_msgs_state_info","id":81704317,"type":"MsgsStateInfo","arguments":[{"name":"req_msg_id","type":"long"},{"name":"info","type":"bytes"}]},{"kind":"class","name":"mt_msgs_all_info","id":2361446705,"type":"MsgsAllInfo","arguments":[{"name":"msg_ids","type":"Vector"},{"name":"info","type":"bytes"}]},{"kind":"class","name":"mt_msg_detailed_info","id":661470918,"type":"MsgDetailedInfo","arguments":[{"name":"msg_id","type":"long"},{"name":"answer_msg_id","type":"long"},{"name":"bytes","type":"int"},{"name":"status","type":"int"}]},{"kind":"class","name":"mt_msg_new_detailed_info","id":2157819615,"type":"MsgDetailedInfo","arguments":[{"name":"answer_msg_id","type":"long"},{"name":"bytes","type":"int"},{"name":"status","type":"int"}]},{"kind":"class","name":"mt_destroy_auth_key_ok","id":4133544404,"type":"DestroyAuthKeyRes","arguments":[]},{"kind":"class","name":"mt_destroy_auth_key_none","id":178201177,"type":"DestroyAuthKeyRes","arguments":[]},{"kind":"class","name":"mt_destroy_auth_key_fail","id":3926956819,"type":"DestroyAuthKeyRes","arguments":[]},{"kind":"class","name":"mt_req_pq_multi","id":3195965169,"type":"ResPQ","arguments":[{"name":"nonce","type":"int128"}]},{"kind":"class","name":"mt_req_DH_params","id":3608339646,"type":"Server_DH_Params","arguments":[{"name":"nonce","type":"int128"},{"name":"server_nonce","type":"int128"},{"name":"p","type":"bytes"},{"name":"q","type":"bytes"},{"name":"public_key_fingerprint","type":"long"},{"name":"encrypted_data","type":"bytes"}]},{"kind":"class","name":"mt_set_client_DH_params","id":4110704415,"type":"Set_client_DH_params_answer","arguments":[{"name":"nonce","type":"int128"},{"name":"server_nonce","type":"int128"},{"name":"encrypted_data","type":"bytes"}]},{"kind":"class","name":"mt_rpc_drop_answer","id":1491380032,"type":"RpcDropAnswer","arguments":[{"name":"req_msg_id","type":"long"}]},{"kind":"class","name":"mt_get_future_salts","id":3105996036,"type":"FutureSalts","arguments":[{"name":"num","type":"int"}]},{"kind":"class","name":"mt_ping","id":2059302892,"type":"Pong","arguments":[{"name":"ping_id","type":"long"}]},{"kind":"class","name":"mt_ping_delay_disconnect","id":4081220492,"type":"Pong","arguments":[{"name":"ping_id","type":"long"},{"name":"disconnect_delay","type":"int"}]},{"kind":"class","name":"mt_destroy_session","id":3880853798,"type":"DestroySessionRes","arguments":[{"name":"session_id","type":"long"}]},{"kind":"class","name":"mt_http_wait","id":2459514271,"type":"HttpWait","arguments":[{"name":"max_delay","type":"int"},{"name":"wait_after","type":"int"},{"name":"max_wait","type":"int"}]},{"kind":"class","name":"mt_destroy_auth_key","id":3510849888,"type":"DestroyAuthKeyRes","arguments":[]}] \ No newline at end of file diff --git a/packages/tl/package.json b/packages/tl/package.json index 99b73a8d..0c93547c 100644 --- a/packages/tl/package.json +++ b/packages/tl/package.json @@ -1,25 +1,30 @@ { "name": "@mtcute/tl", - "version": "1.131.0", + "version": "134.0.0", "description": "TL schema used for MTCute", "main": "index.js", "author": "Alisa Sireneva ", "license": "LGPL-3.0", "scripts": { "test": "tsc --noEmit tests/types.ts", - "generate-schema": "node scripts/generate-schema.js", - "generate-code": "node scripts/generate-types.js && node scripts/generate-binary-reader.js && node scripts/generate-binary-writer.js && node scripts/generate-errors.js", - "generate-all": "npm run generate-schema && npm run generate-code", + "fetch-mtp": "node -r ts-node/register scripts/fetch-mtp.ts", + "fetch-api": "node -r ts-node/register scripts/fetch-api.ts", + "fetch-errors": "node -r ts-node/register scripts/fetch-errors.ts", + "docs-cli": "node -r ts-node/register scripts/documentation.ts", + "gen-code": "node -r ts-node/register scripts/gen-code.ts", + "gen-rsa": "node -r ts-node/register scripts/gen-rsa-keys.ts", + "fetch-and-gen": "yarn fetch-api && yarn gen-code", "docs": "npx typedoc --options typedoc.json" }, "dependencies": { - "big-integer": "^1.6.48" + "@types/long": "^4.0.1", + "long": "^4.0.0" }, "devDependencies": { + "@mtcute/tl-utils": "^1.0.0", + "@mtcute/core": "^1.0.0", "cheerio": "^1.0.0-rc.5", - "eager-async-pool": "^1.0.0", "csv-parser": "^3.0.0", - "js-yaml": "^4.0.0", - "crc-32": "^1.2.0" + "js-yaml": "^4.0.0" } } diff --git a/packages/tl/raw-errors.json b/packages/tl/raw-errors.json index d380c75b..09b8c848 100644 --- a/packages/tl/raw-errors.json +++ b/packages/tl/raw-errors.json @@ -1 +1 @@ -[{"codes":"400","name":"BAD_REQUEST","description":"The query contains errors. In the event that a request was created using a form and contains user generated data, the user should be notified that the data must be corrected before the query is repeated","base":true},{"codes":"401","name":"UNAUTHORIZED","description":"There was an unauthorized attempt to use functionality available only to authorized users.","base":true},{"codes":"403","name":"FORBIDDEN","description":"Privacy violation. For example, an attempt to write a message to someone who has blacklisted the current user.","base":true},{"codes":"404","name":"NOT_FOUND","description":"An attempt to invoke a non-existent object, such as a method.","base":true},{"codes":"420","name":"FLOOD","description":"The maximum allowed number of attempts to invoke the given methodwith the given input parameters has been exceeded. For example, in anattempt to request a large number of text messages (SMS) for the samephone number.","base":true},{"codes":"303","name":"SEE_OTHER","description":"The request must be repeated, but directed to a different data center","base":true},{"codes":"406","name":"NOT_ACCEPTABLE","description":"Similar to 400 BAD_REQUEST, but the app should not display any error messages to user in UI as a result of this response. The error message will be delivered via updateServiceNotification instead.","base":true},{"codes":"500","name":"INTERNAL","description":"An internal server error occurred while a request was being processed; for example, there was a disruption while accessing a database or file storage.","base":true},{"name":"2FA_CONFIRM_WAIT_X","codes":"420","description":"The account is 2FA protected so it will be deleted in a week. Otherwise it can be reset in {seconds}"},{"name":"ABOUT_TOO_LONG","codes":"400","description":"The provided bio is too long"},{"name":"ACCESS_TOKEN_EXPIRED","codes":"400","description":"Bot token expired"},{"name":"ACCESS_TOKEN_INVALID","codes":"400","description":"The provided token is not valid"},{"name":"ACTIVE_USER_REQUIRED","codes":"401","description":"The method is only available to already activated users"},{"name":"ADMINS_TOO_MUCH","codes":"400","description":"Too many admins"},{"name":"ADMIN_RANK_EMOJI_NOT_ALLOWED","codes":"400","description":"Emoji are not allowed in admin titles or ranks"},{"name":"ADMIN_RANK_INVALID","codes":"400","description":"The given admin title or rank was invalid (possibly larger than 16 characters)"},{"name":"ALBUM_PHOTOS_TOO_MANY","codes":"400","description":"Too many photos were included in the album"},{"name":"API_ID_INVALID","codes":"400","description":"The api_id/api_hash combination is invalid"},{"name":"API_ID_PUBLISHED_FLOOD","codes":"400","description":"This API id was published somewhere, you can't use it now"},{"name":"ARTICLE_TITLE_EMPTY","codes":"400","description":"The title of the article is empty"},{"name":"AUDIO_TITLE_EMPTY","codes":"400","description":"The title attribute of the audio must be non-empty"},{"name":"AUDIO_CONTENT_URL_EMPTY","codes":"400","description":""},{"name":"AUTH_BYTES_INVALID","codes":"400","description":"The provided authorization is invalid"},{"name":"AUTH_KEY_DUPLICATED","codes":"406","description":"The authorization key (session file) was used under two different IP addresses simultaneously, and can no longer be used. Use the same session exclusively, or use different sessions"},{"name":"AUTH_KEY_INVALID","codes":"401","description":"The key is invalid"},{"name":"AUTH_KEY_PERM_EMPTY","codes":"401","description":"The method is unavailable for temporary authorization key, not bound to permanent"},{"name":"AUTH_KEY_UNREGISTERED","codes":"401","description":"The key is not registered in the system"},{"name":"AUTH_RESTART","codes":"500","description":"Restart the authorization process"},{"name":"AUTH_TOKEN_ALREADY_ACCEPTED","codes":"400","description":"The authorization token was already used"},{"name":"AUTH_TOKEN_EXPIRED","codes":"400","description":"The provided authorization token has expired and the updated QR-code must be re-scanned"},{"name":"AUTH_TOKEN_INVALID","codes":"400","description":"An invalid authorization token was provided"},{"name":"AUTOARCHIVE_NOT_AVAILABLE","codes":"400","description":"You cannot use this feature yet"},{"name":"BANK_CARD_NUMBER_INVALID","codes":"400","description":"Incorrect credit card number"},{"name":"BASE_PORT_LOC_INVALID","codes":"400","description":"Base port location invalid"},{"name":"BANNED_RIGHTS_INVALID","codes":"400","description":"You cannot use that set of permissions in this request, i.e. restricting view_messages as a default"},{"name":"BOTS_TOO_MUCH","codes":"400","description":"There are too many bots in this chat/channel"},{"name":"BOT_ONESIDE_NOT_AVAIL","codes":"400","description":""},{"name":"BOT_CHANNELS_NA","codes":"400","description":"Bots can't edit admin privileges"},{"name":"BOT_COMMAND_DESCRIPTION_INVALID","codes":"400","description":"The command description was empty, too long or had invalid characters used"},{"name":"BOT_COMMAND_INVALID","codes":"400","description":""},{"name":"BOT_DOMAIN_INVALID","codes":"400","description":"The domain used for the auth button does not match the one configured in @BotFather"},{"name":"BOT_GAMES_DISABLED","codes":"400","description":"Bot games cannot be used in this type of chat"},{"name":"BOT_GROUPS_BLOCKED","codes":"400","description":"This bot can't be added to groups"},{"name":"BOT_INLINE_DISABLED","codes":"400","description":"This bot can't be used in inline mode"},{"name":"BOT_INVALID","codes":"400","description":"This is not a valid bot"},{"name":"BOT_METHOD_INVALID","codes":"400","description":"The API access for bot users is restricted. The method you tried to invoke cannot be executed as a bot"},{"name":"BOT_MISSING","codes":"400","description":"This method can only be run by a bot"},{"name":"BOT_PAYMENTS_DISABLED","codes":"400","description":"This method can only be run by a bot"},{"name":"BOT_POLLS_DISABLED","codes":"400","description":"You cannot create polls under a bot account"},{"name":"BOT_RESPONSE_TIMEOUT","codes":"400","description":"The bot did not answer to the callback query in time"},{"name":"BROADCAST_CALLS_DISABLED","codes":"400","description":""},{"name":"BROADCAST_FORBIDDEN","codes":"403","description":"The request cannot be used in broadcast channels"},{"name":"BROADCAST_ID_INVALID","codes":"400","description":"The channel is invalid"},{"name":"BROADCAST_PUBLIC_VOTERS_FORBIDDEN","codes":"400","description":"You cannot broadcast polls where the voters are public"},{"name":"BROADCAST_REQUIRED","codes":"400","description":"The request can only be used with a broadcast channel"},{"name":"BUTTON_DATA_INVALID","codes":"400","description":"The provided button data is invalid"},{"name":"BUTTON_TYPE_INVALID","codes":"400","description":"The type of one of the buttons you provided is invalid"},{"name":"BUTTON_URL_INVALID","codes":"400","description":"Button URL invalid"},{"name":"CALL_ALREADY_ACCEPTED","codes":"400","description":"The call was already accepted"},{"name":"CALL_ALREADY_DECLINED","codes":"400","description":"The call was already declined"},{"name":"CALL_OCCUPY_FAILED","codes":"500","description":"The call failed because the user is already making another call"},{"name":"CALL_PEER_INVALID","codes":"400","description":"The provided call peer object is invalid"},{"name":"CALL_PROTOCOL_FLAGS_INVALID","codes":"400","description":"Call protocol flags invalid"},{"name":"CDN_METHOD_INVALID","codes":"400","description":"This method cannot be invoked on a CDN server. Refer to https://core.telegram.org/cdn#schema for available methods"},{"name":"CHANNELS_ADMIN_PUBLIC_TOO_MUCH","codes":"400","description":"You're admin of too many public channels, make some channels private to change the username of this channel"},{"name":"CHANNELS_TOO_MUCH","codes":"400","description":"You have joined too many channels/supergroups"},{"name":"CHANNEL_BANNED","codes":"400","description":"The channel is banned"},{"name":"CHANNEL_INVALID","codes":"400","description":"Invalid channel object. Make sure to pass the right types, for instance making sure that the request is designed for channels or otherwise look for a different one more suited"},{"name":"CHANNEL_PRIVATE","codes":"400","description":"The channel specified is private and you lack permission to access it. Another reason may be that you were banned from it"},{"name":"CHANNEL_PUBLIC_GROUP_NA","codes":"403","description":"channel/supergroup not available"},{"name":"CHAT_ABOUT_NOT_MODIFIED","codes":"400","description":"About text has not changed"},{"name":"CHAT_ABOUT_TOO_LONG","codes":"400","description":"Chat about too long"},{"name":"CHAT_ADMIN_INVITE_REQUIRED","codes":"403","description":"You do not have the rights to do this"},{"name":"CHAT_ADMIN_REQUIRED","codes":"400","description":"Chat admin privileges are required to do that in the specified chat (for example, to send a message in a channel which is not yours), or invalid permissions used for the channel or group"},{"name":"CHAT_FORBIDDEN","codes":"403","description":"You cannot write in this chat"},{"name":"CHAT_ID_EMPTY","codes":"400","description":"The provided chat ID is empty"},{"name":"CHAT_ID_INVALID","codes":"400","description":"Invalid object ID for a chat. Make sure to pass the right types, for instance making sure that the request is designed for chats (not channels/megagroups) or otherwise look for a different one more suited\\nAn example working with a megagroup and AddChatUserRequest, it will fail because megagroups are channels. Use InviteToChannelRequest instead"},{"name":"CHAT_INVALID","codes":"400","description":"The chat is invalid for this request"},{"name":"CHAT_LINK_EXISTS","codes":"400","description":"The chat is linked to a channel and cannot be used in that request"},{"name":"CHAT_NOT_MODIFIED","codes":"400","description":"The chat or channel wasn't modified (title, invites, username, admins, etc. are the same)"},{"name":"CHAT_RESTRICTED","codes":"400","description":"The chat is restricted and cannot be used in that request"},{"name":"CHAT_SEND_GIFS_FORBIDDEN","codes":"403","description":"You can't send gifs in this chat"},{"name":"CHAT_SEND_INLINE_FORBIDDEN","codes":"400","description":"You cannot send inline results in this chat"},{"name":"CHAT_SEND_MEDIA_FORBIDDEN","codes":"403","description":"You can't send media in this chat"},{"name":"CHAT_SEND_STICKERS_FORBIDDEN","codes":"403","description":"You can't send stickers in this chat"},{"name":"CHAT_TITLE_EMPTY","codes":"400","description":"No chat title provided"},{"name":"CHAT_WRITE_FORBIDDEN","codes":"403","description":"You can't write in this chat"},{"name":"CHP_CALL_FAIL","codes":"500","description":"The statistics cannot be retrieved at this time"},{"name":"CODE_EMPTY","codes":"400","description":"The provided code is empty"},{"name":"CODE_HASH_INVALID","codes":"400","description":"Code hash invalid"},{"name":"CODE_INVALID","codes":"400","description":"Code invalid (i.e. from email)"},{"name":"CONNECTION_API_ID_INVALID","codes":"400","description":"The provided API id is invalid"},{"name":"CONNECTION_DEVICE_MODEL_EMPTY","codes":"400","description":"Device model empty"},{"name":"CONNECTION_LANG_PACK_INVALID","codes":"400","description":"The specified language pack is not valid. This is meant to be used by official applications only so far, leave it empty"},{"name":"CONNECTION_LAYER_INVALID","codes":"400","description":"The very first request must always be InvokeWithLayerRequest"},{"name":"CONNECTION_NOT_INITED","codes":"400","description":"Connection not initialized"},{"name":"CONNECTION_SYSTEM_EMPTY","codes":"400","description":"Connection system empty"},{"name":"CONNECTION_SYSTEM_LANG_CODE_EMPTY","codes":"400","description":"The system language string was empty during connection"},{"name":"CONTACT_ID_INVALID","codes":"400","description":"The provided contact ID is invalid"},{"name":"CONTACT_NAME_EMPTY","codes":"400","description":"The provided contact name cannot be empty"},{"name":"CURRENCY_TOTAL_AMOUNT_INVALID","codes":"400","description":""},{"name":"DATA_INVALID","codes":"400","description":"Encrypted data invalid"},{"name":"DATA_JSON_INVALID","codes":"400","description":"The provided JSON data is invalid"},{"name":"DATE_EMPTY","codes":"400","description":"Date empty"},{"name":"DC_ID_INVALID","codes":"400","description":"This occurs when an authorization is tried to be exported for the same data center one is currently connected to"},{"name":"DH_G_A_INVALID","codes":"400","description":"g_a invalid"},{"name":"DOCUMENT_INVALID","codes":"400","description":"The document file was invalid and can't be used in inline mode"},{"name":"EMAIL_HASH_EXPIRED","codes":"400","description":"The email hash expired and cannot be used to verify it"},{"name":"EMAIL_INVALID","codes":"400","description":"The given email is invalid"},{"name":"EMAIL_UNCONFIRMED_X","codes":"400","description":"Email unconfirmed, the length of the code must be {code_length}"},{"name":"EMOTICON_EMPTY","codes":"400","description":"The emoticon field cannot be empty"},{"name":"EMOTICON_INVALID","codes":"400","description":"The specified emoticon cannot be used or was not a emoticon"},{"name":"EMOTICON_STICKERPACK_MISSING","codes":"400","description":"The emoticon sticker pack you are trying to get is missing"},{"name":"ENCRYPTED_MESSAGE_INVALID","codes":"400","description":"Encrypted message invalid"},{"name":"ENCRYPTION_ALREADY_ACCEPTED","codes":"400","description":"Secret chat already accepted"},{"name":"ENCRYPTION_ALREADY_DECLINED","codes":"400","description":"The secret chat was already declined"},{"name":"ENCRYPTION_DECLINED","codes":"400","description":"The secret chat was declined"},{"name":"ENCRYPTION_ID_INVALID","codes":"400","description":"The provided secret chat ID is invalid"},{"name":"ENCRYPTION_OCCUPY_FAILED","codes":"500","description":"TDLib developer claimed it is not an error while accepting secret chats and 500 is used instead of 420"},{"name":"ENTITIES_TOO_LONG","codes":"400","description":"It is no longer possible to send such long data inside entity tags (for example inline text URLs)"},{"name":"ENTITY_MENTION_USER_INVALID","codes":"400","description":"You can't use this entity"},{"name":"ERROR_TEXT_EMPTY","codes":"400","description":"The provided error message is empty"},{"name":"EXPIRE_FORBIDDEN","codes":"400","description":""},{"name":"EXPORT_CARD_INVALID","codes":"400","description":"Provided card is invalid"},{"name":"EXTERNAL_URL_INVALID","codes":"400","description":"External URL invalid"},{"name":"FIELD_NAME_EMPTY","codes":"400","description":"The field with the name FIELD_NAME is missing"},{"name":"FIELD_NAME_INVALID","codes":"400","description":"The field with the name FIELD_NAME is invalid"},{"name":"FILEREF_UPGRADE_NEEDED","codes":"406","description":"The file reference needs to be refreshed before being used again"},{"name":"FILE_CONTENT_TYPE_INVALID","codes":"400","description":""},{"name":"FILE_ID_INVALID","codes":"400","description":"The provided file id is invalid. Make sure all parameters are present, have the correct type and are not empty (ID, access hash, file reference, thumb size ...)"},{"name":"FILE_MIGRATE_X","codes":"303","description":"The file to be accessed is currently stored in DC {new_dc}"},{"name":"FILE_PARTS_INVALID","codes":"400","description":"The number of file parts is invalid"},{"name":"FILE_PART_0_MISSING","codes":"400","description":"File part 0 missing"},{"name":"FILE_PART_EMPTY","codes":"400","description":"The provided file part is empty"},{"name":"FILE_PART_INVALID","codes":"400","description":"The file part number is invalid"},{"name":"FILE_PART_LENGTH_INVALID","codes":"400","description":"The length of a file part is invalid"},{"name":"FILE_PART_SIZE_CHANGED","codes":"400","description":"The file part size (chunk size) cannot change during upload"},{"name":"FILE_PART_SIZE_INVALID","codes":"400","description":"The provided file part size is invalid"},{"name":"FILE_PART_X_MISSING","codes":"400","description":"Part {which} of the file is missing from storage"},{"name":"FILE_REFERENCE_EMPTY","codes":"400","description":"The file reference must exist to access the media and it cannot be empty"},{"name":"FILE_REFERENCE_EXPIRED","codes":"400","description":"The file reference has expired and is no longer valid or it belongs to self-destructing media and cannot be resent"},{"name":"FILE_REFERENCE_INVALID","codes":"400","description":"The file reference is invalid or you can't do that operation on such message"},{"name":"FIRSTNAME_INVALID","codes":"400","description":"The first name is invalid"},{"name":"FLOOD_TEST_PHONE_WAIT_X","codes":"420","description":"A wait of {seconds} seconds is required in the test servers"},{"name":"FLOOD_WAIT_X","codes":"420","description":"A wait of {seconds} seconds is required"},{"name":"FOLDER_ID_EMPTY","codes":"400","description":"The folder you tried to delete was already empty"},{"name":"FOLDER_ID_INVALID","codes":"400","description":"The folder you tried to use was not valid"},{"name":"FRESH_CHANGE_ADMINS_FORBIDDEN","codes":"400","description":"Recently logged-in users cannot add or change admins"},{"name":"FRESH_CHANGE_PHONE_FORBIDDEN","codes":"406","description":"Recently logged-in users cannot use this request"},{"name":"FRESH_RESET_AUTHORISATION_FORBIDDEN","codes":"406","description":"The current session is too new and cannot be used to reset other authorisations yet"},{"name":"FROM_PEER_INVALID","codes":"400","description":"The given from_user peer cannot be used for the parameter"},{"name":"GAME_BOT_INVALID","codes":"400","description":"You cannot send that game with the current bot"},{"name":"GIF_CONTENT_TYPE_INVALID","codes":"400","description":""},{"name":"GIF_ID_INVALID","codes":"400","description":"The provided GIF ID is invalid"},{"name":"GRAPH_INVALID_RELOAD","codes":"400","description":""},{"name":"GRAPH_OUTDATED_RELOAD","codes":"400","description":"Data can't be used for the channel statistics, graphs outdated"},{"name":"GROUPCALL_ALREADY_DISCARDED","codes":"400","description":""},{"name":"GROUPCALL_FORBIDDEN","codes":"403","description":""},{"name":"GROUPCALL_JOIN_MISSING","codes":"400","description":""},{"name":"GROUPCALL_SSRC_DUPLICATE_MUCH","codes":"400","description":""},{"name":"GROUPCALL_NOT_MODIFIED","codes":"400","description":""},{"name":"GROUPED_MEDIA_INVALID","codes":"400","description":"Invalid grouped media"},{"name":"GROUP_CALL_INVALID","codes":"400","description":"Group call invalid"},{"name":"HASH_INVALID","codes":"400","description":"The provided hash is invalid"},{"name":"HISTORY_GET_FAILED","codes":"500","description":"Fetching of history failed"},{"name":"IMAGE_PROCESS_FAILED","codes":"400","description":"Failure while processing image"},{"name":"IMPORT_FILE_INVALID","codes":"400","description":"The file is too large to be imported"},{"name":"IMPORT_FORMAT_UNRECOGNIZED","codes":"400","description":"Unknown import format"},{"name":"IMPORT_ID_INVALID","codes":"400","description":""},{"name":"INLINE_BOT_REQUIRED","codes":"403","description":"The action must be performed through an inline bot callback"},{"name":"INLINE_RESULT_EXPIRED","codes":"400","description":"The inline query expired"},{"name":"INPUT_CONSTRUCTOR_INVALID","codes":"400","description":"The provided constructor is invalid"},{"name":"INPUT_FETCH_ERROR","codes":"400","description":"An error occurred while deserializing TL parameters"},{"name":"INPUT_FETCH_FAIL","codes":"400","description":"Failed deserializing TL payload"},{"name":"INPUT_FILTER_INVALID","codes":"400","description":"The search query filter is invalid"},{"name":"INPUT_LAYER_INVALID","codes":"400","description":"The provided layer is invalid"},{"name":"INPUT_METHOD_INVALID","codes":"400","description":"The invoked method does not exist anymore or has never existed"},{"name":"INPUT_REQUEST_TOO_LONG","codes":"400","description":"The input request was too long. This may be a bug in the library as it can occur when serializing more bytes than it should (like appending the vector constructor code at the end of a message)"},{"name":"INPUT_USER_DEACTIVATED","codes":"400","description":"The specified user was deleted"},{"name":"INTERDC_X_CALL_ERROR","codes":"500","description":"An error occurred while communicating with DC {dc}"},{"name":"INTERDC_X_CALL_RICH_ERROR","codes":"500","description":"A rich error occurred while communicating with DC {dc}"},{"name":"INVITE_HASH_EMPTY","codes":"400","description":"The invite hash is empty"},{"name":"INVITE_HASH_EXPIRED","codes":"400","description":"The chat the user tried to join has expired and is not valid anymore"},{"name":"INVITE_HASH_INVALID","codes":"400","description":"The invite hash is invalid"},{"name":"LANG_CODE_INVALID","codes":"400","description":""},{"name":"LANG_PACK_INVALID","codes":"400","description":"The provided language pack is invalid"},{"name":"LASTNAME_INVALID","codes":"400","description":"The last name is invalid"},{"name":"LIMIT_INVALID","codes":"400","description":"An invalid limit was provided. See https://core.telegram.org/api/files#downloading-files"},{"name":"LINK_NOT_MODIFIED","codes":"400","description":"The channel is already linked to this group"},{"name":"LOCATION_INVALID","codes":"400","description":"The location given for a file was invalid. See https://core.telegram.org/api/files#downloading-files"},{"name":"MAX_ID_INVALID","codes":"400","description":"The provided max ID is invalid"},{"name":"MAX_QTS_INVALID","codes":"400","description":"The provided QTS were invalid"},{"name":"MD5_CHECKSUM_INVALID","codes":"400","description":"The MD5 check-sums do not match"},{"name":"MEDIA_CAPTION_TOO_LONG","codes":"400","description":"The caption is too long"},{"name":"MEDIA_EMPTY","codes":"400","description":"The provided media object is invalid or the current account may not be able to send it (such as games as users)"},{"name":"MEDIA_GROUPED_INVALID","codes":"400","description":""},{"name":"MEDIA_INVALID","codes":"400","description":"Media invalid"},{"name":"MEDIA_NEW_INVALID","codes":"400","description":"The new media to edit the message with is invalid (such as stickers or voice notes)"},{"name":"MEDIA_PREV_INVALID","codes":"400","description":"The old media cannot be edited with anything else (such as stickers or voice notes)"},{"name":"MEDIA_TTL_INVALID","codes":"400","description":""},{"name":"MEGAGROUP_ID_INVALID","codes":"400","description":"The group is invalid"},{"name":"MEGAGROUP_PREHISTORY_HIDDEN","codes":"400","description":"You can't set this discussion group because it's history is hidden"},{"name":"MEGAGROUP_REQUIRED","codes":"400","description":"The request can only be used with a megagroup channel"},{"name":"MEMBER_NO_LOCATION","codes":"500","description":"An internal failure occurred while fetching user info (couldn't find location)"},{"name":"MEMBER_OCCUPY_PRIMARY_LOC_FAILED","codes":"500","description":"Occupation of primary member location failed"},{"name":"MESSAGE_AUTHOR_REQUIRED","codes":"403","description":"Message author required"},{"name":"MESSAGE_DELETE_FORBIDDEN","codes":"403","description":"You can't delete one of the messages you tried to delete, most likely because it is a service message."},{"name":"MESSAGE_EDIT_TIME_EXPIRED","codes":"400","description":"You can't edit this message anymore, too much time has passed since its creation."},{"name":"MESSAGE_EMPTY","codes":"400","description":"Empty or invalid UTF-8 message was sent"},{"name":"MESSAGE_IDS_EMPTY","codes":"400","description":"No message ids were provided"},{"name":"MESSAGE_ID_INVALID","codes":"400","description":"The specified message ID is invalid or you can't do that operation on such message"},{"name":"MESSAGE_NOT_MODIFIED","codes":"400","description":"Content of the message was not modified"},{"name":"MESSAGE_POLL_CLOSED","codes":"400","description":"The poll was closed and can no longer be voted on"},{"name":"MESSAGE_TOO_LONG","codes":"400","description":"Message was too long. Current maximum length is 4096 UTF-8 characters"},{"name":"METHOD_INVALID","codes":"400","description":"The API method is invalid and cannot be used"},{"name":"MSGID_DECREASE_RETRY","codes":"500","description":"The request should be retried with a lower message ID"},{"name":"MSG_ID_INVALID","codes":"400","description":"The message ID used in the peer was invalid"},{"name":"MSG_WAIT_FAILED","codes":"400","description":"A waiting call returned an error"},{"name":"MT_SEND_QUEUE_TOO_LONG","codes":"500","description":""},{"name":"MULTI_MEDIA_TOO_LONG","codes":"400","description":"Too many media files were included in the same album"},{"name":"NEED_CHAT_INVALID","codes":"500","description":"The provided chat is invalid"},{"name":"NEED_MEMBER_INVALID","codes":"500","description":"The provided member is invalid or does not exist (for example a thumb size)"},{"name":"NETWORK_MIGRATE_X","codes":"303","description":"The source IP address is associated with DC {new_dc}"},{"name":"NEW_SALT_INVALID","codes":"400","description":"The new salt is invalid"},{"name":"NEW_SETTINGS_INVALID","codes":"400","description":"The new settings are invalid"},{"name":"NEXT_OFFSET_INVALID","codes":"400","description":"The value for next_offset is invalid. Check that it has normal characters and is not too long"},{"name":"OFFSET_INVALID","codes":"400","description":"The given offset was invalid, it must be divisible by 1KB. See https://core.telegram.org/api/files#downloading-files"},{"name":"OFFSET_PEER_ID_INVALID","codes":"400","description":"The provided offset peer is invalid"},{"name":"OPTIONS_TOO_MUCH","codes":"400","description":"You defined too many options for the poll"},{"name":"OPTION_INVALID","codes":"400","description":"The option specified is invalid and does not exist in the target poll"},{"name":"PACK_SHORT_NAME_INVALID","codes":"400","description":"Invalid sticker pack name. It must begin with a letter, can't contain consecutive underscores and must end in \"_by_\"."},{"name":"PACK_SHORT_NAME_OCCUPIED","codes":"400","description":"A stickerpack with this name already exists"},{"name":"PARTICIPANTS_TOO_FEW","codes":"400","description":"Not enough participants"},{"name":"PARTICIPANT_CALL_FAILED","codes":"500","description":"Failure while making call"},{"name":"PARTICIPANT_JOIN_MISSING","codes":"403","description":""},{"name":"PARTICIPANT_VERSION_OUTDATED","codes":"400","description":"The other participant does not use an up to date telegram client with support for calls"},{"name":"PASSWORD_EMPTY","codes":"400","description":"The provided password is empty"},{"name":"PASSWORD_HASH_INVALID","codes":"400","description":"The password (and thus its hash value) you entered is invalid"},{"name":"PASSWORD_MISSING","codes":"400","description":"The account must have 2-factor authentication enabled (a password) before this method can be used"},{"name":"PASSWORD_RECOVERY_EXPIRED","codes":"400","description":""},{"name":"PASSWORD_REQUIRED","codes":"400","description":"The account must have 2-factor authentication enabled (a password) before this method can be used"},{"name":"PASSWORD_TOO_FRESH_X","codes":"400","description":"The password was added too recently and {seconds} seconds must pass before using the method"},{"name":"PAYMENT_PROVIDER_INVALID","codes":"400","description":"The payment provider was not recognised or its token was invalid"},{"name":"PEER_FLOOD","codes":"400","description":"Too many requests"},{"name":"PEER_ID_INVALID","codes":"400","description":"An invalid Peer was used. Make sure to pass the right peer type and that the value is valid (for instance, bots cannot start conversations)"},{"name":"PEER_ID_NOT_SUPPORTED","codes":"400","description":"The provided peer ID is not supported"},{"name":"PERSISTENT_TIMESTAMP_EMPTY","codes":"400","description":"Persistent timestamp empty"},{"name":"PERSISTENT_TIMESTAMP_INVALID","codes":"400","description":"Persistent timestamp invalid"},{"name":"PERSISTENT_TIMESTAMP_OUTDATED","codes":"500","description":"Persistent timestamp outdated"},{"name":"PHONE_CODE_EMPTY","codes":"400","description":"The phone code is missing"},{"name":"PHONE_CODE_EXPIRED","codes":"400","description":"The confirmation code has expired"},{"name":"PHONE_CODE_HASH_EMPTY","codes":"400","description":"The phone code hash is missing"},{"name":"PHONE_CODE_INVALID","codes":"400","description":"The phone code entered was invalid"},{"name":"PHONE_MIGRATE_X","codes":"303","description":"The phone number a user is trying to use for authorization is associated with DC {new_dc}"},{"name":"PHONE_NUMBER_APP_SIGNUP_FORBIDDEN","codes":"400","description":"You can't sign up using this app"},{"name":"PHONE_NUMBER_BANNED","codes":"400","description":"The used phone number has been banned from Telegram and cannot be used anymore. Maybe check https://www.telegram.org/faq_spam"},{"name":"PHONE_NUMBER_FLOOD","codes":"400","description":"You asked for the code too many times."},{"name":"PHONE_NUMBER_INVALID","codes":"400 406","description":"The phone number is invalid"},{"name":"PHONE_NUMBER_OCCUPIED","codes":"400","description":"The phone number is already in use"},{"name":"PHONE_NUMBER_UNOCCUPIED","codes":"400","description":"The phone number is not yet being used"},{"name":"PHONE_PASSWORD_FLOOD","codes":"406","description":"You have tried logging in too many times"},{"name":"PHONE_PASSWORD_PROTECTED","codes":"400","description":"This phone is password protected"},{"name":"PHOTO_CONTENT_TYPE_INVALID","codes":"400","description":""},{"name":"PHOTO_CONTENT_URL_EMPTY","codes":"400","description":"The content from the URL used as a photo appears to be empty or has caused another HTTP error"},{"name":"PHOTO_CROP_SIZE_SMALL","codes":"400","description":"Photo is too small"},{"name":"PHOTO_EXT_INVALID","codes":"400","description":"The extension of the photo is invalid"},{"name":"PHOTO_ID_INVALID","codes":"400","description":"Photo id is invalid"},{"name":"PHOTO_INVALID","codes":"400","description":"Photo invalid"},{"name":"PHOTO_INVALID_DIMENSIONS","codes":"400","description":"The photo dimensions are invalid (hint: `pip install pillow` for `send_file` to resize images)"},{"name":"PHOTO_SAVE_FILE_INVALID","codes":"400","description":"The photo you tried to send cannot be saved by Telegram. A reason may be that it exceeds 10MB. Try resizing it locally"},{"name":"PHOTO_THUMB_URL_EMPTY","codes":"400","description":"The URL used as a thumbnail appears to be empty or has caused another HTTP error"},{"name":"PIN_RESTRICTED","codes":"400","description":"You can't pin messages in private chats with other people"},{"name":"POLL_ANSWERS_INVALID","codes":"400","description":"The poll did not have enough answers or had too many"},{"name":"POLL_OPTION_DUPLICATE","codes":"400","description":"A duplicate option was sent in the same poll"},{"name":"POLL_OPTION_INVALID","codes":"400","description":"A poll option used invalid data (the data may be too long)"},{"name":"POLL_QUESTION_INVALID","codes":"400","description":"The poll question was either empty or too long"},{"name":"POLL_UNSUPPORTED","codes":"400","description":"This layer does not support polls in the issued method"},{"name":"PREVIOUS_CHAT_IMPORT_ACTIVE_WAIT_XMIN","codes":"406","description":"Similar to a flood wait, must wait {minutes} minutes"},{"name":"PRIVACY_KEY_INVALID","codes":"400","description":"The privacy key is invalid"},{"name":"PRIVACY_TOO_LONG","codes":"400","description":"Cannot add that many entities in a single request"},{"name":"PRIVACY_VALUE_INVALID","codes":"400","description":"The privacy value is invalid"},{"name":"PTS_CHANGE_EMPTY","codes":"500","description":"No PTS change"},{"name":"QUERY_ID_EMPTY","codes":"400","description":"The query ID is empty"},{"name":"QUERY_ID_INVALID","codes":"400","description":"The query ID is invalid"},{"name":"QUERY_TOO_SHORT","codes":"400","description":"The query string is too short"},{"name":"QUIZ_CORRECT_ANSWERS_EMPTY","codes":"400","description":"A quiz must specify one correct answer"},{"name":"QUIZ_CORRECT_ANSWERS_TOO_MUCH","codes":"400","description":"There can only be one correct answer"},{"name":"QUIZ_CORRECT_ANSWER_INVALID","codes":"400","description":"The correct answer is not an existing answer"},{"name":"QUIZ_MULTIPLE_INVALID","codes":"400","description":"A poll cannot be both multiple choice and quiz"},{"name":"RANDOM_ID_DUPLICATE","codes":"500","description":"You provided a random ID that was already used"},{"name":"RANDOM_ID_INVALID","codes":"400","description":"A provided random ID is invalid"},{"name":"RANDOM_LENGTH_INVALID","codes":"400","description":"Random length invalid"},{"name":"RANGES_INVALID","codes":"400","description":"Invalid range provided"},{"name":"REACTION_EMPTY","codes":"400","description":"No reaction provided"},{"name":"REACTION_INVALID","codes":"400","description":"Invalid reaction provided (only emoji are allowed)"},{"name":"REFLECTOR_NOT_AVAILABLE","codes":"400","description":"Invalid call reflector server"},{"name":"REG_ID_GENERATE_FAILED","codes":"500","description":"Failure while generating registration ID"},{"name":"REPLY_MARKUP_GAME_EMPTY","codes":"400","description":"The provided reply markup for the game is empty"},{"name":"REPLY_MARKUP_INVALID","codes":"400","description":"The provided reply markup is invalid"},{"name":"REPLY_MARKUP_TOO_LONG","codes":"400","description":"The data embedded in the reply markup buttons was too much"},{"name":"RESET_REQUEST_MISSING","codes":"400","description":""},{"name":"RESULTS_TOO_MUCH","codes":"400","description":"You sent too many results, see https://core.telegram.org/bots/api#answerinlinequery for the current limit"},{"name":"RESULT_ID_DUPLICATE","codes":"400","description":"Duplicated IDs on the sent results. Make sure to use unique IDs"},{"name":"RESULT_ID_INVALID","codes":"400","description":"The given result cannot be used to send the selection to the bot"},{"name":"RESULT_TYPE_INVALID","codes":"400","description":"Result type invalid"},{"name":"RIGHT_FORBIDDEN","codes":"403","description":"Either your admin rights do not allow you to do this or you passed the wrong rights combination (some rights only apply to channels and vice versa)"},{"name":"RPC_CALL_FAIL","codes":"500","description":"Telegram is having internal issues, please try again later."},{"name":"RPC_MCGET_FAIL","codes":"500","description":"Telegram is having internal issues, please try again later."},{"name":"RSA_DECRYPT_FAILED","codes":"400","description":"Internal RSA decryption failed"},{"name":"SCHEDULE_BOT_NOT_ALLOWED","codes":"400","description":"Bots are not allowed to schedule messages"},{"name":"SCHEDULE_DATE_INVALID","codes":"400","description":""},{"name":"SCHEDULE_DATE_TOO_LATE","codes":"400","description":"The date you tried to schedule is too far in the future (last known limit of 1 year and a few hours)"},{"name":"SCHEDULE_STATUS_PRIVATE","codes":"400","description":"You cannot schedule a message until the person comes online if their privacy does not show this information"},{"name":"SCHEDULE_TOO_MUCH","codes":"400","description":"You cannot schedule more messages in this chat (last known limit of 100 per chat)"},{"name":"SEARCH_QUERY_EMPTY","codes":"400","description":"The search query is empty"},{"name":"SECONDS_INVALID","codes":"400","description":"Slow mode only supports certain values (e.g. 0, 10s, 30s, 1m, 5m, 15m and 1h)"},{"name":"SEND_MESSAGE_MEDIA_INVALID","codes":"400","description":"The message media was invalid or not specified"},{"name":"SEND_MESSAGE_TYPE_INVALID","codes":"400","description":"The message type is invalid"},{"name":"SENSITIVE_CHANGE_FORBIDDEN","codes":"403","description":"Your sensitive content settings cannot be changed at this time"},{"name":"SESSION_EXPIRED","codes":"401","description":"The authorization has expired"},{"name":"SESSION_PASSWORD_NEEDED","codes":"401","description":"Two-steps verification is enabled and a password is required"},{"name":"SESSION_REVOKED","codes":"401","description":"The authorization has been invalidated, because of the user terminating all sessions"},{"name":"SESSION_TOO_FRESH_X","codes":"400","description":"The session logged in too recently and {seconds} seconds must pass before calling the method"},{"name":"SHA256_HASH_INVALID","codes":"400","description":"The provided SHA256 hash is invalid"},{"name":"SHORTNAME_OCCUPY_FAILED","codes":"400","description":"An error occurred when trying to register the short-name used for the sticker pack. Try a different name"},{"name":"SHORT_NAME_INVALID","codes":"400","description":""},{"name":"SHORT_NAME_OCCUPIED","codes":"400","description":""},{"name":"SLOWMODE_WAIT_X","codes":"420","description":"A wait of {seconds} seconds is required before sending another message in this chat"},{"name":"SRP_ID_INVALID","codes":"400","description":""},{"name":"START_PARAM_EMPTY","codes":"400","description":"The start parameter is empty"},{"name":"START_PARAM_INVALID","codes":"400","description":"Start parameter invalid"},{"name":"STATS_MIGRATE_X","codes":"303","description":"The channel statistics must be fetched from DC {dc}"},{"name":"STICKERSET_INVALID","codes":"400","description":"The provided sticker set is invalid"},{"name":"STICKERSET_OWNER_ANONYMOUS","codes":"406","description":"This sticker set can't be used as the group's official stickers because it was created by one of its anonymous admins"},{"name":"STICKERS_EMPTY","codes":"400","description":"No sticker provided"},{"name":"STICKER_DOCUMENT_INVALID","codes":"400","description":"The sticker file was invalid (this file has failed Telegram internal checks, make sure to use the correct format and comply with https://core.telegram.org/animated_stickers)"},{"name":"STICKER_EMOJI_INVALID","codes":"400","description":"Sticker emoji invalid"},{"name":"STICKER_FILE_INVALID","codes":"400","description":"Sticker file invalid"},{"name":"STICKER_ID_INVALID","codes":"400","description":"The provided sticker ID is invalid"},{"name":"STICKER_INVALID","codes":"400","description":"The provided sticker is invalid"},{"name":"STICKER_PNG_DIMENSIONS","codes":"400","description":"Sticker png dimensions invalid"},{"name":"STICKER_PNG_NOPNG","codes":"400","description":"Stickers must be a png file but the used image was not a png"},{"name":"STICKER_TGS_NODOC","codes":"400","description":""},{"name":"STICKER_TGS_NOTGS","codes":"400","description":"Stickers must be a tgs file but the used file was not a tgs"},{"name":"STICKER_THUMB_PNG_NOPNG","codes":"400","description":"Stickerset thumb must be a png file but the used file was not png"},{"name":"STICKER_THUMB_TGS_NOTGS","codes":"400","description":"Stickerset thumb must be a tgs file but the used file was not tgs"},{"name":"STORAGE_CHECK_FAILED","codes":"500","description":"Server storage check failed"},{"name":"STORE_INVALID_SCALAR_TYPE","codes":"500","description":""},{"name":"TAKEOUT_INIT_DELAY_X","codes":"420","description":"A wait of {seconds} seconds is required before being able to initiate the takeout"},{"name":"TAKEOUT_INVALID","codes":"400","description":"The takeout session has been invalidated by another data export session"},{"name":"TAKEOUT_REQUIRED","codes":"400","description":"You must initialize a takeout request first"},{"name":"TEMP_AUTH_KEY_EMPTY","codes":"400","description":"No temporary auth key provided"},{"name":"TIMEOUT","codes":"500","description":"A timeout occurred while fetching data from the worker"},{"name":"TITLE_INVALID","codes":"400","description":""},{"name":"THEME_INVALID","codes":"400","description":"Theme invalid"},{"name":"THEME_MIME_INVALID","codes":"400","description":"You cannot create this theme, the mime-type is invalid"},{"name":"TMP_PASSWORD_DISABLED","codes":"400","description":"The temporary password is disabled"},{"name":"TMP_PASSWORD_INVALID","codes":"400","description":"Password auth needs to be regenerated"},{"name":"TOKEN_INVALID","codes":"400","description":"The provided token is invalid"},{"name":"TTL_DAYS_INVALID","codes":"400","description":"The provided TTL is invalid"},{"name":"TTL_PERIOD_INVALID","codes":"400","description":"The provided TTL Period is invalid"},{"name":"TYPES_EMPTY","codes":"400","description":"The types field is empty"},{"name":"TYPE_CONSTRUCTOR_INVALID","codes":"400","description":"The type constructor is invalid"},{"name":"UNKNOWN_METHOD","codes":"500","description":"The method you tried to call cannot be called on non-CDN DCs"},{"name":"UNTIL_DATE_INVALID","codes":"400","description":"That date cannot be specified in this request (try using None)"},{"name":"URL_INVALID","codes":"400","description":"The URL used was invalid (e.g. when answering a callback with a URL that's not t.me/yourbot or your game's URL)"},{"name":"USER_VOLUME_INVALID","codes":"400","description":""},{"name":"USERNAME_INVALID","codes":"400","description":"Nobody is using this username, or the username is unacceptable. If the latter, it must match r\"[a-zA-Z][\\w\\d]{3,30}[a-zA-Z\\d]\""},{"name":"USERNAME_NOT_MODIFIED","codes":"400","description":"The username is not different from the current username"},{"name":"USERNAME_NOT_OCCUPIED","codes":"400","description":"The username is not in use by anyone else yet"},{"name":"USERNAME_OCCUPIED","codes":"400","description":"The username is already taken"},{"name":"USERS_TOO_FEW","codes":"400","description":"Not enough users (to create a chat, for example)"},{"name":"USERS_TOO_MUCH","codes":"400","description":"The maximum number of users has been exceeded (to create a chat, for example)"},{"name":"USER_ADMIN_INVALID","codes":"400","description":"Either you're not an admin or you tried to ban an admin that you didn't promote"},{"name":"USER_ALREADY_PARTICIPANT","codes":"400","description":"The authenticated user is already a participant of the chat"},{"name":"USER_BANNED_IN_CHANNEL","codes":"400","description":"You're banned from sending messages in supergroups/channels"},{"name":"USER_BLOCKED","codes":"400","description":"User blocked"},{"name":"USER_BOT","codes":"400","description":"Bots can only be admins in channels."},{"name":"USER_BOT_INVALID","codes":"400 403","description":"This method can only be called by a bot"},{"name":"USER_BOT_REQUIRED","codes":"400","description":"This method can only be called by a bot"},{"name":"USER_CHANNELS_TOO_MUCH","codes":"403","description":"One of the users you tried to add is already in too many channels/supergroups"},{"name":"USER_CREATOR","codes":"400","description":"You can't leave this channel, because you're its creator"},{"name":"USER_DEACTIVATED","codes":"401","description":"The user has been deleted/deactivated"},{"name":"USER_DEACTIVATED_BAN","codes":"401","description":"The user has been deleted/deactivated"},{"name":"USER_ID_INVALID","codes":"400","description":"Invalid object ID for a user. Make sure to pass the right types, for instance making sure that the request is designed for users or otherwise look for a different one more suited"},{"name":"USER_INVALID","codes":"400","description":"The given user was invalid"},{"name":"USER_IS_BLOCKED","codes":"400 403","description":"User is blocked"},{"name":"USER_IS_BOT","codes":"400","description":"Bots can't send messages to other bots"},{"name":"USER_KICKED","codes":"400","description":"This user was kicked from this supergroup/channel"},{"name":"USER_MIGRATE_X","codes":"303","description":"The user whose identity is being used to execute queries is associated with DC {new_dc}"},{"name":"USER_NOT_MUTUAL_CONTACT","codes":"400 403","description":"The provided user is not a mutual contact"},{"name":"USER_NOT_PARTICIPANT","codes":"400","description":"The target user is not a member of the specified megagroup or channel"},{"name":"USER_PRIVACY_RESTRICTED","codes":"403","description":"The user's privacy settings do not allow you to do this"},{"name":"USER_RESTRICTED","codes":"403","description":"You're spamreported, you can't create channels or chats."},{"name":"USERPIC_UPLOAD_REQUIRED","codes":"400","description":"You must have a profile picture before using this method"},{"name":"VIDEO_CONTENT_TYPE_INVALID","codes":"400","description":"The video content type is not supported with the given parameters (i.e. supports_streaming)"},{"name":"VIDEO_FILE_INVALID","codes":"400","description":"The given video cannot be used"},{"name":"VIDEO_TITLE_EMPTY","codes":"400","description":""},{"name":"WALLPAPER_FILE_INVALID","codes":"400","description":"The given file cannot be used as a wallpaper"},{"name":"WALLPAPER_INVALID","codes":"400","description":"The input wallpaper was not valid"},{"name":"WALLPAPER_MIME_INVALID","codes":"400","description":""},{"name":"WC_CONVERT_URL_INVALID","codes":"400","description":"WC convert URL invalid"},{"name":"WEBDOCUMENT_MIME_INVALID","codes":"400","description":""},{"name":"WEBDOCUMENT_URL_INVALID","codes":"400","description":"The given URL cannot be used"},{"name":"WEBPAGE_CURL_FAILED","codes":"400","description":"Failure while fetching the webpage with cURL"},{"name":"WEBPAGE_MEDIA_EMPTY","codes":"400","description":"Webpage media empty"},{"name":"WORKER_BUSY_TOO_LONG_RETRY","codes":"500","description":"Telegram workers are too busy to respond immediately"},{"name":"YOU_BLOCKED_USER","codes":"400","description":"You blocked this user"},{"virtual":true,"name":"RPC_TIMEOUT","codes":"408","description":"Timeout of {ms} ms exceeded"},{"virtual":true,"name":"MESSAGE_NOT_FOUND","codes":"404","description":"Message was not found"}] \ No newline at end of file +[{"codes":"400","name":"BAD_REQUEST","description":"The query contains errors. In the event that a request was created using a form and contains user generated data, the user should be notified that the data must be corrected before the query is repeated","base":true},{"codes":"401","name":"UNAUTHORIZED","description":"There was an unauthorized attempt to use functionality available only to authorized users.","base":true},{"codes":"403","name":"FORBIDDEN","description":"Privacy violation. For example, an attempt to write a message to someone who has blacklisted the current user.","base":true},{"codes":"404","name":"NOT_FOUND","description":"An attempt to invoke a non-existent object, such as a method.","base":true},{"codes":"420","name":"FLOOD","description":"The maximum allowed number of attempts to invoke the given methodwith the given input parameters has been exceeded. For example, in anattempt to request a large number of text messages (SMS) for the samephone number.","base":true},{"codes":"303","name":"SEE_OTHER","description":"The request must be repeated, but directed to a different data center","base":true},{"codes":"406","name":"NOT_ACCEPTABLE","description":"Similar to 400 BAD_REQUEST, but the app should not display any error messages to user in UI as a result of this response. The error message will be delivered via updateServiceNotification instead.","base":true},{"codes":"500","name":"INTERNAL","description":"An internal server error occurred while a request was being processed; for example, there was a disruption while accessing a database or file storage.","base":true},{"name":"2FA_CONFIRM_WAIT_X","codes":"420","description":"The account is 2FA protected so it will be deleted in a week. Otherwise it can be reset in {seconds}"},{"name":"ABOUT_TOO_LONG","codes":"400","description":"The provided bio is too long"},{"name":"ACCESS_TOKEN_EXPIRED","codes":"400","description":"Bot token expired"},{"name":"ACCESS_TOKEN_INVALID","codes":"400","description":"The provided token is not valid"},{"name":"ACTIVE_USER_REQUIRED","codes":"401","description":"The method is only available to already activated users"},{"name":"ADMINS_TOO_MUCH","codes":"400","description":"Too many admins"},{"name":"ADMIN_RANK_EMOJI_NOT_ALLOWED","codes":"400","description":"Emoji are not allowed in admin titles or ranks"},{"name":"ADMIN_RANK_INVALID","codes":"400","description":"The given admin title or rank was invalid (possibly larger than 16 characters)"},{"name":"ALBUM_PHOTOS_TOO_MANY","codes":"400","description":"Too many photos were included in the album"},{"name":"API_ID_INVALID","codes":"400","description":"The api_id/api_hash combination is invalid"},{"name":"API_ID_PUBLISHED_FLOOD","codes":"400","description":"This API id was published somewhere, you can't use it now"},{"name":"ARTICLE_TITLE_EMPTY","codes":"400","description":"The title of the article is empty"},{"name":"AUDIO_TITLE_EMPTY","codes":"400","description":"The title attribute of the audio must be non-empty"},{"name":"AUDIO_CONTENT_URL_EMPTY","codes":"400","description":""},{"name":"AUTH_BYTES_INVALID","codes":"400","description":"The provided authorization is invalid"},{"name":"AUTH_KEY_DUPLICATED","codes":"406","description":"The authorization key (session file) was used under two different IP addresses simultaneously, and can no longer be used. Use the same session exclusively, or use different sessions"},{"name":"AUTH_KEY_INVALID","codes":"401","description":"The key is invalid"},{"name":"AUTH_KEY_PERM_EMPTY","codes":"401","description":"The method is unavailable for temporary authorization key, not bound to permanent"},{"name":"AUTH_KEY_UNREGISTERED","codes":"401","description":"The key is not registered in the system"},{"name":"AUTH_RESTART","codes":"500","description":"Restart the authorization process"},{"name":"AUTH_TOKEN_ALREADY_ACCEPTED","codes":"400","description":"The authorization token was already used"},{"name":"AUTH_TOKEN_EXPIRED","codes":"400","description":"The provided authorization token has expired and the updated QR-code must be re-scanned"},{"name":"AUTH_TOKEN_INVALID","codes":"400","description":"An invalid authorization token was provided"},{"name":"AUTOARCHIVE_NOT_AVAILABLE","codes":"400","description":"You cannot use this feature yet"},{"name":"BANK_CARD_NUMBER_INVALID","codes":"400","description":"Incorrect credit card number"},{"name":"BASE_PORT_LOC_INVALID","codes":"400","description":"Base port location invalid"},{"name":"BANNED_RIGHTS_INVALID","codes":"400","description":"You cannot use that set of permissions in this request, i.e. restricting view_messages as a default"},{"name":"BOTS_TOO_MUCH","codes":"400","description":"There are too many bots in this chat/channel"},{"name":"BOT_ONESIDE_NOT_AVAIL","codes":"400","description":""},{"name":"BOT_CHANNELS_NA","codes":"400","description":"Bots can't edit admin privileges"},{"name":"BOT_COMMAND_DESCRIPTION_INVALID","codes":"400","description":"The command description was empty, too long or had invalid characters used"},{"name":"BOT_COMMAND_INVALID","codes":"400","description":""},{"name":"BOT_DOMAIN_INVALID","codes":"400","description":"The domain used for the auth button does not match the one configured in @BotFather"},{"name":"BOT_GAMES_DISABLED","codes":"400","description":"Bot games cannot be used in this type of chat"},{"name":"BOT_GROUPS_BLOCKED","codes":"400","description":"This bot can't be added to groups"},{"name":"BOT_INLINE_DISABLED","codes":"400","description":"This bot can't be used in inline mode"},{"name":"BOT_INVALID","codes":"400","description":"This is not a valid bot"},{"name":"BOT_METHOD_INVALID","codes":"400","description":"The API access for bot users is restricted. The method you tried to invoke cannot be executed as a bot"},{"name":"BOT_MISSING","codes":"400","description":"This method can only be run by a bot"},{"name":"BOT_PAYMENTS_DISABLED","codes":"400","description":"This method can only be run by a bot"},{"name":"BOT_POLLS_DISABLED","codes":"400","description":"You cannot create polls under a bot account"},{"name":"BOT_RESPONSE_TIMEOUT","codes":"400","description":"The bot did not answer to the callback query in time"},{"name":"BROADCAST_CALLS_DISABLED","codes":"400","description":""},{"name":"BROADCAST_FORBIDDEN","codes":"403","description":"The request cannot be used in broadcast channels"},{"name":"BROADCAST_ID_INVALID","codes":"400","description":"The channel is invalid"},{"name":"BROADCAST_PUBLIC_VOTERS_FORBIDDEN","codes":"400","description":"You cannot broadcast polls where the voters are public"},{"name":"BROADCAST_REQUIRED","codes":"400","description":"The request can only be used with a broadcast channel"},{"name":"BUTTON_DATA_INVALID","codes":"400","description":"The provided button data is invalid"},{"name":"BUTTON_TYPE_INVALID","codes":"400","description":"The type of one of the buttons you provided is invalid"},{"name":"BUTTON_URL_INVALID","codes":"400","description":"Button URL invalid"},{"name":"CALL_ALREADY_ACCEPTED","codes":"400","description":"The call was already accepted"},{"name":"CALL_ALREADY_DECLINED","codes":"400","description":"The call was already declined"},{"name":"CALL_OCCUPY_FAILED","codes":"500","description":"The call failed because the user is already making another call"},{"name":"CALL_PEER_INVALID","codes":"400","description":"The provided call peer object is invalid"},{"name":"CALL_PROTOCOL_FLAGS_INVALID","codes":"400","description":"Call protocol flags invalid"},{"name":"CDN_METHOD_INVALID","codes":"400","description":"This method cannot be invoked on a CDN server. Refer to https://core.telegram.org/cdn#schema for available methods"},{"name":"CHANNELS_ADMIN_PUBLIC_TOO_MUCH","codes":"400","description":"You're admin of too many public channels, make some channels private to change the username of this channel"},{"name":"CHANNELS_TOO_MUCH","codes":"400","description":"You have joined too many channels/supergroups"},{"name":"CHANNEL_BANNED","codes":"400","description":"The channel is banned"},{"name":"CHANNEL_INVALID","codes":"400","description":"Invalid channel object. Make sure to pass the right types, for instance making sure that the request is designed for channels or otherwise look for a different one more suited"},{"name":"CHANNEL_PRIVATE","codes":"400","description":"The channel specified is private and you lack permission to access it. Another reason may be that you were banned from it"},{"name":"CHANNEL_PUBLIC_GROUP_NA","codes":"403","description":"channel/supergroup not available"},{"name":"CHAT_ABOUT_NOT_MODIFIED","codes":"400","description":"About text has not changed"},{"name":"CHAT_ABOUT_TOO_LONG","codes":"400","description":"Chat about too long"},{"name":"CHAT_ADMIN_INVITE_REQUIRED","codes":"403","description":"You do not have the rights to do this"},{"name":"CHAT_ADMIN_REQUIRED","codes":"400","description":"Chat admin privileges are required to do that in the specified chat (for example, to send a message in a channel which is not yours), or invalid permissions used for the channel or group"},{"name":"CHAT_FORBIDDEN","codes":"403","description":"You cannot write in this chat"},{"name":"CHAT_ID_EMPTY","codes":"400","description":"The provided chat ID is empty"},{"name":"CHAT_ID_INVALID","codes":"400","description":"Invalid object ID for a chat. Make sure to pass the right types, for instance making sure that the request is designed for chats (not channels/megagroups) or otherwise look for a different one more suited\\nAn example working with a megagroup and AddChatUserRequest, it will fail because megagroups are channels. Use InviteToChannelRequest instead"},{"name":"CHAT_INVALID","codes":"400","description":"The chat is invalid for this request"},{"name":"CHAT_LINK_EXISTS","codes":"400","description":"The chat is linked to a channel and cannot be used in that request"},{"name":"CHAT_NOT_MODIFIED","codes":"400","description":"The chat or channel wasn't modified (title, invites, username, admins, etc. are the same)"},{"name":"CHAT_RESTRICTED","codes":"400","description":"The chat is restricted and cannot be used in that request"},{"name":"CHAT_SEND_GIFS_FORBIDDEN","codes":"403","description":"You can't send gifs in this chat"},{"name":"CHAT_SEND_INLINE_FORBIDDEN","codes":"400","description":"You cannot send inline results in this chat"},{"name":"CHAT_SEND_MEDIA_FORBIDDEN","codes":"403","description":"You can't send media in this chat"},{"name":"CHAT_SEND_STICKERS_FORBIDDEN","codes":"403","description":"You can't send stickers in this chat"},{"name":"CHAT_TITLE_EMPTY","codes":"400","description":"No chat title provided"},{"name":"CHAT_TOO_BIG","codes":"400","description":""},{"name":"CHAT_WRITE_FORBIDDEN","codes":"403","description":"You can't write in this chat"},{"name":"CHP_CALL_FAIL","codes":"500","description":"The statistics cannot be retrieved at this time"},{"name":"CODE_EMPTY","codes":"400","description":"The provided code is empty"},{"name":"CODE_HASH_INVALID","codes":"400","description":"Code hash invalid"},{"name":"CODE_INVALID","codes":"400","description":"Code invalid (i.e. from email)"},{"name":"CONNECTION_API_ID_INVALID","codes":"400","description":"The provided API id is invalid"},{"name":"CONNECTION_DEVICE_MODEL_EMPTY","codes":"400","description":"Device model empty"},{"name":"CONNECTION_LANG_PACK_INVALID","codes":"400","description":"The specified language pack is not valid. This is meant to be used by official applications only so far, leave it empty"},{"name":"CONNECTION_LAYER_INVALID","codes":"400","description":"The very first request must always be InvokeWithLayerRequest"},{"name":"CONNECTION_NOT_INITED","codes":"400","description":"Connection not initialized"},{"name":"CONNECTION_SYSTEM_EMPTY","codes":"400","description":"Connection system empty"},{"name":"CONNECTION_SYSTEM_LANG_CODE_EMPTY","codes":"400","description":"The system language string was empty during connection"},{"name":"CONTACT_ID_INVALID","codes":"400","description":"The provided contact ID is invalid"},{"name":"CONTACT_NAME_EMPTY","codes":"400","description":"The provided contact name cannot be empty"},{"name":"CURRENCY_TOTAL_AMOUNT_INVALID","codes":"400","description":""},{"name":"DATA_INVALID","codes":"400","description":"Encrypted data invalid"},{"name":"DATA_JSON_INVALID","codes":"400","description":"The provided JSON data is invalid"},{"name":"DATE_EMPTY","codes":"400","description":"Date empty"},{"name":"DC_ID_INVALID","codes":"400","description":"This occurs when an authorization is tried to be exported for the same data center one is currently connected to"},{"name":"DH_G_A_INVALID","codes":"400","description":"g_a invalid"},{"name":"DOCUMENT_INVALID","codes":"400","description":"The document file was invalid and can't be used in inline mode"},{"name":"EMAIL_HASH_EXPIRED","codes":"400","description":"The email hash expired and cannot be used to verify it"},{"name":"EMAIL_INVALID","codes":"400","description":"The given email is invalid"},{"name":"EMAIL_UNCONFIRMED_X","codes":"400","description":"Email unconfirmed, the length of the code must be {code_length}"},{"name":"EMOJI_INVALID","codes":"400","description":""},{"name":"EMOJI_NOT_MODIFIED","codes":"400","description":""},{"name":"EMOTICON_EMPTY","codes":"400","description":"The emoticon field cannot be empty"},{"name":"EMOTICON_INVALID","codes":"400","description":"The specified emoticon cannot be used or was not a emoticon"},{"name":"EMOTICON_STICKERPACK_MISSING","codes":"400","description":"The emoticon sticker pack you are trying to get is missing"},{"name":"ENCRYPTED_MESSAGE_INVALID","codes":"400","description":"Encrypted message invalid"},{"name":"ENCRYPTION_ALREADY_ACCEPTED","codes":"400","description":"Secret chat already accepted"},{"name":"ENCRYPTION_ALREADY_DECLINED","codes":"400","description":"The secret chat was already declined"},{"name":"ENCRYPTION_DECLINED","codes":"400","description":"The secret chat was declined"},{"name":"ENCRYPTION_ID_INVALID","codes":"400","description":"The provided secret chat ID is invalid"},{"name":"ENCRYPTION_OCCUPY_FAILED","codes":"500","description":"TDLib developer claimed it is not an error while accepting secret chats and 500 is used instead of 420"},{"name":"ENTITIES_TOO_LONG","codes":"400","description":"It is no longer possible to send such long data inside entity tags (for example inline text URLs)"},{"name":"ENTITY_MENTION_USER_INVALID","codes":"400","description":"You can't use this entity"},{"name":"ERROR_TEXT_EMPTY","codes":"400","description":"The provided error message is empty"},{"name":"EXPIRE_FORBIDDEN","codes":"400","description":""},{"name":"EXPORT_CARD_INVALID","codes":"400","description":"Provided card is invalid"},{"name":"EXTERNAL_URL_INVALID","codes":"400","description":"External URL invalid"},{"name":"FIELD_NAME_EMPTY","codes":"400","description":"The field with the name FIELD_NAME is missing"},{"name":"FIELD_NAME_INVALID","codes":"400","description":"The field with the name FIELD_NAME is invalid"},{"name":"FILEREF_UPGRADE_NEEDED","codes":"406","description":"The file reference needs to be refreshed before being used again"},{"name":"FILE_CONTENT_TYPE_INVALID","codes":"400","description":""},{"name":"FILE_ID_INVALID","codes":"400","description":"The provided file id is invalid. Make sure all parameters are present, have the correct type and are not empty (ID, access hash, file reference, thumb size ...)"},{"name":"FILE_MIGRATE_X","codes":"303","description":"The file to be accessed is currently stored in DC {new_dc}"},{"name":"FILE_PARTS_INVALID","codes":"400","description":"The number of file parts is invalid"},{"name":"FILE_PART_0_MISSING","codes":"400","description":"File part 0 missing"},{"name":"FILE_PART_EMPTY","codes":"400","description":"The provided file part is empty"},{"name":"FILE_PART_INVALID","codes":"400","description":"The file part number is invalid"},{"name":"FILE_PART_LENGTH_INVALID","codes":"400","description":"The length of a file part is invalid"},{"name":"FILE_PART_SIZE_CHANGED","codes":"400","description":"The file part size (chunk size) cannot change during upload"},{"name":"FILE_PART_SIZE_INVALID","codes":"400","description":"The provided file part size is invalid"},{"name":"FILE_PART_X_MISSING","codes":"400","description":"Part {which} of the file is missing from storage"},{"name":"FILE_REFERENCE_EMPTY","codes":"400","description":"The file reference must exist to access the media and it cannot be empty"},{"name":"FILE_REFERENCE_EXPIRED","codes":"400","description":"The file reference has expired and is no longer valid or it belongs to self-destructing media and cannot be resent"},{"name":"FILE_REFERENCE_INVALID","codes":"400","description":"The file reference is invalid or you can't do that operation on such message"},{"name":"FILE_TITLE_EMPTY","codes":"400","description":""},{"name":"FIRSTNAME_INVALID","codes":"400","description":"The first name is invalid"},{"name":"FLOOD_TEST_PHONE_WAIT_X","codes":"420","description":"A wait of {seconds} seconds is required in the test servers"},{"name":"FLOOD_WAIT_X","codes":"420","description":"A wait of {seconds} seconds is required"},{"name":"FOLDER_ID_EMPTY","codes":"400","description":"The folder you tried to delete was already empty"},{"name":"FOLDER_ID_INVALID","codes":"400","description":"The folder you tried to use was not valid"},{"name":"FRESH_CHANGE_ADMINS_FORBIDDEN","codes":"400","description":"Recently logged-in users cannot add or change admins"},{"name":"FRESH_CHANGE_PHONE_FORBIDDEN","codes":"406","description":"Recently logged-in users cannot use this request"},{"name":"FRESH_RESET_AUTHORISATION_FORBIDDEN","codes":"406","description":"The current session is too new and cannot be used to reset other authorisations yet"},{"name":"FROM_PEER_INVALID","codes":"400","description":"The given from_user peer cannot be used for the parameter"},{"name":"GAME_BOT_INVALID","codes":"400","description":"You cannot send that game with the current bot"},{"name":"GIF_CONTENT_TYPE_INVALID","codes":"400","description":""},{"name":"GIF_ID_INVALID","codes":"400","description":"The provided GIF ID is invalid"},{"name":"GRAPH_INVALID_RELOAD","codes":"400","description":""},{"name":"GRAPH_OUTDATED_RELOAD","codes":"400","description":"Data can't be used for the channel statistics, graphs outdated"},{"name":"GROUPCALL_ADD_PARTICIPANTS_FAILED","codes":"500","description":""},{"name":"GROUPCALL_ALREADY_DISCARDED","codes":"400","description":""},{"name":"GROUPCALL_FORBIDDEN","codes":"403","description":""},{"name":"GROUPCALL_JOIN_MISSING","codes":"400","description":""},{"name":"GROUPCALL_SSRC_DUPLICATE_MUCH","codes":"400","description":""},{"name":"GROUPCALL_NOT_MODIFIED","codes":"400","description":""},{"name":"GROUPED_MEDIA_INVALID","codes":"400","description":"Invalid grouped media"},{"name":"GROUP_CALL_INVALID","codes":"400","description":"Group call invalid"},{"name":"HASH_INVALID","codes":"400","description":"The provided hash is invalid"},{"name":"HISTORY_GET_FAILED","codes":"500","description":"Fetching of history failed"},{"name":"IMAGE_PROCESS_FAILED","codes":"400","description":"Failure while processing image"},{"name":"IMPORT_FILE_INVALID","codes":"400","description":"The file is too large to be imported"},{"name":"IMPORT_FORMAT_UNRECOGNIZED","codes":"400","description":"Unknown import format"},{"name":"IMPORT_ID_INVALID","codes":"400","description":""},{"name":"INLINE_BOT_REQUIRED","codes":"403","description":"The action must be performed through an inline bot callback"},{"name":"INLINE_RESULT_EXPIRED","codes":"400","description":"The inline query expired"},{"name":"INPUT_CONSTRUCTOR_INVALID","codes":"400","description":"The provided constructor is invalid"},{"name":"INPUT_FETCH_ERROR","codes":"400","description":"An error occurred while deserializing TL parameters"},{"name":"INPUT_FETCH_FAIL","codes":"400","description":"Failed deserializing TL payload"},{"name":"INPUT_FILTER_INVALID","codes":"400","description":"The search query filter is invalid"},{"name":"INPUT_LAYER_INVALID","codes":"400","description":"The provided layer is invalid"},{"name":"INPUT_METHOD_INVALID","codes":"400","description":"The invoked method does not exist anymore or has never existed"},{"name":"INPUT_REQUEST_TOO_LONG","codes":"400","description":"The input request was too long. This may be a bug in the library as it can occur when serializing more bytes than it should (like appending the vector constructor code at the end of a message)"},{"name":"INPUT_USER_DEACTIVATED","codes":"400","description":"The specified user was deleted"},{"name":"INTERDC_X_CALL_ERROR","codes":"500","description":"An error occurred while communicating with DC {dc}"},{"name":"INTERDC_X_CALL_RICH_ERROR","codes":"500","description":"A rich error occurred while communicating with DC {dc}"},{"name":"INVITE_HASH_EMPTY","codes":"400","description":"The invite hash is empty"},{"name":"INVITE_HASH_EXPIRED","codes":"400","description":"The chat the user tried to join has expired and is not valid anymore"},{"name":"INVITE_HASH_INVALID","codes":"400","description":"The invite hash is invalid"},{"name":"LANG_CODE_INVALID","codes":"400","description":""},{"name":"LANG_PACK_INVALID","codes":"400","description":"The provided language pack is invalid"},{"name":"LASTNAME_INVALID","codes":"400","description":"The last name is invalid"},{"name":"LIMIT_INVALID","codes":"400","description":"An invalid limit was provided. See https://core.telegram.org/api/files#downloading-files"},{"name":"LINK_NOT_MODIFIED","codes":"400","description":"The channel is already linked to this group"},{"name":"LOCATION_INVALID","codes":"400","description":"The location given for a file was invalid. See https://core.telegram.org/api/files#downloading-files"},{"name":"MAX_ID_INVALID","codes":"400","description":"The provided max ID is invalid"},{"name":"MAX_QTS_INVALID","codes":"400","description":"The provided QTS were invalid"},{"name":"MD5_CHECKSUM_INVALID","codes":"400","description":"The MD5 check-sums do not match"},{"name":"MEDIA_CAPTION_TOO_LONG","codes":"400","description":"The caption is too long"},{"name":"MEDIA_EMPTY","codes":"400","description":"The provided media object is invalid or the current account may not be able to send it (such as games as users)"},{"name":"MEDIA_GROUPED_INVALID","codes":"400","description":""},{"name":"MEDIA_INVALID","codes":"400","description":"Media invalid"},{"name":"MEDIA_NEW_INVALID","codes":"400","description":"The new media to edit the message with is invalid (such as stickers or voice notes)"},{"name":"MEDIA_PREV_INVALID","codes":"400","description":"The old media cannot be edited with anything else (such as stickers or voice notes)"},{"name":"MEDIA_TTL_INVALID","codes":"400","description":""},{"name":"MEGAGROUP_ID_INVALID","codes":"400","description":"The group is invalid"},{"name":"MEGAGROUP_PREHISTORY_HIDDEN","codes":"400","description":"You can't set this discussion group because it's history is hidden"},{"name":"MEGAGROUP_REQUIRED","codes":"400","description":"The request can only be used with a megagroup channel"},{"name":"MEMBER_NO_LOCATION","codes":"500","description":"An internal failure occurred while fetching user info (couldn't find location)"},{"name":"MEMBER_OCCUPY_PRIMARY_LOC_FAILED","codes":"500","description":"Occupation of primary member location failed"},{"name":"MESSAGE_AUTHOR_REQUIRED","codes":"403","description":"Message author required"},{"name":"MESSAGE_DELETE_FORBIDDEN","codes":"403","description":"You can't delete one of the messages you tried to delete, most likely because it is a service message."},{"name":"MESSAGE_EDIT_TIME_EXPIRED","codes":"400","description":"You can't edit this message anymore, too much time has passed since its creation."},{"name":"MESSAGE_EMPTY","codes":"400","description":"Empty or invalid UTF-8 message was sent"},{"name":"MESSAGE_IDS_EMPTY","codes":"400","description":"No message ids were provided"},{"name":"MESSAGE_ID_INVALID","codes":"400","description":"The specified message ID is invalid or you can't do that operation on such message"},{"name":"MESSAGE_NOT_MODIFIED","codes":"400","description":"Content of the message was not modified"},{"name":"MESSAGE_POLL_CLOSED","codes":"400","description":"The poll was closed and can no longer be voted on"},{"name":"MESSAGE_TOO_LONG","codes":"400","description":"Message was too long. Current maximum length is 4096 UTF-8 characters"},{"name":"METHOD_INVALID","codes":"400","description":"The API method is invalid and cannot be used"},{"name":"MSGID_DECREASE_RETRY","codes":"500","description":"The request should be retried with a lower message ID"},{"name":"MSG_ID_INVALID","codes":"400","description":"The message ID used in the peer was invalid"},{"name":"MSG_WAIT_FAILED","codes":"400","description":"A waiting call returned an error"},{"name":"MT_SEND_QUEUE_TOO_LONG","codes":"500","description":""},{"name":"MULTI_MEDIA_TOO_LONG","codes":"400","description":"Too many media files were included in the same album"},{"name":"NEED_CHAT_INVALID","codes":"500","description":"The provided chat is invalid"},{"name":"NEED_MEMBER_INVALID","codes":"500","description":"The provided member is invalid or does not exist (for example a thumb size)"},{"name":"NETWORK_MIGRATE_X","codes":"303","description":"The source IP address is associated with DC {new_dc}"},{"name":"NEW_SALT_INVALID","codes":"400","description":"The new salt is invalid"},{"name":"NEW_SETTINGS_INVALID","codes":"400","description":"The new settings are invalid"},{"name":"NEXT_OFFSET_INVALID","codes":"400","description":"The value for next_offset is invalid. Check that it has normal characters and is not too long"},{"name":"OFFSET_INVALID","codes":"400","description":"The given offset was invalid, it must be divisible by 1KB. See https://core.telegram.org/api/files#downloading-files"},{"name":"OFFSET_PEER_ID_INVALID","codes":"400","description":"The provided offset peer is invalid"},{"name":"OPTIONS_TOO_MUCH","codes":"400","description":"You defined too many options for the poll"},{"name":"OPTION_INVALID","codes":"400","description":"The option specified is invalid and does not exist in the target poll"},{"name":"PACK_SHORT_NAME_INVALID","codes":"400","description":"Invalid sticker pack name. It must begin with a letter, can't contain consecutive underscores and must end in \"_by_\"."},{"name":"PACK_SHORT_NAME_OCCUPIED","codes":"400","description":"A stickerpack with this name already exists"},{"name":"PARTICIPANTS_TOO_FEW","codes":"400","description":"Not enough participants"},{"name":"PARTICIPANT_CALL_FAILED","codes":"500","description":"Failure while making call"},{"name":"PARTICIPANT_JOIN_MISSING","codes":"403","description":""},{"name":"PARTICIPANT_VERSION_OUTDATED","codes":"400","description":"The other participant does not use an up to date telegram client with support for calls"},{"name":"PASSWORD_EMPTY","codes":"400","description":"The provided password is empty"},{"name":"PASSWORD_HASH_INVALID","codes":"400","description":"The password (and thus its hash value) you entered is invalid"},{"name":"PASSWORD_MISSING","codes":"400","description":"The account must have 2-factor authentication enabled (a password) before this method can be used"},{"name":"PASSWORD_RECOVERY_EXPIRED","codes":"400","description":""},{"name":"PASSWORD_RECOVERY_NA","codes":"400","description":""},{"name":"PASSWORD_REQUIRED","codes":"400","description":"The account must have 2-factor authentication enabled (a password) before this method can be used"},{"name":"PASSWORD_TOO_FRESH_X","codes":"400","description":"The password was added too recently and {seconds} seconds must pass before using the method"},{"name":"PAYMENT_PROVIDER_INVALID","codes":"400","description":"The payment provider was not recognised or its token was invalid"},{"name":"PEER_FLOOD","codes":"400","description":"Too many requests"},{"name":"PEER_ID_INVALID","codes":"400","description":"An invalid Peer was used. Make sure to pass the right peer type and that the value is valid (for instance, bots cannot start conversations)"},{"name":"PEER_ID_NOT_SUPPORTED","codes":"400","description":"The provided peer ID is not supported"},{"name":"PERSISTENT_TIMESTAMP_EMPTY","codes":"400","description":"Persistent timestamp empty"},{"name":"PERSISTENT_TIMESTAMP_INVALID","codes":"400","description":"Persistent timestamp invalid"},{"name":"PERSISTENT_TIMESTAMP_OUTDATED","codes":"500","description":"Persistent timestamp outdated"},{"name":"PHONE_CODE_EMPTY","codes":"400","description":"The phone code is missing"},{"name":"PHONE_CODE_EXPIRED","codes":"400","description":"The confirmation code has expired"},{"name":"PHONE_CODE_HASH_EMPTY","codes":"400","description":"The phone code hash is missing"},{"name":"PHONE_CODE_INVALID","codes":"400","description":"The phone code entered was invalid"},{"name":"PHONE_MIGRATE_X","codes":"303","description":"The phone number a user is trying to use for authorization is associated with DC {new_dc}"},{"name":"PHONE_NUMBER_APP_SIGNUP_FORBIDDEN","codes":"400","description":"You can't sign up using this app"},{"name":"PHONE_NUMBER_BANNED","codes":"400","description":"The used phone number has been banned from Telegram and cannot be used anymore. Maybe check https://www.telegram.org/faq_spam"},{"name":"PHONE_NUMBER_FLOOD","codes":"400","description":"You asked for the code too many times."},{"name":"PHONE_NUMBER_INVALID","codes":"400 406","description":"The phone number is invalid"},{"name":"PHONE_NUMBER_OCCUPIED","codes":"400","description":"The phone number is already in use"},{"name":"PHONE_NUMBER_UNOCCUPIED","codes":"400","description":"The phone number is not yet being used"},{"name":"PHONE_PASSWORD_FLOOD","codes":"406","description":"You have tried logging in too many times"},{"name":"PHONE_PASSWORD_PROTECTED","codes":"400","description":"This phone is password protected"},{"name":"PHOTO_CONTENT_TYPE_INVALID","codes":"400","description":""},{"name":"PHOTO_CONTENT_URL_EMPTY","codes":"400","description":"The content from the URL used as a photo appears to be empty or has caused another HTTP error"},{"name":"PHOTO_CROP_SIZE_SMALL","codes":"400","description":"Photo is too small"},{"name":"PHOTO_EXT_INVALID","codes":"400","description":"The extension of the photo is invalid"},{"name":"PHOTO_ID_INVALID","codes":"400","description":"Photo id is invalid"},{"name":"PHOTO_INVALID","codes":"400","description":"Photo invalid"},{"name":"PHOTO_INVALID_DIMENSIONS","codes":"400","description":"The photo dimensions are invalid (hint: `pip install pillow` for `send_file` to resize images)"},{"name":"PHOTO_SAVE_FILE_INVALID","codes":"400","description":"The photo you tried to send cannot be saved by Telegram. A reason may be that it exceeds 10MB. Try resizing it locally"},{"name":"PHOTO_THUMB_URL_EMPTY","codes":"400","description":"The URL used as a thumbnail appears to be empty or has caused another HTTP error"},{"name":"PIN_RESTRICTED","codes":"400","description":"You can't pin messages in private chats with other people"},{"name":"PINNED_DIALOGS_TOO_MUCH","codes":"400","description":""},{"name":"POLL_ANSWERS_INVALID","codes":"400","description":"The poll did not have enough answers or had too many"},{"name":"POLL_OPTION_DUPLICATE","codes":"400","description":"A duplicate option was sent in the same poll"},{"name":"POLL_OPTION_INVALID","codes":"400","description":"A poll option used invalid data (the data may be too long)"},{"name":"POLL_QUESTION_INVALID","codes":"400","description":"The poll question was either empty or too long"},{"name":"POLL_UNSUPPORTED","codes":"400","description":"This layer does not support polls in the issued method"},{"name":"POLL_VOTE_REQUIRED","codes":"403","description":""},{"name":"PREVIOUS_CHAT_IMPORT_ACTIVE_WAIT_XMIN","codes":"406","description":"Similar to a flood wait, must wait {minutes} minutes"},{"name":"PRIVACY_KEY_INVALID","codes":"400","description":"The privacy key is invalid"},{"name":"PRIVACY_TOO_LONG","codes":"400","description":"Cannot add that many entities in a single request"},{"name":"PRIVACY_VALUE_INVALID","codes":"400","description":"The privacy value is invalid"},{"name":"PTS_CHANGE_EMPTY","codes":"500","description":"No PTS change"},{"name":"PUBLIC_KEY_REQUIRED","codes":"400","description":""},{"name":"QUERY_ID_EMPTY","codes":"400","description":"The query ID is empty"},{"name":"QUERY_ID_INVALID","codes":"400","description":"The query ID is invalid"},{"name":"QUERY_TOO_SHORT","codes":"400","description":"The query string is too short"},{"name":"QUIZ_CORRECT_ANSWERS_EMPTY","codes":"400","description":"A quiz must specify one correct answer"},{"name":"QUIZ_CORRECT_ANSWERS_TOO_MUCH","codes":"400","description":"There can only be one correct answer"},{"name":"QUIZ_CORRECT_ANSWER_INVALID","codes":"400","description":"The correct answer is not an existing answer"},{"name":"QUIZ_MULTIPLE_INVALID","codes":"400","description":"A poll cannot be both multiple choice and quiz"},{"name":"RANDOM_ID_DUPLICATE","codes":"500","description":"You provided a random ID that was already used"},{"name":"RANDOM_ID_INVALID","codes":"400","description":"A provided random ID is invalid"},{"name":"RANDOM_LENGTH_INVALID","codes":"400","description":"Random length invalid"},{"name":"RANGES_INVALID","codes":"400","description":"Invalid range provided"},{"name":"REACTION_EMPTY","codes":"400","description":"No reaction provided"},{"name":"REACTION_INVALID","codes":"400","description":"Invalid reaction provided (only emoji are allowed)"},{"name":"REFLECTOR_NOT_AVAILABLE","codes":"400","description":"Invalid call reflector server"},{"name":"REG_ID_GENERATE_FAILED","codes":"500","description":"Failure while generating registration ID"},{"name":"REPLY_MARKUP_GAME_EMPTY","codes":"400","description":"The provided reply markup for the game is empty"},{"name":"REPLY_MARKUP_INVALID","codes":"400","description":"The provided reply markup is invalid"},{"name":"REPLY_MARKUP_TOO_LONG","codes":"400","description":"The data embedded in the reply markup buttons was too much"},{"name":"RESET_REQUEST_MISSING","codes":"400","description":""},{"name":"RESULTS_TOO_MUCH","codes":"400","description":"You sent too many results, see https://core.telegram.org/bots/api#answerinlinequery for the current limit"},{"name":"RESULT_ID_DUPLICATE","codes":"400","description":"Duplicated IDs on the sent results. Make sure to use unique IDs"},{"name":"RESULT_ID_INVALID","codes":"400","description":"The given result cannot be used to send the selection to the bot"},{"name":"RESULT_TYPE_INVALID","codes":"400","description":"Result type invalid"},{"name":"RIGHT_FORBIDDEN","codes":"403","description":"Either your admin rights do not allow you to do this or you passed the wrong rights combination (some rights only apply to channels and vice versa)"},{"name":"RPC_CALL_FAIL","codes":"500","description":"Telegram is having internal issues, please try again later."},{"name":"RPC_MCGET_FAIL","codes":"500","description":"Telegram is having internal issues, please try again later."},{"name":"RSA_DECRYPT_FAILED","codes":"400","description":"Internal RSA decryption failed"},{"name":"SCHEDULE_BOT_NOT_ALLOWED","codes":"400","description":"Bots are not allowed to schedule messages"},{"name":"SCHEDULE_DATE_INVALID","codes":"400","description":""},{"name":"SCHEDULE_DATE_TOO_LATE","codes":"400","description":"The date you tried to schedule is too far in the future (last known limit of 1 year and a few hours)"},{"name":"SCHEDULE_STATUS_PRIVATE","codes":"400","description":"You cannot schedule a message until the person comes online if their privacy does not show this information"},{"name":"SCHEDULE_TOO_MUCH","codes":"400","description":"You cannot schedule more messages in this chat (last known limit of 100 per chat)"},{"name":"SEARCH_QUERY_EMPTY","codes":"400","description":"The search query is empty"},{"name":"SECONDS_INVALID","codes":"400","description":"Slow mode only supports certain values (e.g. 0, 10s, 30s, 1m, 5m, 15m and 1h)"},{"name":"SEND_MESSAGE_MEDIA_INVALID","codes":"400","description":"The message media was invalid or not specified"},{"name":"SEND_MESSAGE_TYPE_INVALID","codes":"400","description":"The message type is invalid"},{"name":"SENSITIVE_CHANGE_FORBIDDEN","codes":"403","description":"Your sensitive content settings cannot be changed at this time"},{"name":"SESSION_EXPIRED","codes":"401","description":"The authorization has expired"},{"name":"SESSION_PASSWORD_NEEDED","codes":"401","description":"Two-steps verification is enabled and a password is required"},{"name":"SESSION_REVOKED","codes":"401","description":"The authorization has been invalidated, because of the user terminating all sessions"},{"name":"SESSION_TOO_FRESH_X","codes":"400","description":"The session logged in too recently and {seconds} seconds must pass before calling the method"},{"name":"SHA256_HASH_INVALID","codes":"400","description":"The provided SHA256 hash is invalid"},{"name":"SHORTNAME_OCCUPY_FAILED","codes":"400","description":"An error occurred when trying to register the short-name used for the sticker pack. Try a different name"},{"name":"SHORT_NAME_INVALID","codes":"400","description":""},{"name":"SHORT_NAME_OCCUPIED","codes":"400","description":""},{"name":"SLOWMODE_WAIT_X","codes":"420","description":"A wait of {seconds} seconds is required before sending another message in this chat"},{"name":"SRP_ID_INVALID","codes":"400","description":""},{"name":"START_PARAM_EMPTY","codes":"400","description":"The start parameter is empty"},{"name":"START_PARAM_INVALID","codes":"400","description":"Start parameter invalid"},{"name":"STATS_MIGRATE_X","codes":"303","description":"The channel statistics must be fetched from DC {dc}"},{"name":"STICKERSET_INVALID","codes":"400","description":"The provided sticker set is invalid"},{"name":"STICKERSET_OWNER_ANONYMOUS","codes":"406","description":"This sticker set can't be used as the group's official stickers because it was created by one of its anonymous admins"},{"name":"STICKERS_EMPTY","codes":"400","description":"No sticker provided"},{"name":"STICKER_DOCUMENT_INVALID","codes":"400","description":"The sticker file was invalid (this file has failed Telegram internal checks, make sure to use the correct format and comply with https://core.telegram.org/animated_stickers)"},{"name":"STICKER_EMOJI_INVALID","codes":"400","description":"Sticker emoji invalid"},{"name":"STICKER_FILE_INVALID","codes":"400","description":"Sticker file invalid"},{"name":"STICKER_ID_INVALID","codes":"400","description":"The provided sticker ID is invalid"},{"name":"STICKER_INVALID","codes":"400","description":"The provided sticker is invalid"},{"name":"STICKER_PNG_DIMENSIONS","codes":"400","description":"Sticker png dimensions invalid"},{"name":"STICKER_PNG_NOPNG","codes":"400","description":"Stickers must be a png file but the used image was not a png"},{"name":"STICKER_TGS_NODOC","codes":"400","description":""},{"name":"STICKER_TGS_NOTGS","codes":"400","description":"Stickers must be a tgs file but the used file was not a tgs"},{"name":"STICKER_THUMB_PNG_NOPNG","codes":"400","description":"Stickerset thumb must be a png file but the used file was not png"},{"name":"STICKER_THUMB_TGS_NOTGS","codes":"400","description":"Stickerset thumb must be a tgs file but the used file was not tgs"},{"name":"STORAGE_CHECK_FAILED","codes":"500","description":"Server storage check failed"},{"name":"STORE_INVALID_SCALAR_TYPE","codes":"500","description":""},{"name":"TAKEOUT_INIT_DELAY_X","codes":"420","description":"A wait of {seconds} seconds is required before being able to initiate the takeout"},{"name":"TAKEOUT_INVALID","codes":"400","description":"The takeout session has been invalidated by another data export session"},{"name":"TAKEOUT_REQUIRED","codes":"400","description":"You must initialize a takeout request first"},{"name":"TEMP_AUTH_KEY_EMPTY","codes":"400","description":"No temporary auth key provided"},{"name":"TIMEOUT","codes":"500","description":"A timeout occurred while fetching data from the worker"},{"name":"TITLE_INVALID","codes":"400","description":""},{"name":"THEME_INVALID","codes":"400","description":"Theme invalid"},{"name":"THEME_MIME_INVALID","codes":"400","description":"You cannot create this theme, the mime-type is invalid"},{"name":"TMP_PASSWORD_DISABLED","codes":"400","description":"The temporary password is disabled"},{"name":"TMP_PASSWORD_INVALID","codes":"400","description":"Password auth needs to be regenerated"},{"name":"TOKEN_INVALID","codes":"400","description":"The provided token is invalid"},{"name":"TTL_DAYS_INVALID","codes":"400","description":"The provided TTL is invalid"},{"name":"TTL_MEDIA_INVALID","codes":"400","description":"The provided media cannot be used with a TTL"},{"name":"TTL_PERIOD_INVALID","codes":"400","description":"The provided TTL Period is invalid"},{"name":"TYPES_EMPTY","codes":"400","description":"The types field is empty"},{"name":"TYPE_CONSTRUCTOR_INVALID","codes":"400","description":"The type constructor is invalid"},{"name":"UNKNOWN_ERROR","codes":"400","description":""},{"name":"UNKNOWN_METHOD","codes":"500","description":"The method you tried to call cannot be called on non-CDN DCs"},{"name":"UNTIL_DATE_INVALID","codes":"400","description":"That date cannot be specified in this request (try using None)"},{"name":"URL_INVALID","codes":"400","description":"The URL used was invalid (e.g. when answering a callback with a URL that's not t.me/yourbot or your game's URL)"},{"name":"USER_VOLUME_INVALID","codes":"400","description":""},{"name":"USERNAME_INVALID","codes":"400","description":"Nobody is using this username, or the username is unacceptable. If the latter, it must match r\"[a-zA-Z][\\w\\d]{3,30}[a-zA-Z\\d]\""},{"name":"USERNAME_NOT_MODIFIED","codes":"400","description":"The username is not different from the current username"},{"name":"USERNAME_NOT_OCCUPIED","codes":"400","description":"The username is not in use by anyone else yet"},{"name":"USERNAME_OCCUPIED","codes":"400","description":"The username is already taken"},{"name":"USERS_TOO_FEW","codes":"400","description":"Not enough users (to create a chat, for example)"},{"name":"USERS_TOO_MUCH","codes":"400","description":"The maximum number of users has been exceeded (to create a chat, for example)"},{"name":"USER_ADMIN_INVALID","codes":"400","description":"Either you're not an admin or you tried to ban an admin that you didn't promote"},{"name":"USER_ALREADY_PARTICIPANT","codes":"400","description":"The authenticated user is already a participant of the chat"},{"name":"USER_BANNED_IN_CHANNEL","codes":"400","description":"You're banned from sending messages in supergroups/channels"},{"name":"USER_BLOCKED","codes":"400","description":"User blocked"},{"name":"USER_BOT","codes":"400","description":"Bots can only be admins in channels."},{"name":"USER_BOT_INVALID","codes":"400 403","description":"This method can only be called by a bot"},{"name":"USER_BOT_REQUIRED","codes":"400","description":"This method can only be called by a bot"},{"name":"USER_CHANNELS_TOO_MUCH","codes":"403","description":"One of the users you tried to add is already in too many channels/supergroups"},{"name":"USER_CREATOR","codes":"400","description":"You can't leave this channel, because you're its creator"},{"name":"USER_DEACTIVATED","codes":"401","description":"The user has been deleted/deactivated"},{"name":"USER_DEACTIVATED_BAN","codes":"401","description":"The user has been deleted/deactivated"},{"name":"USER_ID_INVALID","codes":"400","description":"Invalid object ID for a user. Make sure to pass the right types, for instance making sure that the request is designed for users or otherwise look for a different one more suited"},{"name":"USER_INVALID","codes":"400","description":"The given user was invalid"},{"name":"USER_IS_BLOCKED","codes":"400 403","description":"User is blocked"},{"name":"USER_IS_BOT","codes":"400","description":"Bots can't send messages to other bots"},{"name":"USER_KICKED","codes":"400","description":"This user was kicked from this supergroup/channel"},{"name":"USER_MIGRATE_X","codes":"303","description":"The user whose identity is being used to execute queries is associated with DC {new_dc}"},{"name":"USER_NOT_MUTUAL_CONTACT","codes":"400 403","description":"The provided user is not a mutual contact"},{"name":"USER_NOT_PARTICIPANT","codes":"400","description":"The target user is not a member of the specified megagroup or channel"},{"name":"USER_PRIVACY_RESTRICTED","codes":"403","description":"The user's privacy settings do not allow you to do this"},{"name":"USER_RESTRICTED","codes":"403","description":"You're spamreported, you can't create channels or chats."},{"name":"USERPIC_UPLOAD_REQUIRED","codes":"400","description":"You must have a profile picture before using this method"},{"name":"VIDEO_CONTENT_TYPE_INVALID","codes":"400","description":"The video content type is not supported with the given parameters (i.e. supports_streaming)"},{"name":"VIDEO_FILE_INVALID","codes":"400","description":"The given video cannot be used"},{"name":"VIDEO_TITLE_EMPTY","codes":"400","description":""},{"name":"WALLPAPER_FILE_INVALID","codes":"400","description":"The given file cannot be used as a wallpaper"},{"name":"WALLPAPER_INVALID","codes":"400","description":"The input wallpaper was not valid"},{"name":"WALLPAPER_MIME_INVALID","codes":"400","description":""},{"name":"WC_CONVERT_URL_INVALID","codes":"400","description":"WC convert URL invalid"},{"name":"WEBDOCUMENT_MIME_INVALID","codes":"400","description":""},{"name":"WEBDOCUMENT_URL_INVALID","codes":"400","description":"The given URL cannot be used"},{"name":"WEBPAGE_CURL_FAILED","codes":"400","description":"Failure while fetching the webpage with cURL"},{"name":"WEBPAGE_MEDIA_EMPTY","codes":"400","description":"Webpage media empty"},{"name":"WORKER_BUSY_TOO_LONG_RETRY","codes":"500","description":"Telegram workers are too busy to respond immediately"},{"name":"YOU_BLOCKED_USER","codes":"400","description":"You blocked this user"},{"virtual":true,"name":"RPC_TIMEOUT","codes":"408","description":"The set RPC timeout has exceeded"},{"virtual":true,"name":"MESSAGE_NOT_FOUND","codes":"404","description":"Message was not found"}] \ No newline at end of file diff --git a/packages/tl/raw-schema.d.ts b/packages/tl/raw-schema.d.ts deleted file mode 100644 index 5d26cd6c..00000000 --- a/packages/tl/raw-schema.d.ts +++ /dev/null @@ -1,187 +0,0 @@ -declare namespace RawJsonTlSchema { - /** - * Namespaces available in the schema. - * - * Key is namespace name, can be `$root` which stands - * for root namespace (i.e. no namespace) - * - * Value is namespace contents. - */ - type TlNamespaces = Record - - /** - * String describing some TL type. - * Can be a namespaced name of a union, - * or one of primitive types: - * - `number`: Signed 32-bit integer - * - `Long`: Signed 64-bit integer - * - `Int128`: Signed 128-bit integer - * - `Int256`: Signed 256-bit integer - * - `Double`: 64-bit floating-point value - * - `string`: UTF16 string - * - `Buffer`: Array of bytes - * - `false`: False - * - `true`: True - * - `boolean`: true or false - * - `null`: Null - * - `any`: Any TL object (usually used in `invokeAs*` methods to wrap other methods) - * - `$FlagsBitField`: 32-bit integer, which value is generated based on the present fields and their `predicate` - */ - type TlType = string - - /** - * Information about a generic. - * - * Translates to TypeScript like this: - * ```typescript - * type Foo = ... - * ``` - */ - interface TlGeneric { - /** - * Name of the type used in generic - */ - name: string - - /** - * Constraint of the generic. - */ - super: TlType - } - - interface TlArgument { - /** - * Argument name in `camelCase` - */ - name: string - - /** - * Argument type, see {@link TlType}. - * Can also be a name of some of {@link TlBaseType.generics} - */ - type: TlType - - /** Argument description */ - description?: string - - /** - * Whether this argument is optional. - * When true, `predicate` is present. - */ - optional?: true - - /** - * String in format `.` which declares - * the `$FlagsBitField` to use and byte index to use when (de-)serializing. - * - * Field name is currently always `flags`, but TL format allows multiple bit fields. - * Byte index starts with 0. - */ - predicate?: string - } - - interface TlBaseType { - /** - * Non-namespaced type name - */ - name: string - - /** - * Unsigned 32-bit ID of the type. - * This is basically the CRC you see in the schema. - */ - id: number - - /** - * Method arguments or class properties. - */ - arguments: TlArgument[] - - /** Type description */ - description?: string - - /** - * Generic types for the type. - */ - generics?: TlGeneric[] - } - - interface TlError { - /** - * Numeric code of the error (like 404) - */ - code: number - - /** - * Name of the error (like `NOT_FOUND`) - */ - name: string - - /** - * Description of the error - */ - description?: string - } - - interface TlMethod extends TlBaseType { - /** - * Type that this method returns when called. - */ - returns: TlType - - /** - * List of errors that this method might throw. - * - * Note that this is a non-exhaustive list! - */ - throws?: TlError[] - } - - interface TlClass extends TlBaseType { - /** - * Namespaced name of the union that this type belongs to. - */ - type: string - } - - /** - * Union is a group of classes that are used interchangeably. - * - * Translates to TypeScript like this: `type {type} = {subtypes.join(' | ')}` - */ - interface TlUnion { - /** - * Non-namespaced name of the union in `PascalCase` - */ - type: string - - /** - * Namespaced names of the types that belong to this union - */ - subtypes: string[] - } - - interface TlSingleNamespace { - /** Classes in the namespace */ - classes: TlClass[] - - /** Methods in the namespace */ - methods: TlMethod[] - - /** Unions in the namespace */ - unions: TlUnion[] - } -} - -declare interface RawJsonTlSchema { - /** - * TL layer number used to generate {@link api} - */ - apiLayer: string - - mtproto: RawJsonTlSchema.TlNamespaces - api: RawJsonTlSchema.TlNamespaces -} - -declare const __rawJsonTlSchema: RawJsonTlSchema -export = __rawJsonTlSchema diff --git a/packages/tl/raw-schema.json b/packages/tl/raw-schema.json deleted file mode 100644 index 2f7b6f66..00000000 --- a/packages/tl/raw-schema.json +++ /dev/null @@ -1 +0,0 @@ -{"mtproto":{"$root":{"classes":[{"name":"resPQ","id":85337187,"type":"ResPQ","arguments":[{"name":"nonce","type":"Int128","description":"Client nonce that was generated earlier"},{"name":"serverNonce","type":"Int128","description":"Random number generated by the server, used in the later steps"},{"name":"pq","type":"Buffer","description":"Big endian representation of a natural number, which is a product of two different odd prime numbers. Normally, this value is `<= 2^63-1`. Client is expected to decompose this product to `p` and `q`.\n"},{"name":"serverPublicKeyFingerprints","type":"Long[]","description":"List of public RSA key fingerprints, which are computed as follows:\n - First, the modulus and the exponent are extracted from the key\n - Then, the following TL type is written: `rsa_public_key n:string e:string = RSAPublicKey`\n - This is a bare type, meaning there's no 4-byte type number before it\n - `n` is the modulus, `e` is the exponent, encoded as big-endian\n - Finally, SHA1 is computed, and its last 8 bytes are taken and parsed as LE long\n (i.e. `parse_int64_le(sha1(rsa_public_key).slice(-8))`)\n\nClient is expected to choose out of those keys any single one that it has\nembedded in itself and return one of them in the following request.\n"}],"description":"Response for the first step of Authorization key derivation process"},{"name":"p_q_inner_data_dc","id":2851430293,"type":"P_Q_inner_data","arguments":[{"name":"pq","type":"Buffer"},{"name":"p","type":"Buffer"},{"name":"q","type":"Buffer"},{"name":"nonce","type":"Int128"},{"name":"serverNonce","type":"Int128"},{"name":"newNonce","type":"Int256"},{"name":"dc","type":"number"}]},{"name":"p_q_inner_data_temp_dc","id":1459478408,"type":"P_Q_inner_data","arguments":[{"name":"pq","type":"Buffer"},{"name":"p","type":"Buffer"},{"name":"q","type":"Buffer"},{"name":"nonce","type":"Int128"},{"name":"serverNonce","type":"Int128"},{"name":"newNonce","type":"Int256"},{"name":"dc","type":"number"},{"name":"expiresIn","type":"number"}]},{"name":"server_DH_params_ok","id":3504867164,"type":"Server_DH_Params","arguments":[{"name":"nonce","type":"Int128","description":"Client nonce that was generated earlier"},{"name":"serverNonce","type":"Int128","description":"Server nonce that was received earlier"},{"name":"encryptedAnswer","type":"Buffer","description":"Encrypted DH parameters, obtained as follows:\n - Let `answer` be a serialization of {@link tl.mtproto.RawServer_DH_inner_data}\n - `hash1 = sha1(concat(newNonce, serverNonce))`\n - `hash2 = sha1(concat(serverNonce, newNonce))`\n - `hash3 = sha1(concat(newNonce, newNonce))`\n - `key = concat(hash1, hash2.slice(0, 12))`\n - `iv = concat(hash2.slice(12, 20), hash3, newNonce.slice(0, 4))`\n - `encryptedAnswer = aes256_ige_encrypt(answer, key, iv)\n"}],"description":"PQ decomposition was correct, server-side variables for DH are returned"},{"name":"server_DH_inner_data","id":3045658042,"type":"Server_DH_inner_data","arguments":[{"name":"nonce","type":"Int128","description":"Client nonce that was generated earlier"},{"name":"serverNonce","type":"Int128","description":"Server nonce that was received earlier"},{"name":"g","type":"number","description":"`g` number (generator) used for Diffie-Hellman"},{"name":"dhPrime","type":"Buffer","description":"`p` prime number (modulus) used for Diffie-Hellman"},{"name":"gA","type":"Buffer","description":"`gA` number (`gA = g ^ A % p`, where `A` is server secret) used for Diffie-Hellman"},{"name":"serverTime","type":"number","description":"Server UNIX timestamp (in seconds)"}],"description":"Inner data that is returned in {@link tl.mtproto.RawServer_DH_params_ok}, containing server-side variables for Diffie-Hellman exchange\n"},{"name":"client_DH_inner_data","id":1715713620,"type":"Client_DH_Inner_Data","arguments":[{"name":"nonce","type":"Int128","description":"Client nonce that was generated earlier"},{"name":"serverNonce","type":"Int128","description":"Server nonce that was received earlier"},{"name":"retryId","type":"Long","description":"Retry ID. When requesting for the first time, `0`, then last `authKeyAuxHash` is used\n"},{"name":"gB","type":"Buffer","description":"`gB` number (`gB = g ^ B % p`, where `B` is client secret) used for Diffie-Hellman"}],"description":"Inner data of {@link tl.mtproto.RawSetClientDHParamsRequest}, containing client-side variables for Diffie-Hellman exchange\n"},{"name":"dh_gen_ok","id":1003222836,"type":"Set_client_DH_params_answer","arguments":[{"name":"nonce","type":"Int128"},{"name":"serverNonce","type":"Int128"},{"name":"newNonceHash1","type":"Int128"}]},{"name":"dh_gen_retry","id":1188831161,"type":"Set_client_DH_params_answer","arguments":[{"name":"nonce","type":"Int128","description":"Client nonce that was generated earlier"},{"name":"serverNonce","type":"Int128","description":"Server nonce that was received earlier"},{"name":"newNonceHash2","type":"Int128","description":"Nonce hash, computed as follows: `sha1(concat([newNonce, [0x02], authKeyAuxHash])`\n"}],"description":"DH exchange need to be retried. Current auth key is `gA ^ b % dhPrime`, and `authKeyAuxHash = sha1(authKey).slice(0, 8)`, but they will change.\nWhen this is received, you are expected to send another {@link tl.mtproto.RawSetClientDHParamsRequest}.\n"},{"name":"dh_gen_fail","id":2795351554,"type":"Set_client_DH_params_answer","arguments":[{"name":"nonce","type":"Int128","description":"Client nonce that was generated earlier"},{"name":"serverNonce","type":"Int128","description":"Server nonce that was received earlier"},{"name":"newNonceHash3","type":"Int128","description":"Nonce hash, computed as follows: `sha1(concat([newNonce, [0x03], authKeyAuxHash])`\n"}],"description":"DH exchange failed. You should restart the entire authorization flow."},{"name":"bind_auth_key_inner","id":1973679973,"type":"BindAuthKeyInner","arguments":[{"name":"nonce","type":"RawLong"},{"name":"tempAuthKeyId","type":"RawLong"},{"name":"permAuthKeyId","type":"RawLong"},{"name":"tempSessionId","type":"RawLong"},{"name":"expiresAt","type":"number"}]},{"name":"rpc_result","id":4082920705,"type":"RpcResult","arguments":[{"name":"reqMsgId","type":"Long","description":"Requesting message ID"},{"name":"result","type":"Object","description":"Result of the call"}],"description":"Result of an RPC call"},{"name":"rpc_error","id":558156313,"type":"RpcError","arguments":[{"name":"errorCode","type":"number","description":"Numeric error code (like 404)"},{"name":"errorMessage","type":"string","description":"String error code"}],"description":"RPC call resulted in an error, information about that error. Error is still a result, and thus it is sent as a `result` of {@link tl.mtproto.RawRpc_result}\n"},{"name":"rpc_answer_unknown","id":1579864942,"type":"RpcDropAnswer","arguments":[]},{"name":"rpc_answer_dropped_running","id":3447252358,"type":"RpcDropAnswer","arguments":[],"description":"Response was canceled while the RPC query was being processed (where the RPC query itself was still fully processed); in this case, the same rpc_answer_dropped_running is also returned in response to the original query, and both of these responses require an acknowledgment from the client.\n"},{"name":"rpc_answer_dropped","id":2755319991,"type":"RpcDropAnswer","arguments":[{"name":"msgId","type":"Long","description":"Message ID of the RPC response"},{"name":"seqNo","type":"number","description":"Seq NO of the RPC response"},{"name":"bytes","type":"number","description":"Length in bytes of the RPC response"}],"description":"The RPC response was removed from the server’s outgoing queue"},{"name":"future_salt","id":155834844,"type":"FutureSalt","arguments":[{"name":"validSince","type":"number","description":"UNIX time in seconds from when this salt will be used"},{"name":"validUntil","type":"number","description":"UNIX time in seconds until which this salt will be used"},{"name":"salt","type":"RawLong","description":"The server salt itself"}],"description":"Information about a single future server salt"},{"name":"future_salts","id":2924480661,"type":"FutureSalts","arguments":[{"name":"reqMsgId","type":"Long","description":"Requesting message ID"},{"name":"now","type":"number","description":"Current server UNIX timestamp in seconds"},{"name":"salts","type":"future_salt[]","description":"List of future salts"}],"description":"Information about future server salts"},{"name":"pong","id":880243653,"type":"Pong","arguments":[{"name":"msgId","type":"Long","description":"Message ID that contained `mt_ping`"},{"name":"pingId","type":"Long","description":"Ping ID that was sent in `mt_ping`"}],"description":"Response to a {@link tl.mtproto.RawPingRequest}"},{"name":"destroy_session_ok","id":3793765884,"type":"DestroySessionRes","arguments":[{"name":"sessionId","type":"RawLong","description":"Old session ID"}],"description":"Session was succesfully destroyed"},{"name":"destroy_session_none","id":1658015945,"type":"DestroySessionRes","arguments":[{"name":"sessionId","type":"RawLong","description":"Old session ID"}],"description":"Session was not destroyed because it does not exist"},{"name":"new_session_created","id":2663516424,"type":"NewSession","arguments":[{"name":"firstMsgId","type":"Long","description":"First message ID that is known by the server to be from this session."},{"name":"uniqueId","type":"Long","description":"Random number generated by the server every time a session is (re-)created"},{"name":"serverSalt","type":"RawLong","description":"Current server salt"}],"description":"The server notifies the client that a new session (from the server’s standpoint) had to be created to handle a client message. If, after this, the server receives a message with an even smaller `msg_id` within the same session, a similar notification will be generated for this `msg_id` as well. No such notifications are generated for high msg_id values.\nThis notification must be acknowledged by the client. It is necessary, for instance, for the client to understand that there is, in fact, a “gap” in the stream of notifications received from the server (the user may have failed to receive notifications during some period of time).\nClient should also resend all the messages that were sent before `firstMsgId`\n"},{"name":"msg_container","id":1945237724,"type":"MessageContainer","arguments":[{"name":"messages","type":"%Message[]","description":"List of messages in the container"}],"description":"A simple container that carries several messages"},{"name":"message","id":1538843921,"type":"Message","arguments":[{"name":"msgId","type":"Long","description":"Original message ID"},{"name":"seqno","type":"number","description":"Original message seq No"},{"name":"bytes","type":"number","description":"Length of the message"},{"name":"body","type":"Object","description":"Contents of the message"}],"description":"A message in the container"},{"name":"msg_copy","id":3764405938,"type":"MessageCopy","arguments":[{"name":"origMessage","type":"Message","description":"Original message"}],"description":"A copy of a message"},{"name":"gzip_packed","id":812830625,"type":"Object","arguments":[{"name":"packedData","type":"Buffer","description":"Gzipped contents of the message"}],"description":"An object, which was gzipped.\nAt the present time, it is supported in the body of an RPC response (i.e., as result in `rpc_result`) and generated by the server for a limited number of high-level queries. In addition, in the future it may be used to transmit non-service messages (i. e. RPC queries) from client to server.\n"},{"name":"msgs_ack","id":1658238041,"type":"MsgsAck","arguments":[{"name":"msgIds","type":"Long[]","description":"IDs of messages to be acknowledged. Maximum 8192 IDs."}],"description":"Receipt of virtually all messages (with the exception of some purely service ones as well as the plain-text messages used in the protocol for creating an authorization key) must be acknowledged.\nThis requires the use of the this service message (not requiring an acknowledgment itself)\nA server usually acknowledges the receipt of a message from a client (normally, an RPC query) using an RPC response. If a response is a long time coming, a server may first send a receipt acknowledgment, and somewhat later, the RPC response itself.\nA client normally acknowledges the receipt of a message from a server (usually, an RPC response) by adding an acknowledgment to the next RPC query if it is not transmitted too late (if it is generated, say, 60-120 seconds following the receipt of a message from the server). However, if for a long period of time there is no reason to send messages to the server or if there is a large number of unacknowledged messages from the server (say, over 16), the client transmits a stand-alone acknowledgment.\n"},{"name":"bad_msg_notification","id":2817521681,"type":"BadMsgNotification","arguments":[{"name":"badMsgId","type":"Long","description":"ID of the \"bad\" message"},{"name":"badMsgSeqno","type":"number","description":"Seq No of the \"bad\" message"},{"name":"errorCode","type":"number","description":"Error code. Known values:\n - `16`: Message ID was too small. Most likely, client time is wrong, it would be\n worthwhile to synchronize it using notification's message ID and re-send the\n original message with the \"correct\" message ID or wrap it in a container\n with a new message ID if the original message had waited too\n long on the client to be transmitted.\n - `17`: Message ID was too big. Similar to the previous case,\n the client time has to be synchronized, and the message re-sent\n - `18`: Incorrect two lower order msg_id bits. The server expects client\n message ID to be divisible by 4.\n - `19`: Container message ID is the same as the message ID of the previous message\n - `20`: Message is too old, and it cannot be verified whether the server\n has received a message with this ID or not\n - `32`: Message seq No was too small (the server has already received a message\n with a higher or same seq No)\n - `33`: Message seq No was too big (the server has already received a message\n with a lower or same seq No)\n - `34`: An even seq No expected (not content-relevant), but odd received\n - `35`: An odd seq No expected (content-relevant), but even received\n - `48`: Incorrect server salt was used. In practice, `mt_bad_server_salt` is used instead.\n - `64`: Incorrect container\n"}],"description":"Used by the server to notify client that the sent message was incorrect."},{"name":"bad_server_salt","id":3987424379,"type":"BadMsgNotification","arguments":[{"name":"badMsgId","type":"Long","description":"ID of the \"bad\" message"},{"name":"badMsgSeqno","type":"number","description":"Seq No of the \"bad\" message"},{"name":"errorCode","type":"number","description":"Always `48`"},{"name":"newServerSalt","type":"RawLong","description":"New server salt to be used"}],"description":"Used by the server to notify client that the sent message was incorrect."},{"name":"msg_resend_req","id":2105940488,"type":"MsgResendReq","arguments":[{"name":"msgIds","type":"Long[]","description":"IDs of the messages to be resent (up to 8192 IDs)"}],"description":"Explicit Request to Re-Send Messages\n\nThe remote party immediately responds by re-sending\nthe requested messages, normally using the same connection\nthat was used to transmit the query. If at least one message\nwith requested ID does not exist or has already been forgotten,\nor has been sent by the requesting party (known from parity),\n`MsgsStateInfo` is returned for all messages requested as if the\n`MsgResendReq` query had been a `MsgsStateReq` query as well.\n"},{"name":"msgs_state_req","id":3664378706,"type":"MsgsStateReq","arguments":[{"name":"msgIds","type":"Long[]","description":"IDs of the messages state of which should be sent (up to 8192 IDs)"}],"description":"Request for Message Status Information. If either party has not received information on the status of its outgoing messages for a while, it may explicitly request it from the other party\n"},{"name":"msgs_state_info","id":81704317,"type":"MsgsStateInfo","arguments":[{"name":"reqMsgId","type":"Long","description":"Requesting message ID"},{"name":"info","type":"Buffer","description":"Byte array containing exactly one byte for each message ID:\n - `1`: nothing is known about the message (ID is too small, the other party may have forgotten it)\n - `2`: message was not received (ID falls within the range of stored identifiers;\n however, the other party has certainly not received a message like that)\n - `3`: message not received (ID is too big; however, the other\n party has certainly not received it yet)\n - `4`: message received (note that this response is also at the same time a receipt acknowledgment)\n - `+8`: message already acknowledged\n - `+16`: message not requiring acknowledgment\n - `+32`: RPC query contained in message being processed or processing already complete\n - `+64`: content-related response to message already generated\n - `+128`: other party knows for a fact that message is already received\n"}],"description":"Informational Message regarding Status of Messages"},{"name":"msgs_all_info","id":2361446705,"type":"MsgsAllInfo","arguments":[{"name":"msgIds","type":"Long[]","description":"Message IDs that the other party is being informed about"},{"name":"info","type":"Buffer","description":"Byte array in the same format as {@link tl.mtproto.RawMsgs_state_info}"}],"description":"Voluntary Communication of Status of Messages. Either party may voluntarily inform the other party of the status of the messages transmitted by the other party.\n"},{"name":"msg_detailed_info","id":661470918,"type":"MsgDetailedInfo","arguments":[{"name":"msgId","type":"Long","description":"Original message ID that this message is informing about"},{"name":"answerMsgId","type":"Long","description":"Message ID that was the response to that message"},{"name":"bytes","type":"number","description":"Size of the answer message"},{"name":"status","type":"number","description":"Always `0`, but this may change in the future"}],"description":"Extended Voluntary Communication of Status of One Message.\n\nNormally used by the server to respond to the receipt of\na duplicate message ID, especially if a response to the\nmessage has already been generated and the response is large.\nIf the response is small, the server may re-send the answer\nitself instead. This message can also be used as a notification\ninstead of resending a large message.\n"},{"name":"msg_new_detailed_info","id":2157819615,"type":"MsgDetailedInfo","arguments":[{"name":"answerMsgId","type":"Long","description":"ID of the message that was sent by the server"},{"name":"bytes","type":"number","description":"Size of the answer message"},{"name":"status","type":"number","description":"Always `0`, but this may change in the future"}],"description":"Similar to {@link tl.mtproto.RawMsg_detailed_info}, but used to notify about messages that were created on the server not in response to an RPC query (e.g. updates) and were transmitted to the client some time ago, but not acknowledged\n"},{"name":"destroy_auth_key_ok","id":4133544404,"type":"DestroyAuthKeyRes","arguments":[]},{"name":"destroy_auth_key_none","id":178201177,"type":"DestroyAuthKeyRes","arguments":[]},{"name":"destroy_auth_key_fail","id":3926956819,"type":"DestroyAuthKeyRes","arguments":[]},{"name":"http_wait","id":2459514271,"type":"HttpWait","arguments":[{"name":"maxDelay","type":"number"},{"name":"waitAfter","type":"number"},{"name":"maxWait","type":"number"}]}],"methods":[{"name":"reqPqMulti","id":3195965169,"returns":"ResPQ","arguments":[{"name":"nonce","type":"Int128","description":"Randomly generated number that will be used in the later steps"}],"description":"Request for the first step of Authorization key derivation process.\n"},{"name":"reqDHParams","id":3608339646,"returns":"Server_DH_Params","arguments":[{"name":"nonce","type":"Int128","description":"Client nonce that was generated earlier"},{"name":"serverNonce","type":"Int128","description":"Server nonce that was received earlier"},{"name":"p","type":"Buffer","description":"Big endian encoded first factor. Note - `p < q`"},{"name":"q","type":"Buffer","description":"Big endian encoded second factor. Node - `p < q`"},{"name":"publicKeyFingerprint","type":"Long","description":"Fingerprint of the RSA key that the client has chosen"},{"name":"encryptedData","type":"Buffer","description":"Encrypted payload, obtained as follows:\n- Let `newNonce` be a random 32-bit number\n- Let `data` be a serialization of either {@link tl.mtproto.RawP_q_inner_data}\n or {@link tl.mtproto.RawP_q_inner_data_temp} with the generated `newNonce`\n- `dataWithHash = concat(sha1(data), data, random_bytes(235 - data.length))`\n- `encryptedData = dataWithHash ^ key.exponent % key.modulus`, where `key` is the\n server public key which was chosen\n"}],"description":"Request for the second step of Authorization key derivation process."},{"name":"setClientDHParams","id":4110704415,"returns":"Set_client_DH_params_answer","arguments":[{"name":"nonce","type":"Int128","description":"Client nonce that was generated earlier"},{"name":"serverNonce","type":"Int128","description":"Server nonce that was received earlier"},{"name":"encryptedData","type":"Buffer","description":"Encrypted DH parameters, obtained as follows:\n - Let `B` be a random 2048-bit (256 bytes) integer\n - `gB = g ^ B % dhPrime` (Diffie-Hellman)\n - Let `data` be a serialization of {@link tl.mtproto.RawClient_DH_inner_data}\n - `dataWithHash = concat(sha1(data), data, padding))`, where `padding` is\n 0-15 random bytes, such that `dataWithHash.length` is divisible by 16\n - `encryptedData = aes256_ige_encrypt(dataWithHash, key, iv)`, where `key` and `iv`\n are the same as ones used in {@link tl.mtproto.RawServer_DH_params_ok}\n"}],"description":"Request containing encrypted client-side variables for Diffie-Hellman exchange"},{"name":"rpcDropAnswer","id":1491380032,"returns":"RpcDropAnswer","arguments":[{"name":"reqMsgId","type":"Long","description":"ID of a message containing an RPC query to be cancelled"}],"description":"Cancellation of an RPC query. In certain situations, the client does not want to receive a response to an already transmitted RPC query, for example because the response turns out to be long and the client has decided to do without it because of insufficient link capacity. Simply interrupting the connection will not have any effect because the server would re-send the missing response at the first opportunity. Therefore, the client needs a way to cancel receipt of the RPC response message, actually acknowledging its receipt prior to it being in fact received, which will settle the server down and prevent it from re-sending the response. However, the client does not know the RPC response's message ID prior to receiving the response; the only thing it knows is the requesting message ID, i.e. the message ID of the relevant RPC query. Therefore, this special query is used\n"},{"name":"getFutureSalts","id":3105996036,"returns":"FutureSalts","arguments":[{"name":"num","type":"number"}]},{"name":"ping","id":2059302892,"returns":"Pong","arguments":[{"name":"pingId","type":"Long","description":"Random ping ID"}],"description":"Ping a server to test connection"},{"name":"pingDelayDisconnect","id":4081220492,"returns":"Pong","arguments":[{"name":"pingId","type":"Long","description":"Random ping ID"},{"name":"disconnectDelay","type":"number","description":"Disconnect delay in seconds"}],"description":"Works like ping. In addition, after this is received, the server starts a timer which will close the current connection `disconnectDelay` seconds later unless it receives a new message of the same type which automatically resets all previous timers.\nIf the client sends these pings once every 60 seconds, for example, it may set `disconnect_delay` equal to 75 seconds.\n"},{"name":"destroySession","id":3880853798,"returns":"DestroySessionRes","arguments":[{"name":"sessionId","type":"RawLong"}]},{"name":"destroyAuthKey","id":3510849888,"returns":"DestroyAuthKeyRes","arguments":[]}],"unions":[{"type":"ResPQ","subtypes":["resPQ"]},{"type":"P_Q_inner_data","subtypes":["p_q_inner_data_dc","p_q_inner_data_temp_dc"]},{"type":"Server_DH_Params","subtypes":["server_DH_params_ok"]},{"type":"Server_DH_inner_data","subtypes":["server_DH_inner_data"]},{"type":"Client_DH_Inner_Data","subtypes":["client_DH_inner_data"]},{"type":"Set_client_DH_params_answer","subtypes":["dh_gen_ok","dh_gen_retry","dh_gen_fail"]},{"type":"BindAuthKeyInner","subtypes":["bind_auth_key_inner"]},{"type":"RpcResult","subtypes":["rpc_result"]},{"type":"RpcError","subtypes":["rpc_error"]},{"type":"RpcDropAnswer","subtypes":["rpc_answer_unknown","rpc_answer_dropped_running","rpc_answer_dropped"]},{"type":"FutureSalt","subtypes":["future_salt"]},{"type":"FutureSalts","subtypes":["future_salts"]},{"type":"Pong","subtypes":["pong"]},{"type":"DestroySessionRes","subtypes":["destroy_session_ok","destroy_session_none"]},{"type":"NewSession","subtypes":["new_session_created"]},{"type":"MessageContainer","subtypes":["msg_container"]},{"type":"Message","subtypes":["message"]},{"type":"MessageCopy","subtypes":["msg_copy"]},{"type":"Object","subtypes":["gzip_packed"]},{"type":"MsgsAck","subtypes":["msgs_ack"]},{"type":"BadMsgNotification","subtypes":["bad_msg_notification","bad_server_salt"]},{"type":"MsgResendReq","subtypes":["msg_resend_req"]},{"type":"MsgsStateReq","subtypes":["msgs_state_req"]},{"type":"MsgsStateInfo","subtypes":["msgs_state_info"]},{"type":"MsgsAllInfo","subtypes":["msgs_all_info"]},{"type":"MsgDetailedInfo","subtypes":["msg_detailed_info","msg_new_detailed_info"]},{"type":"DestroyAuthKeyRes","subtypes":["destroy_auth_key_ok","destroy_auth_key_none","destroy_auth_key_fail"]},{"type":"HttpWait","subtypes":["http_wait"]}]}},"apiLayer":"131","api":{"$root":{"classes":[{"name":"updateMessageReactions","id":357013699,"type":"Update","arguments":[{"name":"peer","type":"Peer","description":"Peer"},{"name":"msgId","type":"number","description":"Message ID"},{"name":"reactions","type":"MessageReactions","description":"Reactions"}],"description":"New message reactions are available"},{"name":"messageReactions","id":3095012561,"type":"MessageReactions","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"min","type":"true","optional":true,"predicate":"flags.0","description":"Similar to min objects, used for message reaction constructors that are the same for all users so they don't have the reactions sent by the current user (you can use {@link messages.getMessagesReactions} to get the full reaction info)."},{"name":"results","type":"ReactionCount[]","description":"Reactions"}],"description":"Message reactions"},{"name":"reactionCount","id":1873957073,"type":"ReactionCount","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"chosen","type":"true","optional":true,"predicate":"flags.0","description":"Whether the current user sent this reaction"},{"name":"reaction","type":"string","description":"Reaction (a UTF8 emoji)"},{"name":"count","type":"number","description":"NUmber of users that reacted with this emoji"}],"description":"Reactions"},{"name":"messageReactionsList","id":3819856136,"type":"MessageReactionsList","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"count","type":"number","description":"Total number of reactions"},{"name":"reactions","type":"MessageUserReaction[]","description":"Reactions"},{"name":"users","type":"User[]","description":"Users that reacted like this"},{"name":"nextOffset","type":"string","optional":true,"predicate":"flags.0","description":"Next offset to use when fetching reactions using {@link messages.getMessageReactionsList}"}],"description":"List of message reactions"},{"name":"messageUserReaction","id":3530022076,"type":"MessageUserReaction","arguments":[{"name":"userId","type":"number","description":"ID of user that reacted this way"},{"name":"reaction","type":"string","description":"Reaction (UTF8 emoji)"}],"description":"Message reaction"},{"name":"error","id":3300522427,"type":"Error","arguments":[{"name":"code","type":"number","description":"Error code"},{"name":"text","type":"string","description":"Message"}],"description":"Error."},{"name":"ipPort","id":3560156531,"type":"IpPort","arguments":[{"name":"ipv4","type":"number"},{"name":"port","type":"number"}]},{"name":"ipPortSecret","id":932718150,"type":"IpPort","arguments":[{"name":"ipv4","type":"number"},{"name":"port","type":"number"},{"name":"secret","type":"Buffer"}]},{"name":"accessPointRule","id":1182381663,"type":"AccessPointRule","arguments":[{"name":"phonePrefixRules","type":"string"},{"name":"dcId","type":"number"},{"name":"ips","type":"IpPort[]"}]},{"name":"inputPeerPhotoFileLocationLegacy","id":668375447,"type":"InputFileLocation","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"big","type":"true","optional":true,"predicate":"flags.0"},{"name":"peer","type":"InputPeer"},{"name":"volumeId","type":"Long"},{"name":"localId","type":"number"}]},{"name":"inputStickerSetThumbLegacy","id":230353641,"type":"InputFileLocation","arguments":[{"name":"stickerset","type":"InputStickerSet"},{"name":"volumeId","type":"Long"},{"name":"localId","type":"number"}]},{"name":"inputPeerEmpty","id":2134579434,"type":"InputPeer","arguments":[],"description":"An empty constructor, no user or chat is defined."},{"name":"inputPeerSelf","id":2107670217,"type":"InputPeer","arguments":[],"description":"Defines the current user."},{"name":"inputPeerChat","id":396093539,"type":"InputPeer","arguments":[{"name":"chatId","type":"number","description":"Chat idientifier"}],"description":"Defines a chat for further interaction."},{"name":"inputPeerUser","id":2072935910,"type":"InputPeer","arguments":[{"name":"userId","type":"number","description":"User identifier"},{"name":"accessHash","type":"Long","description":"access_hash value from the {@link user} constructor"}],"description":"Defines a user for further interaction."},{"name":"inputPeerChannel","id":548253432,"type":"InputPeer","arguments":[{"name":"channelId","type":"number","description":"Channel identifier"},{"name":"accessHash","type":"Long","description":"access_hash value from the {@link channel} constructor"}],"description":"Defines a channel for further interaction."},{"name":"inputPeerUserFromMessage","id":398123750,"type":"InputPeer","arguments":[{"name":"peer","type":"InputPeer","description":"The chat where the user was seen"},{"name":"msgId","type":"number","description":"The message ID"},{"name":"userId","type":"number","description":"The identifier of the user that was seen"}],"description":"Defines a min user that was seen in a certain message of a certain chat."},{"name":"inputPeerChannelFromMessage","id":2627073979,"type":"InputPeer","arguments":[{"name":"peer","type":"InputPeer","description":"The chat where the channel's message was seen"},{"name":"msgId","type":"number","description":"The message ID"},{"name":"channelId","type":"number","description":"The identifier of the channel that was seen"}],"description":"Defines a min channel that was seen in a certain message of a certain chat."},{"name":"inputUserEmpty","id":3112732367,"type":"InputUser","arguments":[],"description":"Empty constructor, does not define a user."},{"name":"inputUserSelf","id":4156666175,"type":"InputUser","arguments":[],"description":"Defines the current user."},{"name":"inputUser","id":3626575894,"type":"InputUser","arguments":[{"name":"userId","type":"number","description":"User identifier"},{"name":"accessHash","type":"Long","description":"access_hash value from the {@link user} constructor"}],"description":"Defines a user for further interaction."},{"name":"inputUserFromMessage","id":756118935,"type":"InputUser","arguments":[{"name":"peer","type":"InputPeer","description":"The chat where the user was seen"},{"name":"msgId","type":"number","description":"The message ID"},{"name":"userId","type":"number","description":"The identifier of the user that was seen"}],"description":"Defines a min user that was seen in a certain message of a certain chat."},{"name":"inputPhoneContact","id":4086478836,"type":"InputContact","arguments":[{"name":"clientId","type":"Long","description":"User identifier on the client"},{"name":"phone","type":"string","description":"Phone number"},{"name":"firstName","type":"string","description":"Contact's first name"},{"name":"lastName","type":"string","description":"Contact's last name"}],"description":"Phone contact. The client_id is just an arbitrary contact ID: it should be set, for example, to an incremental number when using {@link contacts.importContacts}, in order to retry importing only the contacts that weren't imported successfully."},{"name":"inputFile","id":4113560191,"type":"InputFile","arguments":[{"name":"id","type":"Long","description":"Random file identifier created by the client"},{"name":"parts","type":"number","description":"Number of parts saved"},{"name":"name","type":"string","description":"Full name of the file"},{"name":"md5Checksum","type":"string","description":"In case the file's md5-hash was passed, contents of the file will be checked prior to use"}],"description":"Defines a file saved in parts using the method {@link upload.saveFilePart}."},{"name":"inputFileBig","id":4199484341,"type":"InputFile","arguments":[{"name":"id","type":"Long","description":"Random file id, created by the client"},{"name":"parts","type":"number","description":"Number of parts saved"},{"name":"name","type":"string","description":"Full file name"}],"description":"Assigns a big file (over 10Mb in size), saved in part using the method {@link upload.saveBigFilePart}."},{"name":"inputMediaEmpty","id":2523198847,"type":"InputMedia","arguments":[],"description":"Empty media content of a message."},{"name":"inputMediaUploadedPhoto","id":505969924,"type":"InputMedia","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"file","type":"InputFile","description":"The uploaded file"},{"name":"stickers","type":"InputDocument[]","optional":true,"predicate":"flags.0","description":"Attached mask stickers"},{"name":"ttlSeconds","type":"number","optional":true,"predicate":"flags.1","description":"Time to live in seconds of self-destructing photo"}],"description":"Photo"},{"name":"inputMediaPhoto","id":3015312949,"type":"InputMedia","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"id","type":"InputPhoto","description":"Photo to be forwarded"},{"name":"ttlSeconds","type":"number","optional":true,"predicate":"flags.0","description":"Time to live in seconds of self-destructing photo"}],"description":"Forwarded photo"},{"name":"inputMediaGeoPoint","id":4190388548,"type":"InputMedia","arguments":[{"name":"geoPoint","type":"InputGeoPoint","description":"GeoPoint"}],"description":"Map."},{"name":"inputMediaContact","id":4171988475,"type":"InputMedia","arguments":[{"name":"phoneNumber","type":"string","description":"Phone number"},{"name":"firstName","type":"string","description":"Contact's first name"},{"name":"lastName","type":"string","description":"Contact's last name"},{"name":"vcard","type":"string","description":"Contact vcard"}],"description":"Phonebook contact"},{"name":"inputMediaUploadedDocument","id":1530447553,"type":"InputMedia","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"nosoundVideo","type":"true","optional":true,"predicate":"flags.3","description":"Whether the specified document is a video file with no audio tracks (a GIF animation (even as MPEG4), for example)"},{"name":"forceFile","type":"true","optional":true,"predicate":"flags.4","description":"Force the media file to be uploaded as document"},{"name":"file","type":"InputFile","description":"The uploaded file"},{"name":"thumb","type":"InputFile","optional":true,"predicate":"flags.2","description":"Thumbnail of the document, uploaded as for the file"},{"name":"mimeType","type":"string","description":"MIME type of document"},{"name":"attributes","type":"DocumentAttribute[]","description":"Attributes that specify the type of the document (video, audio, voice, sticker, etc.)"},{"name":"stickers","type":"InputDocument[]","optional":true,"predicate":"flags.0","description":"Attached stickers"},{"name":"ttlSeconds","type":"number","optional":true,"predicate":"flags.1","description":"Time to live in seconds of self-destructing document"}],"description":"New document"},{"name":"inputMediaDocument","id":860303448,"type":"InputMedia","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"id","type":"InputDocument","description":"The document to be forwarded."},{"name":"ttlSeconds","type":"number","optional":true,"predicate":"flags.0","description":"Time to live of self-destructing document"},{"name":"query","type":"string","optional":true,"predicate":"flags.1"}],"description":"Forwarded document"},{"name":"inputMediaVenue","id":3242007569,"type":"InputMedia","arguments":[{"name":"geoPoint","type":"InputGeoPoint","description":"Geolocation"},{"name":"title","type":"string","description":"Venue name"},{"name":"address","type":"string","description":"Physical address of the venue"},{"name":"provider","type":"string","description":"Venue provider: currently only \"foursquare\" and \"gplaces\" need to be supported"},{"name":"venueId","type":"string","description":"Venue ID in the provider's database"},{"name":"venueType","type":"string","description":"Venue type in the provider's database"}],"description":"Can be used to send a venue geolocation."},{"name":"inputMediaPhotoExternal","id":3854302746,"type":"InputMedia","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"url","type":"string","description":"URL of the photo"},{"name":"ttlSeconds","type":"number","optional":true,"predicate":"flags.0","description":"Self-destruct time to live of photo"}],"description":"New photo that will be uploaded by the server using the specified URL"},{"name":"inputMediaDocumentExternal","id":4216511641,"type":"InputMedia","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"url","type":"string","description":"URL of the document"},{"name":"ttlSeconds","type":"number","optional":true,"predicate":"flags.0","description":"Self-destruct time to live of document"}],"description":"Document that will be downloaded by the telegram servers"},{"name":"inputMediaGame","id":3544138739,"type":"InputMedia","arguments":[{"name":"id","type":"InputGame","description":"The game to forward"}],"description":"A game"},{"name":"inputMediaInvoice","id":3648624756,"type":"InputMedia","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"title","type":"string","description":"Product name, 1-32 characters"},{"name":"description","type":"string","description":"Product description, 1-255 characters"},{"name":"photo","type":"InputWebDocument","optional":true,"predicate":"flags.0","description":"URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for."},{"name":"invoice","type":"Invoice","description":"The actual invoice"},{"name":"payload","type":"Buffer","description":"Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes."},{"name":"provider","type":"string","description":"Payments provider token, obtained via Botfather"},{"name":"providerData","type":"DataJSON","description":"JSON-encoded data about the invoice, which will be shared with the payment provider. A detailed description of required fields should be provided by the payment provider."},{"name":"startParam","type":"string","optional":true,"predicate":"flags.1","description":"Start parameter"}],"description":"Generated invoice of a bot payment"},{"name":"inputMediaGeoLive","id":2535434307,"type":"InputMedia","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"stopped","type":"true","optional":true,"predicate":"flags.0","description":"Whether sending of the geolocation was stopped"},{"name":"geoPoint","type":"InputGeoPoint","description":"Current geolocation"},{"name":"heading","type":"number","optional":true,"predicate":"flags.2","description":"For live locations, a direction in which the location moves, in degrees; 1-360."},{"name":"period","type":"number","optional":true,"predicate":"flags.1","description":"Validity period of the current location"},{"name":"proximityNotificationRadius","type":"number","optional":true,"predicate":"flags.3","description":"For live locations, a maximum distance to another chat member for proximity alerts, in meters (0-100000)"}],"description":"Live geolocation"},{"name":"inputMediaPoll","id":261416433,"type":"InputMedia","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"poll","type":"Poll","description":"The poll to send"},{"name":"correctAnswers","type":"Buffer[]","optional":true,"predicate":"flags.0","description":"Correct answer IDs (for quiz polls)"},{"name":"solution","type":"string","optional":true,"predicate":"flags.1","description":"Explanation of quiz solution"},{"name":"solutionEntities","type":"MessageEntity[]","optional":true,"predicate":"flags.1","description":"Message entities for styled text"}],"description":"A poll"},{"name":"inputMediaDice","id":3866083195,"type":"InputMedia","arguments":[{"name":"emoticon","type":"string","description":"The emoji, for now \"🏀\", \"🎲\" and \"🎯\" are supported"}],"description":"Send a dice-based animated sticker"},{"name":"inputChatPhotoEmpty","id":480546647,"type":"InputChatPhoto","arguments":[],"description":"Empty constructor, remove group photo."},{"name":"inputChatUploadedPhoto","id":3326243406,"type":"InputChatPhoto","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"file","type":"InputFile","optional":true,"predicate":"flags.0","description":"File saved in parts using the method {@link upload.saveFilePart}"},{"name":"video","type":"InputFile","optional":true,"predicate":"flags.1","description":"Square video for animated profile picture"},{"name":"videoStartTs","type":"Double","optional":true,"predicate":"flags.2","description":"Timestamp that should be shown as static preview to the user (seconds)"}],"description":"New photo to be set as group profile photo."},{"name":"inputChatPhoto","id":2303962423,"type":"InputChatPhoto","arguments":[{"name":"id","type":"InputPhoto","description":"Existing photo"}],"description":"Existing photo to be set as a chat profile photo."},{"name":"inputGeoPointEmpty","id":3837862870,"type":"InputGeoPoint","arguments":[],"description":"Empty GeoPoint constructor."},{"name":"inputGeoPoint","id":1210199983,"type":"InputGeoPoint","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"lat","type":"Double","description":"Latitide"},{"name":"long","type":"Double","description":"Longtitude"},{"name":"accuracyRadius","type":"number","optional":true,"predicate":"flags.0","description":"The estimated horizontal accuracy of the location, in meters; as defined by the sender."}],"description":"Defines a GeoPoint by its coordinates."},{"name":"inputPhotoEmpty","id":483901197,"type":"InputPhoto","arguments":[],"description":"Empty constructor."},{"name":"inputPhoto","id":1001634122,"type":"InputPhoto","arguments":[{"name":"id","type":"Long","description":"Photo identifier"},{"name":"accessHash","type":"Long","description":"access_hash value from the {@link photo} constructor"},{"name":"fileReference","type":"Buffer","description":"File reference"}],"description":"Defines a photo for further interaction."},{"name":"inputFileLocation","id":3755650017,"type":"InputFileLocation","arguments":[{"name":"volumeId","type":"Long","description":"Server volume"},{"name":"localId","type":"number","description":"File identifier"},{"name":"secret","type":"Long","description":"Check sum to access the file"},{"name":"fileReference","type":"Buffer","description":"File reference"}],"description":"DEPRECATED location of a photo"},{"name":"inputEncryptedFileLocation","id":4112735573,"type":"InputFileLocation","arguments":[{"name":"id","type":"Long","description":"File ID, id parameter value from {@link encryptedFile}"},{"name":"accessHash","type":"Long","description":"Checksum, access_hash parameter value from {@link encryptedFile}"}],"description":"Location of encrypted secret chat file."},{"name":"inputDocumentFileLocation","id":3134223748,"type":"InputFileLocation","arguments":[{"name":"id","type":"Long","description":"Document ID"},{"name":"accessHash","type":"Long","description":"access_hash parameter from the {@link document} constructor"},{"name":"fileReference","type":"Buffer","description":"File reference"},{"name":"thumbSize","type":"string","description":"Thumbnail size to download the thumbnail"}],"description":"Document location (video, voice, audio, basically every type except photo)"},{"name":"inputSecureFileLocation","id":3418877480,"type":"InputFileLocation","arguments":[{"name":"id","type":"Long","description":"File ID, id parameter value from {@link secureFile}"},{"name":"accessHash","type":"Long","description":"Checksum, access_hash parameter value from {@link secureFile}"}],"description":"Location of encrypted telegram passport file."},{"name":"inputTakeoutFileLocation","id":700340377,"type":"InputFileLocation","arguments":[],"description":"Empty constructor for takeout"},{"name":"inputPhotoFileLocation","id":1075322878,"type":"InputFileLocation","arguments":[{"name":"id","type":"Long","description":"Photo ID, obtained from the {@link photo} object"},{"name":"accessHash","type":"Long","description":"Photo's access hash, obtained from the {@link photo} object"},{"name":"fileReference","type":"Buffer","description":"File reference"},{"name":"thumbSize","type":"string","description":"The PhotoSize to download: must be set to the type field of the desired PhotoSize object of the {@link photo}"}],"description":"Use this object to download a photo with {@link upload.getFile} method"},{"name":"inputPhotoLegacyFileLocation","id":3627312883,"type":"InputFileLocation","arguments":[{"name":"id","type":"Long","description":"Photo ID"},{"name":"accessHash","type":"Long","description":"Access hash"},{"name":"fileReference","type":"Buffer","description":"File reference"},{"name":"volumeId","type":"Long","description":"Volume ID"},{"name":"localId","type":"number","description":"Local ID"},{"name":"secret","type":"Long","description":"Secret"}],"description":"Legacy photo file location"},{"name":"inputPeerPhotoFileLocation","id":925204121,"type":"InputFileLocation","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"big","type":"true","optional":true,"predicate":"flags.0","description":"Whether to download the high-quality version of the picture"},{"name":"peer","type":"InputPeer","description":"The peer whose profile picture should be downloaded"},{"name":"photoId","type":"Long"}],"description":"Location of profile photo of channel/group/supergroup/user"},{"name":"inputStickerSetThumb","id":2642736091,"type":"InputFileLocation","arguments":[{"name":"stickerset","type":"InputStickerSet","description":"Sticker set"},{"name":"thumbVersion","type":"number"}],"description":"Location of stickerset thumbnail (see files)"},{"name":"inputGroupCallStream","id":3148158521,"type":"InputFileLocation","arguments":[{"name":"call","type":"InputGroupCall"},{"name":"timeMs","type":"Long"},{"name":"scale","type":"number"}]},{"name":"peerUser","id":2645671021,"type":"Peer","arguments":[{"name":"userId","type":"number","description":"User identifier"}],"description":"Chat partner"},{"name":"peerChat","id":3134252475,"type":"Peer","arguments":[{"name":"chatId","type":"number","description":"Group identifier"}],"description":"Group."},{"name":"peerChannel","id":3185435954,"type":"Peer","arguments":[{"name":"channelId","type":"number","description":"Channel ID"}],"description":"Channel/supergroup"},{"name":"userEmpty","id":537022650,"type":"User","arguments":[{"name":"id","type":"number","description":"User identifier or 0"}],"description":"Empty constructor, non-existent user."},{"name":"user","id":2474924225,"type":"User","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"self","type":"true","optional":true,"predicate":"flags.10","description":"Whether this user indicates the currently logged in user"},{"name":"contact","type":"true","optional":true,"predicate":"flags.11","description":"Whether this user is a contact"},{"name":"mutualContact","type":"true","optional":true,"predicate":"flags.12","description":"Whether this user is a mutual contact"},{"name":"deleted","type":"true","optional":true,"predicate":"flags.13","description":"Whether the account of this user was deleted"},{"name":"bot","type":"true","optional":true,"predicate":"flags.14","description":"Is this user a bot?"},{"name":"botChatHistory","type":"true","optional":true,"predicate":"flags.15","description":"Can the bot see all messages in groups?"},{"name":"botNochats","type":"true","optional":true,"predicate":"flags.16","description":"Can the bot be added to groups?"},{"name":"verified","type":"true","optional":true,"predicate":"flags.17","description":"Whether this user is verified"},{"name":"restricted","type":"true","optional":true,"predicate":"flags.18","description":"Access to this user must be restricted for the reason specified in restriction_reason"},{"name":"min","type":"true","optional":true,"predicate":"flags.20","description":"See min"},{"name":"botInlineGeo","type":"true","optional":true,"predicate":"flags.21","description":"Whether the bot can request our geolocation in inline mode"},{"name":"support","type":"true","optional":true,"predicate":"flags.23","description":"Whether this is an official support user"},{"name":"scam","type":"true","optional":true,"predicate":"flags.24","description":"This may be a scam user"},{"name":"applyMinPhoto","type":"true","optional":true,"predicate":"flags.25","description":"If set, the profile picture for this user should be refetched"},{"name":"fake","type":"true","optional":true,"predicate":"flags.26"},{"name":"id","type":"number","description":"ID of the user"},{"name":"accessHash","type":"Long","optional":true,"predicate":"flags.0","description":"Access hash of the user"},{"name":"firstName","type":"string","optional":true,"predicate":"flags.1","description":"First name"},{"name":"lastName","type":"string","optional":true,"predicate":"flags.2","description":"Last name"},{"name":"username","type":"string","optional":true,"predicate":"flags.3","description":"Username"},{"name":"phone","type":"string","optional":true,"predicate":"flags.4","description":"Phone number"},{"name":"photo","type":"UserProfilePhoto","optional":true,"predicate":"flags.5","description":"Profile picture of user"},{"name":"status","type":"UserStatus","optional":true,"predicate":"flags.6","description":"Online status of user"},{"name":"botInfoVersion","type":"number","optional":true,"predicate":"flags.14","description":"Version of the {@link userFull}, incremented every time it changes"},{"name":"restrictionReason","type":"RestrictionReason[]","optional":true,"predicate":"flags.18","description":"Contains the reason why access to this user must be restricted."},{"name":"botInlinePlaceholder","type":"string","optional":true,"predicate":"flags.19","description":"Inline placeholder for this inline bot"},{"name":"langCode","type":"string","optional":true,"predicate":"flags.22","description":"Language code of the user"}],"description":"Indicates info about a certain user"},{"name":"userProfilePhotoEmpty","id":1326562017,"type":"UserProfilePhoto","arguments":[],"description":"Profile photo has not been set, or was hidden."},{"name":"userProfilePhoto","id":2194798342,"type":"UserProfilePhoto","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"hasVideo","type":"true","optional":true,"predicate":"flags.0","description":"Whether an animated profile picture is available for this user"},{"name":"photoId","type":"Long","description":"Identifier of the respective photo
Parameter added in Layer 2"},{"name":"strippedThumb","type":"Buffer","optional":true,"predicate":"flags.1"},{"name":"dcId","type":"number","description":"DC ID where the photo is stored"}],"description":"User profile photo."},{"name":"userStatusEmpty","id":164646985,"type":"UserStatus","arguments":[],"description":"User status has not been set yet."},{"name":"userStatusOnline","id":3988339017,"type":"UserStatus","arguments":[{"name":"expires","type":"number","description":"Time to expiration of the current online status"}],"description":"Online status of the user."},{"name":"userStatusOffline","id":9203775,"type":"UserStatus","arguments":[{"name":"wasOnline","type":"number","description":"Time the user was last seen online"}],"description":"The user's offline status."},{"name":"userStatusRecently","id":3798942449,"type":"UserStatus","arguments":[],"description":"Online status: last seen recently"},{"name":"userStatusLastWeek","id":129960444,"type":"UserStatus","arguments":[],"description":"Online status: last seen last week"},{"name":"userStatusLastMonth","id":2011940674,"type":"UserStatus","arguments":[],"description":"Online status: last seen last month"},{"name":"chatEmpty","id":2611140608,"type":"Chat","arguments":[{"name":"id","type":"number","description":"Group identifier"}],"description":"Empty constructor, group doesn't exist"},{"name":"chat","id":1004149726,"type":"Chat","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"creator","type":"true","optional":true,"predicate":"flags.0","description":"Whether the current user is the creator of the group"},{"name":"kicked","type":"true","optional":true,"predicate":"flags.1","description":"Whether the current user was kicked from the group"},{"name":"left","type":"true","optional":true,"predicate":"flags.2","description":"Whether the current user has left the group"},{"name":"deactivated","type":"true","optional":true,"predicate":"flags.5","description":"Whether the group was migrated"},{"name":"callActive","type":"true","optional":true,"predicate":"flags.23"},{"name":"callNotEmpty","type":"true","optional":true,"predicate":"flags.24"},{"name":"id","type":"number","description":"ID of the group"},{"name":"title","type":"string","description":"Title"},{"name":"photo","type":"ChatPhoto","description":"Chat photo"},{"name":"participantsCount","type":"number","description":"Participant count"},{"name":"date","type":"number","description":"Date of creation of the group"},{"name":"version","type":"number","description":"Used in basic groups to reorder updates and make sure that all of them were received."},{"name":"migratedTo","type":"InputChannel","optional":true,"predicate":"flags.6","description":"Means this chat was upgraded to a supergroup"},{"name":"adminRights","type":"ChatAdminRights","optional":true,"predicate":"flags.14","description":"Admin rights of the user in the group"},{"name":"defaultBannedRights","type":"ChatBannedRights","optional":true,"predicate":"flags.18","description":"Default banned rights of all users in the group"}],"description":"Info about a group"},{"name":"chatForbidden","id":120753115,"type":"Chat","arguments":[{"name":"id","type":"number","description":"User identifier"},{"name":"title","type":"string","description":"Group name"}],"description":"A group to which the user has no access. E.g., because the user was kicked from the group."},{"name":"channel","id":3541734942,"type":"Chat","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"creator","type":"true","optional":true,"predicate":"flags.0","description":"Whether the current user is the creator of this channel"},{"name":"left","type":"true","optional":true,"predicate":"flags.2","description":"Whether the current user has left this channel"},{"name":"broadcast","type":"true","optional":true,"predicate":"flags.5","description":"Is this a channel?"},{"name":"verified","type":"true","optional":true,"predicate":"flags.7","description":"Is this channel verified by telegram?"},{"name":"megagroup","type":"true","optional":true,"predicate":"flags.8","description":"Is this a supergroup?"},{"name":"restricted","type":"true","optional":true,"predicate":"flags.9","description":"Whether viewing/writing in this channel for a reason (see restriction_reason"},{"name":"signatures","type":"true","optional":true,"predicate":"flags.11","description":"Whether signatures are enabled (channels)"},{"name":"min","type":"true","optional":true,"predicate":"flags.12","description":"See min"},{"name":"scam","type":"true","optional":true,"predicate":"flags.19","description":"This channel/supergroup is probably a scam"},{"name":"hasLink","type":"true","optional":true,"predicate":"flags.20","description":"Whether this channel has a private join link"},{"name":"hasGeo","type":"true","optional":true,"predicate":"flags.21","description":"Whether this channel has a geo position"},{"name":"slowmodeEnabled","type":"true","optional":true,"predicate":"flags.22","description":"Whether slow mode is enabled for groups to prevent flood in chat"},{"name":"callActive","type":"true","optional":true,"predicate":"flags.23"},{"name":"callNotEmpty","type":"true","optional":true,"predicate":"flags.24"},{"name":"fake","type":"true","optional":true,"predicate":"flags.25"},{"name":"gigagroup","type":"true","optional":true,"predicate":"flags.26","description":"Is this a broadcast group?"},{"name":"id","type":"number","description":"ID of the channel"},{"name":"accessHash","type":"Long","optional":true,"predicate":"flags.13","description":"Access hash"},{"name":"title","type":"string","description":"Title"},{"name":"username","type":"string","optional":true,"predicate":"flags.6","description":"Username"},{"name":"photo","type":"ChatPhoto","description":"Profile photo"},{"name":"date","type":"number","description":"Date when the user joined the supergroup/channel, or if the user isn't a member, its creation date"},{"name":"version","type":"number","description":"Version of the channel (always 0)"},{"name":"restrictionReason","type":"RestrictionReason[]","optional":true,"predicate":"flags.9","description":"Contains the reason why access to this channel must be restricted."},{"name":"adminRights","type":"ChatAdminRights","optional":true,"predicate":"flags.14","description":"Admin rights of the user in this channel (see rights)"},{"name":"bannedRights","type":"ChatBannedRights","optional":true,"predicate":"flags.15","description":"Banned rights of the user in this channel (see rights)"},{"name":"defaultBannedRights","type":"ChatBannedRights","optional":true,"predicate":"flags.18","description":"Default chat rights (see rights)"},{"name":"participantsCount","type":"number","optional":true,"predicate":"flags.17","description":"Participant count"}],"description":"Channel/supergroup info"},{"name":"channelForbidden","id":681420594,"type":"Chat","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"broadcast","type":"true","optional":true,"predicate":"flags.5","description":"Is this a channel"},{"name":"megagroup","type":"true","optional":true,"predicate":"flags.8","description":"Is this a supergroup"},{"name":"id","type":"number","description":"Channel ID"},{"name":"accessHash","type":"Long","description":"Access hash"},{"name":"title","type":"string","description":"Title"},{"name":"untilDate","type":"number","optional":true,"predicate":"flags.16","description":"The ban is valid until the specified date"}],"description":"Indicates a channel/supergroup we can't access because we were banned, or for some other reason."},{"name":"chatFull","id":2317232515,"type":"ChatFull","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"canSetUsername","type":"true","optional":true,"predicate":"flags.7","description":"Can we change the username of this chat"},{"name":"hasScheduled","type":"true","optional":true,"predicate":"flags.8","description":"Whether scheduled messages are available"},{"name":"id","type":"number","description":"ID of the chat"},{"name":"about","type":"string","description":"About string for this chat"},{"name":"participants","type":"ChatParticipants","description":"Participant list"},{"name":"chatPhoto","type":"Photo","optional":true,"predicate":"flags.2","description":"Chat photo"},{"name":"notifySettings","type":"PeerNotifySettings","description":"Notification settings"},{"name":"exportedInvite","type":"ExportedChatInvite","optional":true,"predicate":"flags.13","description":"Chat invite"},{"name":"botInfo","type":"BotInfo[]","optional":true,"predicate":"flags.3","description":"Info about bots that are in this chat"},{"name":"pinnedMsgId","type":"number","optional":true,"predicate":"flags.6","description":"Message ID of the last pinned message"},{"name":"folderId","type":"number","optional":true,"predicate":"flags.11","description":"Peer folder ID, for more info click here"},{"name":"call","type":"InputGroupCall","optional":true,"predicate":"flags.12"},{"name":"ttlPeriod","type":"number","optional":true,"predicate":"flags.14"},{"name":"groupcallDefaultJoinAs","type":"Peer","optional":true,"predicate":"flags.15"}],"description":"Detailed chat info"},{"name":"channelFull","id":1418477459,"type":"ChatFull","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"canViewParticipants","type":"true","optional":true,"predicate":"flags.3","description":"Can we vew the participant list?"},{"name":"canSetUsername","type":"true","optional":true,"predicate":"flags.6","description":"Can we set the channel's username?"},{"name":"canSetStickers","type":"true","optional":true,"predicate":"flags.7","description":"Can we {@link channels.setStickers} a stickerpack to the supergroup?"},{"name":"hiddenPrehistory","type":"true","optional":true,"predicate":"flags.10","description":"Is the history before we joined hidden to us?"},{"name":"canSetLocation","type":"true","optional":true,"predicate":"flags.16","description":"Can we set the geolocation of this group (for geogroups)"},{"name":"hasScheduled","type":"true","optional":true,"predicate":"flags.19","description":"Whether scheduled messages are available"},{"name":"canViewStats","type":"true","optional":true,"predicate":"flags.20","description":"Can the user view channel/supergroup statistics"},{"name":"blocked","type":"true","optional":true,"predicate":"flags.22","description":"Whether any anonymous admin of this supergroup was blocked: if set, you won't receive messages from anonymous group admins in discussion replies via @replies"},{"name":"id","type":"number","description":"ID of the channel"},{"name":"about","type":"string","description":"Info about the channel"},{"name":"participantsCount","type":"number","optional":true,"predicate":"flags.0","description":"Number of participants of the channel"},{"name":"adminsCount","type":"number","optional":true,"predicate":"flags.1","description":"Number of channel admins"},{"name":"kickedCount","type":"number","optional":true,"predicate":"flags.2","description":"Number of users kicked from the channel"},{"name":"bannedCount","type":"number","optional":true,"predicate":"flags.2","description":"Number of users banned from the channel"},{"name":"onlineCount","type":"number","optional":true,"predicate":"flags.13","description":"Number of users currently online"},{"name":"readInboxMaxId","type":"number","description":"Position up to which all incoming messages are read."},{"name":"readOutboxMaxId","type":"number","description":"Position up to which all outgoing messages are read."},{"name":"unreadCount","type":"number","description":"Count of unread messages"},{"name":"chatPhoto","type":"Photo","description":"Channel picture"},{"name":"notifySettings","type":"PeerNotifySettings","description":"Notification settings"},{"name":"exportedInvite","type":"ExportedChatInvite","optional":true,"predicate":"flags.23","description":"Invite link"},{"name":"botInfo","type":"BotInfo[]","description":"Info about bots in the channel/supergrup"},{"name":"migratedFromChatId","type":"number","optional":true,"predicate":"flags.4","description":"The chat ID from which this group was migrated"},{"name":"migratedFromMaxId","type":"number","optional":true,"predicate":"flags.4","description":"The message ID in the original chat at which this group was migrated"},{"name":"pinnedMsgId","type":"number","optional":true,"predicate":"flags.5","description":"Message ID of the last pinned message"},{"name":"stickerset","type":"StickerSet","optional":true,"predicate":"flags.8","description":"Associated stickerset"},{"name":"availableMinId","type":"number","optional":true,"predicate":"flags.9","description":"Identifier of a maximum unavailable message in a channel due to hidden history."},{"name":"folderId","type":"number","optional":true,"predicate":"flags.11","description":"Peer folder ID, for more info click here"},{"name":"linkedChatId","type":"number","optional":true,"predicate":"flags.14","description":"ID of the linked discussion chat for channels"},{"name":"location","type":"ChannelLocation","optional":true,"predicate":"flags.15","description":"Location of the geo group"},{"name":"slowmodeSeconds","type":"number","optional":true,"predicate":"flags.17","description":"If specified, users in supergroups will only be able to send one message every slowmode_seconds seconds"},{"name":"slowmodeNextSendDate","type":"number","optional":true,"predicate":"flags.18","description":"Indicates when the user will be allowed to send another message in the supergroup (UNIX timestamp in seconds)"},{"name":"statsDc","type":"number","optional":true,"predicate":"flags.12","description":"If set, specifies the DC to use for fetching channel statistics"},{"name":"pts","type":"number","description":"Latest PTS for this channel"},{"name":"call","type":"InputGroupCall","optional":true,"predicate":"flags.21"},{"name":"ttlPeriod","type":"number","optional":true,"predicate":"flags.24"},{"name":"pendingSuggestions","type":"string[]","optional":true,"predicate":"flags.25"},{"name":"groupcallDefaultJoinAs","type":"Peer","optional":true,"predicate":"flags.26"}],"description":"Full info about a channel/supergroup"},{"name":"chatParticipant","id":3369552190,"type":"ChatParticipant","arguments":[{"name":"userId","type":"number","description":"Member user ID"},{"name":"inviterId","type":"number","description":"ID of the user that added the member to the group"},{"name":"date","type":"number","description":"Date added to the group"}],"description":"Group member."},{"name":"chatParticipantCreator","id":3658699658,"type":"ChatParticipant","arguments":[{"name":"userId","type":"number","description":"ID of the user that created the group"}],"description":"Represents the creator of the group"},{"name":"chatParticipantAdmin","id":3805733942,"type":"ChatParticipant","arguments":[{"name":"userId","type":"number","description":"ID of a group member that is admin"},{"name":"inviterId","type":"number","description":"ID of the user that added the member to the group"},{"name":"date","type":"number","description":"Date when the user was added"}],"description":"Chat admin"},{"name":"chatParticipantsForbidden","id":4237298731,"type":"ChatParticipants","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"chatId","type":"number","description":"Group ID"},{"name":"selfParticipant","type":"ChatParticipant","optional":true,"predicate":"flags.0","description":"Info about the group membership of the current user"}],"description":"Info on members is unavailable"},{"name":"chatParticipants","id":1061556205,"type":"ChatParticipants","arguments":[{"name":"chatId","type":"number","description":"Group identifier"},{"name":"participants","type":"ChatParticipant[]","description":"List of group members"},{"name":"version","type":"number","description":"Group version number"}],"description":"Group members."},{"name":"chatPhotoEmpty","id":935395612,"type":"ChatPhoto","arguments":[],"description":"Group photo is not set."},{"name":"chatPhoto","id":476978193,"type":"ChatPhoto","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"hasVideo","type":"true","optional":true,"predicate":"flags.0","description":"Whether the user has an animated profile picture"},{"name":"photoId","type":"Long"},{"name":"strippedThumb","type":"Buffer","optional":true,"predicate":"flags.1"},{"name":"dcId","type":"number","description":"DC where this photo is stored"}],"description":"Group profile photo."},{"name":"messageEmpty","id":2426849924,"type":"Message","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"id","type":"number","description":"Message identifier"},{"name":"peerId","type":"Peer","optional":true,"predicate":"flags.0"}],"description":"Empty constructor, non-existent message."},{"name":"message","id":3169027026,"type":"Message","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"out","type":"true","optional":true,"predicate":"flags.1","description":"Is this an outgoing message"},{"name":"mentioned","type":"true","optional":true,"predicate":"flags.4","description":"Whether we were mentioned in this message"},{"name":"mediaUnread","type":"true","optional":true,"predicate":"flags.5","description":"Whether there are unread media attachments in this message"},{"name":"silent","type":"true","optional":true,"predicate":"flags.13","description":"Whether this is a silent message (no notification triggered)"},{"name":"post","type":"true","optional":true,"predicate":"flags.14","description":"Whether this is a channel post"},{"name":"fromScheduled","type":"true","optional":true,"predicate":"flags.18","description":"Whether this is a scheduled message"},{"name":"legacy","type":"true","optional":true,"predicate":"flags.19","description":"This is a legacy message: it has to be refetched with the new layer"},{"name":"editHide","type":"true","optional":true,"predicate":"flags.21","description":"Whether the message should be shown as not modified to the user, even if an edit date is present"},{"name":"pinned","type":"true","optional":true,"predicate":"flags.24","description":"Whether this message is pinned"},{"name":"id","type":"number","description":"ID of the message"},{"name":"fromId","type":"Peer","optional":true,"predicate":"flags.8","description":"ID of the sender of the message"},{"name":"peerId","type":"Peer","description":"Peer ID, the chat where this message was sent"},{"name":"fwdFrom","type":"MessageFwdHeader","optional":true,"predicate":"flags.2","description":"Info about forwarded messages"},{"name":"viaBotId","type":"number","optional":true,"predicate":"flags.11","description":"ID of the inline bot that generated the message"},{"name":"replyTo","type":"MessageReplyHeader","optional":true,"predicate":"flags.3","description":"Reply information"},{"name":"date","type":"number","description":"Date of the message"},{"name":"message","type":"string","description":"The message"},{"name":"media","type":"MessageMedia","optional":true,"predicate":"flags.9","description":"Media attachment"},{"name":"replyMarkup","type":"ReplyMarkup","optional":true,"predicate":"flags.6","description":"Reply markup (bot/inline keyboards)"},{"name":"entities","type":"MessageEntity[]","optional":true,"predicate":"flags.7","description":"Message entities for styled text"},{"name":"views","type":"number","optional":true,"predicate":"flags.10","description":"View count for channel posts"},{"name":"forwards","type":"number","optional":true,"predicate":"flags.10","description":"Forward counter"},{"name":"replies","type":"MessageReplies","optional":true,"predicate":"flags.23","description":"Info about post comments (for channels) or message replies (for groups)"},{"name":"editDate","type":"number","optional":true,"predicate":"flags.15","description":"Last edit date of this message"},{"name":"postAuthor","type":"string","optional":true,"predicate":"flags.16","description":"Name of the author of this message for channel posts (with signatures enabled)"},{"name":"groupedId","type":"Long","optional":true,"predicate":"flags.17","description":"Multiple media messages sent using {@link messages.sendMultiMedia} with the same grouped ID indicate an album or media group"},{"name":"restrictionReason","type":"RestrictionReason[]","optional":true,"predicate":"flags.22","description":"Contains the reason why access to this message must be restricted."},{"name":"ttlPeriod","type":"number","optional":true,"predicate":"flags.25"}],"description":"A message"},{"name":"messageService","id":721967202,"type":"Message","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"out","type":"true","optional":true,"predicate":"flags.1","description":"Whether the message is outgoing"},{"name":"mentioned","type":"true","optional":true,"predicate":"flags.4","description":"Whether we were mentioned in the message"},{"name":"mediaUnread","type":"true","optional":true,"predicate":"flags.5","description":"Whether the message contains unread media"},{"name":"silent","type":"true","optional":true,"predicate":"flags.13","description":"Whether the message is silent"},{"name":"post","type":"true","optional":true,"predicate":"flags.14","description":"Whether it's a channel post"},{"name":"legacy","type":"true","optional":true,"predicate":"flags.19","description":"This is a legacy message: it has to be refetched with the new layer"},{"name":"id","type":"number","description":"Message ID"},{"name":"fromId","type":"Peer","optional":true,"predicate":"flags.8","description":"ID of the sender of this message"},{"name":"peerId","type":"Peer","description":"Sender of service message"},{"name":"replyTo","type":"MessageReplyHeader","optional":true,"predicate":"flags.3","description":"Reply (thread) information"},{"name":"date","type":"number","description":"Message date"},{"name":"action","type":"MessageAction","description":"Event connected with the service message"},{"name":"ttlPeriod","type":"number","optional":true,"predicate":"flags.25"}],"description":"Indicates a service message"},{"name":"messageMediaEmpty","id":1038967584,"type":"MessageMedia","arguments":[],"description":"Empty constructor."},{"name":"messageMediaPhoto","id":1766936791,"type":"MessageMedia","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"photo","type":"Photo","optional":true,"predicate":"flags.0","description":"Photo"},{"name":"ttlSeconds","type":"number","optional":true,"predicate":"flags.2","description":"Time to live in seconds of self-destructing photo"}],"description":"Attached photo."},{"name":"messageMediaGeo","id":1457575028,"type":"MessageMedia","arguments":[{"name":"geo","type":"GeoPoint","description":"GeoPoint"}],"description":"Attached map."},{"name":"messageMediaContact","id":3421653312,"type":"MessageMedia","arguments":[{"name":"phoneNumber","type":"string","description":"Phone number"},{"name":"firstName","type":"string","description":"Contact's first name"},{"name":"lastName","type":"string","description":"Contact's last name"},{"name":"vcard","type":"string","description":"VCARD of contact"},{"name":"userId","type":"number","description":"User identifier or 0, if the user with the given phone number is not registered"}],"description":"Attached contact."},{"name":"messageMediaUnsupported","id":2676290718,"type":"MessageMedia","arguments":[],"description":"Current version of the client does not support this media type."},{"name":"messageMediaDocument","id":2628808919,"type":"MessageMedia","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"document","type":"Document","optional":true,"predicate":"flags.0","description":"Attached document"},{"name":"ttlSeconds","type":"number","optional":true,"predicate":"flags.2","description":"Time to live of self-destructing document"}],"description":"Document (video, audio, voice, sticker, any media type except photo)"},{"name":"messageMediaWebPage","id":2737690112,"type":"MessageMedia","arguments":[{"name":"webpage","type":"WebPage","description":"Webpage preview"}],"description":"Preview of webpage"},{"name":"messageMediaVenue","id":784356159,"type":"MessageMedia","arguments":[{"name":"geo","type":"GeoPoint","description":"Geolocation of venue"},{"name":"title","type":"string","description":"Venue name"},{"name":"address","type":"string","description":"Address"},{"name":"provider","type":"string","description":"Venue provider: currently only \"foursquare\" and \"gplaces\" need to be supported"},{"name":"venueId","type":"string","description":"Venue ID in the provider's database"},{"name":"venueType","type":"string","description":"Venue type in the provider's database"}],"description":"Venue"},{"name":"messageMediaGame","id":4256272392,"type":"MessageMedia","arguments":[{"name":"game","type":"Game","description":"Game"}],"description":"Telegram game"},{"name":"messageMediaInvoice","id":2220168007,"type":"MessageMedia","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"shippingAddressRequested","type":"true","optional":true,"predicate":"flags.1","description":"Whether the shipping address was requested"},{"name":"test","type":"true","optional":true,"predicate":"flags.3","description":"Whether this is an example invoice"},{"name":"title","type":"string","description":"Product name, 1-32 characters"},{"name":"description","type":"string","description":"Product description, 1-255 characters"},{"name":"photo","type":"WebDocument","optional":true,"predicate":"flags.0","description":"URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for."},{"name":"receiptMsgId","type":"number","optional":true,"predicate":"flags.2","description":"Message ID of receipt: if set, clients should change the text of the first {@link keyboardButtonBuy} button always attached to the {@link message} to a localized version of the word Receipt"},{"name":"currency","type":"string","description":"Three-letter ISO 4217 currency code"},{"name":"totalAmount","type":"Long","description":"Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies)."},{"name":"startParam","type":"string","description":"Unique bot deep-linking parameter that can be used to generate this invoice"}],"description":"Invoice"},{"name":"messageMediaGeoLive","id":3108030054,"type":"MessageMedia","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"geo","type":"GeoPoint","description":"Geolocation"},{"name":"heading","type":"number","optional":true,"predicate":"flags.0","description":"For live locations, a direction in which the location moves, in degrees; 1-360"},{"name":"period","type":"number","description":"Validity period of provided geolocation"},{"name":"proximityNotificationRadius","type":"number","optional":true,"predicate":"flags.1","description":"For live locations, a maximum distance to another chat member for proximity alerts, in meters (0-100000)."}],"description":"Indicates a live geolocation"},{"name":"messageMediaPoll","id":1272375192,"type":"MessageMedia","arguments":[{"name":"poll","type":"Poll","description":"The poll"},{"name":"results","type":"PollResults","description":"The results of the poll"}],"description":"Poll"},{"name":"messageMediaDice","id":1065280907,"type":"MessageMedia","arguments":[{"name":"value","type":"number","description":"Dice value"},{"name":"emoticon","type":"string","description":"The emoji, for now \"🏀\", \"🎲\" and \"🎯\" are supported"}],"description":"Dice-based animated sticker"},{"name":"messageActionEmpty","id":3064919984,"type":"MessageAction","arguments":[],"description":"Empty constructor."},{"name":"messageActionChatCreate","id":2791541658,"type":"MessageAction","arguments":[{"name":"title","type":"string","description":"Group name"},{"name":"users","type":"number[]","description":"List of group members"}],"description":"Group created"},{"name":"messageActionChatEditTitle","id":3047280218,"type":"MessageAction","arguments":[{"name":"title","type":"string","description":"New group name"}],"description":"Group name changed."},{"name":"messageActionChatEditPhoto","id":2144015272,"type":"MessageAction","arguments":[{"name":"photo","type":"Photo","description":"New group pofile photo"}],"description":"Group profile changed"},{"name":"messageActionChatDeletePhoto","id":2514746351,"type":"MessageAction","arguments":[],"description":"Group profile photo removed."},{"name":"messageActionChatAddUser","id":1217033015,"type":"MessageAction","arguments":[{"name":"users","type":"number[]","description":"Users that were invited to the chat"}],"description":"New member in the group"},{"name":"messageActionChatDeleteUser","id":2997787404,"type":"MessageAction","arguments":[{"name":"userId","type":"number","description":"Leaving user ID"}],"description":"User left the group."},{"name":"messageActionChatJoinedByLink","id":4171036136,"type":"MessageAction","arguments":[{"name":"inviterId","type":"number","description":"ID of the user that created the invite link"}],"description":"A user joined the chat via an invite link"},{"name":"messageActionChannelCreate","id":2513611922,"type":"MessageAction","arguments":[{"name":"title","type":"string","description":"Original channel/supergroup title"}],"description":"The channel was created"},{"name":"messageActionChatMigrateTo","id":1371385889,"type":"MessageAction","arguments":[{"name":"channelId","type":"number","description":"The supergroup it was migrated to"}],"description":"Indicates the chat was migrated to the specified supergroup"},{"name":"messageActionChannelMigrateFrom","id":2958420718,"type":"MessageAction","arguments":[{"name":"title","type":"string","description":"The old chat tite"},{"name":"chatId","type":"number","description":"The old chat ID"}],"description":"Indicates the channel was migrated from the specified chat"},{"name":"messageActionPinMessage","id":2495428845,"type":"MessageAction","arguments":[],"description":"A message was pinned"},{"name":"messageActionHistoryClear","id":2679813636,"type":"MessageAction","arguments":[],"description":"Chat history was cleared"},{"name":"messageActionGameScore","id":2460428406,"type":"MessageAction","arguments":[{"name":"gameId","type":"Long","description":"Game ID"},{"name":"score","type":"number","description":"Score"}],"description":"Someone scored in a game"},{"name":"messageActionPaymentSentMe","id":2402399015,"type":"MessageAction","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"currency","type":"string","description":"Three-letter ISO 4217 currency code"},{"name":"totalAmount","type":"Long","description":"Price of the product in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies)."},{"name":"payload","type":"Buffer","description":"Bot specified invoice payload"},{"name":"info","type":"PaymentRequestedInfo","optional":true,"predicate":"flags.0","description":"Order info provided by the user"},{"name":"shippingOptionId","type":"string","optional":true,"predicate":"flags.1","description":"Identifier of the shipping option chosen by the user"},{"name":"charge","type":"PaymentCharge","description":"Provider payment identifier"}],"description":"A user just sent a payment to me (a bot)"},{"name":"messageActionPaymentSent","id":1080663248,"type":"MessageAction","arguments":[{"name":"currency","type":"string","description":"Three-letter ISO 4217 currency code"},{"name":"totalAmount","type":"Long","description":"Price of the product in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies)."}],"description":"A payment was sent"},{"name":"messageActionPhoneCall","id":2162236031,"type":"MessageAction","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"video","type":"true","optional":true,"predicate":"flags.2","description":"Is this a video call?"},{"name":"callId","type":"Long","description":"Call ID"},{"name":"reason","type":"PhoneCallDiscardReason","optional":true,"predicate":"flags.0","description":"If the call has ended, the reason why it ended"},{"name":"duration","type":"number","optional":true,"predicate":"flags.1","description":"Duration of the call in seconds"}],"description":"A phone call"},{"name":"messageActionScreenshotTaken","id":1200788123,"type":"MessageAction","arguments":[],"description":"A screenshot of the chat was taken"},{"name":"messageActionCustomAction","id":4209418070,"type":"MessageAction","arguments":[{"name":"message","type":"string","description":"Action message"}],"description":"Custom action (most likely not supported by the current layer, an upgrade might be needed)"},{"name":"messageActionBotAllowed","id":2884218878,"type":"MessageAction","arguments":[{"name":"domain","type":"string","description":"The domain name of the website on which the user has logged in."}],"description":"The domain name of the website on which the user has logged in. More about Telegram Login »"},{"name":"messageActionSecureValuesSentMe","id":455635795,"type":"MessageAction","arguments":[{"name":"values","type":"SecureValue[]","description":"Vector with information about documents and other Telegram Passport elements that were shared with the bot"},{"name":"credentials","type":"SecureCredentialsEncrypted","description":"Encrypted credentials required to decrypt the data"}],"description":"Secure telegram passport values were received"},{"name":"messageActionSecureValuesSent","id":3646710100,"type":"MessageAction","arguments":[{"name":"types","type":"SecureValueType[]","description":"Secure value types"}],"description":"Request for secure telegram passport values was sent"},{"name":"messageActionContactSignUp","id":4092747638,"type":"MessageAction","arguments":[],"description":"A contact just signed up to telegram"},{"name":"messageActionGeoProximityReached","id":2564871831,"type":"MessageAction","arguments":[{"name":"fromId","type":"Peer","description":"The user or chat that is now in proximity of to_id"},{"name":"toId","type":"Peer","description":"The user or chat that subscribed to live geolocation proximity alerts"},{"name":"distance","type":"number","description":"Distance, in meters (0-100000)"}],"description":"A user of the chat is now in proximity of another user"},{"name":"messageActionGroupCall","id":2047704898,"type":"MessageAction","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"call","type":"InputGroupCall"},{"name":"duration","type":"number","optional":true,"predicate":"flags.0"}]},{"name":"messageActionInviteToGroupCall","id":1991897370,"type":"MessageAction","arguments":[{"name":"call","type":"InputGroupCall"},{"name":"users","type":"number[]"}]},{"name":"messageActionSetMessagesTTL","id":2853895165,"type":"MessageAction","arguments":[{"name":"period","type":"number"}]},{"name":"messageActionGroupCallScheduled","id":3013637729,"type":"MessageAction","arguments":[{"name":"call","type":"InputGroupCall"},{"name":"scheduleDate","type":"number"}]},{"name":"dialog","id":739712882,"type":"Dialog","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"pinned","type":"true","optional":true,"predicate":"flags.2","description":"Is the dialog pinned"},{"name":"unreadMark","type":"true","optional":true,"predicate":"flags.3","description":"Whether the chat was manually marked as unread"},{"name":"peer","type":"Peer","description":"The chat"},{"name":"topMessage","type":"number","description":"The latest message ID"},{"name":"readInboxMaxId","type":"number","description":"Position up to which all incoming messages are read."},{"name":"readOutboxMaxId","type":"number","description":"Position up to which all outgoing messages are read."},{"name":"unreadCount","type":"number","description":"Number of unread messages"},{"name":"unreadMentionsCount","type":"number","description":"Number of unread mentions"},{"name":"notifySettings","type":"PeerNotifySettings","description":"Notification settings"},{"name":"pts","type":"number","optional":true,"predicate":"flags.0","description":"PTS"},{"name":"draft","type":"DraftMessage","optional":true,"predicate":"flags.1","description":"Message draft"},{"name":"folderId","type":"number","optional":true,"predicate":"flags.4","description":"Peer folder ID, for more info click here"}],"description":"Chat"},{"name":"dialogFolder","id":1908216652,"type":"Dialog","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"pinned","type":"true","optional":true,"predicate":"flags.2","description":"Is this folder pinned"},{"name":"folder","type":"Folder","description":"The folder"},{"name":"peer","type":"Peer","description":"Peer in folder"},{"name":"topMessage","type":"number","description":"Latest message ID of dialog"},{"name":"unreadMutedPeersCount","type":"number","description":"Number of unread muted peers in folder"},{"name":"unreadUnmutedPeersCount","type":"number","description":"Number of unread unmuted peers in folder"},{"name":"unreadMutedMessagesCount","type":"number","description":"Number of unread messages from muted peers in folder"},{"name":"unreadUnmutedMessagesCount","type":"number","description":"Number of unread messages from unmuted peers in folder"}],"description":"Dialog in folder"},{"name":"photoEmpty","id":590459437,"type":"Photo","arguments":[{"name":"id","type":"Long","description":"Photo identifier"}],"description":"Empty constructor, non-existent photo"},{"name":"photo","id":4212750949,"type":"Photo","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"hasStickers","type":"true","optional":true,"predicate":"flags.0","description":"Whether the photo has mask stickers attached to it"},{"name":"id","type":"Long","description":"ID"},{"name":"accessHash","type":"Long","description":"Access hash"},{"name":"fileReference","type":"Buffer","description":"file reference"},{"name":"date","type":"number","description":"Date of upload"},{"name":"sizes","type":"PhotoSize[]","description":"Available sizes for download"},{"name":"videoSizes","type":"VideoSize[]","optional":true,"predicate":"flags.1","description":"For animated profiles, the MPEG4 videos"},{"name":"dcId","type":"number","description":"DC ID to use for download"}],"description":"Photo"},{"name":"photoSizeEmpty","id":236446268,"type":"PhotoSize","arguments":[{"name":"type","type":"string","description":"Thumbnail type (see. {@link photoSize})"}],"description":"Empty constructor. Image with this thumbnail is unavailable."},{"name":"photoSize","id":1976012384,"type":"PhotoSize","arguments":[{"name":"type","type":"string","description":"Thumbnail type"},{"name":"w","type":"number","description":"Image width"},{"name":"h","type":"number","description":"Image height"},{"name":"size","type":"number","description":"File size"}],"description":"Image description."},{"name":"photoCachedSize","id":35527382,"type":"PhotoSize","arguments":[{"name":"type","type":"string","description":"Thumbnail type"},{"name":"w","type":"number","description":"Image width"},{"name":"h","type":"number","description":"Image height"},{"name":"bytes","type":"Buffer","description":"Binary data, file content"}],"description":"Description of an image and its content."},{"name":"photoStrippedSize","id":3769678894,"type":"PhotoSize","arguments":[{"name":"type","type":"string","description":"Thumbnail type"},{"name":"bytes","type":"Buffer","description":"Thumbnail data, see here for more info on decompression »"}],"description":"A low-resolution compressed JPG payload"},{"name":"photoSizeProgressive","id":4198431637,"type":"PhotoSize","arguments":[{"name":"type","type":"string","description":"Photosize type"},{"name":"w","type":"number","description":"Photo width"},{"name":"h","type":"number","description":"Photo height"},{"name":"sizes","type":"number[]","description":"Sizes of progressive JPEG file prefixes, which can be used to preliminarily show the image."}],"description":"Progressively encoded photosize"},{"name":"photoPathSize","id":3626061121,"type":"PhotoSize","arguments":[{"name":"type","type":"string","description":"Always j"},{"name":"bytes","type":"Buffer","description":"Compressed SVG path payload, see here for decompression instructions"}],"description":"Messages with animated stickers can have a compressed svg (< 300 bytes) to show the outline of the sticker before fetching the actual lottie animation."},{"name":"geoPointEmpty","id":286776671,"type":"GeoPoint","arguments":[],"description":"Empty constructor."},{"name":"geoPoint","id":2997024355,"type":"GeoPoint","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"long","type":"Double","description":"Longtitude"},{"name":"lat","type":"Double","description":"Latitude"},{"name":"accessHash","type":"Long","description":"Access hash"},{"name":"accuracyRadius","type":"number","optional":true,"predicate":"flags.0","description":"The estimated horizontal accuracy of the location, in meters; as defined by the sender."}],"description":"GeoPoint."},{"name":"inputNotifyPeer","id":3099351820,"type":"InputNotifyPeer","arguments":[{"name":"peer","type":"InputPeer","description":"User or group"}],"description":"Notifications generated by a certain user or group."},{"name":"inputNotifyUsers","id":423314455,"type":"InputNotifyPeer","arguments":[],"description":"Notifications generated by all users."},{"name":"inputNotifyChats","id":1251338318,"type":"InputNotifyPeer","arguments":[],"description":"Notifications generated by all groups."},{"name":"inputNotifyBroadcasts","id":2983951486,"type":"InputNotifyPeer","arguments":[],"description":"All channels"},{"name":"inputPeerNotifySettings","id":2621249934,"type":"InputPeerNotifySettings","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"showPreviews","type":"boolean","optional":true,"predicate":"flags.0","description":"If the text of the message shall be displayed in notification"},{"name":"silent","type":"boolean","optional":true,"predicate":"flags.1","description":"Peer was muted?"},{"name":"muteUntil","type":"number","optional":true,"predicate":"flags.2","description":"Date until which all notifications shall be switched off"},{"name":"sound","type":"string","optional":true,"predicate":"flags.3","description":"Name of an audio file for notification"}],"description":"Notification settings."},{"name":"peerNotifySettings","id":2941295904,"type":"PeerNotifySettings","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"showPreviews","type":"boolean","optional":true,"predicate":"flags.0","description":"Display text in notifications"},{"name":"silent","type":"boolean","optional":true,"predicate":"flags.1","description":"Mute peer?"},{"name":"muteUntil","type":"number","optional":true,"predicate":"flags.2","description":"Mute all notifications until this date"},{"name":"sound","type":"string","optional":true,"predicate":"flags.3","description":"Audio file name for notifications"}],"description":"Notification settings."},{"name":"peerSettings","id":1933519201,"type":"PeerSettings","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"reportSpam","type":"true","optional":true,"predicate":"flags.0","description":"Whether we can still report the user for spam"},{"name":"addContact","type":"true","optional":true,"predicate":"flags.1","description":"Whether we can add the user as contact"},{"name":"blockContact","type":"true","optional":true,"predicate":"flags.2","description":"Whether we can block the user"},{"name":"shareContact","type":"true","optional":true,"predicate":"flags.3","description":"Whether we can share the user's contact"},{"name":"needContactsException","type":"true","optional":true,"predicate":"flags.4","description":"Whether a special exception for contacts is needed"},{"name":"reportGeo","type":"true","optional":true,"predicate":"flags.5","description":"Whether we can report a geo group is irrelevant for this location"},{"name":"autoarchived","type":"true","optional":true,"predicate":"flags.7","description":"Whether this peer was automatically archived according to {@link globalPrivacySettings}"},{"name":"inviteMembers","type":"true","optional":true,"predicate":"flags.8"},{"name":"geoDistance","type":"number","optional":true,"predicate":"flags.6","description":"Distance in meters between us and this peer"}],"description":"Peer settings"},{"name":"wallPaper","id":2755118061,"type":"WallPaper","arguments":[{"name":"id","type":"Long","description":"Identifier"},{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"creator","type":"true","optional":true,"predicate":"flags.0","description":"Creator of the wallpaper"},{"name":"default","type":"true","optional":true,"predicate":"flags.1","description":"Whether this is the default wallpaper"},{"name":"pattern","type":"true","optional":true,"predicate":"flags.3","description":"Pattern"},{"name":"dark","type":"true","optional":true,"predicate":"flags.4","description":"Dark mode"},{"name":"accessHash","type":"Long","description":"Access hash"},{"name":"slug","type":"string","description":"Unique wallpaper ID"},{"name":"document","type":"Document","description":"The actual wallpaper"},{"name":"settings","type":"WallPaperSettings","optional":true,"predicate":"flags.2","description":"Wallpaper settings"}],"description":"Wallpaper settings."},{"name":"wallPaperNoFile","id":3766501654,"type":"WallPaper","arguments":[{"name":"id","type":"Long"},{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"default","type":"true","optional":true,"predicate":"flags.1","description":"Whether this is the default wallpaper"},{"name":"dark","type":"true","optional":true,"predicate":"flags.4","description":"Dark mode"},{"name":"settings","type":"WallPaperSettings","optional":true,"predicate":"flags.2","description":"Wallpaper settings"}],"description":"No file wallpaper"},{"name":"inputReportReasonSpam","id":1490799288,"type":"ReportReason","arguments":[],"description":"Report for spam"},{"name":"inputReportReasonViolence","id":505595789,"type":"ReportReason","arguments":[],"description":"Report for violence"},{"name":"inputReportReasonPornography","id":777640226,"type":"ReportReason","arguments":[],"description":"Report for pornography"},{"name":"inputReportReasonChildAbuse","id":2918469347,"type":"ReportReason","arguments":[],"description":"Report for child abuse"},{"name":"inputReportReasonOther","id":3252986545,"type":"ReportReason","arguments":[],"description":"Other"},{"name":"inputReportReasonCopyright","id":2609510714,"type":"ReportReason","arguments":[],"description":"Report for copyrighted content"},{"name":"inputReportReasonGeoIrrelevant","id":3688169197,"type":"ReportReason","arguments":[],"description":"Report an irrelevant geo group"},{"name":"inputReportReasonFake","id":4124956391,"type":"ReportReason","arguments":[]},{"name":"userFull","id":328899191,"type":"UserFull","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"blocked","type":"true","optional":true,"predicate":"flags.0","description":"Whether you have blocked this user"},{"name":"phoneCallsAvailable","type":"true","optional":true,"predicate":"flags.4","description":"Whether this user can make VoIP calls"},{"name":"phoneCallsPrivate","type":"true","optional":true,"predicate":"flags.5","description":"Whether this user's privacy settings allow you to call them"},{"name":"canPinMessage","type":"true","optional":true,"predicate":"flags.7","description":"Whether you can pin messages in the chat with this user, you can do this only for a chat with yourself"},{"name":"hasScheduled","type":"true","optional":true,"predicate":"flags.12","description":"Whether scheduled messages are available"},{"name":"videoCallsAvailable","type":"true","optional":true,"predicate":"flags.13","description":"Whether the user can receive video calls"},{"name":"user","type":"User","description":"Remaining user info"},{"name":"about","type":"string","optional":true,"predicate":"flags.1","description":"Bio of the user"},{"name":"settings","type":"PeerSettings","description":"Peer settings"},{"name":"profilePhoto","type":"Photo","optional":true,"predicate":"flags.2","description":"Profile photo"},{"name":"notifySettings","type":"PeerNotifySettings","description":"Notification settings"},{"name":"botInfo","type":"BotInfo","optional":true,"predicate":"flags.3","description":"For bots, info about the bot (bot commands, etc)"},{"name":"pinnedMsgId","type":"number","optional":true,"predicate":"flags.6","description":"Message ID of the last pinned message"},{"name":"commonChatsCount","type":"number","description":"Chats in common with this user"},{"name":"folderId","type":"number","optional":true,"predicate":"flags.11","description":"Peer folder ID, for more info click here"},{"name":"ttlPeriod","type":"number","optional":true,"predicate":"flags.14"}],"description":"Extended user info"},{"name":"contact","id":4178692500,"type":"Contact","arguments":[{"name":"userId","type":"number","description":"User identifier"},{"name":"mutual","type":"boolean","description":"Current user is in the user's contact list"}],"description":"A contact of the current user that is registered in the system."},{"name":"importedContact","id":3489825848,"type":"ImportedContact","arguments":[{"name":"userId","type":"number","description":"User identifier"},{"name":"clientId","type":"Long","description":"The contact's client identifier (passed to one of the InputContact constructors)"}],"description":"Successfully imported contact."},{"name":"contactStatus","id":3546811489,"type":"ContactStatus","arguments":[{"name":"userId","type":"number","description":"User identifier"},{"name":"status","type":"UserStatus","description":"Online status"}],"description":"Contact status: online / offline."},{"name":"inputMessagesFilterEmpty","id":1474492012,"type":"MessagesFilter","arguments":[],"description":"Filter is absent."},{"name":"inputMessagesFilterPhotos","id":2517214492,"type":"MessagesFilter","arguments":[],"description":"Filter for messages containing photos."},{"name":"inputMessagesFilterVideo","id":2680163941,"type":"MessagesFilter","arguments":[],"description":"Filter for messages containing videos."},{"name":"inputMessagesFilterPhotoVideo","id":1458172132,"type":"MessagesFilter","arguments":[],"description":"Filter for messages containing photos or videos."},{"name":"inputMessagesFilterDocument","id":2665345416,"type":"MessagesFilter","arguments":[],"description":"Filter for messages containing documents."},{"name":"inputMessagesFilterUrl","id":2129714567,"type":"MessagesFilter","arguments":[],"description":"Return only messages containing URLs"},{"name":"inputMessagesFilterGif","id":4291323271,"type":"MessagesFilter","arguments":[],"description":"Return only messages containing gifs"},{"name":"inputMessagesFilterVoice","id":1358283666,"type":"MessagesFilter","arguments":[],"description":"Return only messages containing voice notes"},{"name":"inputMessagesFilterMusic","id":928101534,"type":"MessagesFilter","arguments":[],"description":"Return only messages containing audio files"},{"name":"inputMessagesFilterChatPhotos","id":975236280,"type":"MessagesFilter","arguments":[],"description":"Return only chat photo changes"},{"name":"inputMessagesFilterPhoneCalls","id":2160695144,"type":"MessagesFilter","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"missed","type":"true","optional":true,"predicate":"flags.0","description":"Return only missed phone calls"}],"description":"Return only phone calls"},{"name":"inputMessagesFilterRoundVoice","id":2054952868,"type":"MessagesFilter","arguments":[],"description":"Return only round videos and voice notes"},{"name":"inputMessagesFilterRoundVideo","id":3041516115,"type":"MessagesFilter","arguments":[],"description":"Return only round videos"},{"name":"inputMessagesFilterMyMentions","id":3254314650,"type":"MessagesFilter","arguments":[],"description":"Return only messages where the current user was mentioned."},{"name":"inputMessagesFilterGeo","id":3875695885,"type":"MessagesFilter","arguments":[],"description":"Return only messages containing geolocations"},{"name":"inputMessagesFilterContacts","id":3764575107,"type":"MessagesFilter","arguments":[],"description":"Return only messages containing contacts"},{"name":"inputMessagesFilterPinned","id":464520273,"type":"MessagesFilter","arguments":[],"description":"Fetch only pinned messages"},{"name":"updateNewMessage","id":522914557,"type":"Update","arguments":[{"name":"message","type":"Message","description":"Message"},{"name":"pts","type":"number","description":"New quantity of actions in a message box"},{"name":"ptsCount","type":"number","description":"Number of generated events"}],"description":"New message in a private chat or in a legacy group."},{"name":"updateMessageID","id":1318109142,"type":"Update","arguments":[{"name":"id","type":"number","description":"id identifier of a respective Message"},{"name":"randomId","type":"Long","description":"Previuosly transferred client random_id identifier"}],"description":"Sent message with random_id client identifier was assigned an identifier."},{"name":"updateDeleteMessages","id":2718806245,"type":"Update","arguments":[{"name":"messages","type":"number[]","description":"List of identifiers of deleted messages"},{"name":"pts","type":"number","description":"New quality of actions in a message box"},{"name":"ptsCount","type":"number","description":"Number of generated events"}],"description":"Messages were deleted."},{"name":"updateUserTyping","id":1548249383,"type":"Update","arguments":[{"name":"userId","type":"number","description":"User id"},{"name":"action","type":"SendMessageAction","description":"Action type
Param added in Layer 17."}],"description":"The user is preparing a message; typing, recording, uploading, etc. This update is valid for 6 seconds. If no repeated update received after 6 seconds, it should be considered that the user stopped doing whatever he's been doing."},{"name":"updateChatUserTyping","id":2261441388,"type":"Update","arguments":[{"name":"chatId","type":"number","description":"Group id"},{"name":"fromId","type":"Peer"},{"name":"action","type":"SendMessageAction","description":"Type of action
Parameter added in Layer 17."}],"description":"The user is preparing a message in a group; typing, recording, uploading, etc. This update is valid for 6 seconds. If no repeated update received after 6 seconds, it should be considered that the user stopped doing whatever he's been doing."},{"name":"updateChatParticipants","id":125178264,"type":"Update","arguments":[{"name":"participants","type":"ChatParticipants","description":"Updated chat participants"}],"description":"Composition of chat participants changed."},{"name":"updateUserStatus","id":469489699,"type":"Update","arguments":[{"name":"userId","type":"number","description":"User identifier"},{"name":"status","type":"UserStatus","description":"New status"}],"description":"Contact status update."},{"name":"updateUserName","id":2805148531,"type":"Update","arguments":[{"name":"userId","type":"number","description":"User identifier"},{"name":"firstName","type":"string","description":"New first name. Corresponds to the new value of real_first_name field of the {@link userFull} constructor."},{"name":"lastName","type":"string","description":"New last name. Corresponds to the new value of real_last_name field of the {@link userFull} constructor."},{"name":"username","type":"string","description":"New username.
Parameter added in Layer 18."}],"description":"Changes the user's first name, last name and username."},{"name":"updateUserPhoto","id":2503031564,"type":"Update","arguments":[{"name":"userId","type":"number","description":"User identifier"},{"name":"date","type":"number","description":"Date of photo update."},{"name":"photo","type":"UserProfilePhoto","description":"New profile photo"},{"name":"previous","type":"boolean","description":"({@link boolTrue}), if one of the previously used photos is set a profile photo."}],"description":"Change of contact's profile photo."},{"name":"updateNewEncryptedMessage","id":314359194,"type":"Update","arguments":[{"name":"message","type":"EncryptedMessage","description":"Message"},{"name":"qts","type":"number","description":"New qts value"}],"description":"New encrypted message."},{"name":"updateEncryptedChatTyping","id":386986326,"type":"Update","arguments":[{"name":"chatId","type":"number","description":"Chat ID"}],"description":"Interlocutor is typing a message in an encrypted chat. Update period is 6 second. If upon this time there is no repeated update, it shall be considered that the interlocutor stopped typing."},{"name":"updateEncryption","id":3030575245,"type":"Update","arguments":[{"name":"chat","type":"EncryptedChat","description":"Encrypted chat"},{"name":"date","type":"number","description":"Date of change"}],"description":"Change of state in an encrypted chat."},{"name":"updateEncryptedMessagesRead","id":956179895,"type":"Update","arguments":[{"name":"chatId","type":"number","description":"Chat ID"},{"name":"maxDate","type":"number","description":"Maximum value of data for read messages"},{"name":"date","type":"number","description":"Time when messages were read"}],"description":"Communication history in an encrypted chat was marked as read."},{"name":"updateChatParticipantAdd","id":3930787420,"type":"Update","arguments":[{"name":"chatId","type":"number","description":"Group ID"},{"name":"userId","type":"number","description":"ID of the new member"},{"name":"inviterId","type":"number","description":"ID of the user, who added member to the group"},{"name":"date","type":"number","description":"When was the participant added"},{"name":"version","type":"number","description":"Chat version number"}],"description":"New group member."},{"name":"updateChatParticipantDelete","id":1851755554,"type":"Update","arguments":[{"name":"chatId","type":"number","description":"Group ID"},{"name":"userId","type":"number","description":"ID of the user"},{"name":"version","type":"number","description":"Used in basic groups to reorder updates and make sure that all of them was received."}],"description":"A member has left the group."},{"name":"updateDcOptions","id":2388564083,"type":"Update","arguments":[{"name":"dcOptions","type":"DcOption[]","description":"New connection options"}],"description":"Changes in the data center configuration options."},{"name":"updateNotifySettings","id":3200411887,"type":"Update","arguments":[{"name":"peer","type":"NotifyPeer","description":"Nofication source"},{"name":"notifySettings","type":"PeerNotifySettings","description":"New notification settings"}],"description":"Changes in notification settings."},{"name":"updateServiceNotification","id":3957614617,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"popup","type":"true","optional":true,"predicate":"flags.0","description":"(boolTrue) if the message must be displayed in a popup."},{"name":"inboxDate","type":"number","optional":true,"predicate":"flags.1","description":"When was the notification received
The message must also be stored locally as part of the message history with the user id 777000 (Telegram Notifications)."},{"name":"type","type":"string","description":"String, identical in format and contents to the type field in API errors. Describes type of service message. It is acceptable to ignore repeated messages of the same type within a short period of time (15 minutes)."},{"name":"message","type":"string","description":"Message text"},{"name":"media","type":"MessageMedia","description":"Media content (optional)"},{"name":"entities","type":"MessageEntity[]","description":"Message entities for styled text"}],"description":"A service message for the user."},{"name":"updatePrivacy","id":3996854058,"type":"Update","arguments":[{"name":"key","type":"PrivacyKey","description":"Peers to which the privacy rules apply"},{"name":"rules","type":"PrivacyRule[]","description":"New privacy rules"}],"description":"Privacy rules were changed"},{"name":"updateUserPhone","id":314130811,"type":"Update","arguments":[{"name":"userId","type":"number","description":"User ID"},{"name":"phone","type":"string","description":"New phone number"}],"description":"A user's phone number was changed"},{"name":"updateReadHistoryInbox","id":2627162079,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"folderId","type":"number","optional":true,"predicate":"flags.0","description":"Peer folder ID, for more info click here"},{"name":"peer","type":"Peer","description":"Peer"},{"name":"maxId","type":"number","description":"Maximum ID of messages read"},{"name":"stillUnreadCount","type":"number","description":"Number of messages that are still unread"},{"name":"pts","type":"number","description":"Event count after generation"},{"name":"ptsCount","type":"number","description":"Number of events that were generated"}],"description":"Incoming messages were read"},{"name":"updateReadHistoryOutbox","id":791617983,"type":"Update","arguments":[{"name":"peer","type":"Peer","description":"Peer"},{"name":"maxId","type":"number","description":"Maximum ID of read outgoing messages"},{"name":"pts","type":"number","description":"Event count after generation"},{"name":"ptsCount","type":"number","description":"Number of events that were generated"}],"description":"Outgoing messages were read"},{"name":"updateWebPage","id":2139689491,"type":"Update","arguments":[{"name":"webpage","type":"WebPage","description":"Webpage preview"},{"name":"pts","type":"number","description":"Event count after generation"},{"name":"ptsCount","type":"number","description":"Number of events that were generated"}],"description":"An instant view webpage preview was generated"},{"name":"updateReadMessagesContents","id":1757493555,"type":"Update","arguments":[{"name":"messages","type":"number[]","description":"IDs of read messages"},{"name":"pts","type":"number","description":"Event count after generation"},{"name":"ptsCount","type":"number","description":"Number of events that were generated"}],"description":"Contents of messages in the common message box were read"},{"name":"updateChannelTooLong","id":3942934523,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"channelId","type":"number","description":"The channel"},{"name":"pts","type":"number","optional":true,"predicate":"flags.0","description":"The PTS."}],"description":"There are new updates in the specified channel, the client must fetch them.
\nIf the difference is too long or if the channel isn't currently in the states, start fetching from the specified pts."},{"name":"updateChannel","id":3067369046,"type":"Update","arguments":[{"name":"channelId","type":"number","description":"Channel ID"}],"description":"A new channel is available"},{"name":"updateNewChannelMessage","id":1656358105,"type":"Update","arguments":[{"name":"message","type":"Message","description":"New message"},{"name":"pts","type":"number","description":"Event count after generation"},{"name":"ptsCount","type":"number","description":"Number of events that were generated"}],"description":"A new message was sent in a channel/supergroup"},{"name":"updateReadChannelInbox","id":856380452,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"folderId","type":"number","optional":true,"predicate":"flags.0","description":"Peer folder ID, for more info click here"},{"name":"channelId","type":"number","description":"Channel/supergroup ID"},{"name":"maxId","type":"number","description":"Position up to which all incoming messages are read."},{"name":"stillUnreadCount","type":"number","description":"Count of messages weren't read yet"},{"name":"pts","type":"number","description":"Event count after generation"}],"description":"Incoming messages in a channel/supergroup were read"},{"name":"updateDeleteChannelMessages","id":3279233481,"type":"Update","arguments":[{"name":"channelId","type":"number","description":"Channel ID"},{"name":"messages","type":"number[]","description":"IDs of messages that were deleted"},{"name":"pts","type":"number","description":"Event count after generation"},{"name":"ptsCount","type":"number","description":"Number of events that were generated"}],"description":"Some messages in a supergroup/channel were deleted"},{"name":"updateChannelMessageViews","id":2560699211,"type":"Update","arguments":[{"name":"channelId","type":"number","description":"Channel ID"},{"name":"id","type":"number","description":"ID of the message"},{"name":"views","type":"number","description":"New view counter"}],"description":"The view counter of a message in a channel has changed"},{"name":"updateChatParticipantAdmin","id":3062896985,"type":"Update","arguments":[{"name":"chatId","type":"number","description":"Chat ID"},{"name":"userId","type":"number","description":"ID of the (de)admined user"},{"name":"isAdmin","type":"boolean","description":"Whether the user was rendered admin"},{"name":"version","type":"number","description":"Used in basic groups to reorder updates and make sure that all of them was received."}],"description":"Admin permissions of a user in a legacy group were changed"},{"name":"updateNewStickerSet","id":1753886890,"type":"Update","arguments":[{"name":"stickerset","type":"messages.StickerSet","description":"The installed stickerset"}],"description":"A new stickerset was installed"},{"name":"updateStickerSetsOrder","id":196268545,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"masks","type":"true","optional":true,"predicate":"flags.0","description":"Whether the updated stickers are mask stickers"},{"name":"order","type":"Long[]","description":"New sticker order by sticker ID"}],"description":"The order of stickersets was changed"},{"name":"updateStickerSets","id":1135492588,"type":"Update","arguments":[],"description":"Installed stickersets have changed, the client should refetch them using {@link messages.getAllStickers}"},{"name":"updateSavedGifs","id":2473931806,"type":"Update","arguments":[],"description":"The saved gif list has changed, the client should refetch it using {@link messages.getSavedGifs}"},{"name":"updateBotInlineQuery","id":1059076315,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"queryId","type":"Long","description":"Query ID"},{"name":"userId","type":"number","description":"User that sent the query"},{"name":"query","type":"string","description":"Text of query"},{"name":"geo","type":"GeoPoint","optional":true,"predicate":"flags.0","description":"Attached geolocation"},{"name":"peerType","type":"InlineQueryPeerType","optional":true,"predicate":"flags.1"},{"name":"offset","type":"string","description":"Offset to navigate through results"}],"description":"An incoming inline query"},{"name":"updateBotInlineSend","id":239663460,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"userId","type":"number","description":"The user that chose the result"},{"name":"query","type":"string","description":"The query that was used to obtain the result"},{"name":"geo","type":"GeoPoint","optional":true,"predicate":"flags.0","description":"Optional. Sender location, only for bots that require user location"},{"name":"id","type":"string","description":"The unique identifier for the result that was chosen"},{"name":"msgId","type":"InputBotInlineMessageID","optional":true,"predicate":"flags.1","description":"Identifier of the sent inline message. Available only if there is an inline keyboard attached to the message. Will be also received in callback queries and can be used to edit the message."}],"description":"The result of an inline query that was chosen by a user and sent to their chat partner. Please see our documentation on the feedback collecting for details on how to enable these updates for your bot."},{"name":"updateEditChannelMessage","id":457133559,"type":"Update","arguments":[{"name":"message","type":"Message","description":"The new message"},{"name":"pts","type":"number","description":"Event count after generation"},{"name":"ptsCount","type":"number","description":"Number of events that were generated"}],"description":"A message was edited in a channel/supergroup"},{"name":"updateBotCallbackQuery","id":3879028705,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"queryId","type":"Long","description":"Query ID"},{"name":"userId","type":"number","description":"ID of the user that pressed the button"},{"name":"peer","type":"Peer","description":"Chat where the inline keyboard was sent"},{"name":"msgId","type":"number","description":"Message ID"},{"name":"chatInstance","type":"Long","description":"Global identifier, uniquely corresponding to the chat to which the message with the callback button was sent. Useful for high scores in games."},{"name":"data","type":"Buffer","optional":true,"predicate":"flags.0","description":"Callback data"},{"name":"gameShortName","type":"string","optional":true,"predicate":"flags.1","description":"Short name of a Game to be returned, serves as the unique identifier for the game"}],"description":"A callback button was pressed, and the button data was sent to the bot that created the button"},{"name":"updateEditMessage","id":3825430691,"type":"Update","arguments":[{"name":"message","type":"Message","description":"The new edited message"},{"name":"pts","type":"number","description":"PTS"},{"name":"ptsCount","type":"number","description":"PTS count"}],"description":"A message was edited"},{"name":"updateInlineBotCallbackQuery","id":4191320666,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"queryId","type":"Long","description":"Query ID"},{"name":"userId","type":"number","description":"ID of the user that pressed the button"},{"name":"msgId","type":"InputBotInlineMessageID","description":"ID of the inline message with the button"},{"name":"chatInstance","type":"Long","description":"Global identifier, uniquely corresponding to the chat to which the message with the callback button was sent. Useful for high scores in games."},{"name":"data","type":"Buffer","optional":true,"predicate":"flags.0","description":"Data associated with the callback button. Be aware that a bad client can send arbitrary data in this field."},{"name":"gameShortName","type":"string","optional":true,"predicate":"flags.1","description":"Short name of a Game to be returned, serves as the unique identifier for the game"}],"description":"This notification is received by bots when a button is pressed"},{"name":"updateReadChannelOutbox","id":634833351,"type":"Update","arguments":[{"name":"channelId","type":"number","description":"Channel/supergroup ID"},{"name":"maxId","type":"number","description":"Position up to which all outgoing messages are read."}],"description":"Outgoing messages in a channel/supergroup were read"},{"name":"updateDraftMessage","id":3995842921,"type":"Update","arguments":[{"name":"peer","type":"Peer","description":"The peer to which the draft is associated"},{"name":"draft","type":"DraftMessage","description":"The draft"}],"description":"Notifies a change of a message draft."},{"name":"updateReadFeaturedStickers","id":1461528386,"type":"Update","arguments":[],"description":"Some featured stickers were marked as read"},{"name":"updateRecentStickers","id":2588027936,"type":"Update","arguments":[],"description":"The recent sticker list was updated"},{"name":"updateConfig","id":2720652550,"type":"Update","arguments":[],"description":"The server-side configuration has changed; the client should re-fetch the config using {@link help.getConfig}"},{"name":"updatePtsChanged","id":861169551,"type":"Update","arguments":[],"description":"Common message box sequence PTS has changed, state has to be refetched using updates.getState"},{"name":"updateChannelWebPage","id":1081547008,"type":"Update","arguments":[{"name":"channelId","type":"number","description":"Channel/supergroup ID"},{"name":"webpage","type":"WebPage","description":"Generated webpage preview"},{"name":"pts","type":"number","description":"Event count after generation"},{"name":"ptsCount","type":"number","description":"Number of events that were generated"}],"description":"A webpage preview of a link in a channel/supergroup message was generated"},{"name":"updateDialogPinned","id":1852826908,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"pinned","type":"true","optional":true,"predicate":"flags.0","description":"Whether the dialog was pinned"},{"name":"folderId","type":"number","optional":true,"predicate":"flags.1","description":"Peer folder ID, for more info click here"},{"name":"peer","type":"DialogPeer","description":"The dialog"}],"description":"A dialog was pinned/unpinned"},{"name":"updatePinnedDialogs","id":4195302562,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"folderId","type":"number","optional":true,"predicate":"flags.1","description":"Peer folder ID, for more info click here"},{"name":"order","type":"DialogPeer[]","optional":true,"predicate":"flags.0","description":"New order of pinned dialogs"}],"description":"Pinned dialogs were updated"},{"name":"updateBotWebhookJSON","id":2199371971,"type":"Update","arguments":[{"name":"data","type":"DataJSON","description":"The event"}],"description":"A new incoming event; for bots only"},{"name":"updateBotWebhookJSONQuery","id":2610053286,"type":"Update","arguments":[{"name":"queryId","type":"Long","description":"Query identifier"},{"name":"data","type":"DataJSON","description":"Query data"},{"name":"timeout","type":"number","description":"Query timeout"}],"description":"A new incoming query; for bots only"},{"name":"updateBotShippingQuery","id":3771582784,"type":"Update","arguments":[{"name":"queryId","type":"Long","description":"Unique query identifier"},{"name":"userId","type":"number","description":"User who sent the query"},{"name":"payload","type":"Buffer","description":"Bot specified invoice payload"},{"name":"shippingAddress","type":"PostAddress","description":"User specified shipping address"}],"description":"This object contains information about an incoming shipping query."},{"name":"updateBotPrecheckoutQuery","id":1563376297,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"queryId","type":"Long","description":"Unique query identifier"},{"name":"userId","type":"number","description":"User who sent the query"},{"name":"payload","type":"Buffer","description":"Bot specified invoice payload"},{"name":"info","type":"PaymentRequestedInfo","optional":true,"predicate":"flags.0","description":"Order info provided by the user"},{"name":"shippingOptionId","type":"string","optional":true,"predicate":"flags.1","description":"Identifier of the shipping option chosen by the user"},{"name":"currency","type":"string","description":"Three-letter ISO 4217 currency code"},{"name":"totalAmount","type":"Long","description":"Total amount in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies)."}],"description":"This object contains information about an incoming pre-checkout query."},{"name":"updatePhoneCall","id":2869914398,"type":"Update","arguments":[{"name":"phoneCall","type":"PhoneCall","description":"Phone call"}],"description":"An incoming phone call"},{"name":"updateLangPackTooLong","id":1180041828,"type":"Update","arguments":[{"name":"langCode","type":"string","description":"Language code"}],"description":"A language pack has changed, the client should manually fetch the changed strings using {@link langpack.getDifference}"},{"name":"updateLangPack","id":1442983757,"type":"Update","arguments":[{"name":"difference","type":"LangPackDifference","description":"Changed strings"}],"description":"Language pack updated"},{"name":"updateFavedStickers","id":3843135853,"type":"Update","arguments":[],"description":"The list of favorited stickers was changed, the client should call {@link messages.getFavedStickers} to refetch the new list"},{"name":"updateChannelReadMessagesContents","id":2307472197,"type":"Update","arguments":[{"name":"channelId","type":"number","description":"Channel/supergroup ID"},{"name":"messages","type":"number[]","description":"IDs of messages that were read"}],"description":"The specified channel/supergroup messages were read"},{"name":"updateContactsReset","id":1887741886,"type":"Update","arguments":[],"description":"All contacts were deleted"},{"name":"updateChannelAvailableMessages","id":1893427255,"type":"Update","arguments":[{"name":"channelId","type":"number","description":"Channel/supergroup ID"},{"name":"availableMinId","type":"number","description":"Identifier of a maximum unavailable message in a channel due to hidden history."}],"description":"The history of a channel/supergroup was hidden."},{"name":"updateDialogUnreadMark","id":3781450179,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"unread","type":"true","optional":true,"predicate":"flags.0","description":"Was the chat marked or unmarked as read"},{"name":"peer","type":"DialogPeer","description":"The dialog"}],"description":"The manual unread mark of a chat was changed"},{"name":"updateMessagePoll","id":2896258427,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"pollId","type":"Long","description":"Poll ID"},{"name":"poll","type":"Poll","optional":true,"predicate":"flags.0","description":"If the server knows the client hasn't cached this poll yet, the poll itself"},{"name":"results","type":"PollResults","description":"New poll results"}],"description":"The results of a poll have changed"},{"name":"updateChatDefaultBannedRights","id":1421875280,"type":"Update","arguments":[{"name":"peer","type":"Peer","description":"The chat"},{"name":"defaultBannedRights","type":"ChatBannedRights","description":"New default banned rights"},{"name":"version","type":"number","description":"Version"}],"description":"Default banned rights in a normal chat were updated"},{"name":"updateFolderPeers","id":422972864,"type":"Update","arguments":[{"name":"folderPeers","type":"FolderPeer[]","description":"New peer list"},{"name":"pts","type":"number","description":"Event count after generation"},{"name":"ptsCount","type":"number","description":"Number of events that were generated"}],"description":"The peer list of a peer folder was updated"},{"name":"updatePeerSettings","id":1786671974,"type":"Update","arguments":[{"name":"peer","type":"Peer","description":"The peer"},{"name":"settings","type":"PeerSettings","description":"Associated peer settings"}],"description":"Settings of a certain peer have changed"},{"name":"updatePeerLocated","id":3031420848,"type":"Update","arguments":[{"name":"peers","type":"PeerLocated[]","description":"Geolocated peer list update"}],"description":"List of peers near you was updated"},{"name":"updateNewScheduledMessage","id":967122427,"type":"Update","arguments":[{"name":"message","type":"Message","description":"Message"}],"description":"A message was added to the schedule queue of a chat"},{"name":"updateDeleteScheduledMessages","id":2424728814,"type":"Update","arguments":[{"name":"peer","type":"Peer","description":"Peer"},{"name":"messages","type":"number[]","description":"Deleted scheduled messages"}],"description":"Some scheduled messages were deleted from the schedule queue of a chat"},{"name":"updateTheme","id":2182544291,"type":"Update","arguments":[{"name":"theme","type":"Theme","description":"Theme"}],"description":"A cloud theme was updated"},{"name":"updateGeoLiveViewed","id":2267003193,"type":"Update","arguments":[{"name":"peer","type":"Peer","description":"The user that viewed the live geo position"},{"name":"msgId","type":"number","description":"Message ID of geo position message"}],"description":"Live geo position message was viewed"},{"name":"updateLoginToken","id":1448076945,"type":"Update","arguments":[],"description":"A login token (for login via QR code) was accepted."},{"name":"updateMessagePollVote","id":938909451,"type":"Update","arguments":[{"name":"pollId","type":"Long","description":"Poll ID"},{"name":"userId","type":"number","description":"User ID"},{"name":"options","type":"Buffer[]","description":"Chosen option(s)"},{"name":"qts","type":"number"}],"description":"A specific user has voted in a poll"},{"name":"updateDialogFilter","id":654302845,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"id","type":"number","description":"Folder ID"},{"name":"filter","type":"DialogFilter","optional":true,"predicate":"flags.0","description":"Folder info"}],"description":"A new folder was added"},{"name":"updateDialogFilterOrder","id":2782339333,"type":"Update","arguments":[{"name":"order","type":"number[]","description":"Ordered folder IDs"}],"description":"New folder order"},{"name":"updateDialogFilters","id":889491791,"type":"Update","arguments":[],"description":"Clients should update folder info"},{"name":"updatePhoneCallSignalingData","id":643940105,"type":"Update","arguments":[{"name":"phoneCallId","type":"Long","description":"Phone call ID"},{"name":"data","type":"Buffer","description":"Signaling payload"}],"description":"Incoming phone call signaling payload"},{"name":"updateChannelMessageForwards","id":1854571743,"type":"Update","arguments":[{"name":"channelId","type":"number","description":"Channel ID"},{"name":"id","type":"number","description":"ID of the message"},{"name":"forwards","type":"number","description":"New forward counter"}],"description":"The forward counter of a message in a channel has changed"},{"name":"updateReadChannelDiscussionInbox","id":482860628,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"channelId","type":"number","description":"Discussion group ID"},{"name":"topMsgId","type":"number","description":"ID of the group message that started the thread (message in linked discussion group)"},{"name":"readMaxId","type":"number","description":"Message ID of latest read incoming message for this thread"},{"name":"broadcastId","type":"number","optional":true,"predicate":"flags.0","description":"If set, contains the ID of the channel that contains the post that started the comment thread in the discussion group (channel_id)"},{"name":"broadcastPost","type":"number","optional":true,"predicate":"flags.0","description":"If set, contains the ID of the channel post that started the the comment thread"}],"description":"Incoming comments in a discussion thread were marked as read"},{"name":"updateReadChannelDiscussionOutbox","id":1178116716,"type":"Update","arguments":[{"name":"channelId","type":"number","description":"Supergroup ID"},{"name":"topMsgId","type":"number","description":"ID of the group message that started the thread"},{"name":"readMaxId","type":"number","description":"Message ID of latest read outgoing message for this thread"}],"description":"Outgoing comments in a discussion thread were marked as read"},{"name":"updatePeerBlocked","id":610945826,"type":"Update","arguments":[{"name":"peerId","type":"Peer","description":"The blocked peer"},{"name":"blocked","type":"boolean","description":"Whether the peer was blocked or unblocked"}],"description":"A peer was blocked"},{"name":"updateChannelUserTyping","id":1796675352,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"channelId","type":"number","description":"Channel ID"},{"name":"topMsgId","type":"number","optional":true,"predicate":"flags.0","description":"Thread ID"},{"name":"fromId","type":"Peer"},{"name":"action","type":"SendMessageAction","description":"Whether the user is typing, sending a media or doing something else"}],"description":"A user is typing in a supergroup, channel or message thread"},{"name":"updatePinnedMessages","id":3984976565,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"pinned","type":"true","optional":true,"predicate":"flags.0","description":"Whether the messages were pinned or unpinned"},{"name":"peer","type":"Peer","description":"Peer"},{"name":"messages","type":"number[]","description":"Message IDs"},{"name":"pts","type":"number","description":"Event count after generation"},{"name":"ptsCount","type":"number","description":"Number of events that were generated"}],"description":"Some messages were pinned in a chat"},{"name":"updatePinnedChannelMessages","id":2240317323,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"pinned","type":"true","optional":true,"predicate":"flags.0","description":"Whether the messages were pinned or unpinned"},{"name":"channelId","type":"number","description":"Channel ID"},{"name":"messages","type":"number[]","description":"Messages"},{"name":"pts","type":"number","description":"Event count after generation"},{"name":"ptsCount","type":"number","description":"Number of events that were generated"}],"description":"Messages were pinned/unpinned in a channel/supergroup"},{"name":"updateChat","id":321954198,"type":"Update","arguments":[{"name":"chatId","type":"number"}]},{"name":"updateGroupCallParticipants","id":4075543374,"type":"Update","arguments":[{"name":"call","type":"InputGroupCall"},{"name":"participants","type":"GroupCallParticipant[]"},{"name":"version","type":"number"}]},{"name":"updateGroupCall","id":2757671323,"type":"Update","arguments":[{"name":"chatId","type":"number"},{"name":"call","type":"GroupCall"}]},{"name":"updatePeerHistoryTTL","id":3147544997,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"peer","type":"Peer"},{"name":"ttlPeriod","type":"number","optional":true,"predicate":"flags.0"}]},{"name":"updateChatParticipant","id":4088625183,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"chatId","type":"number"},{"name":"date","type":"number"},{"name":"actorId","type":"number"},{"name":"userId","type":"number"},{"name":"prevParticipant","type":"ChatParticipant","optional":true,"predicate":"flags.0"},{"name":"newParticipant","type":"ChatParticipant","optional":true,"predicate":"flags.1"},{"name":"invite","type":"ExportedChatInvite","optional":true,"predicate":"flags.2"},{"name":"qts","type":"number"}]},{"name":"updateChannelParticipant","id":2146218476,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"channelId","type":"number","description":"Channel ID"},{"name":"date","type":"number","description":"Date of the event"},{"name":"actorId","type":"number"},{"name":"userId","type":"number","description":"User in question"},{"name":"prevParticipant","type":"ChannelParticipant","optional":true,"predicate":"flags.0","description":"Previous participant status"},{"name":"newParticipant","type":"ChannelParticipant","optional":true,"predicate":"flags.1","description":"New participant status"},{"name":"invite","type":"ExportedChatInvite","optional":true,"predicate":"flags.2"},{"name":"qts","type":"number","description":"PTS"}],"description":"A participant has left, joined, was banned or admined in a channel or supergroup."},{"name":"updateBotStopped","id":133777546,"type":"Update","arguments":[{"name":"userId","type":"number"},{"name":"date","type":"number"},{"name":"stopped","type":"boolean"},{"name":"qts","type":"number"}]},{"name":"updateGroupCallConnection","id":192428418,"type":"Update","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"presentation","type":"true","optional":true,"predicate":"flags.0"},{"name":"params","type":"DataJSON"}]},{"name":"updateBotCommands","id":3481143411,"type":"Update","arguments":[{"name":"peer","type":"Peer"},{"name":"botId","type":"number"},{"name":"commands","type":"BotCommand[]"}]},{"name":"updatesTooLong","id":3809980286,"type":"Updates","arguments":[],"description":"Too many updates, it is necessary to execute {@link updates.getDifference}."},{"name":"updateShortMessage","id":4210030643,"type":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"out","type":"true","optional":true,"predicate":"flags.1","description":"Whether the message is outgoing"},{"name":"mentioned","type":"true","optional":true,"predicate":"flags.4","description":"Whether we were mentioned in the message"},{"name":"mediaUnread","type":"true","optional":true,"predicate":"flags.5","description":"Whether there are some unread mentions in this message"},{"name":"silent","type":"true","optional":true,"predicate":"flags.13","description":"If true, the message is a silent message, no notifications should be triggered"},{"name":"id","type":"number","description":"The message ID"},{"name":"userId","type":"number","description":"The ID of the sender (if outgoing will be the ID of the destination) of the message"},{"name":"message","type":"string","description":"The message"},{"name":"pts","type":"number","description":"PTS"},{"name":"ptsCount","type":"number","description":"PTS count"},{"name":"date","type":"number","description":"date"},{"name":"fwdFrom","type":"MessageFwdHeader","optional":true,"predicate":"flags.2","description":"Info about a forwarded message"},{"name":"viaBotId","type":"number","optional":true,"predicate":"flags.11","description":"Info about the inline bot used to generate this message"},{"name":"replyTo","type":"MessageReplyHeader","optional":true,"predicate":"flags.3","description":"Reply and thread information"},{"name":"entities","type":"MessageEntity[]","optional":true,"predicate":"flags.7","description":"Entities for styled text"},{"name":"ttlPeriod","type":"number","optional":true,"predicate":"flags.25"}],"description":"Info about a message sent to (received from) another user"},{"name":"updateShortChatMessage","id":290961496,"type":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"out","type":"true","optional":true,"predicate":"flags.1","description":"Whether the message is outgoing"},{"name":"mentioned","type":"true","optional":true,"predicate":"flags.4","description":"Whether we were mentioned in this message"},{"name":"mediaUnread","type":"true","optional":true,"predicate":"flags.5","description":"Whether the message contains some unread mentions"},{"name":"silent","type":"true","optional":true,"predicate":"flags.13","description":"If true, the message is a silent message, no notifications should be triggered"},{"name":"id","type":"number","description":"ID of the message"},{"name":"fromId","type":"number","description":"ID of the sender of the message"},{"name":"chatId","type":"number","description":"ID of the chat where the message was sent"},{"name":"message","type":"string","description":"Message"},{"name":"pts","type":"number","description":"PTS"},{"name":"ptsCount","type":"number","description":"PTS count"},{"name":"date","type":"number","description":"date"},{"name":"fwdFrom","type":"MessageFwdHeader","optional":true,"predicate":"flags.2","description":"Info about a forwarded message"},{"name":"viaBotId","type":"number","optional":true,"predicate":"flags.11","description":"Info about the inline bot used to generate this message"},{"name":"replyTo","type":"MessageReplyHeader","optional":true,"predicate":"flags.3","description":"Reply (thread) information"},{"name":"entities","type":"MessageEntity[]","optional":true,"predicate":"flags.7","description":"Entities for styled text"},{"name":"ttlPeriod","type":"number","optional":true,"predicate":"flags.25"}],"description":"Shortened constructor containing info on one new incoming text message from a chat"},{"name":"updateShort","id":2027216577,"type":"Updates","arguments":[{"name":"update","type":"Update","description":"Update"},{"name":"date","type":"number","description":"Date of event"}],"description":"Shortened constructor containing info on one update not requiring auxiliary data"},{"name":"updatesCombined","id":1918567619,"type":"Updates","arguments":[{"name":"updates","type":"Update[]","description":"List of updates"},{"name":"users","type":"User[]","description":"List of users mentioned in updates"},{"name":"chats","type":"Chat[]","description":"List of chats mentioned in updates"},{"name":"date","type":"number","description":"Current date"},{"name":"seqStart","type":"number","description":"Value seq for the earliest update in a group"},{"name":"seq","type":"number","description":"Value seq for the latest update in a group"}],"description":"Constructor for a group of updates."},{"name":"updates","id":1957577280,"type":"Updates","arguments":[{"name":"updates","type":"Update[]","description":"List of updates"},{"name":"users","type":"User[]","description":"List of users mentioned in updates"},{"name":"chats","type":"Chat[]","description":"List of chats mentioned in updates"},{"name":"date","type":"number","description":"Current date"},{"name":"seq","type":"number","description":"Total number of sent updates"}],"description":"Full constructor of updates"},{"name":"updateShortSentMessage","id":2417352961,"type":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"out","type":"true","optional":true,"predicate":"flags.1","description":"Whether the message is outgoing"},{"name":"id","type":"number","description":"ID of the sent message"},{"name":"pts","type":"number","description":"PTS"},{"name":"ptsCount","type":"number","description":"PTS count"},{"name":"date","type":"number","description":"date"},{"name":"media","type":"MessageMedia","optional":true,"predicate":"flags.9","description":"Attached media"},{"name":"entities","type":"MessageEntity[]","optional":true,"predicate":"flags.7","description":"Entities for styled text"},{"name":"ttlPeriod","type":"number","optional":true,"predicate":"flags.25"}],"description":"Shortened constructor containing info on one outgoing message to a contact (the destination chat has to be extracted from the method call that returned this object)."},{"name":"dcOption","id":414687501,"type":"DcOption","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"ipv6","type":"true","optional":true,"predicate":"flags.0","description":"Whether the specified IP is an IPv6 address"},{"name":"mediaOnly","type":"true","optional":true,"predicate":"flags.1","description":"Whether this DC should only be used to download or upload files"},{"name":"tcpoOnly","type":"true","optional":true,"predicate":"flags.2","description":"Whether this DC only supports connection with transport obfuscation"},{"name":"cdn","type":"true","optional":true,"predicate":"flags.3","description":"Whether this is a CDN DC."},{"name":"static","type":"true","optional":true,"predicate":"flags.4","description":"If set, this IP should be used when connecting through a proxy"},{"name":"id","type":"number","description":"DC ID"},{"name":"ipAddress","type":"string","description":"IP address of DC"},{"name":"port","type":"number","description":"Port"},{"name":"secret","type":"Buffer","optional":true,"predicate":"flags.10","description":"If the tcpo_only flag is set, specifies the secret to use when connecting using transport obfuscation"}],"description":"Data centre"},{"name":"config","id":856375399,"type":"Config","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"phonecallsEnabled","type":"true","optional":true,"predicate":"flags.1","description":"Whether phone calls can be used"},{"name":"defaultP2pContacts","type":"true","optional":true,"predicate":"flags.3","description":"Whether the client should use P2P by default for phone calls with contacts"},{"name":"preloadFeaturedStickers","type":"true","optional":true,"predicate":"flags.4","description":"Whether the client should preload featured stickers"},{"name":"ignorePhoneEntities","type":"true","optional":true,"predicate":"flags.5","description":"Whether the client should ignore phone entities"},{"name":"revokePmInbox","type":"true","optional":true,"predicate":"flags.6","description":"Whether incoming private messages can be deleted for both participants"},{"name":"blockedMode","type":"true","optional":true,"predicate":"flags.8","description":"Indicates that telegram is probably censored by governments/ISPs in the current region"},{"name":"pfsEnabled","type":"true","optional":true,"predicate":"flags.13","description":"Whether pfs was used"},{"name":"date","type":"number","description":"Current date at the server"},{"name":"expires","type":"number","description":"Expiration date of this config: when it expires it'll have to be refetched using {@link help.getConfig}"},{"name":"testMode","type":"boolean","description":"Whether we're connected to the test DCs"},{"name":"thisDc","type":"number","description":"ID of the DC that returned the reply"},{"name":"dcOptions","type":"DcOption[]","description":"DC IP list"},{"name":"dcTxtDomainName","type":"string","description":"Domain name for fetching encrypted DC list from DNS TXT record"},{"name":"chatSizeMax","type":"number","description":"Maximum member count for normal groups"},{"name":"megagroupSizeMax","type":"number","description":"Maximum member count for supergroups"},{"name":"forwardedCountMax","type":"number","description":"Maximum number of messages that can be forwarded at once using {@link messages.forwardMessages}."},{"name":"onlineUpdatePeriodMs","type":"number","description":"The client should {@link account.updateStatus} every N milliseconds"},{"name":"offlineBlurTimeoutMs","type":"number","description":"Delay before offline status needs to be sent to the server"},{"name":"offlineIdleTimeoutMs","type":"number","description":"Time without any user activity after which it should be treated offline"},{"name":"onlineCloudTimeoutMs","type":"number","description":"If we are offline, but were online from some other client in last online_cloud_timeout_ms milliseconds after we had gone offline, then delay offline notification for notify_cloud_delay_ms milliseconds."},{"name":"notifyCloudDelayMs","type":"number","description":"If we are offline, but online from some other client then delay sending the offline notification for notify_cloud_delay_ms milliseconds."},{"name":"notifyDefaultDelayMs","type":"number","description":"If some other client is online, then delay notification for notification_default_delay_ms milliseconds"},{"name":"pushChatPeriodMs","type":"number","description":"Not for client use"},{"name":"pushChatLimit","type":"number","description":"Not for client use"},{"name":"savedGifsLimit","type":"number","description":"Maximum count of saved gifs"},{"name":"editTimeLimit","type":"number","description":"Only messages with age smaller than the one specified can be edited"},{"name":"revokeTimeLimit","type":"number","description":"Only channel/supergroup messages with age smaller than the specified can be deleted"},{"name":"revokePmTimeLimit","type":"number","description":"Only private messages with age smaller than the specified can be deleted"},{"name":"ratingEDecay","type":"number","description":"Exponential decay rate for computing top peer rating"},{"name":"stickersRecentLimit","type":"number","description":"Maximum number of recent stickers"},{"name":"stickersFavedLimit","type":"number","description":"Maximum number of faved stickers"},{"name":"channelsReadMediaPeriod","type":"number","description":"Indicates that round videos (video notes) and voice messages sent in channels and older than the specified period must be marked as read"},{"name":"tmpSessions","type":"number","optional":true,"predicate":"flags.0","description":"Temporary passport sessions"},{"name":"pinnedDialogsCountMax","type":"number","description":"Maximum count of pinned dialogs"},{"name":"pinnedInfolderCountMax","type":"number","description":"Maximum count of dialogs per folder"},{"name":"callReceiveTimeoutMs","type":"number","description":"Maximum allowed outgoing ring time in VoIP calls: if the user we're calling doesn't reply within the specified time (in milliseconds), we should hang up the call"},{"name":"callRingTimeoutMs","type":"number","description":"Maximum allowed incoming ring time in VoIP calls: if the current user doesn't reply within the specified time (in milliseconds), the call will be automatically refused"},{"name":"callConnectTimeoutMs","type":"number","description":"VoIP connection timeout: if the instance of libtgvoip on the other side of the call doesn't connect to our instance of libtgvoip within the specified time (in milliseconds), the call must be aborted"},{"name":"callPacketTimeoutMs","type":"number","description":"If during a VoIP call a packet isn't received for the specified period of time, the call must be aborted"},{"name":"meUrlPrefix","type":"string","description":"The domain to use to parse in-app links.
For example t.me indicates that t.me/username links should parsed to @username, t.me/addsticker/name should be parsed to the appropriate stickerset and so on..."},{"name":"autoupdateUrlPrefix","type":"string","optional":true,"predicate":"flags.7","description":"URL to use to auto-update the current app"},{"name":"gifSearchUsername","type":"string","optional":true,"predicate":"flags.9","description":"Username of the bot to use to search for GIFs"},{"name":"venueSearchUsername","type":"string","optional":true,"predicate":"flags.10","description":"Username of the bot to use to search for venues"},{"name":"imgSearchUsername","type":"string","optional":true,"predicate":"flags.11","description":"Username of the bot to use for image search"},{"name":"staticMapsProvider","type":"string","optional":true,"predicate":"flags.12","description":"ID of the map provider to use for venues"},{"name":"captionLengthMax","type":"number","description":"Maximum length of caption (length in utf8 codepoints)"},{"name":"messageLengthMax","type":"number","description":"Maximum length of messages (length in utf8 codepoints)"},{"name":"webfileDcId","type":"number","description":"DC ID to use to download webfiles"},{"name":"suggestedLangCode","type":"string","optional":true,"predicate":"flags.2","description":"Suggested language code"},{"name":"langPackVersion","type":"number","optional":true,"predicate":"flags.2","description":"Language pack version"},{"name":"baseLangPackVersion","type":"number","optional":true,"predicate":"flags.2","description":"Basic language pack version"}],"description":"Current configuration"},{"name":"nearestDc","id":2384074613,"type":"NearestDc","arguments":[{"name":"country","type":"string","description":"Country code determined by geo-ip"},{"name":"thisDc","type":"number","description":"Number of current data centre"},{"name":"nearestDc","type":"number","description":"Number of nearest data centre"}],"description":"Nearest data centre, according to geo-ip."},{"name":"encryptedChatEmpty","id":2877210784,"type":"EncryptedChat","arguments":[{"name":"id","type":"number","description":"Chat ID"}],"description":"Empty constructor."},{"name":"encryptedChatWaiting","id":1006044124,"type":"EncryptedChat","arguments":[{"name":"id","type":"number","description":"Chat ID"},{"name":"accessHash","type":"Long","description":"Checking sum depending on user ID"},{"name":"date","type":"number","description":"Date of chat creation"},{"name":"adminId","type":"number","description":"Chat creator ID"},{"name":"participantId","type":"number","description":"ID of second chat participant"}],"description":"Chat waiting for approval of second participant."},{"name":"encryptedChatRequested","id":1651608194,"type":"EncryptedChat","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"folderId","type":"number","optional":true,"predicate":"flags.0","description":"Peer folder ID, for more info click here"},{"name":"id","type":"number","description":"Chat ID"},{"name":"accessHash","type":"Long","description":"Check sum depending on user ID"},{"name":"date","type":"number","description":"Chat creation date"},{"name":"adminId","type":"number","description":"Chat creator ID"},{"name":"participantId","type":"number","description":"ID of second chat participant"},{"name":"gA","type":"Buffer","description":"A = g ^ a mod p, see Wikipedia"}],"description":"Request to create an encrypted chat."},{"name":"encryptedChat","id":4199992886,"type":"EncryptedChat","arguments":[{"name":"id","type":"number","description":"Chat ID"},{"name":"accessHash","type":"Long","description":"Check sum dependant on the user ID"},{"name":"date","type":"number","description":"Date chat was created"},{"name":"adminId","type":"number","description":"Chat creator ID"},{"name":"participantId","type":"number","description":"ID of the second chat participant"},{"name":"gAOrB","type":"Buffer","description":"B = g ^ b mod p, if the currently authorized user is the chat's creator,
or A = g ^ a mod p otherwise
See Wikipedia for more info"},{"name":"keyFingerprint","type":"Long","description":"64-bit fingerprint of received key"}],"description":"Encrypted chat"},{"name":"encryptedChatDiscarded","id":505183301,"type":"EncryptedChat","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"historyDeleted","type":"true","optional":true,"predicate":"flags.0"},{"name":"id","type":"number","description":"Chat ID"}],"description":"Discarded or deleted chat."},{"name":"inputEncryptedChat","id":4047615457,"type":"InputEncryptedChat","arguments":[{"name":"chatId","type":"number","description":"Chat ID"},{"name":"accessHash","type":"Long","description":"Checking sum from constructor {@link encryptedChat}, {@link encryptedChatWaiting} or {@link encryptedChatRequested}"}],"description":"Creates an encrypted chat."},{"name":"encryptedFileEmpty","id":3256830334,"type":"EncryptedFile","arguments":[],"description":"Empty constructor, unexisitng file."},{"name":"encryptedFile","id":1248893260,"type":"EncryptedFile","arguments":[{"name":"id","type":"Long","description":"File ID"},{"name":"accessHash","type":"Long","description":"Checking sum depending on user ID"},{"name":"size","type":"number","description":"File size in bytes"},{"name":"dcId","type":"number","description":"Number of data centre"},{"name":"keyFingerprint","type":"number","description":"32-bit fingerprint of key used for file encryption"}],"description":"Encrypted file."},{"name":"inputEncryptedFileEmpty","id":406307684,"type":"InputEncryptedFile","arguments":[],"description":"Empty constructor."},{"name":"inputEncryptedFileUploaded","id":1690108678,"type":"InputEncryptedFile","arguments":[{"name":"id","type":"Long","description":"Random file ID created by clien"},{"name":"parts","type":"number","description":"Number of saved parts"},{"name":"md5Checksum","type":"string","description":"In case md5-HASH of the (already encrypted) file was transmitted, file content will be checked prior to use"},{"name":"keyFingerprint","type":"number","description":"32-bit fingerprint of the key used to encrypt a file"}],"description":"Sets new encrypted file saved by parts using upload.saveFilePart method."},{"name":"inputEncryptedFile","id":1511503333,"type":"InputEncryptedFile","arguments":[{"name":"id","type":"Long","description":"File ID, value of id parameter from {@link encryptedFile}"},{"name":"accessHash","type":"Long","description":"Checking sum, value of access_hash parameter from {@link encryptedFile}"}],"description":"Sets forwarded encrypted file for attachment."},{"name":"inputEncryptedFileBigUploaded","id":767652808,"type":"InputEncryptedFile","arguments":[{"name":"id","type":"Long","description":"Random file id, created by the client"},{"name":"parts","type":"number","description":"Number of saved parts"},{"name":"keyFingerprint","type":"number","description":"32-bit imprint of the key used to encrypt the file"}],"description":"Assigns a new big encrypted file (over 10Mb in size), saved in parts using the method {@link upload.saveBigFilePart}."},{"name":"encryptedMessage","id":3977822488,"type":"EncryptedMessage","arguments":[{"name":"randomId","type":"Long","description":"Random message ID, assigned by the author of message"},{"name":"chatId","type":"number","description":"ID of encrypted chat"},{"name":"date","type":"number","description":"Date of sending"},{"name":"bytes","type":"Buffer","description":"TL-serialising of DecryptedMessage type, encrypted with the key creatied at stage of chat initialization"},{"name":"file","type":"EncryptedFile","description":"Attached encrypted file"}],"description":"Encrypted message."},{"name":"encryptedMessageService","id":594758406,"type":"EncryptedMessage","arguments":[{"name":"randomId","type":"Long","description":"Random message ID, assigned by the author of message"},{"name":"chatId","type":"number","description":"ID of encrypted chat"},{"name":"date","type":"number","description":"Date of sending"},{"name":"bytes","type":"Buffer","description":"TL-serialising of DecryptedMessage type, encrypted with the key creatied at stage of chat initialization"}],"description":"Encrypted service message"},{"name":"inputDocumentEmpty","id":1928391342,"type":"InputDocument","arguments":[],"description":"Empty constructor."},{"name":"inputDocument","id":448771445,"type":"InputDocument","arguments":[{"name":"id","type":"Long","description":"Document ID"},{"name":"accessHash","type":"Long","description":"access_hash parameter from the {@link document} constructor"},{"name":"fileReference","type":"Buffer","description":"File reference"}],"description":"Defines a video for subsequent interaction."},{"name":"documentEmpty","id":922273905,"type":"Document","arguments":[{"name":"id","type":"Long","description":"Document ID or 0"}],"description":"Empty constructor, document doesn't exist."},{"name":"document","id":512177195,"type":"Document","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"id","type":"Long","description":"Document ID"},{"name":"accessHash","type":"Long","description":"Check sum, dependant on document ID"},{"name":"fileReference","type":"Buffer","description":"File reference"},{"name":"date","type":"number","description":"Creation date"},{"name":"mimeType","type":"string","description":"MIME type"},{"name":"size","type":"number","description":"Size"},{"name":"thumbs","type":"PhotoSize[]","optional":true,"predicate":"flags.0","description":"Thumbnails"},{"name":"videoThumbs","type":"VideoSize[]","optional":true,"predicate":"flags.1","description":"Video thumbnails"},{"name":"dcId","type":"number","description":"DC ID"},{"name":"attributes","type":"DocumentAttribute[]","description":"Attributes"}],"description":"Document"},{"name":"notifyPeer","id":2681474008,"type":"NotifyPeer","arguments":[{"name":"peer","type":"Peer","description":"user or group"}],"description":"Notifications generated by a certain user or group."},{"name":"notifyUsers","id":3033021260,"type":"NotifyPeer","arguments":[],"description":"Notifications generated by all users."},{"name":"notifyChats","id":3221737155,"type":"NotifyPeer","arguments":[],"description":"Notifications generated by all groups."},{"name":"notifyBroadcasts","id":3591563503,"type":"NotifyPeer","arguments":[],"description":"Channel notification settings"},{"name":"sendMessageTypingAction","id":381645902,"type":"SendMessageAction","arguments":[],"description":"User is typing."},{"name":"sendMessageCancelAction","id":4250847477,"type":"SendMessageAction","arguments":[],"description":"Invalidate all previous action updates. E.g. when user deletes entered text or aborts a video upload."},{"name":"sendMessageRecordVideoAction","id":2710034031,"type":"SendMessageAction","arguments":[],"description":"User is recording a video."},{"name":"sendMessageUploadVideoAction","id":3916839660,"type":"SendMessageAction","arguments":[{"name":"progress","type":"number","description":"Progress percentage"}],"description":"User is uploading a video."},{"name":"sendMessageRecordAudioAction","id":3576656887,"type":"SendMessageAction","arguments":[],"description":"User is recording a voice message."},{"name":"sendMessageUploadAudioAction","id":4082227115,"type":"SendMessageAction","arguments":[{"name":"progress","type":"number","description":"Progress percentage"}],"description":"User is uploading a voice message."},{"name":"sendMessageUploadPhotoAction","id":3520285222,"type":"SendMessageAction","arguments":[{"name":"progress","type":"number","description":"Progress percentage"}],"description":"User is uploading a photo."},{"name":"sendMessageUploadDocumentAction","id":2852968932,"type":"SendMessageAction","arguments":[{"name":"progress","type":"number","description":"Progress percentage"}],"description":"User is uploading a file."},{"name":"sendMessageGeoLocationAction","id":393186209,"type":"SendMessageAction","arguments":[],"description":"User is selecting a location to share."},{"name":"sendMessageChooseContactAction","id":1653390447,"type":"SendMessageAction","arguments":[],"description":"User is selecting a contact to share."},{"name":"sendMessageGamePlayAction","id":3714748232,"type":"SendMessageAction","arguments":[],"description":"User is playing a game"},{"name":"sendMessageRecordRoundAction","id":2297593788,"type":"SendMessageAction","arguments":[],"description":"User is recording a round video to share"},{"name":"sendMessageUploadRoundAction","id":608050278,"type":"SendMessageAction","arguments":[{"name":"progress","type":"number","description":"Progress percentage"}],"description":"User is uploading a round video"},{"name":"speakingInGroupCallAction","id":3643548293,"type":"SendMessageAction","arguments":[]},{"name":"sendMessageHistoryImportAction","id":3688534598,"type":"SendMessageAction","arguments":[{"name":"progress","type":"number"}]},{"name":"inputPrivacyKeyStatusTimestamp","id":1335282456,"type":"InputPrivacyKey","arguments":[],"description":"Whether we can see the exact last online timestamp of the user"},{"name":"inputPrivacyKeyChatInvite","id":3187344422,"type":"InputPrivacyKey","arguments":[],"description":"Whether the user can be invited to chats"},{"name":"inputPrivacyKeyPhoneCall","id":4206550111,"type":"InputPrivacyKey","arguments":[],"description":"Whether the user will accept phone calls"},{"name":"inputPrivacyKeyPhoneP2P","id":3684593874,"type":"InputPrivacyKey","arguments":[],"description":"Whether the user allows P2P communication during VoIP calls"},{"name":"inputPrivacyKeyForwards","id":2765966344,"type":"InputPrivacyKey","arguments":[],"description":"Whether messages forwarded from this user will be anonymous"},{"name":"inputPrivacyKeyProfilePhoto","id":1461304012,"type":"InputPrivacyKey","arguments":[],"description":"Whether people will be able to see the user's profile picture"},{"name":"inputPrivacyKeyPhoneNumber","id":55761658,"type":"InputPrivacyKey","arguments":[],"description":"Whether people will be able to see the user's phone number"},{"name":"inputPrivacyKeyAddedByPhone","id":3508640733,"type":"InputPrivacyKey","arguments":[],"description":"Whether people can add you to their contact list by your phone number"},{"name":"privacyKeyStatusTimestamp","id":3157175088,"type":"PrivacyKey","arguments":[],"description":"Whether we can see the last online timestamp"},{"name":"privacyKeyChatInvite","id":1343122938,"type":"PrivacyKey","arguments":[],"description":"Whether the user can be invited to chats"},{"name":"privacyKeyPhoneCall","id":1030105979,"type":"PrivacyKey","arguments":[],"description":"Whether the user accepts phone calls"},{"name":"privacyKeyPhoneP2P","id":961092808,"type":"PrivacyKey","arguments":[],"description":"Whether P2P connections in phone calls are allowed"},{"name":"privacyKeyForwards","id":1777096355,"type":"PrivacyKey","arguments":[],"description":"Whether messages forwarded from the user will be anonymously forwarded"},{"name":"privacyKeyProfilePhoto","id":2517966829,"type":"PrivacyKey","arguments":[],"description":"Whether the profile picture of the user is visible"},{"name":"privacyKeyPhoneNumber","id":3516589165,"type":"PrivacyKey","arguments":[],"description":"Whether the user allows us to see their phone number"},{"name":"privacyKeyAddedByPhone","id":1124062251,"type":"PrivacyKey","arguments":[],"description":"Whether people can add you to their contact list by your phone number"},{"name":"inputPrivacyValueAllowContacts","id":218751099,"type":"InputPrivacyRule","arguments":[],"description":"Allow only contacts"},{"name":"inputPrivacyValueAllowAll","id":407582158,"type":"InputPrivacyRule","arguments":[],"description":"Allow all users"},{"name":"inputPrivacyValueAllowUsers","id":320652927,"type":"InputPrivacyRule","arguments":[{"name":"users","type":"InputUser[]","description":"Allowed users"}],"description":"Allow only certain users"},{"name":"inputPrivacyValueDisallowContacts","id":195371015,"type":"InputPrivacyRule","arguments":[],"description":"Disallow only contacts"},{"name":"inputPrivacyValueDisallowAll","id":3597362889,"type":"InputPrivacyRule","arguments":[],"description":"Disallow all"},{"name":"inputPrivacyValueDisallowUsers","id":2417034343,"type":"InputPrivacyRule","arguments":[{"name":"users","type":"InputUser[]","description":"Users to disallow"}],"description":"Disallow only certain users"},{"name":"inputPrivacyValueAllowChatParticipants","id":1283572154,"type":"InputPrivacyRule","arguments":[{"name":"chats","type":"number[]","description":"Allowed chat IDs"}],"description":"Allow only participants of certain chats"},{"name":"inputPrivacyValueDisallowChatParticipants","id":3626197935,"type":"InputPrivacyRule","arguments":[{"name":"chats","type":"number[]","description":"Disallowed chat IDs"}],"description":"Disallow only participants of certain chats"},{"name":"privacyValueAllowContacts","id":4294843308,"type":"PrivacyRule","arguments":[],"description":"Allow all contacts"},{"name":"privacyValueAllowAll","id":1698855810,"type":"PrivacyRule","arguments":[],"description":"Allow all users"},{"name":"privacyValueAllowUsers","id":1297858060,"type":"PrivacyRule","arguments":[{"name":"users","type":"number[]","description":"Allowed users"}],"description":"Allow only certain users"},{"name":"privacyValueDisallowContacts","id":4169726490,"type":"PrivacyRule","arguments":[],"description":"Disallow only contacts"},{"name":"privacyValueDisallowAll","id":2339628899,"type":"PrivacyRule","arguments":[],"description":"Disallow all users"},{"name":"privacyValueDisallowUsers","id":209668535,"type":"PrivacyRule","arguments":[{"name":"users","type":"number[]","description":"Disallowed users"}],"description":"Disallow only certain users"},{"name":"privacyValueAllowChatParticipants","id":415136107,"type":"PrivacyRule","arguments":[{"name":"chats","type":"number[]","description":"Allowed chats"}],"description":"Allow all participants of certain chats"},{"name":"privacyValueDisallowChatParticipants","id":2897086096,"type":"PrivacyRule","arguments":[{"name":"chats","type":"number[]","description":"Disallowed chats"}],"description":"Disallow only participants of certain chats"},{"name":"accountDaysTTL","id":3100684255,"type":"AccountDaysTTL","arguments":[{"name":"days","type":"number","description":"This account will self-destruct in the specified number of days"}],"description":"Time to live in days of the current account"},{"name":"documentAttributeImageSize","id":1815593308,"type":"DocumentAttribute","arguments":[{"name":"w","type":"number","description":"Width of image"},{"name":"h","type":"number","description":"Height of image"}],"description":"Defines the width and height of an image uploaded as document"},{"name":"documentAttributeAnimated","id":297109817,"type":"DocumentAttribute","arguments":[],"description":"Defines an animated GIF"},{"name":"documentAttributeSticker","id":1662637586,"type":"DocumentAttribute","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"mask","type":"true","optional":true,"predicate":"flags.1","description":"Whether this is a mask sticker"},{"name":"alt","type":"string","description":"Alternative emoji representation of sticker"},{"name":"stickerset","type":"InputStickerSet","description":"Associated stickerset"},{"name":"maskCoords","type":"MaskCoords","optional":true,"predicate":"flags.0","description":"Mask coordinates (if this is a mask sticker, attached to a photo)"}],"description":"Defines a sticker"},{"name":"documentAttributeVideo","id":250621158,"type":"DocumentAttribute","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"roundMessage","type":"true","optional":true,"predicate":"flags.0","description":"Whether this is a round video"},{"name":"supportsStreaming","type":"true","optional":true,"predicate":"flags.1","description":"Whether the video supports streaming"},{"name":"duration","type":"number","description":"Duration in seconds"},{"name":"w","type":"number","description":"Video width"},{"name":"h","type":"number","description":"Video height"}],"description":"Defines a video"},{"name":"documentAttributeAudio","id":2555574726,"type":"DocumentAttribute","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"voice","type":"true","optional":true,"predicate":"flags.10","description":"Whether this is a voice message"},{"name":"duration","type":"number","description":"Duration in seconds"},{"name":"title","type":"string","optional":true,"predicate":"flags.0","description":"Name of song"},{"name":"performer","type":"string","optional":true,"predicate":"flags.1","description":"Performer"},{"name":"waveform","type":"Buffer","optional":true,"predicate":"flags.2","description":"Waveform"}],"description":"Represents an audio file"},{"name":"documentAttributeFilename","id":358154344,"type":"DocumentAttribute","arguments":[{"name":"fileName","type":"string","description":"The file name"}],"description":"A simple document with a file name"},{"name":"documentAttributeHasStickers","id":2550256375,"type":"DocumentAttribute","arguments":[],"description":"Whether the current document has stickers attached"},{"name":"stickerPack","id":313694676,"type":"StickerPack","arguments":[{"name":"emoticon","type":"string","description":"Emoji"},{"name":"documents","type":"Long[]","description":"Stickers"}],"description":"A stickerpack is a group of stickers associated to the same emoji.
\nIt is not a sticker pack the way it is usually intended, you may be looking for a StickerSet."},{"name":"webPageEmpty","id":3943987176,"type":"WebPage","arguments":[{"name":"id","type":"Long","description":"Preview ID"}],"description":"No preview is available for the webpage"},{"name":"webPagePending","id":3313949212,"type":"WebPage","arguments":[{"name":"id","type":"Long","description":"ID of preview"},{"name":"date","type":"number","description":"When was the processing started"}],"description":"A preview of the webpage is currently being generated"},{"name":"webPage","id":3902555570,"type":"WebPage","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"id","type":"Long","description":"Preview ID"},{"name":"url","type":"string","description":"URL of previewed webpage"},{"name":"displayUrl","type":"string","description":"Webpage URL to be displayed to the user"},{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"},{"name":"type","type":"string","optional":true,"predicate":"flags.0","description":"Type of the web page. Can be: article, photo, audio, video, document, profile, app, or something else"},{"name":"siteName","type":"string","optional":true,"predicate":"flags.1","description":"Short name of the site (e.g., Google Docs, App Store)"},{"name":"title","type":"string","optional":true,"predicate":"flags.2","description":"Title of the content"},{"name":"description","type":"string","optional":true,"predicate":"flags.3","description":"Content description"},{"name":"photo","type":"Photo","optional":true,"predicate":"flags.4","description":"Image representing the content"},{"name":"embedUrl","type":"string","optional":true,"predicate":"flags.5","description":"URL to show in the embedded preview"},{"name":"embedType","type":"string","optional":true,"predicate":"flags.5","description":"MIME type of the embedded preview, (e.g., text/html or video/mp4)"},{"name":"embedWidth","type":"number","optional":true,"predicate":"flags.6","description":"Width of the embedded preview"},{"name":"embedHeight","type":"number","optional":true,"predicate":"flags.6","description":"Height of the embedded preview"},{"name":"duration","type":"number","optional":true,"predicate":"flags.7","description":"Duration of the content, in seconds"},{"name":"author","type":"string","optional":true,"predicate":"flags.8","description":"Author of the content"},{"name":"document","type":"Document","optional":true,"predicate":"flags.9","description":"Preview of the content as a media file"},{"name":"cachedPage","type":"Page","optional":true,"predicate":"flags.10","description":"Page contents in instant view format"},{"name":"attributes","type":"WebPageAttribute[]","optional":true,"predicate":"flags.12","description":"Webpage attributes"}],"description":"Webpage preview"},{"name":"webPageNotModified","id":1930545681,"type":"WebPage","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"cachedPageViews","type":"number","optional":true,"predicate":"flags.0","description":"Page view count"}],"description":"The preview of the webpage hasn't changed"},{"name":"authorization","id":2902578717,"type":"Authorization","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"current","type":"true","optional":true,"predicate":"flags.0","description":"Whether this is the current session"},{"name":"officialApp","type":"true","optional":true,"predicate":"flags.1","description":"Whether the session is from an official app"},{"name":"passwordPending","type":"true","optional":true,"predicate":"flags.2","description":"Whether the session is still waiting for a 2FA password"},{"name":"hash","type":"Long","description":"Identifier"},{"name":"deviceModel","type":"string","description":"Device model"},{"name":"platform","type":"string","description":"Platform"},{"name":"systemVersion","type":"string","description":"System version"},{"name":"apiId","type":"number","description":"API ID"},{"name":"appName","type":"string","description":"App name"},{"name":"appVersion","type":"string","description":"App version"},{"name":"dateCreated","type":"number","description":"When was the session created"},{"name":"dateActive","type":"number","description":"When was the session last active"},{"name":"ip","type":"string","description":"Last known IP"},{"name":"country","type":"string","description":"Country determined from IP"},{"name":"region","type":"string","description":"Region determined from IP"}],"description":"Logged-in session"},{"name":"receivedNotifyMessage","id":2743383929,"type":"ReceivedNotifyMessage","arguments":[{"name":"id","type":"number","description":"Message ID, for which PUSH-notifications were canceled"},{"name":"flags","type":"number","description":"Reserved for future use"}],"description":"Message ID, for which PUSH-notifications were cancelled."},{"name":"chatInviteExported","id":1847917725,"type":"ExportedChatInvite","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"revoked","type":"true","optional":true,"predicate":"flags.0"},{"name":"permanent","type":"true","optional":true,"predicate":"flags.5"},{"name":"link","type":"string","description":"Chat invitation link"},{"name":"adminId","type":"number"},{"name":"date","type":"number"},{"name":"startDate","type":"number","optional":true,"predicate":"flags.4"},{"name":"expireDate","type":"number","optional":true,"predicate":"flags.1"},{"name":"usageLimit","type":"number","optional":true,"predicate":"flags.2"},{"name":"usage","type":"number","optional":true,"predicate":"flags.3"}],"description":"Exported chat invite"},{"name":"chatInviteAlready","id":1516793212,"type":"ChatInvite","arguments":[{"name":"chat","type":"Chat","description":"The chat connected to the invite"}],"description":"The user has already joined this chat"},{"name":"chatInvite","id":3754096014,"type":"ChatInvite","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"channel","type":"true","optional":true,"predicate":"flags.0","description":"Whether this is a channel/supergroup or a normal group"},{"name":"broadcast","type":"true","optional":true,"predicate":"flags.1","description":"Whether this is a channel"},{"name":"public","type":"true","optional":true,"predicate":"flags.2","description":"Whether this is a public channel/supergroup"},{"name":"megagroup","type":"true","optional":true,"predicate":"flags.3","description":"Whether this is a supergroup"},{"name":"title","type":"string","description":"Chat/supergroup/channel title"},{"name":"photo","type":"Photo","description":"Chat/supergroup/channel photo"},{"name":"participantsCount","type":"number","description":"Participant count"},{"name":"participants","type":"User[]","optional":true,"predicate":"flags.4","description":"A few of the participants that are in the group"}],"description":"Chat invite info"},{"name":"chatInvitePeek","id":1634294960,"type":"ChatInvite","arguments":[{"name":"chat","type":"Chat","description":"Chat information"},{"name":"expires","type":"number","description":"Read-only anonymous access to this group will be revoked at this date"}],"description":"A chat invitation that also allows peeking into the group to read messages without joining it."},{"name":"inputStickerSetEmpty","id":4290128789,"type":"InputStickerSet","arguments":[],"description":"Empty constructor"},{"name":"inputStickerSetID","id":2649203305,"type":"InputStickerSet","arguments":[{"name":"id","type":"Long","description":"ID"},{"name":"accessHash","type":"Long","description":"Access hash"}],"description":"Stickerset by ID"},{"name":"inputStickerSetShortName","id":2250033312,"type":"InputStickerSet","arguments":[{"name":"shortName","type":"string","description":"From tg://addstickers?set=short_name"}],"description":"Stickerset by short name, from tg://addstickers?set=short_name"},{"name":"inputStickerSetAnimatedEmoji","id":42402760,"type":"InputStickerSet","arguments":[],"description":"Animated emojis stickerset"},{"name":"inputStickerSetDice","id":3867103758,"type":"InputStickerSet","arguments":[{"name":"emoticon","type":"string","description":"The emoji, for now \"🏀\", \"🎲\" and \"🎯\" are supported"}],"description":"Used for fetching animated dice stickers"},{"name":"stickerSet","id":3621724538,"type":"StickerSet","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"archived","type":"true","optional":true,"predicate":"flags.1","description":"Whether this stickerset was archived (due to too many saved stickers in the current account)"},{"name":"official","type":"true","optional":true,"predicate":"flags.2","description":"Is this stickerset official"},{"name":"masks","type":"true","optional":true,"predicate":"flags.3","description":"Is this a mask stickerset"},{"name":"animated","type":"true","optional":true,"predicate":"flags.5","description":"Is this an animated stickerpack"},{"name":"installedDate","type":"number","optional":true,"predicate":"flags.0","description":"When was this stickerset installed"},{"name":"id","type":"Long","description":"ID of the stickerset"},{"name":"accessHash","type":"Long","description":"Access hash of stickerset"},{"name":"title","type":"string","description":"Title of stickerset"},{"name":"shortName","type":"string","description":"Short name of stickerset to use in tg://addstickers?set=short_name"},{"name":"thumbs","type":"PhotoSize[]","optional":true,"predicate":"flags.4"},{"name":"thumbDcId","type":"number","optional":true,"predicate":"flags.4","description":"DC ID of thumbnail"},{"name":"thumbVersion","type":"number","optional":true,"predicate":"flags.4"},{"name":"count","type":"number","description":"Number of stickers in pack"},{"name":"hash","type":"number","description":"Hash"}],"description":"Represents a stickerset (stickerpack)"},{"name":"botCommand","id":3262826695,"type":"BotCommand","arguments":[{"name":"command","type":"string","description":"/command name"},{"name":"description","type":"string","description":"Description of the command"}],"description":"Describes a bot command that can be used in a chat"},{"name":"botInfo","id":2565348666,"type":"BotInfo","arguments":[{"name":"userId","type":"number","description":"ID of the bot"},{"name":"description","type":"string","description":"Description of the bot"},{"name":"commands","type":"BotCommand[]","description":"Bot commands that can be used in the chat"}],"description":"Info about bots (available bot commands, etc)"},{"name":"keyboardButton","id":2734311552,"type":"KeyboardButton","arguments":[{"name":"text","type":"string","description":"Button text"}],"description":"Bot keyboard button"},{"name":"keyboardButtonUrl","id":629866245,"type":"KeyboardButton","arguments":[{"name":"text","type":"string","description":"Button label"},{"name":"url","type":"string","description":"URL"}],"description":"URL button"},{"name":"keyboardButtonCallback","id":901503851,"type":"KeyboardButton","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"requiresPassword","type":"true","optional":true,"predicate":"flags.0","description":"Whether the user should verify their identity by entering their 2FA SRP parameters to the {@link messages.getBotCallbackAnswer} method. NOTE: telegram and the bot WILL NOT have access to the plaintext password, thanks to SRP. This button is mainly used by the official @botfather bot, for verifying the user's identity before transferring ownership of a bot to another user."},{"name":"text","type":"string","description":"Button text"},{"name":"data","type":"Buffer","description":"Callback data"}],"description":"Callback button"},{"name":"keyboardButtonRequestPhone","id":2976541737,"type":"KeyboardButton","arguments":[{"name":"text","type":"string","description":"Button text"}],"description":"Button to request a user's phone number"},{"name":"keyboardButtonRequestGeoLocation","id":4235815743,"type":"KeyboardButton","arguments":[{"name":"text","type":"string","description":"Button text"}],"description":"Button to request a user's geolocation"},{"name":"keyboardButtonSwitchInline","id":90744648,"type":"KeyboardButton","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"samePeer","type":"true","optional":true,"predicate":"flags.0","description":"If set, pressing the button will insert the bot‘s username and the specified inline query in the current chat's input field."},{"name":"text","type":"string","description":"Button label"},{"name":"query","type":"string","description":"The inline query to use"}],"description":"Button to force a user to switch to inline mode Pressing the button will prompt the user to select one of their chats, open that chat and insert the bot‘s username and the specified inline query in the input field."},{"name":"keyboardButtonGame","id":1358175439,"type":"KeyboardButton","arguments":[{"name":"text","type":"string","description":"Button text"}],"description":"Button to start a game"},{"name":"keyboardButtonBuy","id":2950250427,"type":"KeyboardButton","arguments":[{"name":"text","type":"string","description":"Button text"}],"description":"Button to buy a product"},{"name":"keyboardButtonUrlAuth","id":280464681,"type":"KeyboardButton","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"text","type":"string","description":"Button label"},{"name":"fwdText","type":"string","optional":true,"predicate":"flags.0","description":"New text of the button in forwarded messages."},{"name":"url","type":"string","description":"An HTTP URL to be opened with user authorization data added to the query string when the button is pressed. If the user refuses to provide authorization data, the original URL without information about the user will be opened. The data added is the same as described in Receiving authorization data.

NOTE: Services must always check the hash of the received data to verify the authentication and the integrity of the data as described in Checking authorization."},{"name":"buttonId","type":"number","description":"ID of the button to pass to {@link messages.requestUrlAuth}"}],"description":"Button to request a user to authorize via URL using Seamless Telegram Login. When the user clicks on such a button, {@link messages.requestUrlAuth} should be called, providing the button_id and the ID of the container message. The returned {@link urlAuthResultRequest} object will contain more details about the authorization request (request_write_access if the bot would like to send messages to the user along with the username of the bot which will be used for user authorization). Finally, the user can choose to call {@link messages.acceptUrlAuth} to get a {@link urlAuthResultAccepted} with the URL to open instead of the url of this constructor, or a {@link urlAuthResultDefault}, in which case the url of this constructor must be opened, instead. If the user refuses the authorization request but still wants to open the link, the url of this constructor must be used."},{"name":"inputKeyboardButtonUrlAuth","id":3492708308,"type":"KeyboardButton","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"requestWriteAccess","type":"true","optional":true,"predicate":"flags.0","description":"Set this flag to request the permission for your bot to send messages to the user."},{"name":"text","type":"string","description":"Button text"},{"name":"fwdText","type":"string","optional":true,"predicate":"flags.1","description":"New text of the button in forwarded messages."},{"name":"url","type":"string","description":"An HTTP URL to be opened with user authorization data added to the query string when the button is pressed. If the user refuses to provide authorization data, the original URL without information about the user will be opened. The data added is the same as described in Receiving authorization data.
NOTE: You must always check the hash of the received data to verify the authentication and the integrity of the data as described in Checking authorization."},{"name":"bot","type":"InputUser","description":"Username of a bot, which will be used for user authorization. See Setting up a bot for more details. If not specified, the current bot's username will be assumed. The url's domain must be the same as the domain linked with the bot. See Linking your domain to the bot for more details."}],"description":"Button to request a user to {@link messages.acceptUrlAuth} via URL using Seamless Telegram Login."},{"name":"keyboardButtonRequestPoll","id":3150401885,"type":"KeyboardButton","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"quiz","type":"boolean","optional":true,"predicate":"flags.0","description":"If set, only quiz polls can be sent"},{"name":"text","type":"string","description":"Button text"}],"description":"A button that allows the user to create and send a poll when pressed; available only in private"},{"name":"keyboardButtonRow","id":2002815875,"type":"KeyboardButtonRow","arguments":[{"name":"buttons","type":"KeyboardButton[]","description":"Bot or inline keyboard buttons"}],"description":"Inline keyboard row"},{"name":"replyKeyboardHide","id":2688441221,"type":"ReplyMarkup","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"selective","type":"true","optional":true,"predicate":"flags.2","description":"Use this flag if you want to remove the keyboard for specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.

Example: A user votes in a poll, bot returns confirmation message in reply to the vote and removes the keyboard for that user, while still showing the keyboard with poll options to users who haven't voted yet"}],"description":"Hide sent bot keyboard"},{"name":"replyKeyboardForceReply","id":2259946248,"type":"ReplyMarkup","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"singleUse","type":"true","optional":true,"predicate":"flags.1","description":"Requests clients to hide the keyboard as soon as it's been used. The keyboard will still be available, but clients will automatically display the usual letter-keyboard in the chat – the user can press a special button in the input field to see the custom keyboard again."},{"name":"selective","type":"true","optional":true,"predicate":"flags.2","description":"Use this parameter if you want to show the keyboard to specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.
Example: A user requests to change the bot‘s language, bot replies to the request with a keyboard to select the new language. Other users in the group don’t see the keyboard."},{"name":"placeholder","type":"string","optional":true,"predicate":"flags.3"}],"description":"Force the user to send a reply"},{"name":"replyKeyboardMarkup","id":2245892561,"type":"ReplyMarkup","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"resize","type":"true","optional":true,"predicate":"flags.0","description":"Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). If not set, the custom keyboard is always of the same height as the app's standard keyboard."},{"name":"singleUse","type":"true","optional":true,"predicate":"flags.1","description":"Requests clients to hide the keyboard as soon as it's been used. The keyboard will still be available, but clients will automatically display the usual letter-keyboard in the chat – the user can press a special button in the input field to see the custom keyboard again."},{"name":"selective","type":"true","optional":true,"predicate":"flags.2","description":"Use this parameter if you want to show the keyboard to specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.

Example: A user requests to change the bot‘s language, bot replies to the request with a keyboard to select the new language. Other users in the group don’t see the keyboard."},{"name":"rows","type":"KeyboardButtonRow[]","description":"Button row"},{"name":"placeholder","type":"string","optional":true,"predicate":"flags.3"}],"description":"Bot keyboard"},{"name":"replyInlineMarkup","id":1218642516,"type":"ReplyMarkup","arguments":[{"name":"rows","type":"KeyboardButtonRow[]","description":"Bot or inline keyboard rows"}],"description":"Bot or inline keyboard"},{"name":"messageEntityUnknown","id":3146955413,"type":"MessageEntity","arguments":[{"name":"offset","type":"number","description":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"number","description":"Length of message entity within message (in UTF-8 codepoints)"}],"description":"Unknown message entity"},{"name":"messageEntityMention","id":4194588573,"type":"MessageEntity","arguments":[{"name":"offset","type":"number","description":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"number","description":"Length of message entity within message (in UTF-8 codepoints)"}],"description":"Message entity mentioning the current user"},{"name":"messageEntityHashtag","id":1868782349,"type":"MessageEntity","arguments":[{"name":"offset","type":"number","description":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"number","description":"Length of message entity within message (in UTF-8 codepoints)"}],"description":"#hashtag message entity"},{"name":"messageEntityBotCommand","id":1827637959,"type":"MessageEntity","arguments":[{"name":"offset","type":"number","description":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"number","description":"Length of message entity within message (in UTF-8 codepoints)"}],"description":"Message entity representing a bot /command"},{"name":"messageEntityUrl","id":1859134776,"type":"MessageEntity","arguments":[{"name":"offset","type":"number","description":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"number","description":"Length of message entity within message (in UTF-8 codepoints)"}],"description":"Message entity representing an in-text url: https://google.com; for text urls, use {@link messageEntityTextUrl}."},{"name":"messageEntityEmail","id":1692693954,"type":"MessageEntity","arguments":[{"name":"offset","type":"number","description":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"number","description":"Length of message entity within message (in UTF-8 codepoints)"}],"description":"Message entity representing an email@example.com."},{"name":"messageEntityBold","id":3177253833,"type":"MessageEntity","arguments":[{"name":"offset","type":"number","description":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"number","description":"Length of message entity within message (in UTF-8 codepoints)"}],"description":"Message entity representing bold text."},{"name":"messageEntityItalic","id":2188348256,"type":"MessageEntity","arguments":[{"name":"offset","type":"number","description":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"number","description":"Length of message entity within message (in UTF-8 codepoints)"}],"description":"Message entity representing italic text."},{"name":"messageEntityCode","id":681706865,"type":"MessageEntity","arguments":[{"name":"offset","type":"number","description":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"number","description":"Length of message entity within message (in UTF-8 codepoints)"}],"description":"Message entity representing a codeblock."},{"name":"messageEntityPre","id":1938967520,"type":"MessageEntity","arguments":[{"name":"offset","type":"number","description":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"number","description":"Length of message entity within message (in UTF-8 codepoints)"},{"name":"language","type":"string","description":"Programming language of the code"}],"description":"Message entity representing a preformatted codeblock, allowing the user to specify a programming language for the codeblock."},{"name":"messageEntityTextUrl","id":1990644519,"type":"MessageEntity","arguments":[{"name":"offset","type":"number","description":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"number","description":"Length of message entity within message (in UTF-8 codepoints)"},{"name":"url","type":"string","description":"The actual URL"}],"description":"Message entity representing a text url: for in-text urls like https://google.com use {@link messageEntityUrl}."},{"name":"messageEntityMentionName","id":892193368,"type":"MessageEntity","arguments":[{"name":"offset","type":"number","description":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"number","description":"Length of message entity within message (in UTF-8 codepoints)"},{"name":"userId","type":"number","description":"Identifier of the user that was mentioned"}],"description":"Message entity representing a user mention: for creating a mention use {@link inputMessageEntityMentionName}."},{"name":"inputMessageEntityMentionName","id":546203849,"type":"MessageEntity","arguments":[{"name":"offset","type":"number","description":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"number","description":"Length of message entity within message (in UTF-8 codepoints)"},{"name":"userId","type":"InputUser","description":"Identifier of the user that was mentioned"}],"description":"Message entity that can be used to create a user user mention: received mentions use the {@link messageEntityMentionName} constructor, instead."},{"name":"messageEntityPhone","id":2607407947,"type":"MessageEntity","arguments":[{"name":"offset","type":"number","description":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"number","description":"Length of message entity within message (in UTF-8 codepoints)"}],"description":"Message entity representing a phone number."},{"name":"messageEntityCashtag","id":1280209983,"type":"MessageEntity","arguments":[{"name":"offset","type":"number","description":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"number","description":"Length of message entity within message (in UTF-8 codepoints)"}],"description":"Message entity representing a $cashtag."},{"name":"messageEntityUnderline","id":2622389899,"type":"MessageEntity","arguments":[{"name":"offset","type":"number","description":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"number","description":"Length of message entity within message (in UTF-8 codepoints)"}],"description":"Message entity representing underlined text."},{"name":"messageEntityStrike","id":3204879316,"type":"MessageEntity","arguments":[{"name":"offset","type":"number","description":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"number","description":"Length of message entity within message (in UTF-8 codepoints)"}],"description":"Message entity representing strikethrough text."},{"name":"messageEntityBlockquote","id":34469328,"type":"MessageEntity","arguments":[{"name":"offset","type":"number","description":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"number","description":"Length of message entity within message (in UTF-8 codepoints)"}],"description":"Message entity representing a block quote."},{"name":"messageEntityBankCard","id":1981704948,"type":"MessageEntity","arguments":[{"name":"offset","type":"number","description":"Offset of message entity within message (in UTF-8 codepoints)"},{"name":"length","type":"number","description":"Length of message entity within message (in UTF-8 codepoints)"}],"description":"Indicates a credit card number"},{"name":"inputChannelEmpty","id":4002160262,"type":"InputChannel","arguments":[],"description":"Represents the absence of a channel"},{"name":"inputChannel","id":2951442734,"type":"InputChannel","arguments":[{"name":"channelId","type":"number","description":"Channel ID"},{"name":"accessHash","type":"Long","description":"Access hash taken from the {@link channel} constructor"}],"description":"Represents a channel"},{"name":"inputChannelFromMessage","id":707290417,"type":"InputChannel","arguments":[{"name":"peer","type":"InputPeer","description":"The chat where the channel was seen"},{"name":"msgId","type":"number","description":"The message ID in the chat where the channel was seen"},{"name":"channelId","type":"number","description":"The channel ID"}],"description":"Defines a min channel that was seen in a certain message of a certain chat."},{"name":"messageRange","id":182649427,"type":"MessageRange","arguments":[{"name":"minId","type":"number","description":"Start of range (message ID)"},{"name":"maxId","type":"number","description":"End of range (message ID)"}],"description":"Indicates a range of chat messages"},{"name":"channelMessagesFilterEmpty","id":2496933607,"type":"ChannelMessagesFilter","arguments":[],"description":"No filter"},{"name":"channelMessagesFilter","id":3447183703,"type":"ChannelMessagesFilter","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"excludeNewMessages","type":"true","optional":true,"predicate":"flags.1","description":"Whether to exclude new messages from the search"},{"name":"ranges","type":"MessageRange[]","description":"A range of messages to fetch"}],"description":"Filter for getting only certain types of channel messages"},{"name":"channelParticipant","id":367766557,"type":"ChannelParticipant","arguments":[{"name":"userId","type":"number","description":"Pariticipant user ID"},{"name":"date","type":"number","description":"Date joined"}],"description":"Channel/supergroup participant"},{"name":"channelParticipantSelf","id":2737347181,"type":"ChannelParticipant","arguments":[{"name":"userId","type":"number","description":"User ID"},{"name":"inviterId","type":"number","description":"User that invited me to the channel/supergroup"},{"name":"date","type":"number","description":"When did I join the channel/supergroup"}],"description":"Myself"},{"name":"channelParticipantCreator","id":1149094475,"type":"ChannelParticipant","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"userId","type":"number","description":"User ID"},{"name":"adminRights","type":"ChatAdminRights","description":"Creator admin rights"},{"name":"rank","type":"string","optional":true,"predicate":"flags.0","description":"The role (rank) of the group creator in the group: just an arbitrary string, admin by default"}],"description":"Channel/supergroup creator"},{"name":"channelParticipantAdmin","id":3435051951,"type":"ChannelParticipant","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"canEdit","type":"true","optional":true,"predicate":"flags.0","description":"Can this admin promote other admins with the same permissions?"},{"name":"self","type":"true","optional":true,"predicate":"flags.1","description":"Is this the current user"},{"name":"userId","type":"number","description":"Admin user ID"},{"name":"inviterId","type":"number","optional":true,"predicate":"flags.1","description":"User that invited the admin to the channel/group"},{"name":"promotedBy","type":"number","description":"User that promoted the user to admin"},{"name":"date","type":"number","description":"When did the user join"},{"name":"adminRights","type":"ChatAdminRights","description":"Admin rights"},{"name":"rank","type":"string","optional":true,"predicate":"flags.2","description":"The role (rank) of the admin in the group: just an arbitrary string, admin by default"}],"description":"Admin"},{"name":"channelParticipantBanned","id":1352785878,"type":"ChannelParticipant","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"left","type":"true","optional":true,"predicate":"flags.0","description":"Whether the user has left the group"},{"name":"peer","type":"Peer"},{"name":"kickedBy","type":"number","description":"User was kicked by the specified admin"},{"name":"date","type":"number","description":"When did the user join the group"},{"name":"bannedRights","type":"ChatBannedRights","description":"Banned rights"}],"description":"Banned/kicked user"},{"name":"channelParticipantLeft","id":453242886,"type":"ChannelParticipant","arguments":[{"name":"peer","type":"Peer"}],"description":"A participant that left the channel/supergroup"},{"name":"channelParticipantsRecent","id":3728686201,"type":"ChannelParticipantsFilter","arguments":[],"description":"Fetch only recent participants"},{"name":"channelParticipantsAdmins","id":3026225513,"type":"ChannelParticipantsFilter","arguments":[],"description":"Fetch only admin participants"},{"name":"channelParticipantsKicked","id":2746567045,"type":"ChannelParticipantsFilter","arguments":[{"name":"q","type":"string","description":"Optional filter for searching kicked participants by name (otherwise empty)"}],"description":"Fetch only kicked participants"},{"name":"channelParticipantsBots","id":2966521435,"type":"ChannelParticipantsFilter","arguments":[],"description":"Fetch only bot participants"},{"name":"channelParticipantsBanned","id":338142689,"type":"ChannelParticipantsFilter","arguments":[{"name":"q","type":"string","description":"Optional filter for searching banned participants by name (otherwise empty)"}],"description":"Fetch only banned participants"},{"name":"channelParticipantsSearch","id":106343499,"type":"ChannelParticipantsFilter","arguments":[{"name":"q","type":"string","description":"Search query"}],"description":"Query participants by name"},{"name":"channelParticipantsContacts","id":3144345741,"type":"ChannelParticipantsFilter","arguments":[{"name":"q","type":"string","description":"Optional search query for searching contact participants by name"}],"description":"Fetch only participants that are also contacts"},{"name":"channelParticipantsMentions","id":3763035371,"type":"ChannelParticipantsFilter","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"q","type":"string","optional":true,"predicate":"flags.0","description":"Filter by user name or username"},{"name":"topMsgId","type":"number","optional":true,"predicate":"flags.1","description":"Look only for users that posted in this thread"}],"description":"This filter is used when looking for supergroup members to mention.
\nThis filter will automatically remove anonymous admins, and return even non-participant users that replied to a specific thread through the comment section of a channel."},{"name":"inputBotInlineMessageMediaAuto","id":864077702,"type":"InputBotInlineMessage","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"message","type":"string","description":"Caption"},{"name":"entities","type":"MessageEntity[]","optional":true,"predicate":"flags.1","description":"Message entities for styled text"},{"name":"replyMarkup","type":"ReplyMarkup","optional":true,"predicate":"flags.2","description":"Inline keyboard"}],"description":"A media"},{"name":"inputBotInlineMessageText","id":1036876423,"type":"InputBotInlineMessage","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"noWebpage","type":"true","optional":true,"predicate":"flags.0","description":"Disable webpage preview"},{"name":"message","type":"string","description":"Message"},{"name":"entities","type":"MessageEntity[]","optional":true,"predicate":"flags.1","description":"Message entities for styled text"},{"name":"replyMarkup","type":"ReplyMarkup","optional":true,"predicate":"flags.2","description":"Inline keyboard"}],"description":"Simple text message"},{"name":"inputBotInlineMessageMediaGeo","id":2526190213,"type":"InputBotInlineMessage","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"geoPoint","type":"InputGeoPoint","description":"Geolocation"},{"name":"heading","type":"number","optional":true,"predicate":"flags.0","description":"For live locations, a direction in which the location moves, in degrees; 1-360"},{"name":"period","type":"number","optional":true,"predicate":"flags.1","description":"Validity period"},{"name":"proximityNotificationRadius","type":"number","optional":true,"predicate":"flags.3","description":"For live locations, a maximum distance to another chat member for proximity alerts, in meters (0-100000)"},{"name":"replyMarkup","type":"ReplyMarkup","optional":true,"predicate":"flags.2","description":"Reply markup for bot/inline keyboards"}],"description":"Geolocation"},{"name":"inputBotInlineMessageMediaVenue","id":1098628881,"type":"InputBotInlineMessage","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"geoPoint","type":"InputGeoPoint","description":"Geolocation"},{"name":"title","type":"string","description":"Venue name"},{"name":"address","type":"string","description":"Address"},{"name":"provider","type":"string","description":"Venue provider: currently only \"foursquare\" and \"gplaces\" need to be supported"},{"name":"venueId","type":"string","description":"Venue ID in the provider's database"},{"name":"venueType","type":"string","description":"Venue type in the provider's database"},{"name":"replyMarkup","type":"ReplyMarkup","optional":true,"predicate":"flags.2","description":"Inline keyboard"}],"description":"Venue"},{"name":"inputBotInlineMessageMediaContact","id":2800599037,"type":"InputBotInlineMessage","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"phoneNumber","type":"string","description":"Phone number"},{"name":"firstName","type":"string","description":"First name"},{"name":"lastName","type":"string","description":"Last name"},{"name":"vcard","type":"string","description":"VCard info"},{"name":"replyMarkup","type":"ReplyMarkup","optional":true,"predicate":"flags.2","description":"Inline keyboard"}],"description":"A contact"},{"name":"inputBotInlineMessageGame","id":1262639204,"type":"InputBotInlineMessage","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"replyMarkup","type":"ReplyMarkup","optional":true,"predicate":"flags.2","description":"Inline keyboard"}],"description":"A game"},{"name":"inputBotInlineMessageMediaInvoice","id":3622273573,"type":"InputBotInlineMessage","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"title","type":"string"},{"name":"description","type":"string"},{"name":"photo","type":"InputWebDocument","optional":true,"predicate":"flags.0"},{"name":"invoice","type":"Invoice"},{"name":"payload","type":"Buffer"},{"name":"provider","type":"string"},{"name":"providerData","type":"DataJSON"},{"name":"replyMarkup","type":"ReplyMarkup","optional":true,"predicate":"flags.2"}]},{"name":"inputBotInlineResult","id":2294256409,"type":"InputBotInlineResult","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"id","type":"string","description":"ID of result"},{"name":"type","type":"string","description":"Result type (see bot API docs)"},{"name":"title","type":"string","optional":true,"predicate":"flags.1","description":"Result title"},{"name":"description","type":"string","optional":true,"predicate":"flags.2","description":"Result description"},{"name":"url","type":"string","optional":true,"predicate":"flags.3","description":"URL of result"},{"name":"thumb","type":"InputWebDocument","optional":true,"predicate":"flags.4","description":"Thumbnail for result"},{"name":"content","type":"InputWebDocument","optional":true,"predicate":"flags.5","description":"Result contents"},{"name":"sendMessage","type":"InputBotInlineMessage","description":"Message to send when the result is selected"}],"description":"An inline bot result"},{"name":"inputBotInlineResultPhoto","id":2832753831,"type":"InputBotInlineResult","arguments":[{"name":"id","type":"string","description":"Result ID"},{"name":"type","type":"string","description":"Result type (see bot API docs)"},{"name":"photo","type":"InputPhoto","description":"Photo to send"},{"name":"sendMessage","type":"InputBotInlineMessage","description":"Message to send when the result is selected"}],"description":"Photo"},{"name":"inputBotInlineResultDocument","id":4294507972,"type":"InputBotInlineResult","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"id","type":"string","description":"Result ID"},{"name":"type","type":"string","description":"Result type (see bot API docs)"},{"name":"title","type":"string","optional":true,"predicate":"flags.1","description":"Result title"},{"name":"description","type":"string","optional":true,"predicate":"flags.2","description":"Result description"},{"name":"document","type":"InputDocument","description":"Document to send"},{"name":"sendMessage","type":"InputBotInlineMessage","description":"Message to send when the result is selected"}],"description":"Document (media of any type except for photos)"},{"name":"inputBotInlineResultGame","id":1336154098,"type":"InputBotInlineResult","arguments":[{"name":"id","type":"string","description":"Result ID"},{"name":"shortName","type":"string","description":"Game short name"},{"name":"sendMessage","type":"InputBotInlineMessage","description":"Message to send when the result is selected"}],"description":"Game"},{"name":"botInlineMessageMediaAuto","id":1984755728,"type":"BotInlineMessage","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"message","type":"string","description":"Caption"},{"name":"entities","type":"MessageEntity[]","optional":true,"predicate":"flags.1","description":"Message entities for styled text"},{"name":"replyMarkup","type":"ReplyMarkup","optional":true,"predicate":"flags.2","description":"Inline keyboard"}],"description":"Send whatever media is attached to the {@link botInlineMediaResult}"},{"name":"botInlineMessageText","id":2357159394,"type":"BotInlineMessage","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"noWebpage","type":"true","optional":true,"predicate":"flags.0","description":"Disable webpage preview"},{"name":"message","type":"string","description":"The message"},{"name":"entities","type":"MessageEntity[]","optional":true,"predicate":"flags.1","description":"Message entities for styled text"},{"name":"replyMarkup","type":"ReplyMarkup","optional":true,"predicate":"flags.2","description":"Inline keyboard"}],"description":"Send a simple text message"},{"name":"botInlineMessageMediaGeo","id":85477117,"type":"BotInlineMessage","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"geo","type":"GeoPoint","description":"Geolocation"},{"name":"heading","type":"number","optional":true,"predicate":"flags.0","description":"For live locations, a direction in which the location moves, in degrees; 1-360."},{"name":"period","type":"number","optional":true,"predicate":"flags.1","description":"Validity period"},{"name":"proximityNotificationRadius","type":"number","optional":true,"predicate":"flags.3","description":"For live locations, a maximum distance to another chat member for proximity alerts, in meters (0-100000)."},{"name":"replyMarkup","type":"ReplyMarkup","optional":true,"predicate":"flags.2","description":"Inline keyboard"}],"description":"Send a geolocation"},{"name":"botInlineMessageMediaVenue","id":2324063644,"type":"BotInlineMessage","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"geo","type":"GeoPoint","description":"Geolocation of venue"},{"name":"title","type":"string","description":"Venue name"},{"name":"address","type":"string","description":"Address"},{"name":"provider","type":"string","description":"Venue provider: currently only \"foursquare\" and \"gplaces\" need to be supported"},{"name":"venueId","type":"string","description":"Venue ID in the provider's database"},{"name":"venueType","type":"string","description":"Venue type in the provider's database"},{"name":"replyMarkup","type":"ReplyMarkup","optional":true,"predicate":"flags.2","description":"Inline keyboard"}],"description":"Send a venue"},{"name":"botInlineMessageMediaContact","id":416402882,"type":"BotInlineMessage","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"phoneNumber","type":"string","description":"Phone number"},{"name":"firstName","type":"string","description":"First name"},{"name":"lastName","type":"string","description":"Last name"},{"name":"vcard","type":"string","description":"VCard info"},{"name":"replyMarkup","type":"ReplyMarkup","optional":true,"predicate":"flags.2","description":"Inline keyboard"}],"description":"Send a contact"},{"name":"botInlineMessageMediaInvoice","id":894081801,"type":"BotInlineMessage","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"shippingAddressRequested","type":"true","optional":true,"predicate":"flags.1"},{"name":"test","type":"true","optional":true,"predicate":"flags.3"},{"name":"title","type":"string"},{"name":"description","type":"string"},{"name":"photo","type":"WebDocument","optional":true,"predicate":"flags.0"},{"name":"currency","type":"string"},{"name":"totalAmount","type":"Long"},{"name":"replyMarkup","type":"ReplyMarkup","optional":true,"predicate":"flags.2"}]},{"name":"botInlineResult","id":295067450,"type":"BotInlineResult","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"id","type":"string","description":"Result ID"},{"name":"type","type":"string","description":"Result type (see bot API docs)"},{"name":"title","type":"string","optional":true,"predicate":"flags.1","description":"Result title"},{"name":"description","type":"string","optional":true,"predicate":"flags.2","description":"Result description"},{"name":"url","type":"string","optional":true,"predicate":"flags.3","description":"URL of article or webpage"},{"name":"thumb","type":"WebDocument","optional":true,"predicate":"flags.4","description":"Thumbnail for the result"},{"name":"content","type":"WebDocument","optional":true,"predicate":"flags.5","description":"Content of the result"},{"name":"sendMessage","type":"BotInlineMessage","description":"Message to send"}],"description":"Generic result"},{"name":"botInlineMediaResult","id":400266251,"type":"BotInlineResult","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"id","type":"string","description":"Result ID"},{"name":"type","type":"string","description":"Result type (see bot API docs)"},{"name":"photo","type":"Photo","optional":true,"predicate":"flags.0","description":"If type is photo, the photo to send"},{"name":"document","type":"Document","optional":true,"predicate":"flags.1","description":"If type is document, the document to send"},{"name":"title","type":"string","optional":true,"predicate":"flags.2","description":"Result title"},{"name":"description","type":"string","optional":true,"predicate":"flags.3","description":"Description"},{"name":"sendMessage","type":"BotInlineMessage","description":"Depending on the type and on the constructor, contains the caption of the media or the content of the message to be sent instead of the media"}],"description":"Media result"},{"name":"exportedMessageLink","id":1571494644,"type":"ExportedMessageLink","arguments":[{"name":"link","type":"string","description":"URL"},{"name":"html","type":"string","description":"Embed code"}],"description":"Link to a message in a supergroup/channel"},{"name":"messageFwdHeader","id":1601666510,"type":"MessageFwdHeader","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"imported","type":"true","optional":true,"predicate":"flags.7"},{"name":"fromId","type":"Peer","optional":true,"predicate":"flags.0","description":"The ID of the user that originally sent the message"},{"name":"fromName","type":"string","optional":true,"predicate":"flags.5","description":"The name of the user that originally sent the message"},{"name":"date","type":"number","description":"When was the message originally sent"},{"name":"channelPost","type":"number","optional":true,"predicate":"flags.2","description":"ID of the channel message that was forwarded"},{"name":"postAuthor","type":"string","optional":true,"predicate":"flags.3","description":"For channels and if signatures are enabled, author of the channel message"},{"name":"savedFromPeer","type":"Peer","optional":true,"predicate":"flags.4","description":"Only for messages forwarded to the current user (inputPeerSelf), full info about the user/channel that originally sent the message"},{"name":"savedFromMsgId","type":"number","optional":true,"predicate":"flags.4","description":"Only for messages forwarded to the current user (inputPeerSelf), ID of the message that was forwarded from the original user/channel"},{"name":"psaType","type":"string","optional":true,"predicate":"flags.6","description":"PSA type"}],"description":"Info about a forwarded message"},{"name":"inputBotInlineMessageID","id":2299280777,"type":"InputBotInlineMessageID","arguments":[{"name":"dcId","type":"number","description":"DC ID to use when working with this inline message"},{"name":"id","type":"Long","description":"ID of message"},{"name":"accessHash","type":"Long","description":"Access hash of message"}],"description":"Represents a sent inline message from the perspective of a bot"},{"name":"inlineBotSwitchPM","id":1008755359,"type":"InlineBotSwitchPM","arguments":[{"name":"text","type":"string","description":"Text for the button that switches the user to a private chat with the bot and sends the bot a start message with the parameter start_parameter (can be empty)"},{"name":"startParam","type":"string","description":"The parameter for the /start parameter"}],"description":"The bot requested the user to message them in private"},{"name":"topPeer","id":3989684315,"type":"TopPeer","arguments":[{"name":"peer","type":"Peer","description":"Peer"},{"name":"rating","type":"Double","description":"Rating as computed in top peer rating »"}],"description":"Top peer"},{"name":"topPeerCategoryBotsPM","id":2875595611,"type":"TopPeerCategory","arguments":[],"description":"Most used bots"},{"name":"topPeerCategoryBotsInline","id":344356834,"type":"TopPeerCategory","arguments":[],"description":"Most used inline bots"},{"name":"topPeerCategoryCorrespondents","id":104314861,"type":"TopPeerCategory","arguments":[],"description":"Users we've chatted most frequently with"},{"name":"topPeerCategoryGroups","id":3172442442,"type":"TopPeerCategory","arguments":[],"description":"Often-opened groups and supergroups"},{"name":"topPeerCategoryChannels","id":371037736,"type":"TopPeerCategory","arguments":[],"description":"Most frequently visited channels"},{"name":"topPeerCategoryPhoneCalls","id":511092620,"type":"TopPeerCategory","arguments":[],"description":"Most frequently called users"},{"name":"topPeerCategoryForwardUsers","id":2822794409,"type":"TopPeerCategory","arguments":[],"description":"Users to which the users often forwards messages to"},{"name":"topPeerCategoryForwardChats","id":4226728176,"type":"TopPeerCategory","arguments":[],"description":"Chats to which the users often forwards messages to"},{"name":"topPeerCategoryPeers","id":4219683473,"type":"TopPeerCategoryPeers","arguments":[{"name":"category","type":"TopPeerCategory","description":"Top peer category of peers"},{"name":"count","type":"number","description":"Count of peers"},{"name":"peers","type":"TopPeer[]","description":"Peers"}],"description":"Top peer category"},{"name":"draftMessageEmpty","id":453805082,"type":"DraftMessage","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"date","type":"number","optional":true,"predicate":"flags.0","description":"When was the draft last updated"}],"description":"Empty draft"},{"name":"draftMessage","id":4253970719,"type":"DraftMessage","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"noWebpage","type":"true","optional":true,"predicate":"flags.1","description":"Whether no webpage preview will be generated"},{"name":"replyToMsgId","type":"number","optional":true,"predicate":"flags.0","description":"The message this message will reply to"},{"name":"message","type":"string","description":"The draft"},{"name":"entities","type":"MessageEntity[]","optional":true,"predicate":"flags.3","description":"Message entities for styled text."},{"name":"date","type":"number","description":"Date of last update of the draft."}],"description":"Represents a message draft."},{"name":"stickerSetCovered","id":1678812626,"type":"StickerSetCovered","arguments":[{"name":"set","type":"StickerSet","description":"Stickerset"},{"name":"cover","type":"Document","description":"Preview"}],"description":"Stickerset, with a specific sticker as preview"},{"name":"stickerSetMultiCovered","id":872932635,"type":"StickerSetCovered","arguments":[{"name":"set","type":"StickerSet","description":"Stickerset"},{"name":"covers","type":"Document[]","description":"Preview stickers"}],"description":"Stickerset, with a specific stickers as preview"},{"name":"maskCoords","id":2933316530,"type":"MaskCoords","arguments":[{"name":"n","type":"number","description":"Part of the face, relative to which the mask should be placed"},{"name":"x","type":"Double","description":"Shift by X-axis measured in widths of the mask scaled to the face size, from left to right. (For example, -1.0 will place the mask just to the left of the default mask position)"},{"name":"y","type":"Double","description":"Shift by Y-axis measured in widths of the mask scaled to the face size, from left to right. (For example, -1.0 will place the mask just to the left of the default mask position)"},{"name":"zoom","type":"Double","description":"Mask scaling coefficient. (For example, 2.0 means a doubled size)"}],"description":"Position on a photo where a mask should be placed"},{"name":"inputStickeredMediaPhoto","id":1251549527,"type":"InputStickeredMedia","arguments":[{"name":"id","type":"InputPhoto","description":"The photo"}],"description":"A photo with stickers attached"},{"name":"inputStickeredMediaDocument","id":70813275,"type":"InputStickeredMedia","arguments":[{"name":"id","type":"InputDocument","description":"The document"}],"description":"A document with stickers attached"},{"name":"game","id":3187238203,"type":"Game","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"id","type":"Long","description":"ID of the game"},{"name":"accessHash","type":"Long","description":"Access hash of the game"},{"name":"shortName","type":"string","description":"Short name for the game"},{"name":"title","type":"string","description":"Title of the game"},{"name":"description","type":"string","description":"Game description"},{"name":"photo","type":"Photo","description":"Game preview"},{"name":"document","type":"Document","optional":true,"predicate":"flags.0","description":"Optional attached document"}],"description":"Indicates an already sent game"},{"name":"inputGameID","id":53231223,"type":"InputGame","arguments":[{"name":"id","type":"Long","description":"game ID from Game constructor"},{"name":"accessHash","type":"Long","description":"access hash from Game constructor"}],"description":"Indicates an already sent game"},{"name":"inputGameShortName","id":3274827786,"type":"InputGame","arguments":[{"name":"botId","type":"InputUser","description":"The bot that provides the game"},{"name":"shortName","type":"string","description":"The game's short name"}],"description":"Game by short name"},{"name":"highScore","id":1493171408,"type":"HighScore","arguments":[{"name":"pos","type":"number","description":"Position in highscore list"},{"name":"userId","type":"number","description":"User ID"},{"name":"score","type":"number","description":"Score"}],"description":"Game highscore"},{"name":"textEmpty","id":3695018575,"type":"RichText","arguments":[],"description":"Empty rich text element"},{"name":"textPlain","id":1950782688,"type":"RichText","arguments":[{"name":"text","type":"string","description":"Text"}],"description":"Plain text"},{"name":"textBold","id":1730456516,"type":"RichText","arguments":[{"name":"text","type":"RichText","description":"Text"}],"description":"Bold text"},{"name":"textItalic","id":3641877916,"type":"RichText","arguments":[{"name":"text","type":"RichText","description":"Text"}],"description":"Italic text"},{"name":"textUnderline","id":3240501956,"type":"RichText","arguments":[{"name":"text","type":"RichText","description":"Text"}],"description":"Underlined text"},{"name":"textStrike","id":2616769429,"type":"RichText","arguments":[{"name":"text","type":"RichText","description":"Text"}],"description":"Strikethrough text"},{"name":"textFixed","id":1816074681,"type":"RichText","arguments":[{"name":"text","type":"RichText","description":"Text"}],"description":"fixed-width rich text"},{"name":"textUrl","id":1009288385,"type":"RichText","arguments":[{"name":"text","type":"RichText","description":"Text of link"},{"name":"url","type":"string","description":"Webpage HTTP URL"},{"name":"webpageId","type":"Long","description":"If a preview was already generated for the page, the page ID"}],"description":"Link"},{"name":"textEmail","id":3730443734,"type":"RichText","arguments":[{"name":"text","type":"RichText","description":"Link text"},{"name":"email","type":"string","description":"Email address"}],"description":"Rich text email link"},{"name":"textConcat","id":2120376535,"type":"RichText","arguments":[{"name":"texts","type":"RichText[]","description":"Concatenated rich texts"}],"description":"Concatenation of rich texts"},{"name":"textSubscript","id":3983181060,"type":"RichText","arguments":[{"name":"text","type":"RichText","description":"Text"}],"description":"Subscript text"},{"name":"textSuperscript","id":3355139585,"type":"RichText","arguments":[{"name":"text","type":"RichText","description":"Text"}],"description":"Superscript text"},{"name":"textMarked","id":55281185,"type":"RichText","arguments":[{"name":"text","type":"RichText","description":"Text"}],"description":"Highlighted text"},{"name":"textPhone","id":483104362,"type":"RichText","arguments":[{"name":"text","type":"RichText","description":"Text"},{"name":"phone","type":"string","description":"Phone number"}],"description":"Rich text linked to a phone number"},{"name":"textImage","id":136105807,"type":"RichText","arguments":[{"name":"documentId","type":"Long","description":"Document ID"},{"name":"w","type":"number","description":"Width"},{"name":"h","type":"number","description":"Height"}],"description":"Inline image"},{"name":"textAnchor","id":894777186,"type":"RichText","arguments":[{"name":"text","type":"RichText","description":"Text"},{"name":"name","type":"string","description":"Section name"}],"description":"Text linking to another section of the page"},{"name":"pageBlockUnsupported","id":324435594,"type":"PageBlock","arguments":[],"description":"Unsupported IV element"},{"name":"pageBlockTitle","id":1890305021,"type":"PageBlock","arguments":[{"name":"text","type":"RichText","description":"Title"}],"description":"Title"},{"name":"pageBlockSubtitle","id":2415565343,"type":"PageBlock","arguments":[{"name":"text","type":"RichText","description":"Text"}],"description":"Subtitle"},{"name":"pageBlockAuthorDate","id":3132089824,"type":"PageBlock","arguments":[{"name":"author","type":"RichText","description":"Author name"},{"name":"publishedDate","type":"number","description":"Date of pubblication"}],"description":"Author and date of creation of article"},{"name":"pageBlockHeader","id":3218105580,"type":"PageBlock","arguments":[{"name":"text","type":"RichText","description":"Contents"}],"description":"Page header"},{"name":"pageBlockSubheader","id":4046173921,"type":"PageBlock","arguments":[{"name":"text","type":"RichText","description":"Subheader"}],"description":"Subheader"},{"name":"pageBlockParagraph","id":1182402406,"type":"PageBlock","arguments":[{"name":"text","type":"RichText","description":"Text"}],"description":"A paragraph"},{"name":"pageBlockPreformatted","id":3228621118,"type":"PageBlock","arguments":[{"name":"text","type":"RichText","description":"Text"},{"name":"language","type":"string","description":"Programming language of preformatted text"}],"description":"Preformatted (<pre> text)"},{"name":"pageBlockFooter","id":1216809369,"type":"PageBlock","arguments":[{"name":"text","type":"RichText","description":"Contents"}],"description":"Page footer"},{"name":"pageBlockDivider","id":3676352904,"type":"PageBlock","arguments":[],"description":"An empty block separating a page"},{"name":"pageBlockAnchor","id":3456972720,"type":"PageBlock","arguments":[{"name":"name","type":"string","description":"Name of target section"}],"description":"Link to section within the page itself (like <a href=\"#target\">anchor</a>)"},{"name":"pageBlockList","id":3840442385,"type":"PageBlock","arguments":[{"name":"items","type":"PageListItem[]","description":"List of blocks in an IV page"}],"description":"Unordered list of IV blocks"},{"name":"pageBlockBlockquote","id":641563686,"type":"PageBlock","arguments":[{"name":"text","type":"RichText","description":"Quote contents"},{"name":"caption","type":"RichText","description":"Caption"}],"description":"Quote (equivalent to the HTML <blockquote>)"},{"name":"pageBlockPullquote","id":1329878739,"type":"PageBlock","arguments":[{"name":"text","type":"RichText","description":"Text"},{"name":"caption","type":"RichText","description":"Caption"}],"description":"Pullquote"},{"name":"pageBlockPhoto","id":391759200,"type":"PageBlock","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"photoId","type":"Long","description":"Photo ID"},{"name":"caption","type":"PageCaption","description":"Caption"},{"name":"url","type":"string","optional":true,"predicate":"flags.0","description":"HTTP URL of page the photo leads to when clicked"},{"name":"webpageId","type":"Long","optional":true,"predicate":"flags.0","description":"ID of preview of the page the photo leads to when clicked"}],"description":"A photo"},{"name":"pageBlockVideo","id":2089805750,"type":"PageBlock","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"autoplay","type":"true","optional":true,"predicate":"flags.0","description":"Whether the video is set to autoplay"},{"name":"loop","type":"true","optional":true,"predicate":"flags.1","description":"Whether the video is set to loop"},{"name":"videoId","type":"Long","description":"Video ID"},{"name":"caption","type":"PageCaption","description":"Caption"}],"description":"Video"},{"name":"pageBlockCover","id":972174080,"type":"PageBlock","arguments":[{"name":"cover","type":"PageBlock","description":"Cover"}],"description":"A page cover"},{"name":"pageBlockEmbed","id":2826014149,"type":"PageBlock","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"fullWidth","type":"true","optional":true,"predicate":"flags.0","description":"Whether the block should be full width"},{"name":"allowScrolling","type":"true","optional":true,"predicate":"flags.3","description":"Whether scrolling should be allowed"},{"name":"url","type":"string","optional":true,"predicate":"flags.1","description":"Web page URL, if available"},{"name":"html","type":"string","optional":true,"predicate":"flags.2","description":"HTML-markup of the embedded page"},{"name":"posterPhotoId","type":"Long","optional":true,"predicate":"flags.4","description":"Poster photo, if available"},{"name":"w","type":"number","optional":true,"predicate":"flags.5","description":"Block width, if known"},{"name":"h","type":"number","optional":true,"predicate":"flags.5","description":"Block height, if known"},{"name":"caption","type":"PageCaption","description":"Caption"}],"description":"An embedded webpage"},{"name":"pageBlockEmbedPost","id":4065961995,"type":"PageBlock","arguments":[{"name":"url","type":"string","description":"Web page URL"},{"name":"webpageId","type":"Long","description":"ID of generated webpage preview"},{"name":"authorPhotoId","type":"Long","description":"ID of the author's photo"},{"name":"author","type":"string","description":"Author name"},{"name":"date","type":"number","description":"Creation date"},{"name":"blocks","type":"PageBlock[]","description":"Post contents"},{"name":"caption","type":"PageCaption","description":"Caption"}],"description":"An embedded post"},{"name":"pageBlockCollage","id":1705048653,"type":"PageBlock","arguments":[{"name":"items","type":"PageBlock[]","description":"Media elements"},{"name":"caption","type":"PageCaption","description":"Caption"}],"description":"Collage of media"},{"name":"pageBlockSlideshow","id":52401552,"type":"PageBlock","arguments":[{"name":"items","type":"PageBlock[]","description":"Slideshow items"},{"name":"caption","type":"PageCaption","description":"Caption"}],"description":"Slideshow"},{"name":"pageBlockChannel","id":4011282869,"type":"PageBlock","arguments":[{"name":"channel","type":"Chat","description":"The channel/supergroup/chat"}],"description":"Reference to a telegram channel"},{"name":"pageBlockAudio","id":2151899626,"type":"PageBlock","arguments":[{"name":"audioId","type":"Long","description":"Audio ID (to be fetched from the container {@link page} constructor"},{"name":"caption","type":"PageCaption","description":"Audio caption"}],"description":"Audio"},{"name":"pageBlockKicker","id":504660880,"type":"PageBlock","arguments":[{"name":"text","type":"RichText","description":"Contents"}],"description":"Kicker"},{"name":"pageBlockTable","id":3209554562,"type":"PageBlock","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"bordered","type":"true","optional":true,"predicate":"flags.0","description":"Does the table have a visible border?"},{"name":"striped","type":"true","optional":true,"predicate":"flags.1","description":"Is the table striped?"},{"name":"title","type":"RichText","description":"Title"},{"name":"rows","type":"PageTableRow[]","description":"Table rows"}],"description":"Table"},{"name":"pageBlockOrderedList","id":2592793057,"type":"PageBlock","arguments":[{"name":"items","type":"PageListOrderedItem[]","description":"List items"}],"description":"Ordered list of IV blocks"},{"name":"pageBlockDetails","id":1987480557,"type":"PageBlock","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"open","type":"true","optional":true,"predicate":"flags.0","description":"Whether the block is open by default"},{"name":"blocks","type":"PageBlock[]","description":"Block contents"},{"name":"title","type":"RichText","description":"Always visible heading for the block"}],"description":"A collapsible details block"},{"name":"pageBlockRelatedArticles","id":370236054,"type":"PageBlock","arguments":[{"name":"title","type":"RichText","description":"Title"},{"name":"articles","type":"PageRelatedArticle[]","description":"Related articles"}],"description":"Related articles"},{"name":"pageBlockMap","id":2756656886,"type":"PageBlock","arguments":[{"name":"geo","type":"GeoPoint","description":"Location of the map center"},{"name":"zoom","type":"number","description":"Map zoom level; 13-20"},{"name":"w","type":"number","description":"Map width in pixels before applying scale; 16-102"},{"name":"h","type":"number","description":"Map height in pixels before applying scale; 16-1024"},{"name":"caption","type":"PageCaption","description":"Caption"}],"description":"A map"},{"name":"phoneCallDiscardReasonMissed","id":2246320897,"type":"PhoneCallDiscardReason","arguments":[],"description":"The phone call was missed"},{"name":"phoneCallDiscardReasonDisconnect","id":3767910816,"type":"PhoneCallDiscardReason","arguments":[],"description":"The phone call was disconnected"},{"name":"phoneCallDiscardReasonHangup","id":1471006352,"type":"PhoneCallDiscardReason","arguments":[],"description":"The phone call was ended normally"},{"name":"phoneCallDiscardReasonBusy","id":4210550985,"type":"PhoneCallDiscardReason","arguments":[],"description":"The phone call was discared because the user is busy in another call"},{"name":"dataJSON","id":2104790276,"type":"DataJSON","arguments":[{"name":"data","type":"string","description":"JSON-encoded object"}],"description":"Represents a json-encoded object"},{"name":"labeledPrice","id":3408489464,"type":"LabeledPrice","arguments":[{"name":"label","type":"string","description":"Portion label"},{"name":"amount","type":"Long","description":"Price of the product in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies)."}],"description":"This object represents a portion of the price for goods or services."},{"name":"invoice","id":215516896,"type":"Invoice","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"test","type":"true","optional":true,"predicate":"flags.0","description":"Test invoice"},{"name":"nameRequested","type":"true","optional":true,"predicate":"flags.1","description":"Set this flag if you require the user's full name to complete the order"},{"name":"phoneRequested","type":"true","optional":true,"predicate":"flags.2","description":"Set this flag if you require the user's phone number to complete the order"},{"name":"emailRequested","type":"true","optional":true,"predicate":"flags.3","description":"Set this flag if you require the user's email address to complete the order"},{"name":"shippingAddressRequested","type":"true","optional":true,"predicate":"flags.4","description":"Set this flag if you require the user's shipping address to complete the order"},{"name":"flexible","type":"true","optional":true,"predicate":"flags.5","description":"Set this flag if the final price depends on the shipping method"},{"name":"phoneToProvider","type":"true","optional":true,"predicate":"flags.6","description":"Set this flag if user's phone number should be sent to provider"},{"name":"emailToProvider","type":"true","optional":true,"predicate":"flags.7","description":"Set this flag if user's email address should be sent to provider"},{"name":"currency","type":"string","description":"Three-letter ISO 4217 currency code"},{"name":"prices","type":"LabeledPrice[]","description":"Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)"},{"name":"maxTipAmount","type":"Long","optional":true,"predicate":"flags.8"},{"name":"suggestedTipAmounts","type":"Long[]","optional":true,"predicate":"flags.8"}],"description":"Invoice"},{"name":"paymentCharge","id":3926049406,"type":"PaymentCharge","arguments":[{"name":"id","type":"string","description":"Telegram payment identifier"},{"name":"providerChargeId","type":"string","description":"Provider payment identifier"}],"description":"Payment identifier"},{"name":"postAddress","id":512535275,"type":"PostAddress","arguments":[{"name":"streetLine1","type":"string","description":"First line for the address"},{"name":"streetLine2","type":"string","description":"Second line for the address"},{"name":"city","type":"string","description":"City"},{"name":"state","type":"string","description":"State, if applicable (empty otherwise)"},{"name":"countryIso2","type":"string","description":"ISO 3166-1 alpha-2 country code"},{"name":"postCode","type":"string","description":"Address post code"}],"description":"Shipping address"},{"name":"paymentRequestedInfo","id":2426158996,"type":"PaymentRequestedInfo","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"name","type":"string","optional":true,"predicate":"flags.0","description":"User's full name"},{"name":"phone","type":"string","optional":true,"predicate":"flags.1","description":"User's phone number"},{"name":"email","type":"string","optional":true,"predicate":"flags.2","description":"User's email address"},{"name":"shippingAddress","type":"PostAddress","optional":true,"predicate":"flags.3","description":"User's shipping address"}],"description":"Order info provided by the user"},{"name":"paymentSavedCredentialsCard","id":3452074527,"type":"PaymentSavedCredentials","arguments":[{"name":"id","type":"string","description":"Card ID"},{"name":"title","type":"string","description":"Title"}],"description":"Saved credit card"},{"name":"webDocument","id":475467473,"type":"WebDocument","arguments":[{"name":"url","type":"string","description":"Document URL"},{"name":"accessHash","type":"Long","description":"Access hash"},{"name":"size","type":"number","description":"File size"},{"name":"mimeType","type":"string","description":"MIME type"},{"name":"attributes","type":"DocumentAttribute[]","description":"Attributes for media types"}],"description":"Remote document"},{"name":"webDocumentNoProxy","id":4190682310,"type":"WebDocument","arguments":[{"name":"url","type":"string","description":"Document URL"},{"name":"size","type":"number","description":"File size"},{"name":"mimeType","type":"string","description":"MIME type"},{"name":"attributes","type":"DocumentAttribute[]","description":"Attributes for media types"}],"description":"Remote document that can be downloaded without proxying through telegram"},{"name":"inputWebDocument","id":2616017741,"type":"InputWebDocument","arguments":[{"name":"url","type":"string","description":"Remote document URL to be downloaded using the appropriate method"},{"name":"size","type":"number","description":"Remote file size"},{"name":"mimeType","type":"string","description":"Mime type"},{"name":"attributes","type":"DocumentAttribute[]","description":"Attributes for media types"}],"description":"The document"},{"name":"inputWebFileLocation","id":3258570374,"type":"InputWebFileLocation","arguments":[{"name":"url","type":"string","description":"HTTP URL of file"},{"name":"accessHash","type":"Long","description":"Access hash"}],"description":"Location of a remote HTTP(s) file"},{"name":"inputWebFileGeoPointLocation","id":2669814217,"type":"InputWebFileLocation","arguments":[{"name":"geoPoint","type":"InputGeoPoint","description":"Geolocation"},{"name":"accessHash","type":"Long","description":"Access hash"},{"name":"w","type":"number","description":"Map width in pixels before applying scale; 16-1024"},{"name":"h","type":"number","description":"Map height in pixels before applying scale; 16-1024"},{"name":"zoom","type":"number","description":"Map zoom level; 13-20"},{"name":"scale","type":"number","description":"Map scale; 1-3"}],"description":"Geolocation"},{"name":"inputPaymentCredentialsSaved","id":3238965967,"type":"InputPaymentCredentials","arguments":[{"name":"id","type":"string","description":"Credential ID"},{"name":"tmpPassword","type":"Buffer","description":"Temporary password"}],"description":"Saved payment credentials"},{"name":"inputPaymentCredentials","id":873977640,"type":"InputPaymentCredentials","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"save","type":"true","optional":true,"predicate":"flags.0","description":"Save payment credential for future use"},{"name":"data","type":"DataJSON","description":"Payment credentials"}],"description":"Payment credentials"},{"name":"inputPaymentCredentialsApplePay","id":178373535,"type":"InputPaymentCredentials","arguments":[{"name":"paymentData","type":"DataJSON","description":"Payment data"}],"description":"Apple pay payment credentials"},{"name":"inputPaymentCredentialsGooglePay","id":2328045569,"type":"InputPaymentCredentials","arguments":[{"name":"paymentToken","type":"DataJSON"}]},{"name":"shippingOption","id":3055631583,"type":"ShippingOption","arguments":[{"name":"id","type":"string","description":"Option ID"},{"name":"title","type":"string","description":"Title"},{"name":"prices","type":"LabeledPrice[]","description":"List of price portions"}],"description":"Shipping option"},{"name":"inputStickerSetItem","id":4288717974,"type":"InputStickerSetItem","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"document","type":"InputDocument","description":"The sticker"},{"name":"emoji","type":"string","description":"Associated emoji"},{"name":"maskCoords","type":"MaskCoords","optional":true,"predicate":"flags.0","description":"Coordinates for mask sticker"}],"description":"Sticker in a stickerset"},{"name":"inputPhoneCall","id":506920429,"type":"InputPhoneCall","arguments":[{"name":"id","type":"Long","description":"Call ID"},{"name":"accessHash","type":"Long","description":"Access hash"}],"description":"Phone call"},{"name":"phoneCallEmpty","id":1399245077,"type":"PhoneCall","arguments":[{"name":"id","type":"Long","description":"Call ID"}],"description":"Empty constructor"},{"name":"phoneCallWaiting","id":462375633,"type":"PhoneCall","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"video","type":"true","optional":true,"predicate":"flags.6","description":"Is this a video call"},{"name":"id","type":"Long","description":"Call ID"},{"name":"accessHash","type":"Long","description":"Access hash"},{"name":"date","type":"number","description":"Date"},{"name":"adminId","type":"number","description":"Admin ID"},{"name":"participantId","type":"number","description":"Participant ID"},{"name":"protocol","type":"PhoneCallProtocol","description":"Phone call protocol info"},{"name":"receiveDate","type":"number","optional":true,"predicate":"flags.0","description":"When was the phone call received"}],"description":"Incoming phone call"},{"name":"phoneCallRequested","id":2280307539,"type":"PhoneCall","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"video","type":"true","optional":true,"predicate":"flags.6","description":"Whether this is a video call"},{"name":"id","type":"Long","description":"Phone call ID"},{"name":"accessHash","type":"Long","description":"Access hash"},{"name":"date","type":"number","description":"When was the phone call created"},{"name":"adminId","type":"number","description":"ID of the creator of the phone call"},{"name":"participantId","type":"number","description":"ID of the other participant of the phone call"},{"name":"gAHash","type":"Buffer","description":"Parameter for key exchange"},{"name":"protocol","type":"PhoneCallProtocol","description":"Call protocol info to be passed to libtgvoip"}],"description":"Requested phone call"},{"name":"phoneCallAccepted","id":2575058250,"type":"PhoneCall","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"video","type":"true","optional":true,"predicate":"flags.6","description":"Whether this is a video call"},{"name":"id","type":"Long","description":"ID of accepted phone call"},{"name":"accessHash","type":"Long","description":"Access hash of phone call"},{"name":"date","type":"number","description":"When was the call accepted"},{"name":"adminId","type":"number","description":"ID of the call creator"},{"name":"participantId","type":"number","description":"ID of the other user in the call"},{"name":"gB","type":"Buffer","description":"B parameter for secure E2E phone call key exchange"},{"name":"protocol","type":"PhoneCallProtocol","description":"Protocol to use for phone call"}],"description":"An accepted phone call"},{"name":"phoneCall","id":2269294207,"type":"PhoneCall","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"p2pAllowed","type":"true","optional":true,"predicate":"flags.5","description":"Whether P2P connection to the other peer is allowed"},{"name":"video","type":"true","optional":true,"predicate":"flags.6","description":"Whether this is a video call"},{"name":"id","type":"Long","description":"Call ID"},{"name":"accessHash","type":"Long","description":"Access hash"},{"name":"date","type":"number","description":"Date of creation of the call"},{"name":"adminId","type":"number","description":"User ID of the creator of the call"},{"name":"participantId","type":"number","description":"User ID of the other participant in the call"},{"name":"gAOrB","type":"Buffer","description":"Parameter for key exchange"},{"name":"keyFingerprint","type":"Long","description":"Key fingerprint"},{"name":"protocol","type":"PhoneCallProtocol","description":"Call protocol info to be passed to libtgvoip"},{"name":"connections","type":"PhoneConnection[]","description":"List of endpoints the user can connect to to exchange call data"},{"name":"startDate","type":"number","description":"When was the call actually started"}],"description":"Phone call"},{"name":"phoneCallDiscarded","id":1355435489,"type":"PhoneCall","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"needRating","type":"true","optional":true,"predicate":"flags.2","description":"Whether the server required the user to {@link phone.setCallRating} the call"},{"name":"needDebug","type":"true","optional":true,"predicate":"flags.3","description":"Whether the server required the client to {@link phone.saveCallDebug} the libtgvoip call debug data"},{"name":"video","type":"true","optional":true,"predicate":"flags.6","description":"Whether the call was a video call"},{"name":"id","type":"Long","description":"Call ID"},{"name":"reason","type":"PhoneCallDiscardReason","optional":true,"predicate":"flags.0","description":"Why was the phone call discarded"},{"name":"duration","type":"number","optional":true,"predicate":"flags.1","description":"Duration of the phone call in seconds"}],"description":"Indicates a discarded phone call"},{"name":"phoneConnection","id":2639009728,"type":"PhoneConnection","arguments":[{"name":"id","type":"Long","description":"Endpoint ID"},{"name":"ip","type":"string","description":"IP address of endpoint"},{"name":"ipv6","type":"string","description":"IPv6 address of endpoint"},{"name":"port","type":"number","description":"Port ID"},{"name":"peerTag","type":"Buffer","description":"Our peer tag"}],"description":"Identifies an endpoint that can be used to connect to the other user in a phone call"},{"name":"phoneConnectionWebrtc","id":1667228533,"type":"PhoneConnection","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"turn","type":"true","optional":true,"predicate":"flags.0","description":"Whether this is a TURN endpoint"},{"name":"stun","type":"true","optional":true,"predicate":"flags.1","description":"Whether this is a STUN endpoint"},{"name":"id","type":"Long","description":"Endpoint ID"},{"name":"ip","type":"string","description":"IP address"},{"name":"ipv6","type":"string","description":"IPv6 address"},{"name":"port","type":"number","description":"Port"},{"name":"username","type":"string","description":"Username"},{"name":"password","type":"string","description":"Password"}],"description":"WebRTC connection parameters"},{"name":"phoneCallProtocol","id":4236742600,"type":"PhoneCallProtocol","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"udpP2p","type":"true","optional":true,"predicate":"flags.0","description":"Whether to allow P2P connection to the other participant"},{"name":"udpReflector","type":"true","optional":true,"predicate":"flags.1","description":"Whether to allow connection to the other participants through the reflector servers"},{"name":"minLayer","type":"number","description":"Minimum layer for remote libtgvoip"},{"name":"maxLayer","type":"number","description":"Maximum layer for remote libtgvoip"},{"name":"libraryVersions","type":"string[]","description":"When using {@link phone.requestCall} and {@link phone.acceptCall}, specify all library versions supported by the client.
The server will merge and choose the best library version supported by both peers, returning only the best value in the result of the callee's {@link phone.acceptCall} and in the {@link phoneCallAccepted} update received by the caller."}],"description":"Protocol info for libtgvoip"},{"name":"cdnPublicKey","id":3380800186,"type":"CdnPublicKey","arguments":[{"name":"dcId","type":"number","description":"CDN DC ID"},{"name":"publicKey","type":"string","description":"RSA public key"}],"description":"Public key to use only during handshakes to CDN DCs."},{"name":"cdnConfig","id":1462101002,"type":"CdnConfig","arguments":[{"name":"publicKeys","type":"CdnPublicKey[]","description":"Vector of public keys to use only during handshakes to CDN DCs."}],"description":"Configuration for CDN file downloads."},{"name":"langPackString","id":3402727926,"type":"LangPackString","arguments":[{"name":"key","type":"string","description":"Language key"},{"name":"value","type":"string","description":"Value"}],"description":"Translated localization string"},{"name":"langPackStringPluralized","id":1816636575,"type":"LangPackString","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"key","type":"string","description":"Localization key"},{"name":"zeroValue","type":"string","optional":true,"predicate":"flags.0","description":"Value for zero objects"},{"name":"oneValue","type":"string","optional":true,"predicate":"flags.1","description":"Value for one object"},{"name":"twoValue","type":"string","optional":true,"predicate":"flags.2","description":"Value for two objects"},{"name":"fewValue","type":"string","optional":true,"predicate":"flags.3","description":"Value for a few objects"},{"name":"manyValue","type":"string","optional":true,"predicate":"flags.4","description":"Value for many objects"},{"name":"otherValue","type":"string","description":"Default value"}],"description":"A language pack string which has different forms based on the number of some object it mentions. See https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html for more info"},{"name":"langPackStringDeleted","id":695856818,"type":"LangPackString","arguments":[{"name":"key","type":"string","description":"Localization key"}],"description":"Deleted localization string"},{"name":"langPackDifference","id":4085629430,"type":"LangPackDifference","arguments":[{"name":"langCode","type":"string","description":"Language code"},{"name":"fromVersion","type":"number","description":"Previous version number"},{"name":"version","type":"number","description":"New version number"},{"name":"strings","type":"LangPackString[]","description":"Localized strings"}],"description":"Changes to the app's localization pack"},{"name":"langPackLanguage","id":4006239459,"type":"LangPackLanguage","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"official","type":"true","optional":true,"predicate":"flags.0","description":"Whether the language pack is official"},{"name":"rtl","type":"true","optional":true,"predicate":"flags.2","description":"Is this a localization pack for an RTL language"},{"name":"beta","type":"true","optional":true,"predicate":"flags.3","description":"Is this a beta localization pack?"},{"name":"name","type":"string","description":"Language name"},{"name":"nativeName","type":"string","description":"Language name in the language itself"},{"name":"langCode","type":"string","description":"Language code (pack identifier)"},{"name":"baseLangCode","type":"string","optional":true,"predicate":"flags.1","description":"Identifier of a base language pack; may be empty. If a string is missed in the language pack, then it should be fetched from base language pack. Unsupported in custom language packs"},{"name":"pluralCode","type":"string","description":"A language code to be used to apply plural forms. See https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html for more info"},{"name":"stringsCount","type":"number","description":"Total number of non-deleted strings from the language pack"},{"name":"translatedCount","type":"number","description":"Total number of translated strings from the language pack"},{"name":"translationsUrl","type":"string","description":"Link to language translation interface; empty for custom local language packs"}],"description":"Identifies a localization pack"},{"name":"channelAdminLogEventActionChangeTitle","id":3873421349,"type":"ChannelAdminLogEventAction","arguments":[{"name":"prevValue","type":"string","description":"Previous title"},{"name":"newValue","type":"string","description":"New title"}],"description":"Channel/supergroup title was changed"},{"name":"channelAdminLogEventActionChangeAbout","id":1427671598,"type":"ChannelAdminLogEventAction","arguments":[{"name":"prevValue","type":"string","description":"Previous description"},{"name":"newValue","type":"string","description":"New description"}],"description":"The description was changed"},{"name":"channelAdminLogEventActionChangeUsername","id":1783299128,"type":"ChannelAdminLogEventAction","arguments":[{"name":"prevValue","type":"string","description":"Old username"},{"name":"newValue","type":"string","description":"New username"}],"description":"Channel/supergroup username was changed"},{"name":"channelAdminLogEventActionChangePhoto","id":1129042607,"type":"ChannelAdminLogEventAction","arguments":[{"name":"prevPhoto","type":"Photo","description":"Previous picture"},{"name":"newPhoto","type":"Photo","description":"New picture"}],"description":"The channel/supergroup's picture was changed"},{"name":"channelAdminLogEventActionToggleInvites","id":460916654,"type":"ChannelAdminLogEventAction","arguments":[{"name":"newValue","type":"boolean","description":"New value"}],"description":"Invites were enabled/disabled"},{"name":"channelAdminLogEventActionToggleSignatures","id":648939889,"type":"ChannelAdminLogEventAction","arguments":[{"name":"newValue","type":"boolean","description":"New value"}],"description":"Channel signatures were enabled/disabled"},{"name":"channelAdminLogEventActionUpdatePinned","id":3924306968,"type":"ChannelAdminLogEventAction","arguments":[{"name":"message","type":"Message","description":"The message that was pinned"}],"description":"A message was pinned"},{"name":"channelAdminLogEventActionEditMessage","id":1889215493,"type":"ChannelAdminLogEventAction","arguments":[{"name":"prevMessage","type":"Message","description":"Old message"},{"name":"newMessage","type":"Message","description":"New message"}],"description":"A message was edited"},{"name":"channelAdminLogEventActionDeleteMessage","id":1121994683,"type":"ChannelAdminLogEventAction","arguments":[{"name":"message","type":"Message","description":"The message that was deleted"}],"description":"A message was deleted"},{"name":"channelAdminLogEventActionParticipantJoin","id":405815507,"type":"ChannelAdminLogEventAction","arguments":[],"description":"A user has joined the group (in the case of big groups, info of the user that has joined isn't shown)"},{"name":"channelAdminLogEventActionParticipantLeave","id":4170676210,"type":"ChannelAdminLogEventAction","arguments":[],"description":"A user left the channel/supergroup (in the case of big groups, info of the user that has joined isn't shown)"},{"name":"channelAdminLogEventActionParticipantInvite","id":3810276568,"type":"ChannelAdminLogEventAction","arguments":[{"name":"participant","type":"ChannelParticipant","description":"The user that was invited"}],"description":"A user was invited to the group"},{"name":"channelAdminLogEventActionParticipantToggleBan","id":3872931198,"type":"ChannelAdminLogEventAction","arguments":[{"name":"prevParticipant","type":"ChannelParticipant","description":"Old banned rights of user"},{"name":"newParticipant","type":"ChannelParticipant","description":"New banned rights of user"}],"description":"The banned rights of a user were changed"},{"name":"channelAdminLogEventActionParticipantToggleAdmin","id":3580323600,"type":"ChannelAdminLogEventAction","arguments":[{"name":"prevParticipant","type":"ChannelParticipant","description":"Previous admin rights"},{"name":"newParticipant","type":"ChannelParticipant","description":"New admin rights"}],"description":"The admin rights of a user were changed"},{"name":"channelAdminLogEventActionChangeStickerSet","id":2982398631,"type":"ChannelAdminLogEventAction","arguments":[{"name":"prevStickerset","type":"InputStickerSet","description":"Previous stickerset"},{"name":"newStickerset","type":"InputStickerSet","description":"New stickerset"}],"description":"The supergroup's stickerset was changed"},{"name":"channelAdminLogEventActionTogglePreHistoryHidden","id":1599903217,"type":"ChannelAdminLogEventAction","arguments":[{"name":"newValue","type":"boolean","description":"New value"}],"description":"The hidden prehistory setting was {@link channels.togglePreHistoryHidden}"},{"name":"channelAdminLogEventActionDefaultBannedRights","id":771095562,"type":"ChannelAdminLogEventAction","arguments":[{"name":"prevBannedRights","type":"ChatBannedRights","description":"Previous global banned rights"},{"name":"newBannedRights","type":"ChatBannedRights","description":"New glboal banned rights."}],"description":"The default banned rights were modified"},{"name":"channelAdminLogEventActionStopPoll","id":2399639107,"type":"ChannelAdminLogEventAction","arguments":[{"name":"message","type":"Message","description":"The poll that was stopped"}],"description":"A poll was stopped"},{"name":"channelAdminLogEventActionChangeLinkedChat","id":2725218331,"type":"ChannelAdminLogEventAction","arguments":[{"name":"prevValue","type":"number","description":"Previous linked chat"},{"name":"newValue","type":"number","description":"New linked chat"}],"description":"The linked chat was changed"},{"name":"channelAdminLogEventActionChangeLocation","id":241923758,"type":"ChannelAdminLogEventAction","arguments":[{"name":"prevValue","type":"ChannelLocation","description":"Previous location"},{"name":"newValue","type":"ChannelLocation","description":"New location"}],"description":"The geo group location was changed"},{"name":"channelAdminLogEventActionToggleSlowMode","id":1401984889,"type":"ChannelAdminLogEventAction","arguments":[{"name":"prevValue","type":"number","description":"Previous slow mode value"},{"name":"newValue","type":"number","description":"New slow mode value"}],"description":"{@link channels.toggleSlowMode}"},{"name":"channelAdminLogEventActionStartGroupCall","id":589338437,"type":"ChannelAdminLogEventAction","arguments":[{"name":"call","type":"InputGroupCall"}]},{"name":"channelAdminLogEventActionDiscardGroupCall","id":3684667712,"type":"ChannelAdminLogEventAction","arguments":[{"name":"call","type":"InputGroupCall"}]},{"name":"channelAdminLogEventActionParticipantMute","id":4179895506,"type":"ChannelAdminLogEventAction","arguments":[{"name":"participant","type":"GroupCallParticipant"}]},{"name":"channelAdminLogEventActionParticipantUnmute","id":3863226816,"type":"ChannelAdminLogEventAction","arguments":[{"name":"participant","type":"GroupCallParticipant"}]},{"name":"channelAdminLogEventActionToggleGroupCallSetting","id":1456906823,"type":"ChannelAdminLogEventAction","arguments":[{"name":"joinMuted","type":"boolean"}]},{"name":"channelAdminLogEventActionParticipantJoinByInvite","id":1557846647,"type":"ChannelAdminLogEventAction","arguments":[{"name":"invite","type":"ExportedChatInvite"}]},{"name":"channelAdminLogEventActionExportedInviteDelete","id":1515256996,"type":"ChannelAdminLogEventAction","arguments":[{"name":"invite","type":"ExportedChatInvite"}]},{"name":"channelAdminLogEventActionExportedInviteRevoke","id":1091179342,"type":"ChannelAdminLogEventAction","arguments":[{"name":"invite","type":"ExportedChatInvite"}]},{"name":"channelAdminLogEventActionExportedInviteEdit","id":3910056793,"type":"ChannelAdminLogEventAction","arguments":[{"name":"prevInvite","type":"ExportedChatInvite"},{"name":"newInvite","type":"ExportedChatInvite"}]},{"name":"channelAdminLogEventActionParticipantVolume","id":1048537159,"type":"ChannelAdminLogEventAction","arguments":[{"name":"participant","type":"GroupCallParticipant"}]},{"name":"channelAdminLogEventActionChangeHistoryTTL","id":1855199800,"type":"ChannelAdminLogEventAction","arguments":[{"name":"prevValue","type":"number"},{"name":"newValue","type":"number"}]},{"name":"channelAdminLogEvent","id":995769920,"type":"ChannelAdminLogEvent","arguments":[{"name":"id","type":"Long","description":"Event ID"},{"name":"date","type":"number","description":"Date"},{"name":"userId","type":"number","description":"User ID"},{"name":"action","type":"ChannelAdminLogEventAction","description":"Action"}],"description":"Admin log event"},{"name":"channelAdminLogEventsFilter","id":3926948580,"type":"ChannelAdminLogEventsFilter","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"join","type":"true","optional":true,"predicate":"flags.0","description":"{@link channelAdminLogEventActionParticipantJoin}"},{"name":"leave","type":"true","optional":true,"predicate":"flags.1","description":"{@link channelAdminLogEventActionParticipantLeave}"},{"name":"invite","type":"true","optional":true,"predicate":"flags.2","description":"{@link channelAdminLogEventActionParticipantInvite}"},{"name":"ban","type":"true","optional":true,"predicate":"flags.3","description":"{@link channelAdminLogEventActionParticipantToggleBan}"},{"name":"unban","type":"true","optional":true,"predicate":"flags.4","description":"{@link channelAdminLogEventActionParticipantToggleBan}"},{"name":"kick","type":"true","optional":true,"predicate":"flags.5","description":"{@link channelAdminLogEventActionParticipantToggleBan}"},{"name":"unkick","type":"true","optional":true,"predicate":"flags.6","description":"{@link channelAdminLogEventActionParticipantToggleBan}"},{"name":"promote","type":"true","optional":true,"predicate":"flags.7","description":"{@link channelAdminLogEventActionParticipantToggleAdmin}"},{"name":"demote","type":"true","optional":true,"predicate":"flags.8","description":"{@link channelAdminLogEventActionParticipantToggleAdmin}"},{"name":"info","type":"true","optional":true,"predicate":"flags.9","description":"Info change events (when {@link channelAdminLogEventActionChangeAbout}, {@link channelAdminLogEventActionChangeLinkedChat}, {@link channelAdminLogEventActionChangeLocation}, {@link channelAdminLogEventActionChangePhoto}, {@link channelAdminLogEventActionChangeStickerSet}, {@link channelAdminLogEventActionChangeTitle} or {@link channelAdminLogEventActionChangeUsername} data of a channel gets modified)"},{"name":"settings","type":"true","optional":true,"predicate":"flags.10","description":"Settings change events ({@link channelAdminLogEventActionToggleInvites}, {@link channelAdminLogEventActionTogglePreHistoryHidden}, {@link channelAdminLogEventActionToggleSignatures}, {@link channelAdminLogEventActionDefaultBannedRights})"},{"name":"pinned","type":"true","optional":true,"predicate":"flags.11","description":"{@link channelAdminLogEventActionUpdatePinned}"},{"name":"edit","type":"true","optional":true,"predicate":"flags.12","description":"{@link channelAdminLogEventActionEditMessage}"},{"name":"delete","type":"true","optional":true,"predicate":"flags.13","description":"{@link channelAdminLogEventActionDeleteMessage}"},{"name":"groupCall","type":"true","optional":true,"predicate":"flags.14"},{"name":"invites","type":"true","optional":true,"predicate":"flags.15"}],"description":"Filter only certain admin log events"},{"name":"popularContact","id":1558266229,"type":"PopularContact","arguments":[{"name":"clientId","type":"Long","description":"Contact identifier"},{"name":"importers","type":"number","description":"How many people imported this contact"}],"description":"Popular contact"},{"name":"recentMeUrlUnknown","id":1189204285,"type":"RecentMeUrl","arguments":[{"name":"url","type":"string","description":"URL"}],"description":"Unknown t.me url"},{"name":"recentMeUrlUser","id":2377921334,"type":"RecentMeUrl","arguments":[{"name":"url","type":"string","description":"URL"},{"name":"userId","type":"number","description":"User ID"}],"description":"Recent t.me link to a user"},{"name":"recentMeUrlChat","id":2686132985,"type":"RecentMeUrl","arguments":[{"name":"url","type":"string","description":"t.me URL"},{"name":"chatId","type":"number","description":"Chat ID"}],"description":"Recent t.me link to a chat"},{"name":"recentMeUrlChatInvite","id":3947431965,"type":"RecentMeUrl","arguments":[{"name":"url","type":"string","description":"t.me URL"},{"name":"chatInvite","type":"ChatInvite","description":"Chat invitation"}],"description":"Recent t.me invite link to a chat"},{"name":"recentMeUrlStickerSet","id":3154794460,"type":"RecentMeUrl","arguments":[{"name":"url","type":"string","description":"t.me URL"},{"name":"set","type":"StickerSetCovered","description":"Stickerset"}],"description":"Recent t.me stickerset installation URL"},{"name":"inputSingleMedia","id":482797855,"type":"InputSingleMedia","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"media","type":"InputMedia","description":"The media"},{"name":"randomId","type":"Long","description":"Unique client media ID required to prevent message resending"},{"name":"message","type":"string","description":"A caption for the media"},{"name":"entities","type":"MessageEntity[]","optional":true,"predicate":"flags.0","description":"Message entities for styled text"}],"description":"A single media in an album or grouped media sent with {@link messages.sendMultiMedia}."},{"name":"webAuthorization","id":3402187762,"type":"WebAuthorization","arguments":[{"name":"hash","type":"Long","description":"Authorization hash"},{"name":"botId","type":"number","description":"Bot ID"},{"name":"domain","type":"string","description":"The domain name of the website on which the user has logged in."},{"name":"browser","type":"string","description":"Browser user-agent"},{"name":"platform","type":"string","description":"Platform"},{"name":"dateCreated","type":"number","description":"When was the web session created"},{"name":"dateActive","type":"number","description":"When was the web session last active"},{"name":"ip","type":"string","description":"IP address"},{"name":"region","type":"string","description":"Region, determined from IP address"}],"description":"Represents a bot logged in using the Telegram login widget"},{"name":"inputMessageID","id":2792792866,"type":"InputMessage","arguments":[{"name":"id","type":"number","description":"Message ID"}],"description":"Message by ID"},{"name":"inputMessageReplyTo","id":3134751637,"type":"InputMessage","arguments":[{"name":"id","type":"number","description":"ID of the message that replies to the message we need"}],"description":"Message to which the specified message replies to"},{"name":"inputMessagePinned","id":2257003832,"type":"InputMessage","arguments":[],"description":"Pinned message"},{"name":"inputMessageCallbackQuery","id":2902071934,"type":"InputMessage","arguments":[{"name":"id","type":"number","description":"Message ID"},{"name":"queryId","type":"Long","description":"Callback query ID"}],"description":"Used by bots for fetching information about the message that originated a callback query"},{"name":"inputDialogPeer","id":4239064759,"type":"InputDialogPeer","arguments":[{"name":"peer","type":"InputPeer","description":"Peer"}],"description":"A peer"},{"name":"inputDialogPeerFolder","id":1684014375,"type":"InputDialogPeer","arguments":[{"name":"folderId","type":"number","description":"Peer folder ID, for more info click here"}],"description":"All peers in a peer folder"},{"name":"dialogPeer","id":3849174789,"type":"DialogPeer","arguments":[{"name":"peer","type":"Peer","description":"Peer"}],"description":"Peer"},{"name":"dialogPeerFolder","id":1363483106,"type":"DialogPeer","arguments":[{"name":"folderId","type":"number","description":"Peer folder ID, for more info click here"}],"description":"Peer folder"},{"name":"fileHash","id":1648543603,"type":"FileHash","arguments":[{"name":"offset","type":"number","description":"Offset from where to start computing SHA-256 hash"},{"name":"limit","type":"number","description":"Length"},{"name":"hash","type":"Buffer","description":"SHA-256 Hash of file chunk, to be checked for validity after download"}],"description":"SHA256 Hash of an uploaded file, to be checked for validity after download"},{"name":"inputClientProxy","id":1968737087,"type":"InputClientProxy","arguments":[{"name":"address","type":"string","description":"Proxy address"},{"name":"port","type":"number","description":"Proxy port"}],"description":"Info about an MTProxy used to connect."},{"name":"inputSecureFileUploaded","id":859091184,"type":"InputSecureFile","arguments":[{"name":"id","type":"Long","description":"Secure file ID"},{"name":"parts","type":"number","description":"Secure file part count"},{"name":"md5Checksum","type":"string","description":"MD5 hash of encrypted uploaded file, to be checked server-side"},{"name":"fileHash","type":"Buffer","description":"File hash"},{"name":"secret","type":"Buffer","description":"Secret"}],"description":"Uploaded secure file, for more info see the passport docs »"},{"name":"inputSecureFile","id":1399317950,"type":"InputSecureFile","arguments":[{"name":"id","type":"Long","description":"Secure file ID"},{"name":"accessHash","type":"Long","description":"Secure file access hash"}],"description":"Preuploaded passport file, for more info see the passport docs »"},{"name":"secureFileEmpty","id":1679398724,"type":"SecureFile","arguments":[],"description":"Empty constructor"},{"name":"secureFile","id":3760683618,"type":"SecureFile","arguments":[{"name":"id","type":"Long","description":"ID"},{"name":"accessHash","type":"Long","description":"Access hash"},{"name":"size","type":"number","description":"File size"},{"name":"dcId","type":"number","description":"DC ID"},{"name":"date","type":"number","description":"Date of upload"},{"name":"fileHash","type":"Buffer","description":"File hash"},{"name":"secret","type":"Buffer","description":"Secret"}],"description":"Secure passport file, for more info see the passport docs »"},{"name":"secureData","id":2330640067,"type":"SecureData","arguments":[{"name":"data","type":"Buffer","description":"Data"},{"name":"dataHash","type":"Buffer","description":"Data hash"},{"name":"secret","type":"Buffer","description":"Secret"}],"description":"Secure passport data, for more info see the passport docs »"},{"name":"securePlainPhone","id":2103482845,"type":"SecurePlainData","arguments":[{"name":"phone","type":"string","description":"Phone number"}],"description":"Phone number to use in telegram passport: it must be verified, first »."},{"name":"securePlainEmail","id":569137759,"type":"SecurePlainData","arguments":[{"name":"email","type":"string","description":"Email address"}],"description":"Email address to use in telegram passport: it must be verified, first »."},{"name":"secureValueTypePersonalDetails","id":2636808675,"type":"SecureValueType","arguments":[],"description":"Personal details"},{"name":"secureValueTypePassport","id":1034709504,"type":"SecureValueType","arguments":[],"description":"Passport"},{"name":"secureValueTypeDriverLicense","id":115615172,"type":"SecureValueType","arguments":[],"description":"Driver's license"},{"name":"secureValueTypeIdentityCard","id":2698015819,"type":"SecureValueType","arguments":[],"description":"Identity card"},{"name":"secureValueTypeInternalPassport","id":2577698595,"type":"SecureValueType","arguments":[],"description":"Internal passport"},{"name":"secureValueTypeAddress","id":3420659238,"type":"SecureValueType","arguments":[],"description":"Address"},{"name":"secureValueTypeUtilityBill","id":4231435598,"type":"SecureValueType","arguments":[],"description":"Utility bill"},{"name":"secureValueTypeBankStatement","id":2299755533,"type":"SecureValueType","arguments":[],"description":"Bank statement"},{"name":"secureValueTypeRentalAgreement","id":2340959368,"type":"SecureValueType","arguments":[],"description":"Rental agreement"},{"name":"secureValueTypePassportRegistration","id":2581823594,"type":"SecureValueType","arguments":[],"description":"Internal registration passport"},{"name":"secureValueTypeTemporaryRegistration","id":3926060083,"type":"SecureValueType","arguments":[],"description":"Temporary registration"},{"name":"secureValueTypePhone","id":3005262555,"type":"SecureValueType","arguments":[],"description":"Phone"},{"name":"secureValueTypeEmail","id":2386339822,"type":"SecureValueType","arguments":[],"description":"Email"},{"name":"secureValue","id":411017418,"type":"SecureValue","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"type","type":"SecureValueType","description":"Secure passport value type"},{"name":"data","type":"SecureData","optional":true,"predicate":"flags.0","description":"Encrypted Telegram Passport element data"},{"name":"frontSide","type":"SecureFile","optional":true,"predicate":"flags.1","description":"Encrypted passport file with the front side of the document"},{"name":"reverseSide","type":"SecureFile","optional":true,"predicate":"flags.2","description":"Encrypted passport file with the reverse side of the document"},{"name":"selfie","type":"SecureFile","optional":true,"predicate":"flags.3","description":"Encrypted passport file with a selfie of the user holding the document"},{"name":"translation","type":"SecureFile[]","optional":true,"predicate":"flags.6","description":"Array of encrypted passport files with translated versions of the provided documents"},{"name":"files","type":"SecureFile[]","optional":true,"predicate":"flags.4","description":"Array of encrypted passport files with photos the of the documents"},{"name":"plainData","type":"SecurePlainData","optional":true,"predicate":"flags.5","description":"Plaintext verified passport data"},{"name":"hash","type":"Buffer","description":"Data hash"}],"description":"Secure value"},{"name":"inputSecureValue","id":3676426407,"type":"InputSecureValue","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"type","type":"SecureValueType","description":"Secure passport value type"},{"name":"data","type":"SecureData","optional":true,"predicate":"flags.0","description":"Encrypted Telegram Passport element data"},{"name":"frontSide","type":"InputSecureFile","optional":true,"predicate":"flags.1","description":"Encrypted passport file with the front side of the document"},{"name":"reverseSide","type":"InputSecureFile","optional":true,"predicate":"flags.2","description":"Encrypted passport file with the reverse side of the document"},{"name":"selfie","type":"InputSecureFile","optional":true,"predicate":"flags.3","description":"Encrypted passport file with a selfie of the user holding the document"},{"name":"translation","type":"InputSecureFile[]","optional":true,"predicate":"flags.6","description":"Array of encrypted passport files with translated versions of the provided documents"},{"name":"files","type":"InputSecureFile[]","optional":true,"predicate":"flags.4","description":"Array of encrypted passport files with photos the of the documents"},{"name":"plainData","type":"SecurePlainData","optional":true,"predicate":"flags.5","description":"Plaintext verified passport data"}],"description":"Secure value, for more info see the passport docs »"},{"name":"secureValueHash","id":3978218928,"type":"SecureValueHash","arguments":[{"name":"type","type":"SecureValueType","description":"Secure value type"},{"name":"hash","type":"Buffer","description":"Hash"}],"description":"Secure value hash"},{"name":"secureValueErrorData","id":3903065049,"type":"SecureValueError","arguments":[{"name":"type","type":"SecureValueType","description":"The section of the user's Telegram Passport which has the error, one of {@link secureValueTypePersonalDetails}, {@link secureValueTypePassport}, {@link secureValueTypeDriverLicense}, {@link secureValueTypeIdentityCard}, {@link secureValueTypeInternalPassport}, {@link secureValueTypeAddress}"},{"name":"dataHash","type":"Buffer","description":"Data hash"},{"name":"field","type":"string","description":"Name of the data field which has the error"},{"name":"text","type":"string","description":"Error message"}],"description":"Represents an issue in one of the data fields that was provided by the user. The error is considered resolved when the field's value changes."},{"name":"secureValueErrorFrontSide","id":12467706,"type":"SecureValueError","arguments":[{"name":"type","type":"SecureValueType","description":"One of {@link secureValueTypePassport}, {@link secureValueTypeDriverLicense}, {@link secureValueTypeIdentityCard}, {@link secureValueTypeInternalPassport}"},{"name":"fileHash","type":"Buffer","description":"File hash"},{"name":"text","type":"string","description":"Error message"}],"description":"Represents an issue with the front side of a document. The error is considered resolved when the file with the front side of the document changes."},{"name":"secureValueErrorReverseSide","id":2257201829,"type":"SecureValueError","arguments":[{"name":"type","type":"SecureValueType","description":"One of {@link secureValueTypeDriverLicense}, {@link secureValueTypeIdentityCard}"},{"name":"fileHash","type":"Buffer","description":"File hash"},{"name":"text","type":"string","description":"Error message"}],"description":"Represents an issue with the reverse side of a document. The error is considered resolved when the file with reverse side of the document changes."},{"name":"secureValueErrorSelfie","id":3845639894,"type":"SecureValueError","arguments":[{"name":"type","type":"SecureValueType","description":"One of {@link secureValueTypePassport}, {@link secureValueTypeDriverLicense}, {@link secureValueTypeIdentityCard}, {@link secureValueTypeInternalPassport}"},{"name":"fileHash","type":"Buffer","description":"File hash"},{"name":"text","type":"string","description":"Error message"}],"description":"Represents an issue with the selfie with a document. The error is considered resolved when the file with the selfie changes."},{"name":"secureValueErrorFile","id":2054162547,"type":"SecureValueError","arguments":[{"name":"type","type":"SecureValueType","description":"One of {@link secureValueTypeUtilityBill}, {@link secureValueTypeBankStatement}, {@link secureValueTypeRentalAgreement}, {@link secureValueTypePassportRegistration}, {@link secureValueTypeTemporaryRegistration}"},{"name":"fileHash","type":"Buffer","description":"File hash"},{"name":"text","type":"string","description":"Error message"}],"description":"Represents an issue with a document scan. The error is considered resolved when the file with the document scan changes."},{"name":"secureValueErrorFiles","id":1717706985,"type":"SecureValueError","arguments":[{"name":"type","type":"SecureValueType","description":"One of {@link secureValueTypeUtilityBill}, {@link secureValueTypeBankStatement}, {@link secureValueTypeRentalAgreement}, {@link secureValueTypePassportRegistration}, {@link secureValueTypeTemporaryRegistration}"},{"name":"fileHash","type":"Buffer[]","description":"File hash"},{"name":"text","type":"string","description":"Error message"}],"description":"Represents an issue with a list of scans. The error is considered resolved when the list of files containing the scans changes."},{"name":"secureValueError","id":2258466191,"type":"SecureValueError","arguments":[{"name":"type","type":"SecureValueType","description":"Type of element which has the issue"},{"name":"hash","type":"Buffer","description":"Hash"},{"name":"text","type":"string","description":"Error message"}],"description":"Secure value error"},{"name":"secureValueErrorTranslationFile","id":2702460784,"type":"SecureValueError","arguments":[{"name":"type","type":"SecureValueType","description":"One of {@link secureValueTypePersonalDetails}, {@link secureValueTypePassport}, {@link secureValueTypeDriverLicense}, {@link secureValueTypeIdentityCard}, {@link secureValueTypeInternalPassport}, {@link secureValueTypeUtilityBill}, {@link secureValueTypeBankStatement}, {@link secureValueTypeRentalAgreement}, {@link secureValueTypePassportRegistration}, {@link secureValueTypeTemporaryRegistration}"},{"name":"fileHash","type":"Buffer","description":"File hash"},{"name":"text","type":"string","description":"Error message"}],"description":"Represents an issue with one of the files that constitute the translation of a document. The error is considered resolved when the file changes."},{"name":"secureValueErrorTranslationFiles","id":878931416,"type":"SecureValueError","arguments":[{"name":"type","type":"SecureValueType","description":"One of {@link secureValueTypePersonalDetails}, {@link secureValueTypePassport}, {@link secureValueTypeDriverLicense}, {@link secureValueTypeIdentityCard}, {@link secureValueTypeInternalPassport}, {@link secureValueTypeUtilityBill}, {@link secureValueTypeBankStatement}, {@link secureValueTypeRentalAgreement}, {@link secureValueTypePassportRegistration}, {@link secureValueTypeTemporaryRegistration}"},{"name":"fileHash","type":"Buffer[]","description":"Hash"},{"name":"text","type":"string","description":"Error message"}],"description":"Represents an issue with the translated version of a document. The error is considered resolved when a file with the document translation changes."},{"name":"secureCredentialsEncrypted","id":871426631,"type":"SecureCredentialsEncrypted","arguments":[{"name":"data","type":"Buffer","description":"Encrypted JSON-serialized data with unique user's payload, data hashes and secrets required for EncryptedPassportElement decryption and authentication, as described in decrypting data »"},{"name":"hash","type":"Buffer","description":"Data hash for data authentication as described in decrypting data »"},{"name":"secret","type":"Buffer","description":"Secret, encrypted with the bot's public RSA key, required for data decryption as described in decrypting data »"}],"description":"Encrypted credentials required to decrypt telegram passport data."},{"name":"savedPhoneContact","id":289586518,"type":"SavedContact","arguments":[{"name":"phone","type":"string","description":"Phone number"},{"name":"firstName","type":"string","description":"First name"},{"name":"lastName","type":"string","description":"Last name"},{"name":"date","type":"number","description":"Date added"}],"description":"Saved contact"},{"name":"passwordKdfAlgoUnknown","id":3562713238,"type":"PasswordKdfAlgo","arguments":[],"description":"Unknown KDF (most likely, the client is outdated and does not support the specified KDF algorithm)"},{"name":"passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow","id":982592842,"type":"PasswordKdfAlgo","arguments":[{"name":"salt1","type":"Buffer","description":"One of two salts used by the derivation function (see SRP 2FA login)"},{"name":"salt2","type":"Buffer","description":"One of two salts used by the derivation function (see SRP 2FA login)"},{"name":"g","type":"number","description":"Base (see SRP 2FA login)"},{"name":"p","type":"Buffer","description":"2048-bit modulus (see SRP 2FA login)"}],"description":"This key derivation algorithm defines that SRP 2FA login must be used"},{"name":"securePasswordKdfAlgoUnknown","id":4883767,"type":"SecurePasswordKdfAlgo","arguments":[],"description":"Unknown KDF algo (most likely the client has to be updated)"},{"name":"securePasswordKdfAlgoPBKDF2HMACSHA512iter100000","id":3153255840,"type":"SecurePasswordKdfAlgo","arguments":[{"name":"salt","type":"Buffer","description":"Salt"}],"description":"PBKDF2 with SHA512 and 100000 iterations KDF algo"},{"name":"securePasswordKdfAlgoSHA512","id":2252807570,"type":"SecurePasswordKdfAlgo","arguments":[{"name":"salt","type":"Buffer","description":"Salt"}],"description":"SHA512 KDF algo"},{"name":"secureSecretSettings","id":354925740,"type":"SecureSecretSettings","arguments":[{"name":"secureAlgo","type":"SecurePasswordKdfAlgo","description":"Secure KDF algo"},{"name":"secureSecret","type":"Buffer","description":"Secure secret"},{"name":"secureSecretId","type":"Long","description":"Secret ID"}],"description":"Secure settings"},{"name":"inputCheckPasswordEmpty","id":2558588504,"type":"InputCheckPasswordSRP","arguments":[],"description":"There is no password"},{"name":"inputCheckPasswordSRP","id":3531600002,"type":"InputCheckPasswordSRP","arguments":[{"name":"srpId","type":"Long","description":"SRP ID"},{"name":"A","type":"Buffer","description":"A parameter (see SRP)"},{"name":"M1","type":"Buffer","description":"M1 parameter (see SRP)"}],"description":"Constructor for checking the validity of a 2FA SRP password (see SRP)"},{"name":"secureRequiredType","id":2191366618,"type":"SecureRequiredType","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"nativeNames","type":"true","optional":true,"predicate":"flags.0","description":"Native names"},{"name":"selfieRequired","type":"true","optional":true,"predicate":"flags.1","description":"Is a selfie required"},{"name":"translationRequired","type":"true","optional":true,"predicate":"flags.2","description":"Is a translation required"},{"name":"type","type":"SecureValueType","description":"Secure value type"}],"description":"Required type"},{"name":"secureRequiredTypeOneOf","id":41187252,"type":"SecureRequiredType","arguments":[{"name":"types","type":"SecureRequiredType[]","description":"Secure required value types"}],"description":"One of"},{"name":"inputAppEvent","id":488313413,"type":"InputAppEvent","arguments":[{"name":"time","type":"Double","description":"Client's exact timestamp for the event"},{"name":"type","type":"string","description":"Type of event"},{"name":"peer","type":"Long","description":"Arbitrary numeric value for more convenient selection of certain event types, or events referring to a certain object"},{"name":"data","type":"JSONValue","description":"Details of the event"}],"description":"Event that occured in the application."},{"name":"jsonObjectValue","id":3235781593,"type":"JSONObjectValue","arguments":[{"name":"key","type":"string","description":"Key"},{"name":"value","type":"JSONValue","description":"Value"}],"description":"JSON key: value pair"},{"name":"jsonNull","id":1064139624,"type":"JSONValue","arguments":[],"description":"null JSON value"},{"name":"jsonBool","id":3342098026,"type":"JSONValue","arguments":[{"name":"value","type":"boolean","description":"Value"}],"description":"JSON boolean value"},{"name":"jsonNumber","id":736157604,"type":"JSONValue","arguments":[{"name":"value","type":"Double","description":"Value"}],"description":"JSON numeric value"},{"name":"jsonString","id":3072226938,"type":"JSONValue","arguments":[{"name":"value","type":"string","description":"Value"}],"description":"JSON string"},{"name":"jsonArray","id":4148447075,"type":"JSONValue","arguments":[{"name":"value","type":"JSONValue[]","description":"JSON values"}],"description":"JSON array"},{"name":"jsonObject","id":2579616925,"type":"JSONValue","arguments":[{"name":"value","type":"JSONObjectValue[]","description":"Values"}],"description":"JSON object value"},{"name":"pageTableCell","id":878078826,"type":"PageTableCell","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"header","type":"true","optional":true,"predicate":"flags.0","description":"Is this element part of the column header"},{"name":"alignCenter","type":"true","optional":true,"predicate":"flags.3","description":"Horizontally centered block"},{"name":"alignRight","type":"true","optional":true,"predicate":"flags.4","description":"Right-aligned block"},{"name":"valignMiddle","type":"true","optional":true,"predicate":"flags.5","description":"Vertically centered block"},{"name":"valignBottom","type":"true","optional":true,"predicate":"flags.6","description":"Block vertically-alligned to the bottom"},{"name":"text","type":"RichText","optional":true,"predicate":"flags.7","description":"Content"},{"name":"colspan","type":"number","optional":true,"predicate":"flags.1","description":"For how many columns should this cell extend"},{"name":"rowspan","type":"number","optional":true,"predicate":"flags.2","description":"For how many rows should this cell extend"}],"description":"Table cell"},{"name":"pageTableRow","id":3770729957,"type":"PageTableRow","arguments":[{"name":"cells","type":"PageTableCell[]","description":"Table cells"}],"description":"Table row"},{"name":"pageCaption","id":1869903447,"type":"PageCaption","arguments":[{"name":"text","type":"RichText","description":"Caption"},{"name":"credit","type":"RichText","description":"Credits"}],"description":"Page caption"},{"name":"pageListItemText","id":3106911949,"type":"PageListItem","arguments":[{"name":"text","type":"RichText","description":"Text"}],"description":"List item"},{"name":"pageListItemBlocks","id":635466748,"type":"PageListItem","arguments":[{"name":"blocks","type":"PageBlock[]","description":"Blocks"}],"description":"List item"},{"name":"pageListOrderedItemText","id":1577484359,"type":"PageListOrderedItem","arguments":[{"name":"num","type":"string","description":"Number of element within ordered list"},{"name":"text","type":"RichText","description":"Text"}],"description":"Ordered list of text items"},{"name":"pageListOrderedItemBlocks","id":2564655414,"type":"PageListOrderedItem","arguments":[{"name":"num","type":"string","description":"Number of element within ordered list"},{"name":"blocks","type":"PageBlock[]","description":"Item contents"}],"description":"Ordered list of IV blocks"},{"name":"pageRelatedArticle","id":3012615176,"type":"PageRelatedArticle","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"url","type":"string","description":"URL of article"},{"name":"webpageId","type":"Long","description":"Webpage ID of generated IV preview"},{"name":"title","type":"string","optional":true,"predicate":"flags.0","description":"Title"},{"name":"description","type":"string","optional":true,"predicate":"flags.1","description":"Description"},{"name":"photoId","type":"Long","optional":true,"predicate":"flags.2","description":"ID of preview photo"},{"name":"author","type":"string","optional":true,"predicate":"flags.3","description":"Author name"},{"name":"publishedDate","type":"number","optional":true,"predicate":"flags.4","description":"Date of pubblication"}],"description":"Related article"},{"name":"page","id":2556788493,"type":"Page","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"part","type":"true","optional":true,"predicate":"flags.0","description":"Indicates that not full page preview is available to the client and it will need to fetch full Instant View from the server using {@link messages.getWebPagePreview}."},{"name":"rtl","type":"true","optional":true,"predicate":"flags.1","description":"Whether the page contains RTL text"},{"name":"v2","type":"true","optional":true,"predicate":"flags.2","description":"Whether this is an IV v2 page"},{"name":"url","type":"string","description":"Original page HTTP URL"},{"name":"blocks","type":"PageBlock[]","description":"Page elements (like with HTML elements, only as TL constructors)"},{"name":"photos","type":"Photo[]","description":"Photos in page"},{"name":"documents","type":"Document[]","description":"Media in page"},{"name":"views","type":"number","optional":true,"predicate":"flags.3","description":"Viewcount"}],"description":"Instant view page"},{"name":"pollAnswer","id":1823064809,"type":"PollAnswer","arguments":[{"name":"text","type":"string","description":"Textual representation of the answer"},{"name":"option","type":"Buffer","description":"The param that has to be passed to {@link messages.sendVote}."}],"description":"A possible answer of a poll"},{"name":"poll","id":2262925665,"type":"Poll","arguments":[{"name":"id","type":"Long","description":"ID of the poll"},{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"closed","type":"true","optional":true,"predicate":"flags.0","description":"Whether the poll is closed and doesn't accept any more answers"},{"name":"publicVoters","type":"true","optional":true,"predicate":"flags.1","description":"Whether cast votes are publicly visible to all users (non-anonymous poll)"},{"name":"multipleChoice","type":"true","optional":true,"predicate":"flags.2","description":"Whether multiple options can be chosen as answer"},{"name":"quiz","type":"true","optional":true,"predicate":"flags.3","description":"Whether this is a quiz (with wrong and correct answers, results shown in the return type)"},{"name":"question","type":"string","description":"The question of the poll"},{"name":"answers","type":"PollAnswer[]","description":"The possible answers, vote using {@link messages.sendVote}."},{"name":"closePeriod","type":"number","optional":true,"predicate":"flags.4","description":"Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with close_date."},{"name":"closeDate","type":"number","optional":true,"predicate":"flags.5","description":"Point in time (UNIX timestamp in seconds) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future; can't be used together with close_period."}],"description":"Poll"},{"name":"pollAnswerVoters","id":997055186,"type":"PollAnswerVoters","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"chosen","type":"true","optional":true,"predicate":"flags.0","description":"Whether we have chosen this answer"},{"name":"correct","type":"true","optional":true,"predicate":"flags.1","description":"For quizes, whether the option we have chosen is correct"},{"name":"option","type":"Buffer","description":"The param that has to be passed to {@link messages.sendVote}."},{"name":"voters","type":"number","description":"How many users voted for this option"}],"description":"A poll answer, and how users voted on it"},{"name":"pollResults","id":3135029667,"type":"PollResults","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"min","type":"true","optional":true,"predicate":"flags.0","description":"Similar to min objects, used for poll constructors that are the same for all users so they don't have option chosen by the current user (you can use {@link messages.getPollResults} to get the full poll results)."},{"name":"results","type":"PollAnswerVoters[]","optional":true,"predicate":"flags.1","description":"Poll results"},{"name":"totalVoters","type":"number","optional":true,"predicate":"flags.2","description":"Total number of people that voted in the poll"},{"name":"recentVoters","type":"number[]","optional":true,"predicate":"flags.3","description":"IDs of the last users that recently voted in the poll"},{"name":"solution","type":"string","optional":true,"predicate":"flags.4","description":"Explanation of quiz solution"},{"name":"solutionEntities","type":"MessageEntity[]","optional":true,"predicate":"flags.4","description":"Message entities for styled text in quiz solution"}],"description":"Results of poll"},{"name":"chatOnlines","id":4030849616,"type":"ChatOnlines","arguments":[{"name":"onlines","type":"number","description":"Number of online users"}],"description":"Number of online users in a chat"},{"name":"statsURL","id":1202287072,"type":"StatsURL","arguments":[{"name":"url","type":"string","description":"Chat statistics"}],"description":"URL with chat statistics"},{"name":"chatAdminRights","id":1605510357,"type":"ChatAdminRights","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"changeInfo","type":"true","optional":true,"predicate":"flags.0","description":"If set, allows the admin to modify the description of the channel/supergroup"},{"name":"postMessages","type":"true","optional":true,"predicate":"flags.1","description":"If set, allows the admin to post messages in the channel"},{"name":"editMessages","type":"true","optional":true,"predicate":"flags.2","description":"If set, allows the admin to also edit messages from other admins in the channel"},{"name":"deleteMessages","type":"true","optional":true,"predicate":"flags.3","description":"If set, allows the admin to also delete messages from other admins in the channel"},{"name":"banUsers","type":"true","optional":true,"predicate":"flags.4","description":"If set, allows the admin to ban users from the channel/supergroup"},{"name":"inviteUsers","type":"true","optional":true,"predicate":"flags.5","description":"If set, allows the admin to invite users in the channel/supergroup"},{"name":"pinMessages","type":"true","optional":true,"predicate":"flags.7","description":"If set, allows the admin to pin messages in the channel/supergroup"},{"name":"addAdmins","type":"true","optional":true,"predicate":"flags.9","description":"If set, allows the admin to add other admins with the same (or more limited) permissions in the channel/supergroup"},{"name":"anonymous","type":"true","optional":true,"predicate":"flags.10","description":"Whether this admin is anonymous"},{"name":"manageCall","type":"true","optional":true,"predicate":"flags.11"},{"name":"other","type":"true","optional":true,"predicate":"flags.12"}],"description":"Represents the rights of an admin in a channel/supergroup."},{"name":"chatBannedRights","id":2668758040,"type":"ChatBannedRights","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"viewMessages","type":"true","optional":true,"predicate":"flags.0","description":"If set, does not allow a user to view messages in a supergroup/channel/chat"},{"name":"sendMessages","type":"true","optional":true,"predicate":"flags.1","description":"If set, does not allow a user to send messages in a supergroup/chat"},{"name":"sendMedia","type":"true","optional":true,"predicate":"flags.2","description":"If set, does not allow a user to send any media in a supergroup/chat"},{"name":"sendStickers","type":"true","optional":true,"predicate":"flags.3","description":"If set, does not allow a user to send stickers in a supergroup/chat"},{"name":"sendGifs","type":"true","optional":true,"predicate":"flags.4","description":"If set, does not allow a user to send gifs in a supergroup/chat"},{"name":"sendGames","type":"true","optional":true,"predicate":"flags.5","description":"If set, does not allow a user to send games in a supergroup/chat"},{"name":"sendInline","type":"true","optional":true,"predicate":"flags.6","description":"If set, does not allow a user to use inline bots in a supergroup/chat"},{"name":"embedLinks","type":"true","optional":true,"predicate":"flags.7","description":"If set, does not allow a user to embed links in the messages of a supergroup/chat"},{"name":"sendPolls","type":"true","optional":true,"predicate":"flags.8","description":"If set, does not allow a user to send stickers in a supergroup/chat"},{"name":"changeInfo","type":"true","optional":true,"predicate":"flags.10","description":"If set, does not allow any user to change the description of a supergroup/chat"},{"name":"inviteUsers","type":"true","optional":true,"predicate":"flags.15","description":"If set, does not allow any user to invite users in a supergroup/chat"},{"name":"pinMessages","type":"true","optional":true,"predicate":"flags.17","description":"If set, does not allow any user to pin messages in a supergroup/chat"},{"name":"untilDate","type":"number","description":"Validity of said permissions (it is considered forever any value less then 30 seconds or more then 366 days)."}],"description":"Represents the rights of a normal user in a supergroup/channel/chat. In this case, the flags are inverted: if set, a flag does not allow a user to do X."},{"name":"inputWallPaper","id":3861952889,"type":"InputWallPaper","arguments":[{"name":"id","type":"Long","description":"Wallpaper ID"},{"name":"accessHash","type":"Long","description":"Access hash"}],"description":"Wallpaper"},{"name":"inputWallPaperSlug","id":1913199744,"type":"InputWallPaper","arguments":[{"name":"slug","type":"string","description":"Unique wallpaper ID"}],"description":"Wallpaper by slug (a unique ID)"},{"name":"inputWallPaperNoFile","id":2524595758,"type":"InputWallPaper","arguments":[{"name":"id","type":"Long"}],"description":"Wallpaper with no file"},{"name":"codeSettings","id":3737042563,"type":"CodeSettings","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"allowFlashcall","type":"true","optional":true,"predicate":"flags.0","description":"Whether to allow phone verification via phone calls."},{"name":"currentNumber","type":"true","optional":true,"predicate":"flags.1","description":"Pass true if the phone number is used on the current device. Ignored if allow_flashcall is not set."},{"name":"allowAppHash","type":"true","optional":true,"predicate":"flags.4","description":"If a token that will be included in eventually sent SMSs is required: required in newer versions of android, to use the android SMS receiver APIs"}],"description":"Settings used by telegram servers for sending the confirm code."},{"name":"wallPaperSettings","id":499236004,"type":"WallPaperSettings","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"blur","type":"true","optional":true,"predicate":"flags.1","description":"If set, the wallpaper must be downscaled to fit in 450x450 square and then box-blurred with radius 12"},{"name":"motion","type":"true","optional":true,"predicate":"flags.2","description":"If set, the background needs to be slightly moved when device is rotated"},{"name":"backgroundColor","type":"number","optional":true,"predicate":"flags.0","description":"If set, a PNG pattern is to be combined with the color chosen by the user: the main color of the background in RGB24 format"},{"name":"secondBackgroundColor","type":"number","optional":true,"predicate":"flags.4","description":"If set, a PNG pattern is to be combined with the first and second background colors (RGB24 format) in a top-bottom gradient"},{"name":"thirdBackgroundColor","type":"number","optional":true,"predicate":"flags.5"},{"name":"fourthBackgroundColor","type":"number","optional":true,"predicate":"flags.6"},{"name":"intensity","type":"number","optional":true,"predicate":"flags.3","description":"Intensity of the pattern when it is shown above the main background color, 0-100"},{"name":"rotation","type":"number","optional":true,"predicate":"flags.4","description":"Clockwise rotation angle of the gradient, in degrees; 0-359. Should be always divisible by 45"}],"description":"Wallpaper settings"},{"name":"autoDownloadSettings","id":3762434803,"type":"AutoDownloadSettings","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"disabled","type":"true","optional":true,"predicate":"flags.0","description":"Disable automatic media downloads?"},{"name":"videoPreloadLarge","type":"true","optional":true,"predicate":"flags.1","description":"Whether to preload the first seconds of videos larger than the specified limit"},{"name":"audioPreloadNext","type":"true","optional":true,"predicate":"flags.2","description":"Whether to preload the next audio track when you're listening to music"},{"name":"phonecallsLessData","type":"true","optional":true,"predicate":"flags.3","description":"Whether to enable data saving mode in phone calls"},{"name":"photoSizeMax","type":"number","description":"Maximum size of photos to preload"},{"name":"videoSizeMax","type":"number","description":"Maximum size of videos to preload"},{"name":"fileSizeMax","type":"number","description":"Maximum size of other files to preload"},{"name":"videoUploadMaxbitrate","type":"number","description":"Maximum suggested bitrate for uploading videos"}],"description":"Autodownload settings"},{"name":"emojiKeyword","id":3585325561,"type":"EmojiKeyword","arguments":[{"name":"keyword","type":"string","description":"Keyword"},{"name":"emoticons","type":"string[]","description":"Emojis associated to keyword"}],"description":"Emoji keyword"},{"name":"emojiKeywordDeleted","id":594408994,"type":"EmojiKeyword","arguments":[{"name":"keyword","type":"string","description":"Keyword"},{"name":"emoticons","type":"string[]","description":"Emojis that were associated to keyword"}],"description":"Deleted emoji keyword"},{"name":"emojiKeywordsDifference","id":1556570557,"type":"EmojiKeywordsDifference","arguments":[{"name":"langCode","type":"string","description":"Language code for keywords"},{"name":"fromVersion","type":"number","description":"Previous emoji keyword list version"},{"name":"version","type":"number","description":"Current version of emoji keyword list"},{"name":"keywords","type":"EmojiKeyword[]","description":"Emojis associated to keywords"}],"description":"Changes to emoji keywords"},{"name":"emojiURL","id":2775937949,"type":"EmojiURL","arguments":[{"name":"url","type":"string","description":"An HTTP URL which can be used to automatically log in into translation platform and suggest new emoji replacements. The URL will be valid for 30 seconds after generation"}],"description":"An HTTP URL which can be used to automatically log in into translation platform and suggest new emoji replacements. The URL will be valid for 30 seconds after generation"},{"name":"emojiLanguage","id":3019592545,"type":"EmojiLanguage","arguments":[{"name":"langCode","type":"string","description":"Language code"}],"description":"Emoji language"},{"name":"folder","id":4283715173,"type":"Folder","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"autofillNewBroadcasts","type":"true","optional":true,"predicate":"flags.0","description":"Automatically add new channels to this folder"},{"name":"autofillPublicGroups","type":"true","optional":true,"predicate":"flags.1","description":"Automatically add joined new public supergroups to this folder"},{"name":"autofillNewCorrespondents","type":"true","optional":true,"predicate":"flags.2","description":"Automatically add new private chats to this folder"},{"name":"id","type":"number","description":"Folder ID"},{"name":"title","type":"string","description":"Folder title"},{"name":"photo","type":"ChatPhoto","optional":true,"predicate":"flags.3","description":"Folder picture"}],"description":"Folder"},{"name":"inputFolderPeer","id":4224893590,"type":"InputFolderPeer","arguments":[{"name":"peer","type":"InputPeer","description":"Peer"},{"name":"folderId","type":"number","description":"Peer folder ID, for more info click here"}],"description":"Peer in a folder"},{"name":"folderPeer","id":3921323624,"type":"FolderPeer","arguments":[{"name":"peer","type":"Peer","description":"Folder peer info"},{"name":"folderId","type":"number","description":"Peer folder ID, for more info click here"}],"description":"Peer in a folder"},{"name":"urlAuthResultRequest","id":2463316494,"type":"UrlAuthResult","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"requestWriteAccess","type":"true","optional":true,"predicate":"flags.0","description":"Whether the bot would like to send messages to the user"},{"name":"bot","type":"User","description":"Username of a bot, which will be used for user authorization. If not specified, the current bot's username will be assumed. The url's domain must be the same as the domain linked with the bot. See Linking your domain to the bot for more details."},{"name":"domain","type":"string","description":"The domain name of the website on which the user will log in."}],"description":"Details about the authorization request, for more info click here »"},{"name":"urlAuthResultAccepted","id":2408320590,"type":"UrlAuthResult","arguments":[{"name":"url","type":"string","description":"The URL name of the website on which the user has logged in."}],"description":"Details about an accepted authorization request, for more info click here »"},{"name":"urlAuthResultDefault","id":2849430303,"type":"UrlAuthResult","arguments":[],"description":"Details about an accepted authorization request, for more info click here »"},{"name":"channelLocationEmpty","id":3216354699,"type":"ChannelLocation","arguments":[],"description":"No location (normal supergroup)"},{"name":"channelLocation","id":547062491,"type":"ChannelLocation","arguments":[{"name":"geoPoint","type":"GeoPoint","description":"Geographical location of supergrup"},{"name":"address","type":"string","description":"Textual description of the address"}],"description":"Geographical location of supergroup (geogroups)"},{"name":"peerLocated","id":3393592157,"type":"PeerLocated","arguments":[{"name":"peer","type":"Peer","description":"Peer"},{"name":"expires","type":"number","description":"Validity period of current data"},{"name":"distance","type":"number","description":"Distance from the peer in meters"}],"description":"Peer geolocated nearby"},{"name":"peerSelfLocated","id":4176226379,"type":"PeerLocated","arguments":[{"name":"expires","type":"number","description":"Expiry of geolocation info for current peer"}],"description":"Current peer"},{"name":"restrictionReason","id":3497176244,"type":"RestrictionReason","arguments":[{"name":"platform","type":"string","description":"Platform identifier (ios, android, wp, all, etc.), can be concatenated with a dash as separator (android-ios, ios-wp, etc)"},{"name":"reason","type":"string","description":"Restriction reason (porno, terms, etc.)"},{"name":"text","type":"string","description":"Error message to be shown to the user"}],"description":"Restriction reason."},{"name":"inputTheme","id":1012306921,"type":"InputTheme","arguments":[{"name":"id","type":"Long","description":"ID"},{"name":"accessHash","type":"Long","description":"Access hash"}],"description":"Theme"},{"name":"inputThemeSlug","id":4119399921,"type":"InputTheme","arguments":[{"name":"slug","type":"string","description":"Unique theme ID"}],"description":"Theme by theme ID"},{"name":"theme","id":42930452,"type":"Theme","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"creator","type":"true","optional":true,"predicate":"flags.0","description":"Whether the current user is the creator of this theme"},{"name":"default","type":"true","optional":true,"predicate":"flags.1","description":"Whether this is the default theme"},{"name":"id","type":"Long","description":"Theme ID"},{"name":"accessHash","type":"Long","description":"Theme access hash"},{"name":"slug","type":"string","description":"Unique theme ID"},{"name":"title","type":"string","description":"Theme name"},{"name":"document","type":"Document","optional":true,"predicate":"flags.2","description":"Theme"},{"name":"settings","type":"ThemeSettings","optional":true,"predicate":"flags.3","description":"Theme settings"},{"name":"installsCount","type":"number","description":"Installation count"}],"description":"Theme"},{"name":"baseThemeClassic","id":3282117730,"type":"BaseTheme","arguments":[],"description":"Classic theme"},{"name":"baseThemeDay","id":4225242760,"type":"BaseTheme","arguments":[],"description":"Day theme"},{"name":"baseThemeNight","id":3081969320,"type":"BaseTheme","arguments":[],"description":"Night theme"},{"name":"baseThemeTinted","id":1834973166,"type":"BaseTheme","arguments":[],"description":"Tinted theme"},{"name":"baseThemeArctic","id":1527845466,"type":"BaseTheme","arguments":[],"description":"Arctic theme"},{"name":"inputThemeSettings","id":3176168657,"type":"InputThemeSettings","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"baseTheme","type":"BaseTheme","description":"Default theme on which this theme is based"},{"name":"accentColor","type":"number","description":"Accent color, RGB24 format"},{"name":"messageTopColor","type":"number","optional":true,"predicate":"flags.0","description":"Message gradient color (top), RGB24 format"},{"name":"messageBottomColor","type":"number","optional":true,"predicate":"flags.0","description":"Message gradient color (bottom), RGB24 format"},{"name":"wallpaper","type":"InputWallPaper","optional":true,"predicate":"flags.1","description":"Wallpaper"},{"name":"wallpaperSettings","type":"WallPaperSettings","optional":true,"predicate":"flags.1","description":"Wallpaper settings"}],"description":"Theme settings"},{"name":"themeSettings","id":2618595402,"type":"ThemeSettings","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"baseTheme","type":"BaseTheme","description":"Base theme"},{"name":"accentColor","type":"number","description":"Accent color, RGB24 format"},{"name":"messageTopColor","type":"number","optional":true,"predicate":"flags.0","description":"Message gradient color (top), RGB24 format"},{"name":"messageBottomColor","type":"number","optional":true,"predicate":"flags.0","description":"Message gradient color (bottom), RGB24 format"},{"name":"wallpaper","type":"WallPaper","optional":true,"predicate":"flags.1","description":"Wallpaper"}],"description":"Theme settings"},{"name":"webPageAttributeTheme","id":1421174295,"type":"WebPageAttribute","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"documents","type":"Document[]","optional":true,"predicate":"flags.0","description":"Theme files"},{"name":"settings","type":"ThemeSettings","optional":true,"predicate":"flags.1","description":"Theme settings"}],"description":"Page theme"},{"name":"messageUserVote","id":2727236953,"type":"MessageUserVote","arguments":[{"name":"userId","type":"number","description":"User ID"},{"name":"option","type":"Buffer","description":"The option chosen by the user"},{"name":"date","type":"number","description":"When did the user cast the vote"}],"description":"How a user voted in a poll"},{"name":"messageUserVoteInputOption","id":909603888,"type":"MessageUserVote","arguments":[{"name":"userId","type":"number","description":"The user that voted for the queried option"},{"name":"date","type":"number","description":"When did the user cast the vote"}],"description":"How a user voted in a poll (reduced constructor, returned if an option was provided to {@link messages.getPollVotes})"},{"name":"messageUserVoteMultiple","id":244310238,"type":"MessageUserVote","arguments":[{"name":"userId","type":"number","description":"User ID"},{"name":"options","type":"Buffer[]","description":"Options chosen by the user"},{"name":"date","type":"number","description":"When did the user cast their votes"}],"description":"How a user voted in a multiple-choice poll"},{"name":"bankCardOpenUrl","id":4117234314,"type":"BankCardOpenUrl","arguments":[{"name":"url","type":"string","description":"Info URL"},{"name":"name","type":"string","description":"Bank name"}],"description":"Credit card info URL provided by the bank"},{"name":"dialogFilter","id":1949890536,"type":"DialogFilter","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"contacts","type":"true","optional":true,"predicate":"flags.0","description":"Whether to include all contacts in this folder"},{"name":"nonContacts","type":"true","optional":true,"predicate":"flags.1","description":"Whether to include all non-contacts in this folder"},{"name":"groups","type":"true","optional":true,"predicate":"flags.2","description":"Whether to include all groups in this folder"},{"name":"broadcasts","type":"true","optional":true,"predicate":"flags.3","description":"Whether to include all channels in this folder"},{"name":"bots","type":"true","optional":true,"predicate":"flags.4","description":"Whether to include all bots in this folder"},{"name":"excludeMuted","type":"true","optional":true,"predicate":"flags.11","description":"Whether to exclude muted chats from this folder"},{"name":"excludeRead","type":"true","optional":true,"predicate":"flags.12","description":"Whether to exclude read chats from this folder"},{"name":"excludeArchived","type":"true","optional":true,"predicate":"flags.13","description":"Whether to exclude archived chats from this folder"},{"name":"id","type":"number","description":"Folder ID"},{"name":"title","type":"string","description":"Folder name"},{"name":"emoticon","type":"string","optional":true,"predicate":"flags.25","description":"Folder emoticon"},{"name":"pinnedPeers","type":"InputPeer[]","description":"Pinned chats, folders can have unlimited pinned chats"},{"name":"includePeers","type":"InputPeer[]","description":"Include the following chats in this folder"},{"name":"excludePeers","type":"InputPeer[]","description":"Exclude the following chats from this folder"}],"description":"Dialog filter AKA folder"},{"name":"dialogFilterSuggested","id":2004110666,"type":"DialogFilterSuggested","arguments":[{"name":"filter","type":"DialogFilter","description":"Folder info"},{"name":"description","type":"string","description":"Folder description"}],"description":"Suggested folders"},{"name":"statsDateRangeDays","id":3057118639,"type":"StatsDateRangeDays","arguments":[{"name":"minDate","type":"number","description":"Initial date"},{"name":"maxDate","type":"number","description":"Final date"}],"description":"Channel statistics date range"},{"name":"statsAbsValueAndPrev","id":3410210014,"type":"StatsAbsValueAndPrev","arguments":[{"name":"current","type":"Double","description":"Current value"},{"name":"previous","type":"Double","description":"Previous value"}],"description":"Statistics value couple; intial and final value for period of time currently in consideration"},{"name":"statsPercentValue","id":3419287520,"type":"StatsPercentValue","arguments":[{"name":"part","type":"Double","description":"Partial value"},{"name":"total","type":"Double","description":"Total value"}],"description":"Channel statistics percentage.
\nCompute the percentage simply by doing part * total / 100"},{"name":"statsGraphAsync","id":1244130093,"type":"StatsGraph","arguments":[{"name":"token","type":"string","description":"Token to use for fetching the async graph"}],"description":"This channel statistics graph must be generated asynchronously using {@link stats.loadAsyncGraph} to reduce server load"},{"name":"statsGraphError","id":3202127906,"type":"StatsGraph","arguments":[{"name":"error","type":"string","description":"The error"}],"description":"An error occurred while generating the statistics graph"},{"name":"statsGraph","id":2393138358,"type":"StatsGraph","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"json","type":"DataJSON","description":"Statistics data"},{"name":"zoomToken","type":"string","optional":true,"predicate":"flags.0","description":"Zoom token"}],"description":"Channel statistics graph"},{"name":"messageInteractionCounters","id":2907687357,"type":"MessageInteractionCounters","arguments":[{"name":"msgId","type":"number","description":"Message ID"},{"name":"views","type":"number","description":"Views"},{"name":"forwards","type":"number","description":"Number of times this message was forwarded"}],"description":"Message interaction counters"},{"name":"videoSize","id":3727929492,"type":"VideoSize","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"type","type":"string","description":"u for animated profile pictures, and v for trimmed and downscaled video previews"},{"name":"w","type":"number","description":"Video width"},{"name":"h","type":"number","description":"Video height"},{"name":"size","type":"number","description":"File size"},{"name":"videoStartTs","type":"Double","optional":true,"predicate":"flags.0","description":"Timestamp that should be shown as static preview to the user (seconds)"}],"description":"Animated profile picture in MPEG4 format"},{"name":"statsGroupTopPoster","id":418631927,"type":"StatsGroupTopPoster","arguments":[{"name":"userId","type":"number","description":"User ID"},{"name":"messages","type":"number","description":"Number of messages for statistics period in consideration"},{"name":"avgChars","type":"number","description":"Average number of characters per message"}],"description":"Information about an active user in a supergroup"},{"name":"statsGroupTopAdmin","id":1611985938,"type":"StatsGroupTopAdmin","arguments":[{"name":"userId","type":"number","description":"User ID"},{"name":"deleted","type":"number","description":"Number of deleted messages for statistics period in consideration"},{"name":"kicked","type":"number","description":"Number of kicked users for statistics period in consideration"},{"name":"banned","type":"number","description":"Number of banned users for statistics period in consideration"}],"description":"Information about an active admin in a supergroup"},{"name":"statsGroupTopInviter","id":831924812,"type":"StatsGroupTopInviter","arguments":[{"name":"userId","type":"number","description":"User ID"},{"name":"invitations","type":"number","description":"Number of invitations for statistics period in consideration"}],"description":"Information about an active supergroup inviter"},{"name":"globalPrivacySettings","id":3198350372,"type":"GlobalPrivacySettings","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"archiveAndMuteNewNoncontactPeers","type":"boolean","optional":true,"predicate":"flags.0","description":"Whether to archive and mute new chats from non-contacts"}],"description":"Global privacy settings"},{"name":"messageViews","id":1163625789,"type":"MessageViews","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"views","type":"number","optional":true,"predicate":"flags.0","description":"Viewcount of message"},{"name":"forwards","type":"number","optional":true,"predicate":"flags.1","description":"Forward count of message"},{"name":"replies","type":"MessageReplies","optional":true,"predicate":"flags.2","description":"Reply and thread information of message"}],"description":"View, forward counter + info about replies of a specific message"},{"name":"messageReplyHeader","id":2799007587,"type":"MessageReplyHeader","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"replyToMsgId","type":"number","description":"ID of message to which this message is replying"},{"name":"replyToPeerId","type":"Peer","optional":true,"predicate":"flags.0","description":"For replies sent in channel discussion threads of which the current user is not a member, the discussion group ID"},{"name":"replyToTopId","type":"number","optional":true,"predicate":"flags.1","description":"ID of the message that started this message thread"}],"description":"Message replies and thread information"},{"name":"messageReplies","id":1093204652,"type":"MessageReplies","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"comments","type":"true","optional":true,"predicate":"flags.0","description":"Whether this constructor contains information about the comment section of a channel post, or a simple message thread"},{"name":"replies","type":"number","description":"Contains the total number of replies in this thread or comment section."},{"name":"repliesPts","type":"number","description":"PTS of the message that started this thread."},{"name":"recentRepliers","type":"Peer[]","optional":true,"predicate":"flags.1","description":"For channel post comments, contains information about the last few comment posters for a specific thread, to show a small list of commenter profile pictures in client previews."},{"name":"channelId","type":"number","optional":true,"predicate":"flags.0","description":"For channel post comments, contains the ID of the associated discussion supergroup"},{"name":"maxId","type":"number","optional":true,"predicate":"flags.2","description":"ID of the latest message in this thread or comment section."},{"name":"readMaxId","type":"number","optional":true,"predicate":"flags.3","description":"Contains the ID of the latest read message in this thread or comment section."}],"description":"Info about the comment section of a channel post, or a simple message thread"},{"name":"peerBlocked","id":3908927508,"type":"PeerBlocked","arguments":[{"name":"peerId","type":"Peer","description":"Peer ID"},{"name":"date","type":"number","description":"When was the peer blocked"}],"description":"Information about a blocked peer"},{"name":"groupCallDiscarded","id":2004925620,"type":"GroupCall","arguments":[{"name":"id","type":"Long"},{"name":"accessHash","type":"Long"},{"name":"duration","type":"number"}]},{"name":"groupCall","id":3583468812,"type":"GroupCall","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"joinMuted","type":"true","optional":true,"predicate":"flags.1"},{"name":"canChangeJoinMuted","type":"true","optional":true,"predicate":"flags.2"},{"name":"joinDateAsc","type":"true","optional":true,"predicate":"flags.6"},{"name":"scheduleStartSubscribed","type":"true","optional":true,"predicate":"flags.8"},{"name":"canStartVideo","type":"true","optional":true,"predicate":"flags.9"},{"name":"id","type":"Long"},{"name":"accessHash","type":"Long"},{"name":"participantsCount","type":"number"},{"name":"title","type":"string","optional":true,"predicate":"flags.3"},{"name":"streamDcId","type":"number","optional":true,"predicate":"flags.4"},{"name":"recordStartDate","type":"number","optional":true,"predicate":"flags.5"},{"name":"scheduleDate","type":"number","optional":true,"predicate":"flags.7"},{"name":"unmutedVideoCount","type":"number","optional":true,"predicate":"flags.10"},{"name":"unmutedVideoLimit","type":"number"},{"name":"version","type":"number"}]},{"name":"inputGroupCall","id":3635053583,"type":"InputGroupCall","arguments":[{"name":"id","type":"Long"},{"name":"accessHash","type":"Long"}]},{"name":"groupCallParticipant","id":3953538814,"type":"GroupCallParticipant","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"muted","type":"true","optional":true,"predicate":"flags.0"},{"name":"left","type":"true","optional":true,"predicate":"flags.1"},{"name":"canSelfUnmute","type":"true","optional":true,"predicate":"flags.2"},{"name":"justJoined","type":"true","optional":true,"predicate":"flags.4"},{"name":"versioned","type":"true","optional":true,"predicate":"flags.5"},{"name":"min","type":"true","optional":true,"predicate":"flags.8"},{"name":"mutedByYou","type":"true","optional":true,"predicate":"flags.9"},{"name":"volumeByAdmin","type":"true","optional":true,"predicate":"flags.10"},{"name":"self","type":"true","optional":true,"predicate":"flags.12"},{"name":"videoJoined","type":"true","optional":true,"predicate":"flags.15"},{"name":"peer","type":"Peer"},{"name":"date","type":"number"},{"name":"activeDate","type":"number","optional":true,"predicate":"flags.3"},{"name":"source","type":"number"},{"name":"volume","type":"number","optional":true,"predicate":"flags.7"},{"name":"about","type":"string","optional":true,"predicate":"flags.11"},{"name":"raiseHandRating","type":"Long","optional":true,"predicate":"flags.13"},{"name":"video","type":"GroupCallParticipantVideo","optional":true,"predicate":"flags.6"},{"name":"presentation","type":"GroupCallParticipantVideo","optional":true,"predicate":"flags.14"}]},{"name":"inlineQueryPeerTypeSameBotPM","id":813821341,"type":"InlineQueryPeerType","arguments":[]},{"name":"inlineQueryPeerTypePM","id":2201751468,"type":"InlineQueryPeerType","arguments":[]},{"name":"inlineQueryPeerTypeChat","id":3613836554,"type":"InlineQueryPeerType","arguments":[]},{"name":"inlineQueryPeerTypeMegagroup","id":1589952067,"type":"InlineQueryPeerType","arguments":[]},{"name":"inlineQueryPeerTypeBroadcast","id":1664413338,"type":"InlineQueryPeerType","arguments":[]},{"name":"chatInviteImporter","id":507405952,"type":"ChatInviteImporter","arguments":[{"name":"userId","type":"number"},{"name":"date","type":"number"}]},{"name":"chatAdminWithInvites","id":3755094799,"type":"ChatAdminWithInvites","arguments":[{"name":"adminId","type":"number"},{"name":"invitesCount","type":"number"},{"name":"revokedInvitesCount","type":"number"}]},{"name":"groupCallParticipantVideoSourceGroup","id":3702593719,"type":"GroupCallParticipantVideoSourceGroup","arguments":[{"name":"semantics","type":"string"},{"name":"sources","type":"number[]"}]},{"name":"groupCallParticipantVideo","id":1735736008,"type":"GroupCallParticipantVideo","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"paused","type":"true","optional":true,"predicate":"flags.0"},{"name":"endpoint","type":"string"},{"name":"sourceGroups","type":"GroupCallParticipantVideoSourceGroup[]"},{"name":"audioSource","type":"number","optional":true,"predicate":"flags.1"}]},{"name":"botCommandScopeDefault","id":795652779,"type":"BotCommandScope","arguments":[]},{"name":"botCommandScopeUsers","id":1011811544,"type":"BotCommandScope","arguments":[]},{"name":"botCommandScopeChats","id":1877059713,"type":"BotCommandScope","arguments":[]},{"name":"botCommandScopeChatAdmins","id":3114950762,"type":"BotCommandScope","arguments":[]},{"name":"botCommandScopePeer","id":3684534653,"type":"BotCommandScope","arguments":[{"name":"peer","type":"InputPeer"}]},{"name":"botCommandScopePeerAdmins","id":1071145937,"type":"BotCommandScope","arguments":[{"name":"peer","type":"InputPeer"}]},{"name":"botCommandScopePeerUser","id":169026035,"type":"BotCommandScope","arguments":[{"name":"peer","type":"InputPeer"},{"name":"userId","type":"InputUser"}]}],"methods":[{"name":"invokeAfterMsg","id":3416209197,"returns":"X","arguments":[{"name":"msgId","type":"Long","description":"Message identifier on which a current query depends"},{"name":"query","type":"X","description":"The query itself"}],"generics":[{"name":"X","super":"any"}],"description":"Invokes a query after successfull completion of one of the previous queries.","available":"both"},{"name":"invokeAfterMsgs","id":1036301552,"returns":"X","arguments":[{"name":"msgIds","type":"Long[]","description":"List of messages on which a current query depends"},{"name":"query","type":"X","description":"The query itself"}],"generics":[{"name":"X","super":"any"}],"description":"Invokes a query after a successfull completion of previous queries","available":"both"},{"name":"initConnection","id":3251461801,"returns":"X","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"apiId","type":"number","description":"Application identifier (see. App configuration)"},{"name":"deviceModel","type":"string","description":"Device model"},{"name":"systemVersion","type":"string","description":"Operation system version"},{"name":"appVersion","type":"string","description":"Application version"},{"name":"systemLangCode","type":"string","description":"Code for the language used on the device's OS, ISO 639-1 standard"},{"name":"langPack","type":"string","description":"Language pack to use"},{"name":"langCode","type":"string","description":"Code for the language used on the client, ISO 639-1 standard"},{"name":"proxy","type":"InputClientProxy","optional":true,"predicate":"flags.0","description":"Info about an MTProto proxy"},{"name":"params","type":"JSONValue","optional":true,"predicate":"flags.1","description":"Additional initConnection parameters.
For now, only the tz_offset field is supported, for specifying timezone offset in seconds."},{"name":"query","type":"X","description":"The query itself"}],"generics":[{"name":"X","super":"any"}],"description":"Initialize connection","throws":[{"code":400,"name":"CONNECTION_LAYER_INVALID","description":"Layer invalid"}],"available":"both"},{"name":"invokeWithLayer","id":3667594509,"returns":"X","arguments":[{"name":"layer","type":"number","description":"The layer to use"},{"name":"query","type":"X","description":"The query"}],"generics":[{"name":"X","super":"any"}],"description":"Invoke the specified query using the specified API layer","throws":[{"code":400,"name":"AUTH_BYTES_INVALID","description":"The provided authorization is invalid"},{"code":400,"name":"CDN_METHOD_INVALID","description":"You can't call this method in a CDN DC"},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"},{"code":400,"name":"CONNECTION_API_ID_INVALID","description":"The provided API id is invalid"},{"code":400,"name":"CONNECTION_DEVICE_MODEL_EMPTY","description":"Device model empty"},{"code":400,"name":"CONNECTION_LANG_PACK_INVALID","description":"Language pack invalid"},{"code":400,"name":"CONNECTION_NOT_INITED","description":"Connection not initialized"},{"code":400,"name":"CONNECTION_SYSTEM_EMPTY","description":"Connection system empty"},{"code":400,"name":"INPUT_LAYER_INVALID","description":"The provided layer is invalid"},{"code":400,"name":"INVITE_HASH_EXPIRED","description":"The invite link has expired"}],"available":"both"},{"name":"invokeWithoutUpdates","id":3214170551,"returns":"X","arguments":[{"name":"query","type":"X","description":"The query"}],"generics":[{"name":"X","super":"any"}],"description":"Invoke a request without subscribing the used connection for updates (this is enabled by default for file queries).","available":"both"},{"name":"invokeWithMessagesRange","id":911373810,"returns":"X","arguments":[{"name":"range","type":"MessageRange","description":"Message range"},{"name":"query","type":"X","description":"Query"}],"generics":[{"name":"X","super":"any"}],"description":"Invoke with the given message range","available":"both"},{"name":"invokeWithTakeout","id":2896821550,"returns":"X","arguments":[{"name":"takeoutId","type":"Long","description":"Takeout session ID"},{"name":"query","type":"X","description":"Query"}],"generics":[{"name":"X","super":"any"}],"description":"Invoke a method within a takeout session","throws":[{"code":400,"name":"INPUT_METHOD_INVALID_1400137063_X","description":"Invalid method"}],"available":"both"}],"unions":[{"type":"Update","subtypes":["updateMessageReactions","updateNewMessage","updateMessageID","updateDeleteMessages","updateUserTyping","updateChatUserTyping","updateChatParticipants","updateUserStatus","updateUserName","updateUserPhoto","updateNewEncryptedMessage","updateEncryptedChatTyping","updateEncryption","updateEncryptedMessagesRead","updateChatParticipantAdd","updateChatParticipantDelete","updateDcOptions","updateNotifySettings","updateServiceNotification","updatePrivacy","updateUserPhone","updateReadHistoryInbox","updateReadHistoryOutbox","updateWebPage","updateReadMessagesContents","updateChannelTooLong","updateChannel","updateNewChannelMessage","updateReadChannelInbox","updateDeleteChannelMessages","updateChannelMessageViews","updateChatParticipantAdmin","updateNewStickerSet","updateStickerSetsOrder","updateStickerSets","updateSavedGifs","updateBotInlineQuery","updateBotInlineSend","updateEditChannelMessage","updateBotCallbackQuery","updateEditMessage","updateInlineBotCallbackQuery","updateReadChannelOutbox","updateDraftMessage","updateReadFeaturedStickers","updateRecentStickers","updateConfig","updatePtsChanged","updateChannelWebPage","updateDialogPinned","updatePinnedDialogs","updateBotWebhookJSON","updateBotWebhookJSONQuery","updateBotShippingQuery","updateBotPrecheckoutQuery","updatePhoneCall","updateLangPackTooLong","updateLangPack","updateFavedStickers","updateChannelReadMessagesContents","updateContactsReset","updateChannelAvailableMessages","updateDialogUnreadMark","updateMessagePoll","updateChatDefaultBannedRights","updateFolderPeers","updatePeerSettings","updatePeerLocated","updateNewScheduledMessage","updateDeleteScheduledMessages","updateTheme","updateGeoLiveViewed","updateLoginToken","updateMessagePollVote","updateDialogFilter","updateDialogFilterOrder","updateDialogFilters","updatePhoneCallSignalingData","updateChannelMessageForwards","updateReadChannelDiscussionInbox","updateReadChannelDiscussionOutbox","updatePeerBlocked","updateChannelUserTyping","updatePinnedMessages","updatePinnedChannelMessages","updateChat","updateGroupCallParticipants","updateGroupCall","updatePeerHistoryTTL","updateChatParticipant","updateChannelParticipant","updateBotStopped","updateGroupCallConnection","updateBotCommands"],"description":"Object contains info on events occured."},{"type":"MessageReactions","subtypes":["messageReactions"],"description":"Message reactions"},{"type":"ReactionCount","subtypes":["reactionCount"],"description":"Number of users that reacted with a certain emoji"},{"type":"MessageReactionsList","subtypes":["messageReactionsList"],"description":"List of message reactions"},{"type":"MessageUserReaction","subtypes":["messageUserReaction"],"description":"Message reaction"},{"type":"Error","subtypes":["error"],"description":"An object containing a query error."},{"type":"IpPort","subtypes":["ipPort","ipPortSecret"]},{"type":"AccessPointRule","subtypes":["accessPointRule"]},{"type":"InputFileLocation","subtypes":["inputPeerPhotoFileLocationLegacy","inputStickerSetThumbLegacy","inputFileLocation","inputEncryptedFileLocation","inputDocumentFileLocation","inputSecureFileLocation","inputTakeoutFileLocation","inputPhotoFileLocation","inputPhotoLegacyFileLocation","inputPeerPhotoFileLocation","inputStickerSetThumb","inputGroupCallStream"],"description":"Defines the location of a file for download."},{"type":"InputPeer","subtypes":["inputPeerEmpty","inputPeerSelf","inputPeerChat","inputPeerUser","inputPeerChannel","inputPeerUserFromMessage","inputPeerChannelFromMessage"],"description":"Peer"},{"type":"InputUser","subtypes":["inputUserEmpty","inputUserSelf","inputUser","inputUserFromMessage"],"description":"Defines a user for subsequent interaction."},{"type":"InputContact","subtypes":["inputPhoneContact"],"description":"Object defines a contact from the user's phonebook."},{"type":"InputFile","subtypes":["inputFile","inputFileBig"],"description":"Defines a file uploaded by the client."},{"type":"InputMedia","subtypes":["inputMediaEmpty","inputMediaUploadedPhoto","inputMediaPhoto","inputMediaGeoPoint","inputMediaContact","inputMediaUploadedDocument","inputMediaDocument","inputMediaVenue","inputMediaPhotoExternal","inputMediaDocumentExternal","inputMediaGame","inputMediaInvoice","inputMediaGeoLive","inputMediaPoll","inputMediaDice"],"description":"Defines media content of a message."},{"type":"InputChatPhoto","subtypes":["inputChatPhotoEmpty","inputChatUploadedPhoto","inputChatPhoto"],"description":"Defines a new group profile photo."},{"type":"InputGeoPoint","subtypes":["inputGeoPointEmpty","inputGeoPoint"],"description":"Defines a GeoPoint."},{"type":"InputPhoto","subtypes":["inputPhotoEmpty","inputPhoto"],"description":"Defines a photo for further interaction."},{"type":"Peer","subtypes":["peerUser","peerChat","peerChannel"],"description":"Chat partner or group."},{"type":"User","subtypes":["userEmpty","user"],"description":"Object defines a user."},{"type":"UserProfilePhoto","subtypes":["userProfilePhotoEmpty","userProfilePhoto"],"description":"Object contains info on the user's profile photo."},{"type":"UserStatus","subtypes":["userStatusEmpty","userStatusOnline","userStatusOffline","userStatusRecently","userStatusLastWeek","userStatusLastMonth"],"description":"User online status"},{"type":"Chat","subtypes":["chatEmpty","chat","chatForbidden","channel","channelForbidden"],"description":"Object defines a group."},{"type":"ChatFull","subtypes":["chatFull","channelFull"],"description":"Object containing detailed group info"},{"type":"ChatParticipant","subtypes":["chatParticipant","chatParticipantCreator","chatParticipantAdmin"],"description":"Details of a group member."},{"type":"ChatParticipants","subtypes":["chatParticipantsForbidden","chatParticipants"],"description":"Object contains info on group members."},{"type":"ChatPhoto","subtypes":["chatPhotoEmpty","chatPhoto"],"description":"Object defines a group profile photo."},{"type":"Message","subtypes":["messageEmpty","message","messageService"],"description":"Object describing a message."},{"type":"MessageMedia","subtypes":["messageMediaEmpty","messageMediaPhoto","messageMediaGeo","messageMediaContact","messageMediaUnsupported","messageMediaDocument","messageMediaWebPage","messageMediaVenue","messageMediaGame","messageMediaInvoice","messageMediaGeoLive","messageMediaPoll","messageMediaDice"],"description":"Media"},{"type":"MessageAction","subtypes":["messageActionEmpty","messageActionChatCreate","messageActionChatEditTitle","messageActionChatEditPhoto","messageActionChatDeletePhoto","messageActionChatAddUser","messageActionChatDeleteUser","messageActionChatJoinedByLink","messageActionChannelCreate","messageActionChatMigrateTo","messageActionChannelMigrateFrom","messageActionPinMessage","messageActionHistoryClear","messageActionGameScore","messageActionPaymentSentMe","messageActionPaymentSent","messageActionPhoneCall","messageActionScreenshotTaken","messageActionCustomAction","messageActionBotAllowed","messageActionSecureValuesSentMe","messageActionSecureValuesSent","messageActionContactSignUp","messageActionGeoProximityReached","messageActionGroupCall","messageActionInviteToGroupCall","messageActionSetMessagesTTL","messageActionGroupCallScheduled"],"description":"Object describing actions connected to a service message."},{"type":"Dialog","subtypes":["dialog","dialogFolder"],"description":"Chat info."},{"type":"Photo","subtypes":["photoEmpty","photo"],"description":"Object describes a photo."},{"type":"PhotoSize","subtypes":["photoSizeEmpty","photoSize","photoCachedSize","photoStrippedSize","photoSizeProgressive","photoPathSize"],"description":"Location of a certain size of a picture"},{"type":"GeoPoint","subtypes":["geoPointEmpty","geoPoint"],"description":"Object defines a GeoPoint."},{"type":"InputNotifyPeer","subtypes":["inputNotifyPeer","inputNotifyUsers","inputNotifyChats","inputNotifyBroadcasts"],"description":"Object defines the set of users and/or groups that generate notifications."},{"type":"InputPeerNotifySettings","subtypes":["inputPeerNotifySettings"],"description":"Notifications settings."},{"type":"PeerNotifySettings","subtypes":["peerNotifySettings"],"description":"Notification settings."},{"type":"PeerSettings","subtypes":["peerSettings"],"description":"Peer settings"},{"type":"WallPaper","subtypes":["wallPaper","wallPaperNoFile"],"description":"Object contains info on a wallpaper."},{"type":"ReportReason","subtypes":["inputReportReasonSpam","inputReportReasonViolence","inputReportReasonPornography","inputReportReasonChildAbuse","inputReportReasonOther","inputReportReasonCopyright","inputReportReasonGeoIrrelevant","inputReportReasonFake"],"description":"Report reason"},{"type":"UserFull","subtypes":["userFull"],"description":"Object contains extended user info."},{"type":"Contact","subtypes":["contact"],"description":"A contact of the current user."},{"type":"ImportedContact","subtypes":["importedContact"],"description":"Object contains info on a successfully imported contact."},{"type":"ContactStatus","subtypes":["contactStatus"],"description":"Contact status: online / offline."},{"type":"MessagesFilter","subtypes":["inputMessagesFilterEmpty","inputMessagesFilterPhotos","inputMessagesFilterVideo","inputMessagesFilterPhotoVideo","inputMessagesFilterDocument","inputMessagesFilterUrl","inputMessagesFilterGif","inputMessagesFilterVoice","inputMessagesFilterMusic","inputMessagesFilterChatPhotos","inputMessagesFilterPhoneCalls","inputMessagesFilterRoundVoice","inputMessagesFilterRoundVideo","inputMessagesFilterMyMentions","inputMessagesFilterGeo","inputMessagesFilterContacts","inputMessagesFilterPinned"],"description":"Object describes message filter."},{"type":"Updates","subtypes":["updatesTooLong","updateShortMessage","updateShortChatMessage","updateShort","updatesCombined","updates","updateShortSentMessage"],"description":"Object which is perceived by the client without a call on its part when an event occurs."},{"type":"DcOption","subtypes":["dcOption"],"description":"Information for connection to data centre."},{"type":"Config","subtypes":["config"],"description":"Object contains info on API configuring parameters."},{"type":"NearestDc","subtypes":["nearestDc"],"description":"Object contains info on nearest data centre."},{"type":"EncryptedChat","subtypes":["encryptedChatEmpty","encryptedChatWaiting","encryptedChatRequested","encryptedChat","encryptedChatDiscarded"],"description":"Object contains info on an encrypted chat."},{"type":"InputEncryptedChat","subtypes":["inputEncryptedChat"],"description":"Object sets an encrypted chat ID."},{"type":"EncryptedFile","subtypes":["encryptedFileEmpty","encryptedFile"],"description":"Seta an encrypted file."},{"type":"InputEncryptedFile","subtypes":["inputEncryptedFileEmpty","inputEncryptedFileUploaded","inputEncryptedFile","inputEncryptedFileBigUploaded"],"description":"Object sets encrypted file for attachment"},{"type":"EncryptedMessage","subtypes":["encryptedMessage","encryptedMessageService"],"description":"Object contains encrypted message."},{"type":"InputDocument","subtypes":["inputDocumentEmpty","inputDocument"],"description":"Defines a document for subsequent interaction."},{"type":"Document","subtypes":["documentEmpty","document"],"description":"A document."},{"type":"NotifyPeer","subtypes":["notifyPeer","notifyUsers","notifyChats","notifyBroadcasts"],"description":"Object defines the set of users and/or groups that generate notifications."},{"type":"SendMessageAction","subtypes":["sendMessageTypingAction","sendMessageCancelAction","sendMessageRecordVideoAction","sendMessageUploadVideoAction","sendMessageRecordAudioAction","sendMessageUploadAudioAction","sendMessageUploadPhotoAction","sendMessageUploadDocumentAction","sendMessageGeoLocationAction","sendMessageChooseContactAction","sendMessageGamePlayAction","sendMessageRecordRoundAction","sendMessageUploadRoundAction","speakingInGroupCallAction","sendMessageHistoryImportAction"],"description":"User actions. Use this to provide users with detailed info about their chat partners' actions: typing or sending attachments of all kinds."},{"type":"InputPrivacyKey","subtypes":["inputPrivacyKeyStatusTimestamp","inputPrivacyKeyChatInvite","inputPrivacyKeyPhoneCall","inputPrivacyKeyPhoneP2P","inputPrivacyKeyForwards","inputPrivacyKeyProfilePhoto","inputPrivacyKeyPhoneNumber","inputPrivacyKeyAddedByPhone"],"description":"Privacy key"},{"type":"PrivacyKey","subtypes":["privacyKeyStatusTimestamp","privacyKeyChatInvite","privacyKeyPhoneCall","privacyKeyPhoneP2P","privacyKeyForwards","privacyKeyProfilePhoto","privacyKeyPhoneNumber","privacyKeyAddedByPhone"],"description":"Privacy key"},{"type":"InputPrivacyRule","subtypes":["inputPrivacyValueAllowContacts","inputPrivacyValueAllowAll","inputPrivacyValueAllowUsers","inputPrivacyValueDisallowContacts","inputPrivacyValueDisallowAll","inputPrivacyValueDisallowUsers","inputPrivacyValueAllowChatParticipants","inputPrivacyValueDisallowChatParticipants"],"description":"Privacy rule"},{"type":"PrivacyRule","subtypes":["privacyValueAllowContacts","privacyValueAllowAll","privacyValueAllowUsers","privacyValueDisallowContacts","privacyValueDisallowAll","privacyValueDisallowUsers","privacyValueAllowChatParticipants","privacyValueDisallowChatParticipants"],"description":"Privacy rule"},{"type":"AccountDaysTTL","subtypes":["accountDaysTTL"],"description":"Time-to-live of current account"},{"type":"DocumentAttribute","subtypes":["documentAttributeImageSize","documentAttributeAnimated","documentAttributeSticker","documentAttributeVideo","documentAttributeAudio","documentAttributeFilename","documentAttributeHasStickers"],"description":"Various possible attributes of a document (used to define if it's a sticker, a GIF, a video, a mask sticker, an image, an audio, and so on)"},{"type":"StickerPack","subtypes":["stickerPack"],"description":"Stickerpack"},{"type":"WebPage","subtypes":["webPageEmpty","webPagePending","webPage","webPageNotModified"],"description":"Instant View webpage preview"},{"type":"Authorization","subtypes":["authorization"],"description":"Represents a logged-in session"},{"type":"ReceivedNotifyMessage","subtypes":["receivedNotifyMessage"],"description":"Confirmation of message receipt"},{"type":"ExportedChatInvite","subtypes":["chatInviteExported"],"description":"Exported chat invite"},{"type":"ChatInvite","subtypes":["chatInviteAlready","chatInvite","chatInvitePeek"],"description":"Chat invite"},{"type":"InputStickerSet","subtypes":["inputStickerSetEmpty","inputStickerSetID","inputStickerSetShortName","inputStickerSetAnimatedEmoji","inputStickerSetDice"],"description":"Represents a stickerset"},{"type":"StickerSet","subtypes":["stickerSet"],"description":"Represents a stickerset (stickerpack)"},{"type":"BotCommand","subtypes":["botCommand"],"description":"Describes a bot command that can be used in a chat"},{"type":"BotInfo","subtypes":["botInfo"],"description":"Info about bots (available bot commands, etc)"},{"type":"KeyboardButton","subtypes":["keyboardButton","keyboardButtonUrl","keyboardButtonCallback","keyboardButtonRequestPhone","keyboardButtonRequestGeoLocation","keyboardButtonSwitchInline","keyboardButtonGame","keyboardButtonBuy","keyboardButtonUrlAuth","inputKeyboardButtonUrlAuth","keyboardButtonRequestPoll"],"description":"Bot or inline keyboard buttons"},{"type":"KeyboardButtonRow","subtypes":["keyboardButtonRow"],"description":"Bot or inline keyboard rows"},{"type":"ReplyMarkup","subtypes":["replyKeyboardHide","replyKeyboardForceReply","replyKeyboardMarkup","replyInlineMarkup"],"description":"Reply markup for bot and inline keyboards"},{"type":"MessageEntity","subtypes":["messageEntityUnknown","messageEntityMention","messageEntityHashtag","messageEntityBotCommand","messageEntityUrl","messageEntityEmail","messageEntityBold","messageEntityItalic","messageEntityCode","messageEntityPre","messageEntityTextUrl","messageEntityMentionName","inputMessageEntityMentionName","messageEntityPhone","messageEntityCashtag","messageEntityUnderline","messageEntityStrike","messageEntityBlockquote","messageEntityBankCard"],"description":"Message entities, representing styled text in a message"},{"type":"InputChannel","subtypes":["inputChannelEmpty","inputChannel","inputChannelFromMessage"],"description":"Represents a channel"},{"type":"MessageRange","subtypes":["messageRange"],"description":"Indicates a range of chat messages"},{"type":"ChannelMessagesFilter","subtypes":["channelMessagesFilterEmpty","channelMessagesFilter"],"description":"Filter for fetching only certain types of channel messages"},{"type":"ChannelParticipant","subtypes":["channelParticipant","channelParticipantSelf","channelParticipantCreator","channelParticipantAdmin","channelParticipantBanned","channelParticipantLeft"],"description":"Channel participant"},{"type":"ChannelParticipantsFilter","subtypes":["channelParticipantsRecent","channelParticipantsAdmins","channelParticipantsKicked","channelParticipantsBots","channelParticipantsBanned","channelParticipantsSearch","channelParticipantsContacts","channelParticipantsMentions"],"description":"Filter for fetching channel participants"},{"type":"InputBotInlineMessage","subtypes":["inputBotInlineMessageMediaAuto","inputBotInlineMessageText","inputBotInlineMessageMediaGeo","inputBotInlineMessageMediaVenue","inputBotInlineMessageMediaContact","inputBotInlineMessageGame","inputBotInlineMessageMediaInvoice"],"description":"Represents a sent inline message from the perspective of a bot"},{"type":"InputBotInlineResult","subtypes":["inputBotInlineResult","inputBotInlineResultPhoto","inputBotInlineResultDocument","inputBotInlineResultGame"],"description":"Inline bot result"},{"type":"BotInlineMessage","subtypes":["botInlineMessageMediaAuto","botInlineMessageText","botInlineMessageMediaGeo","botInlineMessageMediaVenue","botInlineMessageMediaContact","botInlineMessageMediaInvoice"],"description":"Inline message"},{"type":"BotInlineResult","subtypes":["botInlineResult","botInlineMediaResult"],"description":"Results of an inline query"},{"type":"ExportedMessageLink","subtypes":["exportedMessageLink"],"description":"HTTP link and embed info of channel message"},{"type":"MessageFwdHeader","subtypes":["messageFwdHeader"],"description":"Info about a forwarded message"},{"type":"InputBotInlineMessageID","subtypes":["inputBotInlineMessageID"],"description":"Represents a sent inline message from the perspective of a bot"},{"type":"InlineBotSwitchPM","subtypes":["inlineBotSwitchPM"],"description":"The bot requested the user to message them in private"},{"type":"TopPeer","subtypes":["topPeer"],"description":"Top peer"},{"type":"TopPeerCategory","subtypes":["topPeerCategoryBotsPM","topPeerCategoryBotsInline","topPeerCategoryCorrespondents","topPeerCategoryGroups","topPeerCategoryChannels","topPeerCategoryPhoneCalls","topPeerCategoryForwardUsers","topPeerCategoryForwardChats"],"description":"Top peer category"},{"type":"TopPeerCategoryPeers","subtypes":["topPeerCategoryPeers"],"description":"Top peers by top peer category"},{"type":"DraftMessage","subtypes":["draftMessageEmpty","draftMessage"],"description":"Represents a message draft."},{"type":"StickerSetCovered","subtypes":["stickerSetCovered","stickerSetMultiCovered"],"description":"Stickerset, with a specific sticker as preview"},{"type":"MaskCoords","subtypes":["maskCoords"],"description":"Mask coordinates (if this is a mask sticker, attached to a photo)"},{"type":"InputStickeredMedia","subtypes":["inputStickeredMediaPhoto","inputStickeredMediaDocument"],"description":"Represents a media with attached stickers"},{"type":"Game","subtypes":["game"],"description":"Indicates an already sent game"},{"type":"InputGame","subtypes":["inputGameID","inputGameShortName"],"description":"A game to send"},{"type":"HighScore","subtypes":["highScore"],"description":"Game high score"},{"type":"RichText","subtypes":["textEmpty","textPlain","textBold","textItalic","textUnderline","textStrike","textFixed","textUrl","textEmail","textConcat","textSubscript","textSuperscript","textMarked","textPhone","textImage","textAnchor"],"description":"Rich text"},{"type":"PageBlock","subtypes":["pageBlockUnsupported","pageBlockTitle","pageBlockSubtitle","pageBlockAuthorDate","pageBlockHeader","pageBlockSubheader","pageBlockParagraph","pageBlockPreformatted","pageBlockFooter","pageBlockDivider","pageBlockAnchor","pageBlockList","pageBlockBlockquote","pageBlockPullquote","pageBlockPhoto","pageBlockVideo","pageBlockCover","pageBlockEmbed","pageBlockEmbedPost","pageBlockCollage","pageBlockSlideshow","pageBlockChannel","pageBlockAudio","pageBlockKicker","pageBlockTable","pageBlockOrderedList","pageBlockDetails","pageBlockRelatedArticles","pageBlockMap"],"description":"Represents an instant view page element"},{"type":"PhoneCallDiscardReason","subtypes":["phoneCallDiscardReasonMissed","phoneCallDiscardReasonDisconnect","phoneCallDiscardReasonHangup","phoneCallDiscardReasonBusy"],"description":"Why was the phone call discarded?"},{"type":"DataJSON","subtypes":["dataJSON"],"description":"Represent a JSON-encoded object"},{"type":"LabeledPrice","subtypes":["labeledPrice"],"description":"Labeled pricetag"},{"type":"Invoice","subtypes":["invoice"],"description":"Invoice"},{"type":"PaymentCharge","subtypes":["paymentCharge"],"description":"Charged payment"},{"type":"PostAddress","subtypes":["postAddress"],"description":"Shipping address"},{"type":"PaymentRequestedInfo","subtypes":["paymentRequestedInfo"],"description":"Requested payment info"},{"type":"PaymentSavedCredentials","subtypes":["paymentSavedCredentialsCard"],"description":"Saved payment credentials"},{"type":"WebDocument","subtypes":["webDocument","webDocumentNoProxy"],"description":"Remote document"},{"type":"InputWebDocument","subtypes":["inputWebDocument"],"description":"Specifies a document that will have to be downloaded from the URL by the telegram servers"},{"type":"InputWebFileLocation","subtypes":["inputWebFileLocation","inputWebFileGeoPointLocation"],"description":"Location of remote file"},{"type":"InputPaymentCredentials","subtypes":["inputPaymentCredentialsSaved","inputPaymentCredentials","inputPaymentCredentialsApplePay","inputPaymentCredentialsGooglePay"],"description":"Payment credentials"},{"type":"ShippingOption","subtypes":["shippingOption"],"description":"Shipping options"},{"type":"InputStickerSetItem","subtypes":["inputStickerSetItem"],"description":"Sticker"},{"type":"InputPhoneCall","subtypes":["inputPhoneCall"],"description":"Phone call"},{"type":"PhoneCall","subtypes":["phoneCallEmpty","phoneCallWaiting","phoneCallRequested","phoneCallAccepted","phoneCall","phoneCallDiscarded"],"description":"Phone call"},{"type":"PhoneConnection","subtypes":["phoneConnection","phoneConnectionWebrtc"],"description":"Phone call connection"},{"type":"PhoneCallProtocol","subtypes":["phoneCallProtocol"],"description":"Phone call protocol"},{"type":"CdnPublicKey","subtypes":["cdnPublicKey"],"description":"Public key to use only during handshakes to CDN DCs."},{"type":"CdnConfig","subtypes":["cdnConfig"],"description":"Configuration for CDN file downloads."},{"type":"LangPackString","subtypes":["langPackString","langPackStringPluralized","langPackStringDeleted"],"description":"Language pack string"},{"type":"LangPackDifference","subtypes":["langPackDifference"],"description":"Language pack changes"},{"type":"LangPackLanguage","subtypes":["langPackLanguage"],"description":"Language pack language"},{"type":"ChannelAdminLogEventAction","subtypes":["channelAdminLogEventActionChangeTitle","channelAdminLogEventActionChangeAbout","channelAdminLogEventActionChangeUsername","channelAdminLogEventActionChangePhoto","channelAdminLogEventActionToggleInvites","channelAdminLogEventActionToggleSignatures","channelAdminLogEventActionUpdatePinned","channelAdminLogEventActionEditMessage","channelAdminLogEventActionDeleteMessage","channelAdminLogEventActionParticipantJoin","channelAdminLogEventActionParticipantLeave","channelAdminLogEventActionParticipantInvite","channelAdminLogEventActionParticipantToggleBan","channelAdminLogEventActionParticipantToggleAdmin","channelAdminLogEventActionChangeStickerSet","channelAdminLogEventActionTogglePreHistoryHidden","channelAdminLogEventActionDefaultBannedRights","channelAdminLogEventActionStopPoll","channelAdminLogEventActionChangeLinkedChat","channelAdminLogEventActionChangeLocation","channelAdminLogEventActionToggleSlowMode","channelAdminLogEventActionStartGroupCall","channelAdminLogEventActionDiscardGroupCall","channelAdminLogEventActionParticipantMute","channelAdminLogEventActionParticipantUnmute","channelAdminLogEventActionToggleGroupCallSetting","channelAdminLogEventActionParticipantJoinByInvite","channelAdminLogEventActionExportedInviteDelete","channelAdminLogEventActionExportedInviteRevoke","channelAdminLogEventActionExportedInviteEdit","channelAdminLogEventActionParticipantVolume","channelAdminLogEventActionChangeHistoryTTL"],"description":"Channel admin log event"},{"type":"ChannelAdminLogEvent","subtypes":["channelAdminLogEvent"],"description":"An event in a channel admin log"},{"type":"ChannelAdminLogEventsFilter","subtypes":["channelAdminLogEventsFilter"],"description":"Filter for fetching events in the channel admin log"},{"type":"PopularContact","subtypes":["popularContact"],"description":"Popular contact"},{"type":"RecentMeUrl","subtypes":["recentMeUrlUnknown","recentMeUrlUser","recentMeUrlChat","recentMeUrlChatInvite","recentMeUrlStickerSet"],"description":"Recent t.me urls"},{"type":"InputSingleMedia","subtypes":["inputSingleMedia"],"description":"A single media in an album or grouped media sent with {@link messages.sendMultiMedia}."},{"type":"WebAuthorization","subtypes":["webAuthorization"],"description":"Web authorization"},{"type":"InputMessage","subtypes":["inputMessageID","inputMessageReplyTo","inputMessagePinned","inputMessageCallbackQuery"],"description":"A message"},{"type":"InputDialogPeer","subtypes":["inputDialogPeer","inputDialogPeerFolder"],"description":"Peer, or all peers in a certain folder"},{"type":"DialogPeer","subtypes":["dialogPeer","dialogPeerFolder"],"description":"Peer, or all peers in a folder"},{"type":"FileHash","subtypes":["fileHash"],"description":"Hash of an uploaded file, to be checked for validity after download"},{"type":"InputClientProxy","subtypes":["inputClientProxy"],"description":"Info about an MTProxy used to connect."},{"type":"InputSecureFile","subtypes":["inputSecureFileUploaded","inputSecureFile"],"description":"Secure passport file, for more info see the passport docs »"},{"type":"SecureFile","subtypes":["secureFileEmpty","secureFile"],"description":"Secure passport file, for more info see the passport docs »"},{"type":"SecureData","subtypes":["secureData"],"description":"Secure passport data, for more info see the passport docs »"},{"type":"SecurePlainData","subtypes":["securePlainPhone","securePlainEmail"],"description":"Plaintext verified passport data."},{"type":"SecureValueType","subtypes":["secureValueTypePersonalDetails","secureValueTypePassport","secureValueTypeDriverLicense","secureValueTypeIdentityCard","secureValueTypeInternalPassport","secureValueTypeAddress","secureValueTypeUtilityBill","secureValueTypeBankStatement","secureValueTypeRentalAgreement","secureValueTypePassportRegistration","secureValueTypeTemporaryRegistration","secureValueTypePhone","secureValueTypeEmail"],"description":"Secure value type"},{"type":"SecureValue","subtypes":["secureValue"],"description":"Secure tgpassport value"},{"type":"InputSecureValue","subtypes":["inputSecureValue"],"description":"Secure value, for more info see the passport docs »"},{"type":"SecureValueHash","subtypes":["secureValueHash"],"description":"Secure value hash"},{"type":"SecureValueError","subtypes":["secureValueErrorData","secureValueErrorFrontSide","secureValueErrorReverseSide","secureValueErrorSelfie","secureValueErrorFile","secureValueErrorFiles","secureValueError","secureValueErrorTranslationFile","secureValueErrorTranslationFiles"],"description":"Secure value error"},{"type":"SecureCredentialsEncrypted","subtypes":["secureCredentialsEncrypted"],"description":"Encrypted secure credentials"},{"type":"SavedContact","subtypes":["savedPhoneContact"],"description":"Saved contact"},{"type":"PasswordKdfAlgo","subtypes":["passwordKdfAlgoUnknown","passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow"],"description":"Key derivation function to use when generating the password hash for SRP two-factor authorization"},{"type":"SecurePasswordKdfAlgo","subtypes":["securePasswordKdfAlgoUnknown","securePasswordKdfAlgoPBKDF2HMACSHA512iter100000","securePasswordKdfAlgoSHA512"],"description":"KDF algorithm to use for computing telegram passport hash"},{"type":"SecureSecretSettings","subtypes":["secureSecretSettings"],"description":"Telegram passport settings"},{"type":"InputCheckPasswordSRP","subtypes":["inputCheckPasswordEmpty","inputCheckPasswordSRP"],"description":"Constructors for checking the validity of a 2FA SRP password"},{"type":"SecureRequiredType","subtypes":["secureRequiredType","secureRequiredTypeOneOf"],"description":"Required secure file type"},{"type":"InputAppEvent","subtypes":["inputAppEvent"],"description":"Object contains info about an event that occured in the application."},{"type":"JSONObjectValue","subtypes":["jsonObjectValue"],"description":"JSON key: value pair"},{"type":"JSONValue","subtypes":["jsonNull","jsonBool","jsonNumber","jsonString","jsonArray","jsonObject"],"description":"JSON value"},{"type":"PageTableCell","subtypes":["pageTableCell"],"description":"Represents a table in an instant view table"},{"type":"PageTableRow","subtypes":["pageTableRow"],"description":"Table row"},{"type":"PageCaption","subtypes":["pageCaption"],"description":"Page caption"},{"type":"PageListItem","subtypes":["pageListItemText","pageListItemBlocks"],"description":"Item in block list"},{"type":"PageListOrderedItem","subtypes":["pageListOrderedItemText","pageListOrderedItemBlocks"],"description":"Represents an instant view ordered list"},{"type":"PageRelatedArticle","subtypes":["pageRelatedArticle"],"description":"Related articles"},{"type":"Page","subtypes":["page"],"description":"Instant view page"},{"type":"PollAnswer","subtypes":["pollAnswer"],"description":"Indicates a possible answer to a poll."},{"type":"Poll","subtypes":["poll"],"description":"Indicates a poll message"},{"type":"PollAnswerVoters","subtypes":["pollAnswerVoters"],"description":"How users voted on a certain poll answer"},{"type":"PollResults","subtypes":["pollResults"],"description":"Results of poll"},{"type":"ChatOnlines","subtypes":["chatOnlines"],"description":"Number of online users in a chat"},{"type":"StatsURL","subtypes":["statsURL"],"description":"URL with chat statistics"},{"type":"ChatAdminRights","subtypes":["chatAdminRights"],"description":"Represents the rights of an admin in a channel/supergroup."},{"type":"ChatBannedRights","subtypes":["chatBannedRights"],"description":"Represents the rights of a normal user in a supergroup/channel/chat."},{"type":"InputWallPaper","subtypes":["inputWallPaper","inputWallPaperSlug","inputWallPaperNoFile"],"description":"Wallpaper"},{"type":"CodeSettings","subtypes":["codeSettings"],"description":"Settings for the code type to send"},{"type":"WallPaperSettings","subtypes":["wallPaperSettings"],"description":"Wallpaper settings"},{"type":"AutoDownloadSettings","subtypes":["autoDownloadSettings"],"description":"Media autodownload settings"},{"type":"EmojiKeyword","subtypes":["emojiKeyword","emojiKeywordDeleted"],"description":"Emoji keyword"},{"type":"EmojiKeywordsDifference","subtypes":["emojiKeywordsDifference"],"description":"New emoji keywords"},{"type":"EmojiURL","subtypes":["emojiURL"],"description":"Emoji URL"},{"type":"EmojiLanguage","subtypes":["emojiLanguage"],"description":"Emoji language"},{"type":"Folder","subtypes":["folder"],"description":"A folder"},{"type":"InputFolderPeer","subtypes":["inputFolderPeer"],"description":"Peer in a folder"},{"type":"FolderPeer","subtypes":["folderPeer"],"description":"Peer associated to folder"},{"type":"UrlAuthResult","subtypes":["urlAuthResultRequest","urlAuthResultAccepted","urlAuthResultDefault"],"description":"URL authorization result"},{"type":"ChannelLocation","subtypes":["channelLocationEmpty","channelLocation"],"description":"Geographical location of supergroup (geogroups)"},{"type":"PeerLocated","subtypes":["peerLocated","peerSelfLocated"],"description":"Geolocated peer"},{"type":"RestrictionReason","subtypes":["restrictionReason"],"description":"Restriction reason"},{"type":"InputTheme","subtypes":["inputTheme","inputThemeSlug"],"description":"Cloud theme"},{"type":"Theme","subtypes":["theme"],"description":"Cloud theme"},{"type":"BaseTheme","subtypes":["baseThemeClassic","baseThemeDay","baseThemeNight","baseThemeTinted","baseThemeArctic"],"description":"Basic theme settings"},{"type":"InputThemeSettings","subtypes":["inputThemeSettings"],"description":"Theme settings"},{"type":"ThemeSettings","subtypes":["themeSettings"],"description":"Theme settings"},{"type":"WebPageAttribute","subtypes":["webPageAttributeTheme"],"description":"Webpage attributes"},{"type":"MessageUserVote","subtypes":["messageUserVote","messageUserVoteInputOption","messageUserVoteMultiple"],"description":"How a user voted in a poll"},{"type":"BankCardOpenUrl","subtypes":["bankCardOpenUrl"],"description":"Credit card info URL provided by the bank"},{"type":"DialogFilter","subtypes":["dialogFilter"],"description":"Dialog filter (folders)"},{"type":"DialogFilterSuggested","subtypes":["dialogFilterSuggested"],"description":"Suggested dialog filters (folders)"},{"type":"StatsDateRangeDays","subtypes":["statsDateRangeDays"],"description":"Channel statistics date range"},{"type":"StatsAbsValueAndPrev","subtypes":["statsAbsValueAndPrev"],"description":"Channel statistics value pair"},{"type":"StatsPercentValue","subtypes":["statsPercentValue"],"description":"Channel statistics percentage"},{"type":"StatsGraph","subtypes":["statsGraphAsync","statsGraphError","statsGraph"],"description":"Channel statistics graph"},{"type":"MessageInteractionCounters","subtypes":["messageInteractionCounters"],"description":"Message interaction counters"},{"type":"VideoSize","subtypes":["videoSize"],"description":"Represents an animated video thumbnail"},{"type":"StatsGroupTopPoster","subtypes":["statsGroupTopPoster"],"description":"Most active user in a supergroup"},{"type":"StatsGroupTopAdmin","subtypes":["statsGroupTopAdmin"],"description":"Most active admin in a supergroup"},{"type":"StatsGroupTopInviter","subtypes":["statsGroupTopInviter"],"description":"Most active inviter in a supergroup"},{"type":"GlobalPrivacySettings","subtypes":["globalPrivacySettings"],"description":"Global privacy settings"},{"type":"MessageViews","subtypes":["messageViews"],"description":"View, forward counter + info about replies of a specific message"},{"type":"MessageReplyHeader","subtypes":["messageReplyHeader"],"description":"Reply information"},{"type":"MessageReplies","subtypes":["messageReplies"],"description":"Info about post comments (for channels) or message replies (for groups)"},{"type":"PeerBlocked","subtypes":["peerBlocked"],"description":"Info about a blocked user"},{"type":"GroupCall","subtypes":["groupCallDiscarded","groupCall"]},{"type":"InputGroupCall","subtypes":["inputGroupCall"]},{"type":"GroupCallParticipant","subtypes":["groupCallParticipant"]},{"type":"InlineQueryPeerType","subtypes":["inlineQueryPeerTypeSameBotPM","inlineQueryPeerTypePM","inlineQueryPeerTypeChat","inlineQueryPeerTypeMegagroup","inlineQueryPeerTypeBroadcast"]},{"type":"ChatInviteImporter","subtypes":["chatInviteImporter"]},{"type":"ChatAdminWithInvites","subtypes":["chatAdminWithInvites"]},{"type":"GroupCallParticipantVideoSourceGroup","subtypes":["groupCallParticipantVideoSourceGroup"]},{"type":"GroupCallParticipantVideo","subtypes":["groupCallParticipantVideo"]},{"type":"BotCommandScope","subtypes":["botCommandScopeDefault","botCommandScopeUsers","botCommandScopeChats","botCommandScopeChatAdmins","botCommandScopePeer","botCommandScopePeerAdmins","botCommandScopePeerUser"]}]},"messages":{"classes":[{"name":"dialogs","id":364538944,"type":"messages.Dialogs","arguments":[{"name":"dialogs","type":"Dialog[]","description":"List of chats"},{"name":"messages","type":"Message[]","description":"List of last messages from each chat"},{"name":"chats","type":"Chat[]","description":"List of groups mentioned in the chats"},{"name":"users","type":"User[]","description":"List of users mentioned in messages and groups"}],"description":"Full list of chats with messages and auxiliary data."},{"name":"dialogsSlice","id":1910543603,"type":"messages.Dialogs","arguments":[{"name":"count","type":"number","description":"Total number of dialogs"},{"name":"dialogs","type":"Dialog[]","description":"List of dialogs"},{"name":"messages","type":"Message[]","description":"List of last messages from dialogs"},{"name":"chats","type":"Chat[]","description":"List of chats mentioned in dialogs"},{"name":"users","type":"User[]","description":"List of users mentioned in messages and chats"}],"description":"Incomplete list of dialogs with messages and auxiliary data."},{"name":"dialogsNotModified","id":4041467286,"type":"messages.Dialogs","arguments":[{"name":"count","type":"number","description":"Number of dialogs found server-side by the query"}],"description":"Dialogs haven't changed"},{"name":"messages","id":2356252295,"type":"messages.Messages","arguments":[{"name":"messages","type":"Message[]","description":"List of messages"},{"name":"chats","type":"Chat[]","description":"List of chats mentioned in dialogs"},{"name":"users","type":"User[]","description":"List of users mentioned in messages and chats"}],"description":"Full list of messages with auxilary data."},{"name":"messagesSlice","id":978610270,"type":"messages.Messages","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"inexact","type":"true","optional":true,"predicate":"flags.1","description":"If set, indicates that the results may be inexact"},{"name":"count","type":"number","description":"Total number of messages in the list"},{"name":"nextRate","type":"number","optional":true,"predicate":"flags.0","description":"Rate to use in the offset_rate parameter in the next call to {@link messages.searchGlobal}"},{"name":"offsetIdOffset","type":"number","optional":true,"predicate":"flags.2","description":"Indicates the absolute position of messages[0] within the total result set with count count.
This is useful, for example, if the result was fetched using offset_id, and we need to display a progress/total counter (like photo 134 of 200, for all media in a chat, we could simply use photo ${offset_id_offset} of ${count}."},{"name":"messages","type":"Message[]","description":"List of messages"},{"name":"chats","type":"Chat[]","description":"List of chats mentioned in messages"},{"name":"users","type":"User[]","description":"List of users mentioned in messages and chats"}],"description":"Incomplete list of messages and auxiliary data."},{"name":"channelMessages","id":1682413576,"type":"messages.Messages","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"inexact","type":"true","optional":true,"predicate":"flags.1","description":"If set, returned results may be inexact"},{"name":"pts","type":"number","description":"Event count after generation"},{"name":"count","type":"number","description":"Total number of results were found server-side (may not be all included here)"},{"name":"offsetIdOffset","type":"number","optional":true,"predicate":"flags.2","description":"Indicates the absolute position of messages[0] within the total result set with count count.
This is useful, for example, if the result was fetched using offset_id, and we need to display a progress/total counter (like photo 134 of 200, for all media in a chat, we could simply use photo ${offset_id_offset} of ${count}."},{"name":"messages","type":"Message[]","description":"Found messages"},{"name":"chats","type":"Chat[]","description":"Chats"},{"name":"users","type":"User[]","description":"Users"}],"description":"Channel messages"},{"name":"messagesNotModified","id":1951620897,"type":"messages.Messages","arguments":[{"name":"count","type":"number","description":"Number of results found server-side by the given query"}],"description":"No new messages matching the query were found"},{"name":"chats","id":1694474197,"type":"messages.Chats","arguments":[{"name":"chats","type":"Chat[]","description":"List of chats"}],"description":"List of chats with auxiliary data."},{"name":"chatsSlice","id":2631405892,"type":"messages.Chats","arguments":[{"name":"count","type":"number","description":"Total number of results that were found server-side (not all are included in chats)"},{"name":"chats","type":"Chat[]","description":"Chats"}],"description":"Partial list of chats, more would have to be fetched with pagination"},{"name":"chatFull","id":3856126364,"type":"messages.ChatFull","arguments":[{"name":"fullChat","type":"ChatFull","description":"Extended info on a chat"},{"name":"chats","type":"Chat[]","description":"List containing basic info on chat"},{"name":"users","type":"User[]","description":"List of users mentioned above"}],"description":"Extended info on chat and auxiliary data."},{"name":"affectedHistory","id":3025955281,"type":"messages.AffectedHistory","arguments":[{"name":"pts","type":"number","description":"Number of events occured in a text box"},{"name":"ptsCount","type":"number","description":"Number of affected events"},{"name":"offset","type":"number","description":"If a parameter contains positive value, it is necessary to repeat the method call using the given value; during the proceeding of all the history the value itself shall gradually decrease"}],"description":"Affected part of communication history with the user or in a chat."},{"name":"dhConfigNotModified","id":3236054581,"type":"messages.DhConfig","arguments":[{"name":"random","type":"Buffer","description":"Random sequence of bytes of assigned length"}],"description":"Configuring parameters did not change."},{"name":"dhConfig","id":740433629,"type":"messages.DhConfig","arguments":[{"name":"g","type":"number","description":"New value prime, see Wikipedia"},{"name":"p","type":"Buffer","description":"New value primitive root, see Wikipedia"},{"name":"version","type":"number","description":"Vestion of set of parameters"},{"name":"random","type":"Buffer","description":"Random sequence of bytes of assigned length"}],"description":"New set of configuring parameters."},{"name":"sentEncryptedMessage","id":1443858741,"type":"messages.SentEncryptedMessage","arguments":[{"name":"date","type":"number","description":"Date of sending"}],"description":"Message without file attachemts sent to an encrypted file."},{"name":"sentEncryptedFile","id":2492727090,"type":"messages.SentEncryptedMessage","arguments":[{"name":"date","type":"number","description":"Sending date"},{"name":"file","type":"EncryptedFile","description":"Attached file"}],"description":"Message with a file enclosure sent to a protected chat"},{"name":"stickersNotModified","id":4050950690,"type":"messages.Stickers","arguments":[],"description":"No new stickers were found for the given query"},{"name":"stickers","id":3831077821,"type":"messages.Stickers","arguments":[{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"},{"name":"stickers","type":"Document[]","description":"Stickers"}],"description":"Found stickers"},{"name":"allStickersNotModified","id":3898999491,"type":"messages.AllStickers","arguments":[],"description":"Info about all installed stickers hasn't changed"},{"name":"allStickers","id":3992797279,"type":"messages.AllStickers","arguments":[{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"},{"name":"sets","type":"StickerSet[]","description":"All stickersets"}],"description":"Info about all installed stickers"},{"name":"affectedMessages","id":2228326789,"type":"messages.AffectedMessages","arguments":[{"name":"pts","type":"number","description":"Event count after generation"},{"name":"ptsCount","type":"number","description":"Number of events that were generated"}],"description":"Events affected by operation"},{"name":"stickerSet","id":3054118054,"type":"messages.StickerSet","arguments":[{"name":"set","type":"StickerSet","description":"The stickerset"},{"name":"packs","type":"StickerPack[]","description":"Emoji info for stickers"},{"name":"documents","type":"Document[]","description":"Stickers in stickerset"}],"description":"Stickerset and stickers inside it"},{"name":"savedGifsNotModified","id":3892468898,"type":"messages.SavedGifs","arguments":[],"description":"No new saved gifs were found"},{"name":"savedGifs","id":772213157,"type":"messages.SavedGifs","arguments":[{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"},{"name":"gifs","type":"Document[]","description":"List of saved gifs"}],"description":"Saved gifs"},{"name":"botResults","id":2491197512,"type":"messages.BotResults","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"gallery","type":"true","optional":true,"predicate":"flags.0","description":"Whether the result is a picture gallery"},{"name":"queryId","type":"Long","description":"Query ID"},{"name":"nextOffset","type":"string","optional":true,"predicate":"flags.1","description":"The next offset to use when navigating through results"},{"name":"switchPm","type":"InlineBotSwitchPM","optional":true,"predicate":"flags.2","description":"Whether the bot requested the user to message them in private"},{"name":"results","type":"BotInlineResult[]","description":"The results"},{"name":"cacheTime","type":"number","description":"Caching validity of the results"},{"name":"users","type":"User[]","description":"Users mentioned in the results"}],"description":"Result of a query to an inline bot"},{"name":"botCallbackAnswer","id":911761060,"type":"messages.BotCallbackAnswer","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"alert","type":"true","optional":true,"predicate":"flags.1","description":"Whether an alert should be shown to the user instead of a toast notification"},{"name":"hasUrl","type":"true","optional":true,"predicate":"flags.3","description":"Whether an URL is present"},{"name":"nativeUi","type":"true","optional":true,"predicate":"flags.4","description":"Whether to show games in WebView or in native UI."},{"name":"message","type":"string","optional":true,"predicate":"flags.0","description":"Alert to show"},{"name":"url","type":"string","optional":true,"predicate":"flags.2","description":"URL to open"},{"name":"cacheTime","type":"number","description":"For how long should this answer be cached"}],"description":"Callback answer sent by the bot in response to a button press"},{"name":"messageEditData","id":649453030,"type":"messages.MessageEditData","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"caption","type":"true","optional":true,"predicate":"flags.0","description":"Media caption, if the specified media's caption can be edited"}],"description":"Message edit data for media"},{"name":"peerDialogs","id":863093588,"type":"messages.PeerDialogs","arguments":[{"name":"dialogs","type":"Dialog[]","description":"Dialog info"},{"name":"messages","type":"Message[]","description":"Messages mentioned in dialog info"},{"name":"chats","type":"Chat[]","description":"Chats"},{"name":"users","type":"User[]","description":"Users"},{"name":"state","type":"updates.State","description":"Current update state of dialog"}],"description":"Dialog info of multiple peers"},{"name":"featuredStickersNotModified","id":3336309862,"type":"messages.FeaturedStickers","arguments":[{"name":"count","type":"number","description":"Total number of featured stickers"}],"description":"Featured stickers haven't changed"},{"name":"featuredStickers","id":3064709953,"type":"messages.FeaturedStickers","arguments":[{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"},{"name":"count","type":"number","description":"Total number of featured stickers"},{"name":"sets","type":"StickerSetCovered[]","description":"Featured stickersets"},{"name":"unread","type":"Long[]","description":"IDs of new featured stickersets"}],"description":"Featured stickersets"},{"name":"recentStickersNotModified","id":186120336,"type":"messages.RecentStickers","arguments":[],"description":"No new recent sticker was found"},{"name":"recentStickers","id":586395571,"type":"messages.RecentStickers","arguments":[{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"},{"name":"packs","type":"StickerPack[]","description":"Emojis associated to stickers"},{"name":"stickers","type":"Document[]","description":"Recent stickers"},{"name":"dates","type":"number[]","description":"When was each sticker last used"}],"description":"Recently used stickers"},{"name":"archivedStickers","id":1338747336,"type":"messages.ArchivedStickers","arguments":[{"name":"count","type":"number","description":"Number of archived stickers"},{"name":"sets","type":"StickerSetCovered[]","description":"Archived stickersets"}],"description":"Archived stickersets"},{"name":"stickerSetInstallResultSuccess","id":946083368,"type":"messages.StickerSetInstallResult","arguments":[],"description":"The stickerset was installed successfully"},{"name":"stickerSetInstallResultArchive","id":904138920,"type":"messages.StickerSetInstallResult","arguments":[{"name":"sets","type":"StickerSetCovered[]","description":"Archived stickersets"}],"description":"The stickerset was installed, but since there are too many stickersets some were archived"},{"name":"highScores","id":2587622809,"type":"messages.HighScores","arguments":[{"name":"scores","type":"HighScore[]","description":"Highscores"},{"name":"users","type":"User[]","description":"Users, associated to the highscores"}],"description":"Highscores in a game"},{"name":"favedStickersNotModified","id":2660214483,"type":"messages.FavedStickers","arguments":[],"description":"No new favorited stickers were found"},{"name":"favedStickers","id":4085198614,"type":"messages.FavedStickers","arguments":[{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"},{"name":"packs","type":"StickerPack[]","description":"Emojis associated to stickers"},{"name":"stickers","type":"Document[]","description":"Favorited stickers"}],"description":"Favorited stickers"},{"name":"foundStickerSetsNotModified","id":223655517,"type":"messages.FoundStickerSets","arguments":[],"description":"No further results were found"},{"name":"foundStickerSets","id":1359533640,"type":"messages.FoundStickerSets","arguments":[{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"},{"name":"sets","type":"StickerSetCovered[]","description":"Found stickersets"}],"description":"Found stickersets"},{"name":"searchCounter","id":3896830975,"type":"messages.SearchCounter","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"inexact","type":"true","optional":true,"predicate":"flags.1","description":"If set, the results may be inexact"},{"name":"filter","type":"MessagesFilter","description":"Provided message filter"},{"name":"count","type":"number","description":"Number of results that were found server-side"}],"description":"Indicates how many results would be found by a {@link messages.search} call with the same parameters"},{"name":"inactiveChats","id":2837970629,"type":"messages.InactiveChats","arguments":[{"name":"dates","type":"number[]","description":"When was the chat last active"},{"name":"chats","type":"Chat[]","description":"Chat list"},{"name":"users","type":"User[]","description":"Users mentioned in the chat list"}],"description":"Inactive chat list"},{"name":"votesList","id":136574537,"type":"messages.VotesList","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"count","type":"number","description":"Total number of votes for all options (or only for the chosen option, if provided to {@link messages.getPollVotes})"},{"name":"votes","type":"MessageUserVote[]","description":"Vote info for each user"},{"name":"users","type":"User[]","description":"Info about users that voted in the poll"},{"name":"nextOffset","type":"string","optional":true,"predicate":"flags.0","description":"Offset to use with the next {@link messages.getPollVotes} request, empty string if no more results are available."}],"description":"How users voted in a poll"},{"name":"messageViews","id":3066361155,"type":"messages.MessageViews","arguments":[{"name":"views","type":"MessageViews[]","description":"View, forward counter + info about replies"},{"name":"chats","type":"Chat[]","description":"Chats mentioned in constructor"},{"name":"users","type":"User[]","description":"Users mentioned in constructor"}],"description":"View, forward counter + info about replies"},{"name":"discussionMessage","id":4124938141,"type":"messages.DiscussionMessage","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"messages","type":"Message[]","description":"Discussion messages"},{"name":"maxId","type":"number","optional":true,"predicate":"flags.0","description":"Message ID of latest reply in this thread"},{"name":"readInboxMaxId","type":"number","optional":true,"predicate":"flags.1","description":"Message ID of latest read incoming message in this thread"},{"name":"readOutboxMaxId","type":"number","optional":true,"predicate":"flags.2","description":"Message ID of latest read outgoing message in this thread"},{"name":"chats","type":"Chat[]","description":"Chats mentioned in constructor"},{"name":"users","type":"User[]","description":"Users mentioned in constructor"}],"description":"Information about a message thread"},{"name":"historyImport","id":375566091,"type":"messages.HistoryImport","arguments":[{"name":"id","type":"Long"}]},{"name":"historyImportParsed","id":1578088377,"type":"messages.HistoryImportParsed","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"pm","type":"true","optional":true,"predicate":"flags.0"},{"name":"group","type":"true","optional":true,"predicate":"flags.1"},{"name":"title","type":"string","optional":true,"predicate":"flags.2"}]},{"name":"affectedFoundMessages","id":4019011180,"type":"messages.AffectedFoundMessages","arguments":[{"name":"pts","type":"number"},{"name":"ptsCount","type":"number"},{"name":"offset","type":"number"},{"name":"messages","type":"number[]"}]},{"name":"exportedChatInvites","id":3183881676,"type":"messages.ExportedChatInvites","arguments":[{"name":"count","type":"number"},{"name":"invites","type":"ExportedChatInvite[]"},{"name":"users","type":"User[]"}]},{"name":"exportedChatInvite","id":410107472,"type":"messages.ExportedChatInvite","arguments":[{"name":"invite","type":"ExportedChatInvite"},{"name":"users","type":"User[]"}]},{"name":"exportedChatInviteReplaced","id":572915951,"type":"messages.ExportedChatInvite","arguments":[{"name":"invite","type":"ExportedChatInvite"},{"name":"newInvite","type":"ExportedChatInvite"},{"name":"users","type":"User[]"}]},{"name":"chatInviteImporters","id":2176233482,"type":"messages.ChatInviteImporters","arguments":[{"name":"count","type":"number"},{"name":"importers","type":"ChatInviteImporter[]"},{"name":"users","type":"User[]"}]},{"name":"chatAdminsWithInvites","id":3063640791,"type":"messages.ChatAdminsWithInvites","arguments":[{"name":"admins","type":"ChatAdminWithInvites[]"},{"name":"users","type":"User[]"}]},{"name":"checkedHistoryImportPeer","id":2723014423,"type":"messages.CheckedHistoryImportPeer","arguments":[{"name":"confirmText","type":"string"}]}],"methods":[{"name":"sendReaction","id":627641572,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"peer","type":"InputPeer","description":"Peer"},{"name":"msgId","type":"number","description":"Message ID to react to"},{"name":"reaction","type":"string","optional":true,"predicate":"flags.0","description":"Reaction (a UTF8 emoji)"}],"description":"Send reaction to message","throws":[{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"},{"code":400,"name":"REACTION_EMPTY","description":"Empty reaction provided"}],"available":"both"},{"name":"getMessagesReactions","id":2344259814,"returns":"Updates","arguments":[{"name":"peer","type":"InputPeer","description":"Peer"},{"name":"id","type":"number[]","description":"Message IDs"}],"description":"Get message reactions","available":"both"},{"name":"getMessageReactionsList","id":363935594,"returns":"MessageReactionsList","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"peer","type":"InputPeer","description":"Peer"},{"name":"id","type":"number","description":"Message ID"},{"name":"reaction","type":"string","optional":true,"predicate":"flags.0","description":"Get only reactions of this type (UTF8 emoji)"},{"name":"offset","type":"string","optional":true,"predicate":"flags.1","description":"Offset (typically taken from the next_offset field of the returned MessageReactionsList)"},{"name":"limit","type":"number","description":"Maximum number of results to return, see pagination"}],"description":"Get full message reaction list","available":"both"},{"name":"getMessages","id":1673946374,"returns":"messages.Messages","arguments":[{"name":"id","type":"InputMessage[]","description":"Message ID list"}],"description":"Returns the list of messages by their IDs.","available":"both"},{"name":"getDialogs","id":2699967347,"returns":"messages.Dialogs","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"excludePinned","type":"true","optional":true,"predicate":"flags.0","description":"Exclude pinned dialogs"},{"name":"folderId","type":"number","optional":true,"predicate":"flags.1","description":"Peer folder ID, for more info click here"},{"name":"offsetDate","type":"number","description":"Offsets for pagination, for more info click here"},{"name":"offsetId","type":"number","description":"Offsets for pagination, for more info click here"},{"name":"offsetPeer","type":"InputPeer","description":"Offset peer for pagination"},{"name":"limit","type":"number","description":"Number of list elements to be returned"},{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Returns the current user dialog list.","throws":[{"code":400,"name":"FOLDER_ID_INVALID","description":"Invalid folder ID"},{"code":400,"name":"INPUT_CONSTRUCTOR_INVALID","description":"The provided constructor is invalid"},{"code":400,"name":"OFFSET_PEER_ID_INVALID","description":"The provided offset peer is invalid"}],"available":"user"},{"name":"getHistory","id":3703276128,"returns":"messages.Messages","arguments":[{"name":"peer","type":"InputPeer","description":"Target peer"},{"name":"offsetId","type":"number","description":"Only return messages starting from the specified message ID"},{"name":"offsetDate","type":"number","description":"Only return messages sent before the specified date"},{"name":"addOffset","type":"number","description":"Number of list elements to be skipped, negative values are also accepted."},{"name":"limit","type":"number","description":"Number of results to return"},{"name":"maxId","type":"number","description":"If a positive value was transferred, the method will return only messages with IDs less than max_id"},{"name":"minId","type":"number","description":"If a positive value was transferred, the method will return only messages with IDs more than min_id"},{"name":"hash","type":"number","description":"Result hash"}],"description":"Gets back the conversation history with one interlocutor / within a chat","throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","description":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"},{"code":400,"name":"CONNECTION_DEVICE_MODEL_EMPTY","description":"Device model empty"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"search","id":204812012,"returns":"messages.Messages","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"peer","type":"InputPeer","description":"User or chat, histories with which are searched, or {@link inputPeerEmpty} constructor for global search"},{"name":"q","type":"string","description":"Text search request"},{"name":"fromId","type":"InputPeer","optional":true,"predicate":"flags.0","description":"Only return messages sent by the specified user ID"},{"name":"topMsgId","type":"number","optional":true,"predicate":"flags.1","description":"Thread ID"},{"name":"filter","type":"MessagesFilter","description":"Filter to return only specified message types"},{"name":"minDate","type":"number","description":"If a positive value was transferred, only messages with a sending date bigger than the transferred one will be returned"},{"name":"maxDate","type":"number","description":"If a positive value was transferred, only messages with a sending date smaller than the transferred one will be returned"},{"name":"offsetId","type":"number","description":"Only return messages starting from the specified message ID"},{"name":"addOffset","type":"number","description":"Additional offset"},{"name":"limit","type":"number","description":"Number of results to return"},{"name":"maxId","type":"number","description":"Maximum message ID to return"},{"name":"minId","type":"number","description":"Minimum message ID to return"},{"name":"hash","type":"number","description":"Hash"}],"description":"Gets back found messages","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"INPUT_CONSTRUCTOR_INVALID","description":"The provided constructor is invalid"},{"code":400,"name":"INPUT_USER_DEACTIVATED","description":"The specified user was deleted"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"PEER_ID_NOT_SUPPORTED","description":"The provided peer ID is not supported"},{"code":400,"name":"SEARCH_QUERY_EMPTY","description":"The search query is empty"},{"code":400,"name":"USER_ID_INVALID","description":"The provided user ID is invalid"}],"available":"user"},{"name":"readHistory","id":238054714,"returns":"messages.AffectedMessages","arguments":[{"name":"peer","type":"InputPeer","description":"Target user or group"},{"name":"maxId","type":"number","description":"If a positive value is passed, only messages with identifiers less or equal than the given one will be read"}],"description":"Marks message history as read.","throws":[{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"deleteHistory","id":469850889,"returns":"messages.AffectedHistory","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"justClear","type":"true","optional":true,"predicate":"flags.0","description":"Just clear history for the current user, without actually removing messages for every chat user"},{"name":"revoke","type":"true","optional":true,"predicate":"flags.1","description":"Whether to delete the message history for all chat participants"},{"name":"peer","type":"InputPeer","description":"User or chat, communication history of which will be deleted"},{"name":"maxId","type":"number","description":"Maximum ID of message to delete"}],"description":"Deletes communication history.","throws":[{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"},{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"deleteMessages","id":3851326930,"returns":"messages.AffectedMessages","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"revoke","type":"true","optional":true,"predicate":"flags.0","description":"Whether to delete messages for all participants of the chat"},{"name":"id","type":"number[]","description":"Message ID list"}],"description":"Deletes messages by their identifiers.","throws":[{"code":403,"name":"MESSAGE_DELETE_FORBIDDEN","description":"You can't delete one of the messages you tried to delete, most likely because it is a service message."}],"available":"both"},{"name":"receivedMessages","id":94983360,"returns":"ReceivedNotifyMessage[]","arguments":[{"name":"maxId","type":"number","description":"Maximum message ID available in a client."}],"description":"Confirms receipt of messages by a client, cancels PUSH-notification sending.","available":"user"},{"name":"setTyping","id":1486110434,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"peer","type":"InputPeer","description":"Target user or group"},{"name":"topMsgId","type":"number","optional":true,"predicate":"flags.0","description":"Thread ID"},{"name":"action","type":"SendMessageAction","description":"Type of action
Parameter added in Layer 17."}],"description":"Sends a current user typing event (see SendMessageAction for all event types) to a conversation partner or group.","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"},{"code":400,"name":"INPUT_USER_DEACTIVATED","description":"The specified user was deleted"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"USER_BANNED_IN_CHANNEL","description":"You're banned from sending messages in supergroups/channels"},{"code":400,"name":"USER_IS_BLOCKED","description":"You were blocked by this user"},{"code":400,"name":"USER_IS_BOT","description":"Bots can't send messages to other bots"}],"available":"both"},{"name":"sendMessage","id":1376532592,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"noWebpage","type":"true","optional":true,"predicate":"flags.1","description":"Set this flag to disable generation of the webpage preview"},{"name":"silent","type":"true","optional":true,"predicate":"flags.5","description":"Send this message silently (no notifications for the receivers)"},{"name":"background","type":"true","optional":true,"predicate":"flags.6","description":"Send this message as background message"},{"name":"clearDraft","type":"true","optional":true,"predicate":"flags.7","description":"Clear the draft field"},{"name":"peer","type":"InputPeer","description":"The destination where the message will be sent"},{"name":"replyToMsgId","type":"number","optional":true,"predicate":"flags.0","description":"The message ID to which this message will reply to"},{"name":"message","type":"string","description":"The message"},{"name":"randomId","type":"Long","description":"Unique client message ID required to prevent message resending"},{"name":"replyMarkup","type":"ReplyMarkup","optional":true,"predicate":"flags.2","description":"Reply markup for sending bot buttons"},{"name":"entities","type":"MessageEntity[]","optional":true,"predicate":"flags.3","description":"Message entities for sending styled text"},{"name":"scheduleDate","type":"number","optional":true,"predicate":"flags.10","description":"Scheduled message date for scheduled messages"}],"description":"Sends a message to a chat","throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","description":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"BOT_DOMAIN_INVALID","description":"Bot domain invalid"},{"code":400,"name":"BOT_INVALID","description":"This is not a valid bot"},{"code":400,"name":"BUTTON_DATA_INVALID","description":"The data of one or more of the buttons you provided is invalid"},{"code":400,"name":"BUTTON_TYPE_INVALID","description":"The type of one or more of the buttons you provided is invalid"},{"code":400,"name":"BUTTON_URL_INVALID","description":"Button URL invalid"},{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"},{"code":400,"name":"CHAT_RESTRICTED","description":"You can't send messages in this chat, you were restricted"},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"},{"code":400,"name":"ENCRYPTION_DECLINED","description":"The secret chat was declined"},{"code":400,"name":"ENTITY_MENTION_USER_INVALID","description":"You mentioned an invalid user"},{"code":400,"name":"FROM_MESSAGE_BOT_DISABLED","description":"Bots can't use fromMessage min constructors"},{"code":400,"name":"INPUT_USER_DEACTIVATED","description":"The specified user was deleted"},{"code":400,"name":"MESSAGE_EMPTY","description":"The provided message is empty"},{"code":400,"name":"MESSAGE_TOO_LONG","description":"The provided message is too long"},{"code":400,"name":"MSG_ID_INVALID","description":"Provided reply_to_msg_id is invalid"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"PINNED_DIALOGS_TOO_MUCH","description":"Too many pinned dialogs"},{"code":400,"name":"REPLY_MARKUP_INVALID","description":"The provided reply markup is invalid"},{"code":400,"name":"SCHEDULE_BOT_NOT_ALLOWED","description":"Bots cannot schedule messages"},{"code":400,"name":"SCHEDULE_DATE_TOO_LATE","description":"You can't schedule a message this far in the future"},{"code":400,"name":"SCHEDULE_TOO_MUCH","description":"There are too many scheduled messages"},{"code":420,"name":"SLOWMODE_WAIT_X","description":"Slowmode is enabled in this chat: you must wait for the specified number of seconds before sending another message to the chat."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","description":"You're banned from sending messages in supergroups/channels"},{"code":400,"name":"USER_IS_BLOCKED","description":"You were blocked by this user"},{"code":400,"name":"USER_IS_BOT","description":"Bots can't send messages to other bots"},{"code":400,"name":"YOU_BLOCKED_USER","description":"You blocked this user"}],"available":"both"},{"name":"sendMedia","id":881978281,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"silent","type":"true","optional":true,"predicate":"flags.5","description":"Send message silently (no notification should be triggered)"},{"name":"background","type":"true","optional":true,"predicate":"flags.6","description":"Send message in background"},{"name":"clearDraft","type":"true","optional":true,"predicate":"flags.7","description":"Clear the draft"},{"name":"peer","type":"InputPeer","description":"Destination"},{"name":"replyToMsgId","type":"number","optional":true,"predicate":"flags.0","description":"Message ID to which this message should reply to"},{"name":"media","type":"InputMedia","description":"Attached media"},{"name":"message","type":"string","description":"Caption"},{"name":"randomId","type":"Long","description":"Random ID to avoid resending the same message"},{"name":"replyMarkup","type":"ReplyMarkup","optional":true,"predicate":"flags.2","description":"Reply markup for bot keyboards"},{"name":"entities","type":"MessageEntity[]","optional":true,"predicate":"flags.3","description":"Message entities for styled text"},{"name":"scheduleDate","type":"number","optional":true,"predicate":"flags.10","description":"Scheduled message date for scheduled messages"}],"description":"Send a media","throws":[{"code":400,"name":"BROADCAST_PUBLIC_VOTERS_FORBIDDEN","description":"You can't forward polls with public voters"},{"code":400,"name":"BUTTON_DATA_INVALID","description":"The data of one or more of the buttons you provided is invalid"},{"code":400,"name":"BUTTON_TYPE_INVALID","description":"The type of one or more of the buttons you provided is invalid"},{"code":400,"name":"BUTTON_URL_INVALID","description":"Button URL invalid"},{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"CHAT_RESTRICTED","description":"You can't send messages in this chat, you were restricted"},{"code":403,"name":"CHAT_SEND_GIFS_FORBIDDEN","description":"You can't send gifs in this chat"},{"code":403,"name":"CHAT_SEND_MEDIA_FORBIDDEN","description":"You can't send media in this chat"},{"code":403,"name":"CHAT_SEND_STICKERS_FORBIDDEN","description":"You can't send stickers in this chat."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"},{"code":400,"name":"EXTERNAL_URL_INVALID","description":"External URL invalid"},{"code":400,"name":"FILE_PARTS_INVALID","description":"The number of file parts is invalid"},{"code":400,"name":"FILE_PART_LENGTH_INVALID","description":"The length of a file part is invalid"},{"code":400,"name":"IMAGE_PROCESS_FAILED","description":"Failure while processing image"},{"code":400,"name":"INPUT_USER_DEACTIVATED","description":"The specified user was deleted"},{"code":400,"name":"MD5_CHECKSUM_INVALID","description":"The MD5 checksums do not match"},{"code":400,"name":"MEDIA_CAPTION_TOO_LONG","description":"The caption is too long"},{"code":400,"name":"MEDIA_EMPTY","description":"The provided media object is invalid"},{"code":400,"name":"MEDIA_INVALID","description":"Media invalid"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"PHOTO_EXT_INVALID","description":"The extension of the photo is invalid"},{"code":400,"name":"PHOTO_INVALID_DIMENSIONS","description":"The photo dimensions are invalid"},{"code":400,"name":"PHOTO_SAVE_FILE_INVALID","description":"Internal issues, try again later"},{"code":400,"name":"POLL_ANSWERS_INVALID","description":"Invalid poll answers were provided"},{"code":400,"name":"POLL_OPTION_DUPLICATE","description":"Duplicate poll options provided"},{"code":400,"name":"POLL_OPTION_INVALID","description":"Invalid poll option provided"},{"code":400,"name":"QUIZ_CORRECT_ANSWERS_EMPTY","description":"No correct quiz answer was specified"},{"code":400,"name":"QUIZ_CORRECT_ANSWER_INVALID","description":"An invalid value was provided to the correct_answers field"},{"code":400,"name":"REPLY_MARKUP_BUY_EMPTY","description":"Reply markup for buy button empty"},{"code":400,"name":"REPLY_MARKUP_INVALID","description":"The provided reply markup is invalid"},{"code":400,"name":"SCHEDULE_TOO_MUCH","description":"There are too many scheduled messages"},{"code":420,"name":"SLOWMODE_WAIT_X","description":"Slowmode is enabled in this chat: you must wait for the specified number of seconds before sending another message to the chat."},{"code":400,"name":"TTL_MEDIA_INVALID","description":"Invalid media Time To Live was provided"},{"code":400,"name":"USER_BANNED_IN_CHANNEL","description":"You're banned from sending messages in supergroups/channels"},{"code":400,"name":"USER_IS_BLOCKED","description":"You were blocked by this user"},{"code":400,"name":"USER_IS_BOT","description":"Bots can't send messages to other bots"},{"code":400,"name":"WEBPAGE_CURL_FAILED","description":"Failure while fetching the webpage with cURL"},{"code":400,"name":"WEBPAGE_MEDIA_EMPTY","description":"Webpage media empty"},{"code":400,"name":"YOU_BLOCKED_USER","description":"You blocked this user"}],"available":"both"},{"name":"forwardMessages","id":3657360910,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"silent","type":"true","optional":true,"predicate":"flags.5","description":"Whether to send messages silently (no notification will be triggered on the destination clients)"},{"name":"background","type":"true","optional":true,"predicate":"flags.6","description":"Whether to send the message in background"},{"name":"withMyScore","type":"true","optional":true,"predicate":"flags.8","description":"When forwarding games, whether to include your score in the game"},{"name":"fromPeer","type":"InputPeer","description":"Source of messages"},{"name":"id","type":"number[]","description":"IDs of messages"},{"name":"randomId","type":"Long[]","description":"Random ID to prevent resending of messages"},{"name":"toPeer","type":"InputPeer","description":"Destination peer"},{"name":"scheduleDate","type":"number","optional":true,"predicate":"flags.10","description":"Scheduled message date for scheduled messages"}],"description":"Forwards messages by their IDs.","throws":[{"code":400,"name":"BROADCAST_PUBLIC_VOTERS_FORBIDDEN","description":"You can't forward polls with public voters"},{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"},{"code":400,"name":"CHAT_RESTRICTED","description":"You can't send messages in this chat, you were restricted"},{"code":403,"name":"CHAT_SEND_GIFS_FORBIDDEN","description":"You can't send gifs in this chat"},{"code":403,"name":"CHAT_SEND_MEDIA_FORBIDDEN","description":"You can't send media in this chat"},{"code":403,"name":"CHAT_SEND_POLL_FORBIDDEN","description":"You can't send polls in this chat"},{"code":403,"name":"CHAT_SEND_STICKERS_FORBIDDEN","description":"You can't send stickers in this chat."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"},{"code":400,"name":"GROUPED_MEDIA_INVALID","description":"Invalid grouped media"},{"code":400,"name":"INPUT_USER_DEACTIVATED","description":"The specified user was deleted"},{"code":400,"name":"MEDIA_EMPTY","description":"The provided media object is invalid"},{"code":400,"name":"MESSAGE_IDS_EMPTY","description":"No message ids were provided"},{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":420,"name":"P0NY_FLOODWAIT","description":" "},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"RANDOM_ID_INVALID","description":"A provided random ID is invalid"},{"code":400,"name":"SCHEDULE_TOO_MUCH","description":"There are too many scheduled messages"},{"code":400,"name":"SLOWMODE_MULTI_MSGS_DISABLED","description":"Slowmode is enabled, you cannot forward multiple messages to this group."},{"code":420,"name":"SLOWMODE_WAIT_X","description":"Slowmode is enabled in this chat: you must wait for the specified number of seconds before sending another message to the chat."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","description":"You're banned from sending messages in supergroups/channels"},{"code":400,"name":"USER_IS_BLOCKED","description":"You were blocked by this user"},{"code":400,"name":"USER_IS_BOT","description":"Bots can't send messages to other bots"},{"code":400,"name":"YOU_BLOCKED_USER","description":"You blocked this user"}],"available":"both"},{"name":"reportSpam","id":3474297563,"returns":"boolean","arguments":[{"name":"peer","type":"InputPeer","description":"Peer to report"}],"description":"Report a new incoming chat for spam, if the {@link peerSettings} of the chat allow us to do that","throws":[{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"getPeerSettings","id":913498268,"returns":"PeerSettings","arguments":[{"name":"peer","type":"InputPeer","description":"The peer"}],"description":"Get peer settings","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"report","id":2303961934,"returns":"boolean","arguments":[{"name":"peer","type":"InputPeer","description":"Peer"},{"name":"id","type":"number[]","description":"IDs of messages to report"},{"name":"reason","type":"ReportReason","description":"Why are these messages being reported"},{"name":"message","type":"string"}],"description":"Report a message in a chat for violation of telegram's Terms of Service","throws":[{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"getChats","id":1013621127,"returns":"messages.Chats","arguments":[{"name":"id","type":"number[]","description":"List of chat IDs"}],"description":"Returns chat basic info on their IDs.","throws":[{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"both"},{"name":"getFullChat","id":998448230,"returns":"messages.ChatFull","arguments":[{"name":"chatId","type":"number","description":"Chat ID"}],"description":"Returns full chat info according to its ID.","throws":[{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"both"},{"name":"editChatTitle","id":3695519829,"returns":"Updates","arguments":[{"name":"chatId","type":"number","description":"Chat ID"},{"name":"title","type":"string","description":"New chat name, different from the old one"}],"description":"Chanages chat name and sends a service message on it.","throws":[{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"},{"code":400,"name":"CHAT_NOT_MODIFIED","description":"The pinned message wasn't modified"},{"code":400,"name":"CHAT_TITLE_EMPTY","description":"No chat title provided"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"both"},{"name":"editChatPhoto","id":3394009560,"returns":"Updates","arguments":[{"name":"chatId","type":"number","description":"Chat ID"},{"name":"photo","type":"InputChatPhoto","description":"Photo to be set"}],"description":"Changes chat photo and sends a service message on it","throws":[{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"},{"code":400,"name":"CHAT_NOT_MODIFIED","description":"The pinned message wasn't modified"},{"code":400,"name":"INPUT_CONSTRUCTOR_INVALID","description":"The provided constructor is invalid"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"PHOTO_CROP_SIZE_SMALL","description":"Photo is too small"},{"code":400,"name":"PHOTO_EXT_INVALID","description":"The extension of the photo is invalid"},{"code":400,"name":"PHOTO_INVALID","description":"Photo invalid"}],"available":"both"},{"name":"addChatUser","id":4188056073,"returns":"Updates","arguments":[{"name":"chatId","type":"number","description":"Chat ID"},{"name":"userId","type":"InputUser","description":"User ID to be added"},{"name":"fwdLimit","type":"number","description":"Number of last messages to be forwarded"}],"description":"Adds a user to a chat and sends a service message on it.","throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"},{"code":400,"name":"INPUT_USER_DEACTIVATED","description":"The specified user was deleted"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"USERS_TOO_MUCH","description":"The maximum number of users has been exceeded (to create a chat, for example)"},{"code":400,"name":"USER_ALREADY_PARTICIPANT","description":"The user is already in the group"},{"code":400,"name":"USER_ID_INVALID","description":"The provided user ID is invalid"},{"code":403,"name":"USER_NOT_MUTUAL_CONTACT","description":"The provided user is not a mutual contact"},{"code":403,"name":"USER_PRIVACY_RESTRICTED","description":"The user's privacy settings do not allow you to do this"}],"available":"user"},{"name":"deleteChatUser","id":3308537242,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"revokeHistory","type":"true","optional":true,"predicate":"flags.0"},{"name":"chatId","type":"number","description":"Chat ID"},{"name":"userId","type":"InputUser","description":"User ID to be deleted"}],"description":"Deletes a user from a chat and sends a service message on it.","throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"USER_ID_INVALID","description":"The provided user ID is invalid"},{"code":400,"name":"USER_NOT_PARTICIPANT","description":"You're not a member of this supergroup/channel"}],"available":"both"},{"name":"createChat","id":164303470,"returns":"Updates","arguments":[{"name":"users","type":"InputUser[]","description":"List of user IDs to be invited"},{"name":"title","type":"string","description":"Chat name"}],"description":"Creates a new chat.","throws":[{"code":400,"name":"CHAT_INVALID","description":"Invalid chat"},{"code":400,"name":"CHAT_TITLE_EMPTY","description":"No chat title provided"},{"code":400,"name":"INPUT_USER_DEACTIVATED","description":"The specified user was deleted"},{"code":400,"name":"USERS_TOO_FEW","description":"Not enough users (to create a chat, for example)"},{"code":403,"name":"USER_RESTRICTED","description":"You're spamreported, you can't create channels or chats."}],"available":"user"},{"name":"getDhConfig","id":651135312,"returns":"messages.DhConfig","arguments":[{"name":"version","type":"number","description":"Value of the version parameter from {@link messages.dhConfig}, avialable at the client"},{"name":"randomLength","type":"number","description":"Length of the required random sequence"}],"description":"Returns configuration parameters for Diffie-Hellman key generation. Can also return a random sequence of bytes of required length.","throws":[{"code":400,"name":"RANDOM_LENGTH_INVALID","description":"Random length invalid"}],"available":"user"},{"name":"requestEncryption","id":4132286275,"returns":"EncryptedChat","arguments":[{"name":"userId","type":"InputUser","description":"User ID"},{"name":"randomId","type":"number","description":"Unique client request ID required to prevent resending. This also doubles as the chat ID."},{"name":"gA","type":"Buffer","description":"A = g ^ a mod p, see Wikipedia"}],"description":"Sends a request to start a secret chat to the user.","throws":[{"code":400,"name":"DH_G_A_INVALID","description":"g_a invalid"},{"code":400,"name":"USER_ID_INVALID","description":"The provided user ID is invalid"}],"available":"user"},{"name":"acceptEncryption","id":1035731989,"returns":"EncryptedChat","arguments":[{"name":"peer","type":"InputEncryptedChat","description":"Secret chat ID"},{"name":"gB","type":"Buffer","description":"B = g ^ b mod p, see Wikipedia"},{"name":"keyFingerprint","type":"Long","description":"64-bit fingerprint of the received key"}],"description":"Confirms creation of a secret chat","throws":[{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"},{"code":400,"name":"ENCRYPTION_ALREADY_ACCEPTED","description":"Secret chat already accepted"},{"code":400,"name":"ENCRYPTION_ALREADY_DECLINED","description":"The secret chat was already declined"}],"available":"user"},{"name":"discardEncryption","id":4086541984,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"deleteHistory","type":"true","optional":true,"predicate":"flags.0"},{"name":"chatId","type":"number","description":"Secret chat ID"}],"description":"Cancels a request for creation and/or delete info on secret chat.","throws":[{"code":400,"name":"CHAT_ID_EMPTY","description":"The provided chat ID is empty"},{"code":400,"name":"ENCRYPTION_ALREADY_DECLINED","description":"The secret chat was already declined"},{"code":400,"name":"ENCRYPTION_ID_INVALID","description":"The provided secret chat ID is invalid"}],"available":"user"},{"name":"setEncryptedTyping","id":2031374829,"returns":"boolean","arguments":[{"name":"peer","type":"InputEncryptedChat","description":"Secret chat ID"},{"name":"typing","type":"boolean","description":"Typing.
Possible values:
{@link boolTrue}, if the user started typing and more than 5 seconds have passed since the last request
{@link boolFalse}, if the user stopped typing"}],"description":"Send typing event by the current user to a secret chat.","throws":[{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"}],"available":"user"},{"name":"readEncryptedHistory","id":2135648522,"returns":"boolean","arguments":[{"name":"peer","type":"InputEncryptedChat","description":"Secret chat ID"},{"name":"maxDate","type":"number","description":"Maximum date value for received messages in history"}],"description":"Marks message history within a secret chat as read.","throws":[{"code":400,"name":"MSG_WAIT_FAILED","description":"A waiting call returned an error"}],"available":"user"},{"name":"sendEncrypted","id":1157265941,"returns":"messages.SentEncryptedMessage","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"silent","type":"true","optional":true,"predicate":"flags.0","description":"Send encrypted message without a notification"},{"name":"peer","type":"InputEncryptedChat","description":"Secret chat ID"},{"name":"randomId","type":"Long","description":"Unique client message ID, necessary to avoid message resending"},{"name":"data","type":"Buffer","description":"TL-serialization of DecryptedMessage type, encrypted with a key that was created during chat initialization"}],"description":"Sends a text message to a secret chat.","throws":[{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"},{"code":400,"name":"DATA_INVALID","description":"Encrypted data invalid"},{"code":400,"name":"ENCRYPTION_DECLINED","description":"The secret chat was declined"},{"code":400,"name":"MSG_WAIT_FAILED","description":"A waiting call returned an error"}],"available":"user"},{"name":"sendEncryptedFile","id":1431914525,"returns":"messages.SentEncryptedMessage","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"silent","type":"true","optional":true,"predicate":"flags.0","description":"Whether to send the file without triggering a notification"},{"name":"peer","type":"InputEncryptedChat","description":"Secret chat ID"},{"name":"randomId","type":"Long","description":"Unique client message ID necessary to prevent message resending"},{"name":"data","type":"Buffer","description":"TL-serialization of DecryptedMessage type, encrypted with a key generated during chat initialization"},{"name":"file","type":"InputEncryptedFile","description":"File attachment for the secret chat"}],"description":"Sends a message with a file attachment to a secret chat","throws":[{"code":400,"name":"DATA_TOO_LONG","description":"Data too long"},{"code":400,"name":"ENCRYPTION_DECLINED","description":"The secret chat was declined"},{"code":400,"name":"MD5_CHECKSUM_INVALID","description":"The MD5 checksums do not match"},{"code":400,"name":"MSG_WAIT_FAILED","description":"A waiting call returned an error"}],"available":"user"},{"name":"sendEncryptedService","id":852769188,"returns":"messages.SentEncryptedMessage","arguments":[{"name":"peer","type":"InputEncryptedChat","description":"Secret chat ID"},{"name":"randomId","type":"Long","description":"Unique client message ID required to prevent message resending"},{"name":"data","type":"Buffer","description":"TL-serialization of DecryptedMessage type, encrypted with a key generated during chat initialization"}],"description":"Sends a service message to a secret chat.","throws":[{"code":400,"name":"DATA_INVALID","description":"Encrypted data invalid"},{"code":400,"name":"ENCRYPTION_DECLINED","description":"The secret chat was declined"},{"code":400,"name":"ENCRYPTION_ID_INVALID","description":"The provided secret chat ID is invalid"},{"code":400,"name":"MSG_WAIT_FAILED","description":"A waiting call returned an error"},{"code":403,"name":"USER_IS_BLOCKED","description":"You were blocked by this user"}],"available":"user"},{"name":"receivedQueue","id":1436924774,"returns":"Long[]","arguments":[{"name":"maxQts","type":"number","description":"Maximum qts value available at the client"}],"description":"Confirms receipt of messages in a secret chat by client, cancels push notifications.","throws":[{"code":400,"name":"MSG_WAIT_FAILED","description":"A waiting call returned an error"}],"available":"user"},{"name":"reportEncryptedSpam","id":1259113487,"returns":"boolean","arguments":[{"name":"peer","type":"InputEncryptedChat","description":"The secret chat to report"}],"description":"Report a secret chat for spam","throws":[{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"}],"available":"user"},{"name":"readMessageContents","id":916930423,"returns":"messages.AffectedMessages","arguments":[{"name":"id","type":"number[]","description":"Message ID list"}],"description":"Notifies the sender about the recipient having listened a voice message or watched a video.","available":"user"},{"name":"getStickers","id":71126828,"returns":"messages.Stickers","arguments":[{"name":"emoticon","type":"string","description":"The emoji"},{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Get stickers by emoji","available":"user"},{"name":"getAllStickers","id":479598769,"returns":"messages.AllStickers","arguments":[{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Get all installed stickers","available":"user"},{"name":"getWebPagePreview","id":2338894028,"returns":"MessageMedia","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"message","type":"string","description":"Message from which to extract the preview"},{"name":"entities","type":"MessageEntity[]","optional":true,"predicate":"flags.3","description":"Message entities for styled text"}],"description":"Get preview of webpage","throws":[{"code":400,"name":"MESSAGE_EMPTY","description":"The provided message is empty"}],"available":"user"},{"name":"exportChatInvite","id":347716823,"returns":"ExportedChatInvite","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"legacyRevokePermanent","type":"true","optional":true,"predicate":"flags.2"},{"name":"peer","type":"InputPeer","description":"Chat"},{"name":"expireDate","type":"number","optional":true,"predicate":"flags.0"},{"name":"usageLimit","type":"number","optional":true,"predicate":"flags.1"}],"description":"Export an invite link for a chat","throws":[{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"both"},{"name":"checkChatInvite","id":1051570619,"returns":"ChatInvite","arguments":[{"name":"hash","type":"string","description":"Invite hash in t.me/joinchat/hash"}],"description":"Check the validity of a chat invite link and get basic info about it","throws":[{"code":400,"name":"INVITE_HASH_EMPTY","description":"The invite hash is empty"},{"code":400,"name":"INVITE_HASH_EXPIRED","description":"The invite link has expired"},{"code":400,"name":"INVITE_HASH_INVALID","description":"The invite hash is invalid"}],"available":"user"},{"name":"importChatInvite","id":1817183516,"returns":"Updates","arguments":[{"name":"hash","type":"string","description":"hash from t.me/joinchat/hash"}],"description":"Import a chat invite and join a private chat/supergroup/channel","throws":[{"code":400,"name":"CHANNELS_TOO_MUCH","description":"You have joined too many channels/supergroups"},{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"INVITE_HASH_EMPTY","description":"The invite hash is empty"},{"code":400,"name":"INVITE_HASH_EXPIRED","description":"The invite link has expired"},{"code":400,"name":"INVITE_HASH_INVALID","description":"The invite hash is invalid"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"USERS_TOO_MUCH","description":"The maximum number of users has been exceeded (to create a chat, for example)"},{"code":400,"name":"USER_ALREADY_PARTICIPANT","description":"The user is already in the group"},{"code":400,"name":"USER_CHANNELS_TOO_MUCH","description":"One of the users you tried to add is already in too many channels/supergroups"}],"available":"user"},{"name":"getStickerSet","id":639215886,"returns":"messages.StickerSet","arguments":[{"name":"stickerset","type":"InputStickerSet","description":"Stickerset"}],"description":"Get info about a stickerset","throws":[{"code":400,"name":"STICKERSET_INVALID","description":"The provided sticker set is invalid"}],"available":"both"},{"name":"installStickerSet","id":3348096096,"returns":"messages.StickerSetInstallResult","arguments":[{"name":"stickerset","type":"InputStickerSet","description":"Stickerset to install"},{"name":"archived","type":"boolean","description":"Whether to archive stickerset"}],"description":"Install a stickerset","throws":[{"code":400,"name":"STICKERSET_INVALID","description":"The provided sticker set is invalid"}],"available":"user"},{"name":"uninstallStickerSet","id":4184757726,"returns":"boolean","arguments":[{"name":"stickerset","type":"InputStickerSet","description":"The stickerset to uninstall"}],"description":"Uninstall a stickerset","throws":[{"code":400,"name":"STICKERSET_INVALID","description":"The provided sticker set is invalid"}],"available":"user"},{"name":"startBot","id":3873403768,"returns":"Updates","arguments":[{"name":"bot","type":"InputUser","description":"The bot"},{"name":"peer","type":"InputPeer","description":"The chat where to start the bot, can be the bot's private chat or a group"},{"name":"randomId","type":"Long","description":"Random ID to avoid resending the same message"},{"name":"startParam","type":"string","description":"Deep linking parameter"}],"description":"Start a conversation with a bot using a deep linking parameter","throws":[{"code":400,"name":"BOT_INVALID","description":"This is not a valid bot"},{"code":400,"name":"INPUT_USER_DEACTIVATED","description":"The specified user was deleted"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"START_PARAM_EMPTY","description":"The start parameter is empty"},{"code":400,"name":"START_PARAM_INVALID","description":"Start parameter invalid"},{"code":400,"name":"START_PARAM_TOO_LONG","description":"Start parameter is too long"}],"available":"user"},{"name":"getMessagesViews","id":1468322785,"returns":"messages.MessageViews","arguments":[{"name":"peer","type":"InputPeer","description":"Peer where the message was found"},{"name":"id","type":"number[]","description":"ID of message"},{"name":"increment","type":"boolean","description":"Whether to mark the message as viewed and increment the view counter"}],"description":"Get and increase the view counter of a message sent or forwarded from a channel","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"editChatAdmin","id":2850463534,"returns":"boolean","arguments":[{"name":"chatId","type":"number","description":"The ID of the group"},{"name":"userId","type":"InputUser","description":"The user to make admin"},{"name":"isAdmin","type":"boolean","description":"Whether to make them admin"}],"description":"Make a user admin in a legacy group.","throws":[{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"USER_ID_INVALID","description":"The provided user ID is invalid"},{"code":400,"name":"USER_NOT_PARTICIPANT","description":"You're not a member of this supergroup/channel"}],"available":"user"},{"name":"migrateChat","id":363051235,"returns":"Updates","arguments":[{"name":"chatId","type":"number","description":"Legacy group to migrate"}],"description":"Turn a legacy group into a supergroup","throws":[{"code":403,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"searchGlobal","id":1271290010,"returns":"messages.Messages","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"folderId","type":"number","optional":true,"predicate":"flags.0","description":"Peer folder ID, for more info click here"},{"name":"q","type":"string","description":"Query"},{"name":"filter","type":"MessagesFilter","description":"Global search filter"},{"name":"minDate","type":"number","description":"If a positive value was specified, the method will return only messages with date bigger than min_date"},{"name":"maxDate","type":"number","description":"If a positive value was transferred, the method will return only messages with date smaller than max_date"},{"name":"offsetRate","type":"number","description":"Initially 0, then set to the {@link messages.messagesSlice}"},{"name":"offsetPeer","type":"InputPeer","description":"Offsets for pagination, for more info click here"},{"name":"offsetId","type":"number","description":"Offsets for pagination, for more info click here"},{"name":"limit","type":"number","description":"Offsets for pagination, for more info click here"}],"description":"Search for messages and peers globally","throws":[{"code":400,"name":"FOLDER_ID_INVALID","description":"Invalid folder ID"},{"code":400,"name":"SEARCH_QUERY_EMPTY","description":"The search query is empty"}],"available":"user"},{"name":"reorderStickerSets","id":2016638777,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"masks","type":"true","optional":true,"predicate":"flags.0","description":"Reorder mask stickersets"},{"name":"order","type":"Long[]","description":"New stickerset order by stickerset IDs"}],"description":"Reorder installed stickersets","available":"user"},{"name":"getDocumentByHash","id":864953444,"returns":"Document","arguments":[{"name":"sha256","type":"Buffer","description":"SHA256 of file"},{"name":"size","type":"number","description":"Size of the file in bytes"},{"name":"mimeType","type":"string","description":"Mime type"}],"description":"Get a document by its SHA256 hash, mainly used for gifs","throws":[{"code":400,"name":"SHA256_HASH_INVALID","description":"The provided SHA256 hash is invalid"}],"available":"both"},{"name":"getSavedGifs","id":2210348370,"returns":"messages.SavedGifs","arguments":[{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Get saved GIFs","available":"user"},{"name":"saveGif","id":846868683,"returns":"boolean","arguments":[{"name":"id","type":"InputDocument","description":"GIF to save"},{"name":"unsave","type":"boolean","description":"Whether to remove GIF from saved gifs list"}],"description":"Add GIF to saved gifs list","throws":[{"code":400,"name":"GIF_ID_INVALID","description":"The provided GIF ID is invalid"}],"available":"user"},{"name":"getInlineBotResults","id":1364105629,"returns":"messages.BotResults","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"bot","type":"InputUser","description":"The bot to query"},{"name":"peer","type":"InputPeer","description":"The currently opened chat"},{"name":"geoPoint","type":"InputGeoPoint","optional":true,"predicate":"flags.0","description":"The geolocation, if requested"},{"name":"query","type":"string","description":"The query"},{"name":"offset","type":"string","description":"The offset within the results, will be passed directly as-is to the bot."}],"description":"Query an inline bot","throws":[{"code":400,"name":"BOT_INLINE_DISABLED","description":"This bot can't be used in inline mode"},{"code":400,"name":"BOT_INVALID","description":"This is not a valid bot"},{"code":400,"name":"BOT_RESPONSE_TIMEOUT","description":"A timeout occurred while fetching data from the bot"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"INPUT_USER_DEACTIVATED","description":"The specified user was deleted"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":-503,"name":"Timeout","description":"Timeout while fetching data"}],"available":"user"},{"name":"setInlineBotResults","id":3948847622,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"gallery","type":"true","optional":true,"predicate":"flags.0","description":"Set this flag if the results are composed of media files"},{"name":"private","type":"true","optional":true,"predicate":"flags.1","description":"Set this flag if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query"},{"name":"queryId","type":"Long","description":"Unique identifier for the answered query"},{"name":"results","type":"InputBotInlineResult[]","description":"Vector of results for the inline query"},{"name":"cacheTime","type":"number","description":"The maximum amount of time in seconds that the result of the inline query may be cached on the server. Defaults to 300."},{"name":"nextOffset","type":"string","optional":true,"predicate":"flags.2","description":"Pass the offset that a client should send in the next query with the same text to receive more results. Pass an empty string if there are no more results or if you don‘t support pagination. Offset length can’t exceed 64 bytes."},{"name":"switchPm","type":"InlineBotSwitchPM","optional":true,"predicate":"flags.3","description":"If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with a certain parameter."}],"description":"Answer an inline query, for bots only","throws":[{"code":400,"name":"ARTICLE_TITLE_EMPTY","description":"The title of the article is empty"},{"code":400,"name":"BUTTON_DATA_INVALID","description":"The data of one or more of the buttons you provided is invalid"},{"code":400,"name":"BUTTON_TYPE_INVALID","description":"The type of one or more of the buttons you provided is invalid"},{"code":400,"name":"BUTTON_URL_INVALID","description":"Button URL invalid"},{"code":400,"name":"GIF_CONTENT_TYPE_INVALID","description":"GIF content-type invalid"},{"code":400,"name":"MESSAGE_EMPTY","description":"The provided message is empty"},{"code":400,"name":"MESSAGE_TOO_LONG","description":"The provided message is too long"},{"code":400,"name":"PHOTO_CONTENT_TYPE_INVALID","description":"Photo mime-type invalid"},{"code":400,"name":"PHOTO_CONTENT_URL_EMPTY","description":"Photo URL invalid"},{"code":400,"name":"PHOTO_INVALID","description":"Photo invalid"},{"code":400,"name":"PHOTO_THUMB_URL_EMPTY","description":"Photo thumbnail URL is empty"},{"code":400,"name":"QUERY_ID_INVALID","description":"The query ID is invalid"},{"code":400,"name":"REPLY_MARKUP_INVALID","description":"The provided reply markup is invalid"},{"code":400,"name":"RESULTS_TOO_MUCH","description":"Too many results were provided"},{"code":400,"name":"RESULT_ID_DUPLICATE","description":"You provided a duplicate result ID"},{"code":400,"name":"RESULT_TYPE_INVALID","description":"Result type invalid"},{"code":400,"name":"SEND_MESSAGE_MEDIA_INVALID","description":"Invalid media provided"},{"code":400,"name":"SEND_MESSAGE_TYPE_INVALID","description":"The message type is invalid"},{"code":400,"name":"START_PARAM_INVALID","description":"Start parameter invalid"},{"code":403,"name":"USER_BOT_INVALID","description":"This method can only be called by a bot"},{"code":400,"name":"WEBDOCUMENT_INVALID","description":"Invalid webdocument URL provided"},{"code":400,"name":"WEBDOCUMENT_MIME_INVALID","description":"Invalid webdocument mime type provided"},{"code":400,"name":"WEBDOCUMENT_SIZE_TOO_BIG","description":"Webdocument is too big!"}],"available":"bot"},{"name":"sendInlineBotResult","id":570955184,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"silent","type":"true","optional":true,"predicate":"flags.5","description":"Whether to send the message silently (no notification will be triggered on the other client)"},{"name":"background","type":"true","optional":true,"predicate":"flags.6","description":"Whether to send the message in background"},{"name":"clearDraft","type":"true","optional":true,"predicate":"flags.7","description":"Whether to clear the draft"},{"name":"hideVia","type":"true","optional":true,"predicate":"flags.11","description":"Whether to hide the via @botname in the resulting message (only for bot usernames encountered in the {@link config})"},{"name":"peer","type":"InputPeer","description":"Destination"},{"name":"replyToMsgId","type":"number","optional":true,"predicate":"flags.0","description":"ID of the message this message should reply to"},{"name":"randomId","type":"Long","description":"Random ID to avoid resending the same query"},{"name":"queryId","type":"Long","description":"Query ID from {@link messages.getInlineBotResults}"},{"name":"id","type":"string","description":"Result ID from {@link messages.getInlineBotResults}"},{"name":"scheduleDate","type":"number","optional":true,"predicate":"flags.10","description":"Scheduled message date for scheduled messages"}],"description":"Send a result obtained using {@link messages.getInlineBotResults}.","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"CHAT_RESTRICTED","description":"You can't send messages in this chat, you were restricted"},{"code":403,"name":"CHAT_SEND_GIFS_FORBIDDEN","description":"You can't send gifs in this chat"},{"code":403,"name":"CHAT_SEND_INLINE_FORBIDDEN","description":"You can't send inline messages in this group"},{"code":403,"name":"CHAT_SEND_MEDIA_FORBIDDEN","description":"You can't send media in this chat"},{"code":403,"name":"CHAT_SEND_STICKERS_FORBIDDEN","description":"You can't send stickers in this chat."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"},{"code":400,"name":"INLINE_RESULT_EXPIRED","description":"The inline query expired"},{"code":400,"name":"INPUT_USER_DEACTIVATED","description":"The specified user was deleted"},{"code":400,"name":"MEDIA_EMPTY","description":"The provided media object is invalid"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"QUERY_ID_EMPTY","description":"The query ID is empty"},{"code":400,"name":"RESULT_ID_EMPTY","description":"Result ID empty"},{"code":420,"name":"SLOWMODE_WAIT_X","description":"Slowmode is enabled in this chat: you must wait for the specified number of seconds before sending another message to the chat."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","description":"You're banned from sending messages in supergroups/channels"},{"code":400,"name":"WEBPAGE_CURL_FAILED","description":"Failure while fetching the webpage with cURL"},{"code":400,"name":"WEBPAGE_MEDIA_EMPTY","description":"Webpage media empty"},{"code":400,"name":"YOU_BLOCKED_USER","description":"You blocked this user"}],"available":"user"},{"name":"getMessageEditData","id":4255550774,"returns":"messages.MessageEditData","arguments":[{"name":"peer","type":"InputPeer","description":"Peer where the media was sent"},{"name":"id","type":"number","description":"ID of message"}],"description":"Find out if a media message's caption can be edited","throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":403,"name":"MESSAGE_AUTHOR_REQUIRED","description":"Message author required"},{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"editMessage","id":1224152952,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"noWebpage","type":"true","optional":true,"predicate":"flags.1","description":"Disable webpage preview"},{"name":"peer","type":"InputPeer","description":"Where was the message sent"},{"name":"id","type":"number","description":"ID of the message to edit"},{"name":"message","type":"string","optional":true,"predicate":"flags.11","description":"New message"},{"name":"media","type":"InputMedia","optional":true,"predicate":"flags.14","description":"New attached media"},{"name":"replyMarkup","type":"ReplyMarkup","optional":true,"predicate":"flags.2","description":"Reply markup for inline keyboards"},{"name":"entities","type":"MessageEntity[]","optional":true,"predicate":"flags.3","description":"Message entities for styled text"},{"name":"scheduleDate","type":"number","optional":true,"predicate":"flags.15","description":"Scheduled message date for scheduled messages"}],"description":"Edit message","throws":[{"code":400,"name":"BUTTON_DATA_INVALID","description":"The data of one or more of the buttons you provided is invalid"},{"code":400,"name":"BUTTON_TYPE_INVALID","description":"The type of one or more of the buttons you provided is invalid"},{"code":400,"name":"BUTTON_URL_INVALID","description":"Button URL invalid"},{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"},{"code":403,"name":"INLINE_BOT_REQUIRED","description":"Only the inline bot can edit message"},{"code":400,"name":"INPUT_USER_DEACTIVATED","description":"The specified user was deleted"},{"code":400,"name":"MEDIA_CAPTION_TOO_LONG","description":"The caption is too long"},{"code":400,"name":"MEDIA_PREV_INVALID","description":"Previous media invalid"},{"code":403,"name":"MESSAGE_AUTHOR_REQUIRED","description":"Message author required"},{"code":400,"name":"MESSAGE_EDIT_TIME_EXPIRED","description":"You can't edit this message anymore, too much time has passed since its creation."},{"code":400,"name":"MESSAGE_EMPTY","description":"The provided message is empty"},{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"},{"code":400,"name":"MESSAGE_NOT_MODIFIED","description":"The message text has not changed"},{"code":400,"name":"MESSAGE_TOO_LONG","description":"The provided message is too long"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"REPLY_MARKUP_INVALID","description":"The provided reply markup is invalid"},{"code":400,"name":"SCHEDULE_DATE_INVALID","description":"Invalid schedule date provided"},{"code":400,"name":"USER_BANNED_IN_CHANNEL","description":"You're banned from sending messages in supergroups/channels"}],"available":"both"},{"name":"editInlineBotMessage","id":2203418042,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"noWebpage","type":"true","optional":true,"predicate":"flags.1","description":"Disable webpage preview"},{"name":"id","type":"InputBotInlineMessageID","description":"Sent inline message ID"},{"name":"message","type":"string","optional":true,"predicate":"flags.11","description":"Message"},{"name":"media","type":"InputMedia","optional":true,"predicate":"flags.14","description":"Media"},{"name":"replyMarkup","type":"ReplyMarkup","optional":true,"predicate":"flags.2","description":"Reply markup for inline keyboards"},{"name":"entities","type":"MessageEntity[]","optional":true,"predicate":"flags.3","description":"Message entities for styled text"}],"description":"Edit an inline bot message","throws":[{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"},{"code":400,"name":"MESSAGE_NOT_MODIFIED","description":"The message text has not changed"}],"available":"both"},{"name":"getBotCallbackAnswer","id":2470627847,"returns":"messages.BotCallbackAnswer","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"game","type":"true","optional":true,"predicate":"flags.1","description":"Whether this is a \"play game\" button"},{"name":"peer","type":"InputPeer","description":"Where was the inline keyboard sent"},{"name":"msgId","type":"number","description":"ID of the Message with the inline keyboard"},{"name":"data","type":"Buffer","optional":true,"predicate":"flags.0","description":"Callback data"},{"name":"password","type":"InputCheckPasswordSRP","optional":true,"predicate":"flags.2","description":"For buttons {@link keyboardButtonCallback}, the SRP payload generated using SRP."}],"description":"Press an inline callback button and get a callback answer from the bot","throws":[{"code":400,"name":"BOT_RESPONSE_TIMEOUT","description":"A timeout occurred while fetching data from the bot"},{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"DATA_INVALID","description":"Encrypted data invalid"},{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":-503,"name":"Timeout","description":"Timeout while fetching data"}],"available":"user"},{"name":"setBotCallbackAnswer","id":3582923530,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"alert","type":"true","optional":true,"predicate":"flags.1","description":"Whether to show the message as a popup instead of a toast notification"},{"name":"queryId","type":"Long","description":"Query ID"},{"name":"message","type":"string","optional":true,"predicate":"flags.0","description":"Popup to show"},{"name":"url","type":"string","optional":true,"predicate":"flags.2","description":"URL to open"},{"name":"cacheTime","type":"number","description":"Cache validity"}],"description":"Set the callback answer to a user button press (bots only)","throws":[{"code":400,"name":"QUERY_ID_INVALID","description":"The query ID is invalid"},{"code":400,"name":"URL_INVALID","description":"Invalid URL provided"}],"available":"both"},{"name":"getPeerDialogs","id":3832593661,"returns":"messages.PeerDialogs","arguments":[{"name":"peers","type":"InputDialogPeer[]","description":"Peers"}],"description":"Get dialog info of specified peers","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CONNECTION_DEVICE_MODEL_EMPTY","description":"Device model empty"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"saveDraft","id":3157909835,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"noWebpage","type":"true","optional":true,"predicate":"flags.1","description":"Disable generation of the webpage preview"},{"name":"replyToMsgId","type":"number","optional":true,"predicate":"flags.0","description":"Message ID the message should reply to"},{"name":"peer","type":"InputPeer","description":"Destination of the message that should be sent"},{"name":"message","type":"string","description":"The draft"},{"name":"entities","type":"MessageEntity[]","optional":true,"predicate":"flags.3","description":"Message entities for styled text"}],"description":"Save a message draft associated to a chat.","throws":[{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"getAllDrafts","id":1782549861,"returns":"Updates","arguments":[],"description":"Save get all message drafts.","available":"user"},{"name":"getFeaturedStickers","id":766298703,"returns":"messages.FeaturedStickers","arguments":[{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Get featured stickers","available":"user"},{"name":"readFeaturedStickers","id":1527873830,"returns":"boolean","arguments":[{"name":"id","type":"Long[]","description":"IDs of stickersets to mark as read"}],"description":"Mark new featured stickers as read","available":"user"},{"name":"getRecentStickers","id":1587647177,"returns":"messages.RecentStickers","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"attached","type":"true","optional":true,"predicate":"flags.0","description":"Get stickers recently attached to photo or video files"},{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Get recent stickers","available":"user"},{"name":"saveRecentSticker","id":958863608,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"attached","type":"true","optional":true,"predicate":"flags.0","description":"Whether to add/remove stickers recently attached to photo or video files"},{"name":"id","type":"InputDocument","description":"Sticker"},{"name":"unsave","type":"boolean","description":"Whether to save or unsave the sticker"}],"description":"Add/remove sticker from recent stickers list","throws":[{"code":400,"name":"STICKER_ID_INVALID","description":"The provided sticker ID is invalid"}],"available":"user"},{"name":"clearRecentStickers","id":2308530221,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"attached","type":"true","optional":true,"predicate":"flags.0","description":"Set this flag to clear the list of stickers recently attached to photo or video files"}],"description":"Clear recent stickers","available":"user"},{"name":"getArchivedStickers","id":1475442322,"returns":"messages.ArchivedStickers","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"masks","type":"true","optional":true,"predicate":"flags.0","description":"Get mask stickers"},{"name":"offsetId","type":"Long","description":"Offsets for pagination, for more info click here"},{"name":"limit","type":"number","description":"Maximum number of results to return, see pagination"}],"description":"Get all archived stickers","available":"user"},{"name":"getMaskStickers","id":1706608543,"returns":"messages.AllStickers","arguments":[{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Get installed mask stickers","available":"user"},{"name":"getAttachedStickers","id":3428542412,"returns":"StickerSetCovered[]","arguments":[{"name":"media","type":"InputStickeredMedia","description":"Stickered media"}],"description":"Get stickers attached to a photo or video","available":"user"},{"name":"setGameScore","id":2398678208,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"editMessage","type":"true","optional":true,"predicate":"flags.0","description":"Set this flag if the game message should be automatically edited to include the current scoreboard"},{"name":"force","type":"true","optional":true,"predicate":"flags.1","description":"Set this flag if the high score is allowed to decrease. This can be useful when fixing mistakes or banning cheaters"},{"name":"peer","type":"InputPeer","description":"Unique identifier of target chat"},{"name":"id","type":"number","description":"Identifier of the sent message"},{"name":"userId","type":"InputUser","description":"User identifier"},{"name":"score","type":"number","description":"New score"}],"description":"Use this method to set the score of the specified user in a game sent as a normal message (bots only).","throws":[{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"USER_BOT_REQUIRED","description":"This method can only be called by a bot"}],"available":"both"},{"name":"setInlineGameScore","id":363700068,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"editMessage","type":"true","optional":true,"predicate":"flags.0","description":"Set this flag if the game message should be automatically edited to include the current scoreboard"},{"name":"force","type":"true","optional":true,"predicate":"flags.1","description":"Set this flag if the high score is allowed to decrease. This can be useful when fixing mistakes or banning cheaters"},{"name":"id","type":"InputBotInlineMessageID","description":"ID of the inline message"},{"name":"userId","type":"InputUser","description":"User identifier"},{"name":"score","type":"number","description":"New score"}],"description":"Use this method to set the score of the specified user in a game sent as an inline message (bots only).","throws":[{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"},{"code":400,"name":"USER_BOT_REQUIRED","description":"This method can only be called by a bot"}],"available":"both"},{"name":"getGameHighScores","id":3894568093,"returns":"messages.HighScores","arguments":[{"name":"peer","type":"InputPeer","description":"Where was the game sent"},{"name":"id","type":"number","description":"ID of message with game media attachment"},{"name":"userId","type":"InputUser","description":"Get high scores made by a certain user"}],"description":"Get highscores of a game","throws":[{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"USER_BOT_REQUIRED","description":"This method can only be called by a bot"}],"available":"both"},{"name":"getInlineGameHighScores","id":258170395,"returns":"messages.HighScores","arguments":[{"name":"id","type":"InputBotInlineMessageID","description":"ID of inline message"},{"name":"userId","type":"InputUser","description":"Get high scores of a certain user"}],"description":"Get highscores of a game sent using an inline bot","throws":[{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"},{"code":400,"name":"USER_BOT_REQUIRED","description":"This method can only be called by a bot"}],"available":"both"},{"name":"getCommonChats","id":218777796,"returns":"messages.Chats","arguments":[{"name":"userId","type":"InputUser","description":"User ID"},{"name":"maxId","type":"number","description":"Maximum ID of chat to return (see pagination)"},{"name":"limit","type":"number","description":"Maximum number of results to return, see pagination"}],"description":"Get chats in common with a user","throws":[{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"USER_ID_INVALID","description":"The provided user ID is invalid"}],"available":"user"},{"name":"getAllChats","id":3953659888,"returns":"messages.Chats","arguments":[{"name":"exceptIds","type":"number[]","description":"Except these chats/channels/supergroups"}],"description":"Get all chats, channels and supergroups","available":"user"},{"name":"getWebPage","id":852135825,"returns":"WebPage","arguments":[{"name":"url","type":"string","description":"URL of IV page to fetch"},{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Get instant view page","throws":[{"code":400,"name":"WC_CONVERT_URL_INVALID","description":"WC convert URL invalid"}],"available":"user"},{"name":"toggleDialogPin","id":2805064279,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"pinned","type":"true","optional":true,"predicate":"flags.0","description":"Whether to pin or unpin the dialog"},{"name":"peer","type":"InputDialogPeer","description":"The dialog to pin"}],"description":"Pin/unpin a dialog","throws":[{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"reorderPinnedDialogs","id":991616823,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"force","type":"true","optional":true,"predicate":"flags.0","description":"If set, dialogs pinned server-side but not present in the order field will be unpinned."},{"name":"folderId","type":"number","description":"Peer folder ID, for more info click here"},{"name":"order","type":"InputDialogPeer[]","description":"New dialog order"}],"description":"Reorder pinned dialogs","throws":[{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"getPinnedDialogs","id":3602468338,"returns":"messages.PeerDialogs","arguments":[{"name":"folderId","type":"number","description":"Peer folder ID, for more info click here"}],"description":"Get pinned dialogs","throws":[{"code":400,"name":"FOLDER_ID_INVALID","description":"Invalid folder ID"}],"available":"user"},{"name":"setBotShippingResults","id":3858133754,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"queryId","type":"Long","description":"Unique identifier for the query to be answered"},{"name":"error","type":"string","optional":true,"predicate":"flags.0","description":"Error message in human readable form that explains why it is impossible to complete the order (e.g. \"Sorry, delivery to your desired address is unavailable'). Telegram will display this message to the user."},{"name":"shippingOptions","type":"ShippingOption[]","optional":true,"predicate":"flags.1","description":"A vector of available shipping options."}],"description":"If you sent an invoice requesting a shipping address and the parameter is_flexible was specified, the bot will receive an {@link updateBotShippingQuery} update. Use this method to reply to shipping queries.","throws":[{"code":400,"name":"QUERY_ID_INVALID","description":"The query ID is invalid"}],"available":"both"},{"name":"setBotPrecheckoutResults","id":163765653,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"success","type":"true","optional":true,"predicate":"flags.1","description":"Set this flag if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order, otherwise do not set it, and set the error field, instead"},{"name":"queryId","type":"Long","description":"Unique identifier for the query to be answered"},{"name":"error","type":"string","optional":true,"predicate":"flags.0","description":"Required if the success isn't set. Error message in human readable form that explains the reason for failure to proceed with the checkout (e.g. \"Sorry, somebody just bought the last of our amazing black T-shirts while you were busy filling out your payment details. Please choose a different color or garment!\"). Telegram will display this message to the user."}],"description":"Once the user has confirmed their payment and shipping details, the bot receives an {@link updateBotPrecheckoutQuery} update.
\nUse this method to respond to such pre-checkout queries.
\nNote: Telegram must receive an answer within 10 seconds after the pre-checkout query was sent.","throws":[{"code":400,"name":"ERROR_TEXT_EMPTY","description":"The provided error message is empty"}],"available":"both"},{"name":"uploadMedia","id":1369162417,"returns":"MessageMedia","arguments":[{"name":"peer","type":"InputPeer","description":"The chat, can be an {@link inputPeerEmpty} for bots"},{"name":"media","type":"InputMedia","description":"File uploaded in chunks as described in files »"}],"description":"Upload a file and associate it to a chat (without actually sending it to the chat)","throws":[{"code":400,"name":"BOT_MISSING","description":"This method can only be run by a bot"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"},{"code":400,"name":"FILE_PARTS_INVALID","description":"The number of file parts is invalid"},{"code":400,"name":"IMAGE_PROCESS_FAILED","description":"Failure while processing image"},{"code":400,"name":"INPUT_USER_DEACTIVATED","description":"The specified user was deleted"},{"code":400,"name":"MEDIA_INVALID","description":"Media invalid"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"PHOTO_EXT_INVALID","description":"The extension of the photo is invalid"},{"code":400,"name":"PHOTO_SAVE_FILE_INVALID","description":"Internal issues, try again later"},{"code":400,"name":"USER_BANNED_IN_CHANNEL","description":"You're banned from sending messages in supergroups/channels"},{"code":400,"name":"WEBPAGE_CURL_FAILED","description":"Failure while fetching the webpage with cURL"}],"available":"both"},{"name":"sendScreenshotNotification","id":3380473888,"returns":"Updates","arguments":[{"name":"peer","type":"InputPeer","description":"Other user"},{"name":"replyToMsgId","type":"number","description":"ID of message that was screenshotted, can be 0"},{"name":"randomId","type":"Long","description":"Random ID to avoid message resending"}],"description":"Notify the other user in a private chat that a screenshot of the chat was taken","throws":[{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"getFavedStickers","id":567151374,"returns":"messages.FavedStickers","arguments":[{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Get faved stickers","available":"user"},{"name":"faveSticker","id":3120547163,"returns":"boolean","arguments":[{"name":"id","type":"InputDocument","description":"Sticker to mark as favorite"},{"name":"unfave","type":"boolean","description":"Unfavorite"}],"description":"Mark a sticker as favorite","throws":[{"code":400,"name":"STICKER_ID_INVALID","description":"The provided sticker ID is invalid"}],"available":"user"},{"name":"getUnreadMentions","id":1180140658,"returns":"messages.Messages","arguments":[{"name":"peer","type":"InputPeer","description":"Peer where to look for mentions"},{"name":"offsetId","type":"number","description":"Offsets for pagination, for more info click here"},{"name":"addOffset","type":"number","description":"Offsets for pagination, for more info click here"},{"name":"limit","type":"number","description":"Maximum number of results to return, see pagination"},{"name":"maxId","type":"number","description":"Maximum message ID to return, see pagination"},{"name":"minId","type":"number","description":"Minimum message ID to return, see pagination"}],"description":"Get unread messages where we were mentioned","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"readMentions","id":251759059,"returns":"messages.AffectedHistory","arguments":[{"name":"peer","type":"InputPeer","description":"Dialog"}],"description":"Mark mentions as read","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"getRecentLocations","id":3150207753,"returns":"messages.Messages","arguments":[{"name":"peer","type":"InputPeer","description":"User"},{"name":"limit","type":"number","description":"Maximum number of results to return, see pagination"},{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Get live location history of a certain user","available":"user"},{"name":"sendMultiMedia","id":3422621899,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"silent","type":"true","optional":true,"predicate":"flags.5","description":"Whether to send the album silently (no notification triggered)"},{"name":"background","type":"true","optional":true,"predicate":"flags.6","description":"Send in background?"},{"name":"clearDraft","type":"true","optional":true,"predicate":"flags.7","description":"Whether to clear drafts"},{"name":"peer","type":"InputPeer","description":"The destination chat"},{"name":"replyToMsgId","type":"number","optional":true,"predicate":"flags.0","description":"The message to reply to"},{"name":"multiMedia","type":"InputSingleMedia[]","description":"The medias to send"},{"name":"scheduleDate","type":"number","optional":true,"predicate":"flags.10","description":"Scheduled message date for scheduled messages"}],"description":"Send an album or grouped media","throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"MEDIA_EMPTY","description":"The provided media object is invalid"},{"code":400,"name":"MEDIA_INVALID","description":"Media invalid"},{"code":400,"name":"MULTI_MEDIA_TOO_LONG","description":"Too many media files for album"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"RANDOM_ID_EMPTY","description":"Random ID empty"}],"available":"both"},{"name":"uploadEncryptedFile","id":1347929239,"returns":"EncryptedFile","arguments":[{"name":"peer","type":"InputEncryptedChat","description":"The secret chat to associate the file to"},{"name":"file","type":"InputEncryptedFile","description":"The file"}],"description":"Upload encrypted file and associate it to a secret chat","available":"user"},{"name":"searchStickerSets","id":3266826379,"returns":"messages.FoundStickerSets","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"excludeFeatured","type":"true","optional":true,"predicate":"flags.0","description":"Exclude featured stickersets from results"},{"name":"q","type":"string","description":"Query string"},{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Search for stickersets","available":"user"},{"name":"getSplitRanges","id":486505992,"returns":"MessageRange[]","arguments":[],"description":"Get message ranges for saving the user's chat history","available":"user"},{"name":"markDialogUnread","id":3263617423,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"unread","type":"true","optional":true,"predicate":"flags.0","description":"Mark as unread/read"},{"name":"peer","type":"InputDialogPeer","description":"Dialog"}],"description":"Manually mark dialog as unread","available":"user"},{"name":"getDialogUnreadMarks","id":585256482,"returns":"DialogPeer[]","arguments":[],"description":"Get dialogs manually marked as unread","available":"user"},{"name":"clearAllDrafts","id":2119757468,"returns":"boolean","arguments":[],"description":"Clear all drafts.","available":"user"},{"name":"updatePinnedMessage","id":3534419948,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"silent","type":"true","optional":true,"predicate":"flags.0","description":"Pin the message silently, without triggering a notification"},{"name":"unpin","type":"true","optional":true,"predicate":"flags.1","description":"Whether the message should unpinned or pinned"},{"name":"pmOneside","type":"true","optional":true,"predicate":"flags.2","description":"Whether the message should only be pinned on the local side of a one-to-one chat"},{"name":"peer","type":"InputPeer","description":"The peer where to pin the message"},{"name":"id","type":"number","description":"The message to pin or unpin"}],"description":"Pin a message","throws":[{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"CHAT_NOT_MODIFIED","description":"The pinned message wasn't modified"},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"},{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"},{"code":400,"name":"PIN_RESTRICTED","description":"You can't pin messages"}],"available":"both"},{"name":"sendVote","id":283795844,"returns":"Updates","arguments":[{"name":"peer","type":"InputPeer","description":"The chat where the poll was sent"},{"name":"msgId","type":"number","description":"The message ID of the poll"},{"name":"options","type":"Buffer[]","description":"The options that were chosen"}],"description":"Vote in a {@link poll}","throws":[{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"},{"code":400,"name":"MESSAGE_POLL_CLOSED","description":"Poll closed"},{"code":400,"name":"OPTIONS_TOO_MUCH","description":"Too many options provided"},{"code":400,"name":"OPTION_INVALID","description":"Invalid option selected"},{"code":400,"name":"REVOTE_NOT_ALLOWED","description":"You cannot change your vote"}],"available":"user"},{"name":"getPollResults","id":1941660731,"returns":"Updates","arguments":[{"name":"peer","type":"InputPeer","description":"Peer where the poll was found"},{"name":"msgId","type":"number","description":"Message ID of poll message"}],"description":"Get poll results","throws":[{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"}],"available":"user"},{"name":"getOnlines","id":1848369232,"returns":"ChatOnlines","arguments":[{"name":"peer","type":"InputPeer","description":"The chat"}],"description":"Get count of online users in a chat","throws":[{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"getStatsURL","id":2167155430,"returns":"StatsURL","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"dark","type":"true","optional":true,"predicate":"flags.0","description":"Pass true if a URL with the dark theme must be returned"},{"name":"peer","type":"InputPeer","description":"Chat identifier"},{"name":"params","type":"string","description":"Parameters from tg://statsrefresh?params=****** link"}],"description":"Returns URL with the chat statistics. Currently this method can be used only for channels","throws":[{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"editChatAbout","id":3740665751,"returns":"boolean","arguments":[{"name":"peer","type":"InputPeer","description":"The group/supergroup/channel."},{"name":"about","type":"string","description":"The new description"}],"description":"Edit the description of a group/supergroup/channel.","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ABOUT_NOT_MODIFIED","description":"About text has not changed"},{"code":400,"name":"CHAT_ABOUT_TOO_LONG","description":"Chat about too long"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"CHAT_NOT_MODIFIED","description":"The pinned message wasn't modified"},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"both"},{"name":"editChatDefaultBannedRights","id":2777049921,"returns":"Updates","arguments":[{"name":"peer","type":"InputPeer","description":"The peer"},{"name":"bannedRights","type":"ChatBannedRights","description":"The new global rights"}],"description":"Edit the default banned rights of a channel/supergroup/group.","throws":[{"code":400,"name":"BANNED_RIGHTS_INVALID","description":"You provided some invalid flags in the banned rights"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"CHAT_NOT_MODIFIED","description":"The pinned message wasn't modified"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"UNTIL_DATE_INVALID","description":"Invalid until date provided"}],"available":"both"},{"name":"getEmojiKeywords","id":899735650,"returns":"EmojiKeywordsDifference","arguments":[{"name":"langCode","type":"string","description":"Language code"}],"description":"Get localized emoji keywords","available":"user"},{"name":"getEmojiKeywordsDifference","id":352892591,"returns":"EmojiKeywordsDifference","arguments":[{"name":"langCode","type":"string","description":"Language code"},{"name":"fromVersion","type":"number","description":"Previous emoji keyword localization version"}],"description":"Get changed emoji keywords","available":"user"},{"name":"getEmojiKeywordsLanguages","id":1318675378,"returns":"EmojiLanguage[]","arguments":[{"name":"langCodes","type":"string[]","description":"Language codes"}],"description":"Get info about an emoji keyword localization","available":"user"},{"name":"getEmojiURL","id":3585149990,"returns":"EmojiURL","arguments":[{"name":"langCode","type":"string","description":"Language code for which the emoji replacements will be suggested"}],"description":"Returns an HTTP URL which can be used to automatically log in into translation platform and suggest new emoji replacements. The URL will be valid for 30 seconds after generation","available":"user"},{"name":"getSearchCounters","id":1932455680,"returns":"messages.SearchCounter[]","arguments":[{"name":"peer","type":"InputPeer","description":"Peer where to search"},{"name":"filters","type":"MessagesFilter[]","description":"Search filters"}],"description":"Get the number of results that would be found by a {@link messages.search} call with the same parameters","available":"user"},{"name":"requestUrlAuth","id":428848198,"returns":"UrlAuthResult","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"peer","type":"InputPeer","optional":true,"predicate":"flags.1","description":"Peer where the message is located"},{"name":"msgId","type":"number","optional":true,"predicate":"flags.1","description":"The message"},{"name":"buttonId","type":"number","optional":true,"predicate":"flags.1","description":"The ID of the button with the authorization request"},{"name":"url","type":"string","optional":true,"predicate":"flags.2"}],"description":"Get more info about a Seamless Telegram Login authorization request, for more info click here »","available":"user"},{"name":"acceptUrlAuth","id":2972479781,"returns":"UrlAuthResult","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"writeAllowed","type":"true","optional":true,"predicate":"flags.0","description":"Set this flag to allow the bot to send messages to you (if requested)"},{"name":"peer","type":"InputPeer","optional":true,"predicate":"flags.1","description":"The location of the message"},{"name":"msgId","type":"number","optional":true,"predicate":"flags.1","description":"Message ID of the message with the login button"},{"name":"buttonId","type":"number","optional":true,"predicate":"flags.1","description":"ID of the login button"},{"name":"url","type":"string","optional":true,"predicate":"flags.2"}],"description":"Use this to accept a Seamless Telegram Login authorization request, for more info click here »","available":"user"},{"name":"hidePeerSettingsBar","id":1336717624,"returns":"boolean","arguments":[{"name":"peer","type":"InputPeer","description":"Peer"}],"description":"Should be called after the user hides the report spam/add as contact bar of a new chat, effectively prevents the user from executing the actions specified in the {@link peerSettings}.","available":"user"},{"name":"getScheduledHistory","id":3804391515,"returns":"messages.Messages","arguments":[{"name":"peer","type":"InputPeer","description":"Peer"},{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Get scheduled messages","throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"getScheduledMessages","id":3183150180,"returns":"messages.Messages","arguments":[{"name":"peer","type":"InputPeer","description":"Peer"},{"name":"id","type":"number[]","description":"IDs of scheduled messages"}],"description":"Get scheduled messages","throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"sendScheduledMessages","id":3174597898,"returns":"Updates","arguments":[{"name":"peer","type":"InputPeer","description":"Peer"},{"name":"id","type":"number[]","description":"Scheduled message IDs"}],"description":"Send scheduled messages right away","throws":[{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"}],"available":"user"},{"name":"deleteScheduledMessages","id":1504586518,"returns":"Updates","arguments":[{"name":"peer","type":"InputPeer","description":"Peer"},{"name":"id","type":"number[]","description":"Scheduled message IDs"}],"description":"Delete scheduled messages","available":"user"},{"name":"getPollVotes","id":3094231054,"returns":"messages.VotesList","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"peer","type":"InputPeer","description":"Chat where the poll was sent"},{"name":"id","type":"number","description":"Message ID"},{"name":"option","type":"Buffer","optional":true,"predicate":"flags.0","description":"Get only results for the specified poll option"},{"name":"offset","type":"string","optional":true,"predicate":"flags.1","description":"Offset for results, taken from the next_offset field of {@link messages.votesList}, initially an empty string.
Note: if no more results are available, the method call will return an empty next_offset; thus, avoid providing the next_offset returned in {@link messages.votesList} if it is empty, to avoid an infinite loop."},{"name":"limit","type":"number","description":"Number of results to return"}],"description":"Get poll results for non-anonymous polls","throws":[{"code":403,"name":"BROADCAST_FORBIDDEN","description":" "},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":403,"name":"POLL_VOTE_REQUIRED","description":"Cast a vote in the poll before calling this method"}],"available":"user"},{"name":"toggleStickerSets","id":3037016042,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"uninstall","type":"true","optional":true,"predicate":"flags.0","description":"Uninstall the specified stickersets"},{"name":"archive","type":"true","optional":true,"predicate":"flags.1","description":"Archive the specified stickersets"},{"name":"unarchive","type":"true","optional":true,"predicate":"flags.2","description":"Unarchive the specified stickersets"},{"name":"stickersets","type":"InputStickerSet[]","description":"Stickersets to act upon"}],"description":"Apply changes to multiple stickersets","available":"user"},{"name":"getDialogFilters","id":4053719405,"returns":"DialogFilter[]","arguments":[],"description":"Get folders","available":"user"},{"name":"getSuggestedDialogFilters","id":2728186924,"returns":"DialogFilterSuggested[]","arguments":[],"description":"Get suggested folders","available":"user"},{"name":"updateDialogFilter","id":450142282,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"id","type":"number","description":"Folder ID"},{"name":"filter","type":"DialogFilter","optional":true,"predicate":"flags.0","description":"Folder info"}],"description":"Update folder","throws":[{"code":400,"name":"FILTER_ID_INVALID","description":"The specified filter ID is invalid"}],"available":"user"},{"name":"updateDialogFiltersOrder","id":3311649252,"returns":"boolean","arguments":[{"name":"order","type":"number[]","description":"New folder order"}],"description":"Reorder folders","available":"user"},{"name":"getOldFeaturedStickers","id":1608974939,"returns":"messages.FeaturedStickers","arguments":[{"name":"offset","type":"number","description":"Offset"},{"name":"limit","type":"number","description":"Maximum number of results to return, see pagination"},{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Method for fetching previously featured stickers","available":"user"},{"name":"getReplies","id":615875002,"returns":"messages.Messages","arguments":[{"name":"peer","type":"InputPeer","description":"Peer"},{"name":"msgId","type":"number","description":"Message ID"},{"name":"offsetId","type":"number","description":"Offsets for pagination, for more info click here"},{"name":"offsetDate","type":"number","description":"Offsets for pagination, for more info click here"},{"name":"addOffset","type":"number","description":"Offsets for pagination, for more info click here"},{"name":"limit","type":"number","description":"Maximum number of results to return, see pagination"},{"name":"maxId","type":"number","description":"If a positive value was transferred, the method will return only messages with ID smaller than max_id"},{"name":"minId","type":"number","description":"If a positive value was transferred, the method will return only messages with ID bigger than min_id"},{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Get messages in a reply thread","available":"both"},{"name":"getDiscussionMessage","id":1147761405,"returns":"messages.DiscussionMessage","arguments":[{"name":"peer","type":"InputPeer","description":"Channel ID"},{"name":"msgId","type":"number","description":"Message ID"}],"description":"Get discussion message from the associated discussion group of a channel to show it on top of the comment section, without actually joining the group","available":"both"},{"name":"readDiscussion","id":4147227124,"returns":"boolean","arguments":[{"name":"peer","type":"InputPeer","description":"Group ID"},{"name":"msgId","type":"number","description":"ID of message that started the thread"},{"name":"readMaxId","type":"number","description":"ID up to which thread messages were read"}],"description":"Mark a thread as read","available":"both"},{"name":"unpinAllMessages","id":4029004939,"returns":"messages.AffectedHistory","arguments":[{"name":"peer","type":"InputPeer","description":"Chat where to unpin"}],"description":"Unpin all pinned messages","available":"both"},{"name":"deleteChat","id":2200206609,"returns":"boolean","arguments":[{"name":"chatId","type":"number"}]},{"name":"deletePhoneCallHistory","id":4190888969,"returns":"messages.AffectedFoundMessages","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"revoke","type":"true","optional":true,"predicate":"flags.0"}]},{"name":"checkHistoryImport","id":1140726259,"returns":"messages.HistoryImportParsed","arguments":[{"name":"importHead","type":"string"}]},{"name":"initHistoryImport","id":873008187,"returns":"messages.HistoryImport","arguments":[{"name":"peer","type":"InputPeer"},{"name":"file","type":"InputFile"},{"name":"mediaCount","type":"number"}]},{"name":"uploadImportedMedia","id":713433234,"returns":"MessageMedia","arguments":[{"name":"peer","type":"InputPeer"},{"name":"importId","type":"Long"},{"name":"fileName","type":"string"},{"name":"media","type":"InputMedia"}]},{"name":"startHistoryImport","id":3023958852,"returns":"boolean","arguments":[{"name":"peer","type":"InputPeer"},{"name":"importId","type":"Long"}]},{"name":"getExportedChatInvites","id":2729812982,"returns":"messages.ExportedChatInvites","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"revoked","type":"true","optional":true,"predicate":"flags.3"},{"name":"peer","type":"InputPeer"},{"name":"adminId","type":"InputUser"},{"name":"offsetDate","type":"number","optional":true,"predicate":"flags.2"},{"name":"offsetLink","type":"string","optional":true,"predicate":"flags.2"},{"name":"limit","type":"number"}]},{"name":"getExportedChatInvite","id":1937010524,"returns":"messages.ExportedChatInvite","arguments":[{"name":"peer","type":"InputPeer"},{"name":"link","type":"string"}]},{"name":"editExportedChatInvite","id":48562110,"returns":"messages.ExportedChatInvite","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"revoked","type":"true","optional":true,"predicate":"flags.2"},{"name":"peer","type":"InputPeer"},{"name":"link","type":"string"},{"name":"expireDate","type":"number","optional":true,"predicate":"flags.0"},{"name":"usageLimit","type":"number","optional":true,"predicate":"flags.1"}]},{"name":"deleteRevokedExportedChatInvites","id":1452833749,"returns":"boolean","arguments":[{"name":"peer","type":"InputPeer"},{"name":"adminId","type":"InputUser"}]},{"name":"deleteExportedChatInvite","id":3563365419,"returns":"boolean","arguments":[{"name":"peer","type":"InputPeer"},{"name":"link","type":"string"}]},{"name":"getAdminsWithInvites","id":958457583,"returns":"messages.ChatAdminsWithInvites","arguments":[{"name":"peer","type":"InputPeer"}]},{"name":"getChatInviteImporters","id":654013065,"returns":"messages.ChatInviteImporters","arguments":[{"name":"peer","type":"InputPeer"},{"name":"link","type":"string"},{"name":"offsetDate","type":"number"},{"name":"offsetUser","type":"InputUser"},{"name":"limit","type":"number"}]},{"name":"setHistoryTTL","id":3087949796,"returns":"Updates","arguments":[{"name":"peer","type":"InputPeer"},{"name":"period","type":"number"}]},{"name":"checkHistoryImportPeer","id":1573261059,"returns":"messages.CheckedHistoryImportPeer","arguments":[{"name":"peer","type":"InputPeer"}]}],"unions":[{"type":"Dialogs","subtypes":["messages.dialogs","messages.dialogsSlice","messages.dialogsNotModified"],"description":"Object contains a list of chats with messages and auxiliary data."},{"type":"Messages","subtypes":["messages.messages","messages.messagesSlice","messages.channelMessages","messages.messagesNotModified"],"description":"Object contains infor on list of messages with auxiliary data."},{"type":"Chats","subtypes":["messages.chats","messages.chatsSlice"],"description":"Object contains list of chats with auxiliary data."},{"type":"ChatFull","subtypes":["messages.chatFull"],"description":"Object contains extended info on chat with auxiliary data."},{"type":"AffectedHistory","subtypes":["messages.affectedHistory"],"description":"Object contains info on affected part of communication history with the user or in a chat."},{"type":"DhConfig","subtypes":["messages.dhConfigNotModified","messages.dhConfig"],"description":"Contains info on cofiguring parameters for key generation by Diffie-Hellman protocol."},{"type":"SentEncryptedMessage","subtypes":["messages.sentEncryptedMessage","messages.sentEncryptedFile"],"description":"Contains info on message sent to an encrypted chat."},{"type":"Stickers","subtypes":["messages.stickersNotModified","messages.stickers"],"description":"Stickers"},{"type":"AllStickers","subtypes":["messages.allStickersNotModified","messages.allStickers"],"description":"All stickers"},{"type":"AffectedMessages","subtypes":["messages.affectedMessages"],"description":"Messages affected by changes"},{"type":"StickerSet","subtypes":["messages.stickerSet"],"description":"Stickerset"},{"type":"SavedGifs","subtypes":["messages.savedGifsNotModified","messages.savedGifs"],"description":"Saved GIFs"},{"type":"BotResults","subtypes":["messages.botResults"],"description":"Result of a query to an inline bot"},{"type":"BotCallbackAnswer","subtypes":["messages.botCallbackAnswer"],"description":"Callback answer of bot"},{"type":"MessageEditData","subtypes":["messages.messageEditData"],"description":"Message edit data for media"},{"type":"PeerDialogs","subtypes":["messages.peerDialogs"],"description":"List of dialogs"},{"type":"FeaturedStickers","subtypes":["messages.featuredStickersNotModified","messages.featuredStickers"],"description":"Featured stickers"},{"type":"RecentStickers","subtypes":["messages.recentStickersNotModified","messages.recentStickers"],"description":"Recent stickers"},{"type":"ArchivedStickers","subtypes":["messages.archivedStickers"],"description":"Archived stickers"},{"type":"StickerSetInstallResult","subtypes":["messages.stickerSetInstallResultSuccess","messages.stickerSetInstallResultArchive"],"description":"Result of stickerset installation process"},{"type":"HighScores","subtypes":["messages.highScores"],"description":"High scores (in games)"},{"type":"FavedStickers","subtypes":["messages.favedStickersNotModified","messages.favedStickers"],"description":"Favorited stickers"},{"type":"FoundStickerSets","subtypes":["messages.foundStickerSetsNotModified","messages.foundStickerSets"],"description":"Found stickersets"},{"type":"SearchCounter","subtypes":["messages.searchCounter"],"description":"Number of results that would be returned by a search"},{"type":"InactiveChats","subtypes":["messages.inactiveChats"],"description":"Inactive chat list"},{"type":"VotesList","subtypes":["messages.votesList"],"description":"How users voted in a poll"},{"type":"MessageViews","subtypes":["messages.messageViews"],"description":"View, forward counter + info about replies"},{"type":"DiscussionMessage","subtypes":["messages.discussionMessage"],"description":"Info about a message thread"},{"type":"HistoryImport","subtypes":["messages.historyImport"]},{"type":"HistoryImportParsed","subtypes":["messages.historyImportParsed"]},{"type":"AffectedFoundMessages","subtypes":["messages.affectedFoundMessages"]},{"type":"ExportedChatInvites","subtypes":["messages.exportedChatInvites"]},{"type":"ExportedChatInvite","subtypes":["messages.exportedChatInvite","messages.exportedChatInviteReplaced"]},{"type":"ChatInviteImporters","subtypes":["messages.chatInviteImporters"]},{"type":"ChatAdminsWithInvites","subtypes":["messages.chatAdminsWithInvites"]},{"type":"CheckedHistoryImportPeer","subtypes":["messages.checkedHistoryImportPeer"]}]},"help":{"classes":[{"name":"configSimple","id":1515793004,"type":"help.ConfigSimple","arguments":[{"name":"date","type":"number"},{"name":"expires","type":"number"},{"name":"rules","type":"AccessPointRule[]"}]},{"name":"appUpdate","id":3434860080,"type":"help.AppUpdate","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"canNotSkip","type":"true","optional":true,"predicate":"flags.0","description":"Unskippable, the new info must be shown to the user (with a popup or something else)"},{"name":"id","type":"number","description":"Update ID"},{"name":"version","type":"string","description":"New version name"},{"name":"text","type":"string","description":"Text description of the update"},{"name":"entities","type":"MessageEntity[]","description":"Message entities for styled text"},{"name":"document","type":"Document","optional":true,"predicate":"flags.1","description":"Application binary"},{"name":"url","type":"string","optional":true,"predicate":"flags.2","description":"Application download URL"},{"name":"sticker","type":"Document","optional":true,"predicate":"flags.3"}],"description":"An update is available for the application."},{"name":"noAppUpdate","id":3294258486,"type":"help.AppUpdate","arguments":[],"description":"No updates are available for the application."},{"name":"inviteText","id":415997816,"type":"help.InviteText","arguments":[{"name":"message","type":"string","description":"Text of the message"}],"description":"Text of a text message with an invitation to install Telegram."},{"name":"support","id":398898678,"type":"help.Support","arguments":[{"name":"phoneNumber","type":"string","description":"Phone number"},{"name":"user","type":"User","description":"User"}],"description":"Info on support user."},{"name":"termsOfService","id":2013922064,"type":"help.TermsOfService","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"popup","type":"true","optional":true,"predicate":"flags.0","description":"Whether a prompt must be showed to the user, in order to accept the new terms."},{"name":"id","type":"DataJSON","description":"ID of the new terms"},{"name":"text","type":"string","description":"Text of the new terms"},{"name":"entities","type":"MessageEntity[]","description":"Message entities for styled text"},{"name":"minAgeConfirm","type":"number","optional":true,"predicate":"flags.1","description":"Minimum age required to sign up to telegram, the user must confirm that they is older than the minimum age."}],"description":"Info about the latest telegram Terms Of Service"},{"name":"recentMeUrls","id":235081943,"type":"help.RecentMeUrls","arguments":[{"name":"urls","type":"RecentMeUrl[]","description":"URLs"},{"name":"chats","type":"Chat[]","description":"Chats"},{"name":"users","type":"User[]","description":"Users"}],"description":"Recent t.me URLs"},{"name":"termsOfServiceUpdateEmpty","id":3811614591,"type":"help.TermsOfServiceUpdate","arguments":[{"name":"expires","type":"number","description":"New TOS updates will have to be queried using {@link help.getTermsOfServiceUpdate} in expires seconds"}],"description":"No changes were made to telegram's terms of service"},{"name":"termsOfServiceUpdate","id":686618977,"type":"help.TermsOfServiceUpdate","arguments":[{"name":"expires","type":"number","description":"New TOS updates will have to be queried using {@link help.getTermsOfServiceUpdate} in expires seconds"},{"name":"termsOfService","type":"help.TermsOfService","description":"New terms of service"}],"description":"Info about an update of telegram's terms of service. If the terms of service are declined, then the {@link account.deleteAccount} method should be called with the reason \"Decline ToS update\""},{"name":"deepLinkInfoEmpty","id":1722786150,"type":"help.DeepLinkInfo","arguments":[],"description":"Deep link info empty"},{"name":"deepLinkInfo","id":1783556146,"type":"help.DeepLinkInfo","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"updateApp","type":"true","optional":true,"predicate":"flags.0","description":"An update of the app is required to parse this link"},{"name":"message","type":"string","description":"Message to show to the user"},{"name":"entities","type":"MessageEntity[]","optional":true,"predicate":"flags.1","description":"Message entities for styled text"}],"description":"Deep linking info"},{"name":"passportConfigNotModified","id":3216634967,"type":"help.PassportConfig","arguments":[],"description":"Password configuration not modified"},{"name":"passportConfig","id":2694370991,"type":"help.PassportConfig","arguments":[{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"},{"name":"countriesLangs","type":"DataJSON","description":"Localization"}],"description":"Telegram passport configuration"},{"name":"supportName","id":2349199817,"type":"help.SupportName","arguments":[{"name":"name","type":"string","description":"Localized name"}],"description":"Localized name for telegram support"},{"name":"userInfoEmpty","id":4088278765,"type":"help.UserInfo","arguments":[],"description":"Internal use"},{"name":"userInfo","id":32192344,"type":"help.UserInfo","arguments":[{"name":"message","type":"string","description":"Info"},{"name":"entities","type":"MessageEntity[]","description":"Message entities for styled text"},{"name":"author","type":"string","description":"Author"},{"name":"date","type":"number","description":"Date"}],"description":"Internal use"},{"name":"promoDataEmpty","id":2566302837,"type":"help.PromoData","arguments":[{"name":"expires","type":"number","description":"Re-fetch PSA/MTProxy info after the specified number of seconds"}],"description":"No PSA/MTProxy info is available"},{"name":"promoData","id":2352576831,"type":"help.PromoData","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"proxy","type":"true","optional":true,"predicate":"flags.0","description":"MTProxy-related channel"},{"name":"expires","type":"number","description":"Expiry of PSA/MTProxy info"},{"name":"peer","type":"Peer","description":"MTProxy/PSA peer"},{"name":"chats","type":"Chat[]","description":"Chat info"},{"name":"users","type":"User[]","description":"User info"},{"name":"psaType","type":"string","optional":true,"predicate":"flags.1","description":"PSA type"},{"name":"psaMessage","type":"string","optional":true,"predicate":"flags.2","description":"PSA message"}],"description":"MTProxy/Public Service Announcement information"},{"name":"countryCode","id":1107543535,"type":"help.CountryCode","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"countryCode","type":"string","description":"ISO country code"},{"name":"prefixes","type":"string[]","optional":true,"predicate":"flags.0","description":"Possible phone prefixes"},{"name":"patterns","type":"string[]","optional":true,"predicate":"flags.1","description":"Phone patterns: for example, XXX XXX XXX"}],"description":"Country code and phone number pattern of a specific country"},{"name":"country","id":3280440867,"type":"help.Country","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"hidden","type":"true","optional":true,"predicate":"flags.0","description":"Whether this country should not be shown in the list"},{"name":"iso2","type":"string","description":"ISO code of country"},{"name":"defaultName","type":"string","description":"Name of the country in the country's language"},{"name":"name","type":"string","optional":true,"predicate":"flags.1","description":"Name of the country in the user's language, if different from the original name"},{"name":"countryCodes","type":"help.CountryCode[]","description":"Phone codes/patterns"}],"description":"Name, ISO code, localized name and phone codes/patterns of a specific country"},{"name":"countriesListNotModified","id":2479628082,"type":"help.CountriesList","arguments":[],"description":"The country list has not changed"},{"name":"countriesList","id":2278585758,"type":"help.CountriesList","arguments":[{"name":"countries","type":"help.Country[]","description":"Name, ISO code, localized name and phone codes/patterns of all available countries"},{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Name, ISO code, localized name and phone codes/patterns of all available countries"}],"methods":[{"name":"getConfig","id":3304659051,"returns":"Config","arguments":[],"description":"Returns current configuration, including data center configuration.","throws":[{"code":400,"name":"CONNECTION_API_ID_INVALID","description":"The provided API id is invalid"},{"code":400,"name":"CONNECTION_APP_VERSION_EMPTY","description":"App version is empty"},{"code":400,"name":"CONNECTION_DEVICE_MODEL_EMPTY","description":"Device model empty"},{"code":400,"name":"CONNECTION_LANG_PACK_INVALID","description":"Language pack invalid"},{"code":400,"name":"CONNECTION_LAYER_INVALID","description":"Layer invalid"},{"code":400,"name":"CONNECTION_NOT_INITED","description":"Connection not initialized"},{"code":400,"name":"CONNECTION_SYSTEM_EMPTY","description":"Connection system empty"},{"code":400,"name":"CONNECTION_SYSTEM_LANG_CODE_EMPTY","description":"The system_lang_code field is empty"},{"code":400,"name":"DATA_INVALID","description":"Encrypted data invalid"},{"code":400,"name":"INPUT_LAYER_INVALID","description":"The provided layer is invalid"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"}],"available":"both"},{"name":"getNearestDc","id":531836966,"returns":"NearestDc","arguments":[],"description":"Returns info on data centre nearest to the user.","available":"user"},{"name":"getAppUpdate","id":1378703997,"returns":"help.AppUpdate","arguments":[{"name":"source","type":"string","description":"Source"}],"description":"Returns information on update availability for the current application.","available":"user"},{"name":"getInviteText","id":1295590211,"returns":"help.InviteText","arguments":[],"description":"Returns localized text of a text message with an invitation.","available":"user"},{"name":"getSupport","id":2631862477,"returns":"help.Support","arguments":[],"description":"Returns the support user for the 'ask a question' feature.","available":"user"},{"name":"getAppChangelog","id":2417028975,"returns":"Updates","arguments":[{"name":"prevAppVersion","type":"string","description":"Previous app version"}],"description":"Get changelog of current app.
\nTypically, an {@link updates} constructor will be returned, containing one or more {@link updateServiceNotification} updates with app-specific changelogs.","available":"user"},{"name":"setBotUpdatesStatus","id":3961704397,"returns":"boolean","arguments":[{"name":"pendingUpdatesCount","type":"number","description":"Number of pending updates"},{"name":"message","type":"string","description":"Error message, if present"}],"description":"Informs the server about the number of pending bot updates if they haven't been processed for a long time; for bots only","available":"bot"},{"name":"getCdnConfig","id":1375900482,"returns":"CdnConfig","arguments":[],"description":"Get configuration for CDN file downloads.","throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","description":"The temporary auth key must be binded to the permanent auth key to use these methods."}],"available":"both"},{"name":"getRecentMeUrls","id":1036054804,"returns":"help.RecentMeUrls","arguments":[{"name":"referer","type":"string","description":"Referer"}],"description":"Get recently used t.me links","available":"user"},{"name":"getTermsOfServiceUpdate","id":749019089,"returns":"help.TermsOfServiceUpdate","arguments":[],"description":"Look for updates of telegram's terms of service","available":"user"},{"name":"acceptTermsOfService","id":4000511898,"returns":"boolean","arguments":[{"name":"id","type":"DataJSON","description":"ID of terms of service"}],"description":"Accept the new terms of service","available":"user"},{"name":"getDeepLinkInfo","id":1072547679,"returns":"help.DeepLinkInfo","arguments":[{"name":"path","type":"string","description":"Path in t.me/path"}],"description":"Get info about a t.me link","available":"user"},{"name":"getAppConfig","id":2559656208,"returns":"JSONValue","arguments":[],"description":"Get app-specific configuration, see client configuration for more info on the result.","available":"user"},{"name":"saveAppLog","id":1862465352,"returns":"boolean","arguments":[{"name":"events","type":"InputAppEvent[]","description":"List of input events"}],"description":"Saves logs of application on the server.","available":"user"},{"name":"getPassportConfig","id":3328290056,"returns":"help.PassportConfig","arguments":[{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Get passport configuration","available":"user"},{"name":"getSupportName","id":3546343212,"returns":"help.SupportName","arguments":[],"description":"Get localized name of the telegram support user","throws":[{"code":403,"name":"USER_INVALID","description":"Invalid user provided"}],"available":"user"},{"name":"getUserInfo","id":59377875,"returns":"help.UserInfo","arguments":[{"name":"userId","type":"InputUser","description":"User ID"}],"description":"Internal use","throws":[{"code":403,"name":"USER_INVALID","description":"Invalid user provided"}],"available":"user"},{"name":"editUserInfo","id":1723407216,"returns":"help.UserInfo","arguments":[{"name":"userId","type":"InputUser","description":"User"},{"name":"message","type":"string","description":"Message"},{"name":"entities","type":"MessageEntity[]","description":"Message entities for styled text"}],"description":"Internal use","available":"user"},{"name":"getPromoData","id":3231151137,"returns":"help.PromoData","arguments":[],"description":"Get MTProxy/Public Service Announcement information","available":"both"},{"name":"hidePromoData","id":505748629,"returns":"boolean","arguments":[{"name":"peer","type":"InputPeer","description":"Peer to hide"}],"description":"Hide MTProxy/Public Service Announcement information","available":"both"},{"name":"dismissSuggestion","id":4111317665,"returns":"boolean","arguments":[{"name":"peer","type":"InputPeer"},{"name":"suggestion","type":"string","description":"Suggestion"}],"description":"Dismiss a suggestion","available":"both"},{"name":"getCountriesList","id":1935116200,"returns":"help.CountriesList","arguments":[{"name":"langCode","type":"string","description":"Language code of the current user"},{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Get name, ISO code, localized name and phone codes/patterns of all available countries","available":"both"}],"unions":[{"type":"ConfigSimple","subtypes":["help.configSimple"]},{"type":"AppUpdate","subtypes":["help.appUpdate","help.noAppUpdate"],"description":"Contains info on app update availability."},{"type":"InviteText","subtypes":["help.inviteText"],"description":"Object contains info on the text of a message with an invitation."},{"type":"Support","subtypes":["help.support"],"description":"Info about the support user, relevant to the current user."},{"type":"TermsOfService","subtypes":["help.termsOfService"],"description":"Contains info about the latest telegram Terms Of Service."},{"type":"RecentMeUrls","subtypes":["help.recentMeUrls"],"description":"Recent t.me URLs"},{"type":"TermsOfServiceUpdate","subtypes":["help.termsOfServiceUpdateEmpty","help.termsOfServiceUpdate"],"description":"Update of Telegram's terms of service"},{"type":"DeepLinkInfo","subtypes":["help.deepLinkInfoEmpty","help.deepLinkInfo"],"description":"Contains information about a tg:// deep link"},{"type":"PassportConfig","subtypes":["help.passportConfigNotModified","help.passportConfig"],"description":"Telegram passport configuration"},{"type":"SupportName","subtypes":["help.supportName"],"description":"Get localized name for support user"},{"type":"UserInfo","subtypes":["help.userInfoEmpty","help.userInfo"],"description":"User info"},{"type":"PromoData","subtypes":["help.promoDataEmpty","help.promoData"],"description":"Info about pinned MTProxy or Public Service Announcement peers."},{"type":"CountryCode","subtypes":["help.countryCode"],"description":"Country code and phone number pattern of a specific country"},{"type":"Country","subtypes":["help.country"],"description":"Name, ISO code, localized name and phone codes/patterns of a specific country"},{"type":"CountriesList","subtypes":["help.countriesListNotModified","help.countriesList"],"description":"Name, ISO code, localized name and phone codes/patterns of all available countries"}]},"test":{"classes":[],"methods":[{"name":"useError","id":4000689921,"returns":"Error","arguments":[]},{"name":"useConfigSimple","id":4189565501,"returns":"help.ConfigSimple","arguments":[]}],"unions":[]},"storage":{"classes":[{"name":"fileUnknown","id":2861972229,"type":"storage.FileType","arguments":[],"description":"Unknown type."},{"name":"filePartial","id":1086091090,"type":"storage.FileType","arguments":[],"description":"Part of a bigger file."},{"name":"fileJpeg","id":8322574,"type":"storage.FileType","arguments":[],"description":"JPEG image. MIME type: image/jpeg."},{"name":"fileGif","id":3403786975,"type":"storage.FileType","arguments":[],"description":"GIF image. MIME type: image/gif."},{"name":"filePng","id":172975040,"type":"storage.FileType","arguments":[],"description":"PNG image. MIME type: image/png."},{"name":"filePdf","id":2921222285,"type":"storage.FileType","arguments":[],"description":"PDF document image. MIME type: application/pdf."},{"name":"fileMp3","id":1384777335,"type":"storage.FileType","arguments":[],"description":"Mp3 audio. MIME type: audio/mpeg."},{"name":"fileMov","id":1258941372,"type":"storage.FileType","arguments":[],"description":"Quicktime video. MIME type: video/quicktime."},{"name":"fileMp4","id":3016663268,"type":"storage.FileType","arguments":[],"description":"MPEG-4 video. MIME type: video/mp4."},{"name":"fileWebp","id":276907596,"type":"storage.FileType","arguments":[],"description":"WEBP image. MIME type: image/webp."}],"methods":[],"unions":[{"type":"FileType","subtypes":["storage.fileUnknown","storage.filePartial","storage.fileJpeg","storage.fileGif","storage.filePng","storage.filePdf","storage.fileMp3","storage.fileMov","storage.fileMp4","storage.fileWebp"],"description":"Object describes the file type."}]},"auth":{"classes":[{"name":"sentCode","id":1577067778,"type":"auth.SentCode","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"type","type":"auth.SentCodeType","description":"Phone code type"},{"name":"phoneCodeHash","type":"string","description":"Phone code hash, to be stored and later re-used with {@link auth.signIn}"},{"name":"nextType","type":"auth.CodeType","optional":true,"predicate":"flags.1","description":"Phone code type that will be sent next, if the phone code is not received within timeout seconds: to send it use {@link auth.resendCode}"},{"name":"timeout","type":"number","optional":true,"predicate":"flags.2","description":"Timeout for reception of the phone code"}],"description":"Contains info about a sent verification code."},{"name":"authorization","id":3439659286,"type":"auth.Authorization","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"tmpSessions","type":"number","optional":true,"predicate":"flags.0","description":"Temporary passport sessions"},{"name":"user","type":"User","description":"Info on authorized user"}],"description":"Contains user authorization info."},{"name":"authorizationSignUpRequired","id":1148485274,"type":"auth.Authorization","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"termsOfService","type":"help.TermsOfService","optional":true,"predicate":"flags.0","description":"Telegram's terms of service: the user must read and accept the terms of service before signing up to telegram"}],"description":"An account with this phone number doesn't exist on telegram: the user has to enter basic information and sign up"},{"name":"exportedAuthorization","id":3751189549,"type":"auth.ExportedAuthorization","arguments":[{"name":"id","type":"number","description":"current user identifier"},{"name":"bytes","type":"Buffer","description":"authorizes key"}],"description":"Data for copying of authorization between data centres."},{"name":"passwordRecovery","id":326715557,"type":"auth.PasswordRecovery","arguments":[{"name":"emailPattern","type":"string","description":"The email to which the recovery code was sent must match this pattern."}],"description":"Recovery info of a 2FA password, only for accounts with a recovery email configured."},{"name":"codeTypeSms","id":1923290508,"type":"auth.CodeType","arguments":[],"description":"Type of verification code that will be sent next if you call the resendCode method: SMS code"},{"name":"codeTypeCall","id":1948046307,"type":"auth.CodeType","arguments":[],"description":"Type of verification code that will be sent next if you call the resendCode method: SMS code"},{"name":"codeTypeFlashCall","id":577556219,"type":"auth.CodeType","arguments":[],"description":"Type of verification code that will be sent next if you call the resendCode method: SMS code"},{"name":"sentCodeTypeApp","id":1035688326,"type":"auth.SentCodeType","arguments":[{"name":"length","type":"number","description":"Length of the code in bytes"}],"description":"The code was sent through the telegram app"},{"name":"sentCodeTypeSms","id":3221273506,"type":"auth.SentCodeType","arguments":[{"name":"length","type":"number","description":"Length of the code in bytes"}],"description":"The code was sent via SMS"},{"name":"sentCodeTypeCall","id":1398007207,"type":"auth.SentCodeType","arguments":[{"name":"length","type":"number","description":"Length of the verification code"}],"description":"The code will be sent via a phone call: a synthesized voice will tell the user which verification code to input."},{"name":"sentCodeTypeFlashCall","id":2869151449,"type":"auth.SentCodeType","arguments":[{"name":"pattern","type":"string","description":"pattern to match"}],"description":"The code will be sent via a flash phone call, that will be closed immediately. The phone code will then be the phone number itself, just make sure that the phone number matches the specified pattern."},{"name":"loginToken","id":1654593920,"type":"auth.LoginToken","arguments":[{"name":"expires","type":"number","description":"Expiry date of QR code"},{"name":"token","type":"Buffer","description":"Token to render in QR code"}],"description":"Login token (for QR code login)"},{"name":"loginTokenMigrateTo","id":110008598,"type":"auth.LoginToken","arguments":[{"name":"dcId","type":"number","description":"DC ID"},{"name":"token","type":"Buffer","description":"Token to use for login"}],"description":"Repeat the query to the specified DC"},{"name":"loginTokenSuccess","id":957176926,"type":"auth.LoginToken","arguments":[{"name":"authorization","type":"auth.Authorization","description":"Authorization info"}],"description":"Login via token (QR code) succeded!"}],"methods":[{"name":"sendCode","id":2792825935,"returns":"auth.SentCode","arguments":[{"name":"phoneNumber","type":"string","description":"Phone number in international format"},{"name":"apiId","type":"number","description":"Application identifier (see App configuration)"},{"name":"apiHash","type":"string","description":"Application secret hash (see App configuration)"},{"name":"settings","type":"CodeSettings","description":"Settings for the code type to send"}],"description":"Send the verification code for login","throws":[{"code":400,"name":"API_ID_INVALID","description":"API ID invalid"},{"code":400,"name":"API_ID_PUBLISHED_FLOOD","description":"This API id was published somewhere, you can't use it now"},{"code":401,"name":"AUTH_KEY_PERM_EMPTY","description":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"INPUT_REQUEST_TOO_LONG","description":"The request is too big"},{"code":303,"name":"NETWORK_MIGRATE_X","description":"Repeat the query to data-center X"},{"code":303,"name":"PHONE_MIGRATE_X","description":"Repeat the query to data-center X"},{"code":400,"name":"PHONE_NUMBER_APP_SIGNUP_FORBIDDEN","description":"You can't sign up using this app"},{"code":400,"name":"PHONE_NUMBER_BANNED","description":"The provided phone number is banned from telegram"},{"code":400,"name":"PHONE_NUMBER_FLOOD","description":"You asked for the code too many times."},{"code":400,"name":"PHONE_NUMBER_INVALID","description":"Invalid phone number"},{"code":406,"name":"PHONE_PASSWORD_FLOOD","description":"You have tried logging in too many times"},{"code":400,"name":"PHONE_PASSWORD_PROTECTED","description":"This phone is password protected"},{"code":400,"name":"SMS_CODE_CREATE_FAILED","description":"An error occurred while creating the SMS code"}],"available":"user"},{"name":"signUp","id":2163139623,"returns":"auth.Authorization","arguments":[{"name":"phoneNumber","type":"string","description":"Phone number in the international format"},{"name":"phoneCodeHash","type":"string","description":"SMS-message ID"},{"name":"firstName","type":"string","description":"New user first name"},{"name":"lastName","type":"string","description":"New user last name"}],"description":"Registers a validated phone number in the system.","throws":[{"code":400,"name":"FIRSTNAME_INVALID","description":"Invalid first name"},{"code":400,"name":"INPUT_REQUEST_TOO_LONG","description":"The request is too big"},{"code":400,"name":"LASTNAME_INVALID","description":"Invalid last name"},{"code":400,"name":"PHONE_CODE_EMPTY","description":"phone_code from a SMS is empty"},{"code":400,"name":"PHONE_CODE_EXPIRED","description":"SMS expired"},{"code":400,"name":"PHONE_CODE_INVALID","description":"Invalid SMS code was sent"},{"code":400,"name":"PHONE_NUMBER_FLOOD","description":"You asked for the code too many times."},{"code":400,"name":"PHONE_NUMBER_INVALID","description":"Invalid phone number"},{"code":400,"name":"PHONE_NUMBER_OCCUPIED","description":"The phone number is already in use"}],"available":"user"},{"name":"signIn","id":3168081281,"returns":"auth.Authorization","arguments":[{"name":"phoneNumber","type":"string","description":"Phone number in the international format"},{"name":"phoneCodeHash","type":"string","description":"SMS-message ID, obtained from {@link auth.sendCode}"},{"name":"phoneCode","type":"string","description":"Valid numerical code from the SMS-message"}],"description":"Signs in a user with a validated phone number.","throws":[{"code":400,"name":"PHONE_CODE_EMPTY","description":"phone_code from the SMS is empty"},{"code":400,"name":"PHONE_CODE_EXPIRED","description":"SMS expired"},{"code":400,"name":"PHONE_CODE_INVALID","description":"Invalid SMS code was sent"},{"code":400,"name":"PHONE_NUMBER_INVALID","description":"Invalid phone number"},{"code":400,"name":"PHONE_NUMBER_UNOCCUPIED","description":"The code is valid but no user with the given number is registered"}],"available":"user"},{"name":"logOut","id":1461180992,"returns":"boolean","arguments":[],"description":"Logs out the user.","available":"both"},{"name":"resetAuthorizations","id":2678787354,"returns":"boolean","arguments":[],"description":"Terminates all user's authorized sessions except for the current one.","throws":[{"code":406,"name":"FRESH_RESET_AUTHORISATION_FORBIDDEN","description":"You can't logout other sessions if less than 24 hours have passed since you logged on the current session"}],"available":"user"},{"name":"exportAuthorization","id":3854565325,"returns":"auth.ExportedAuthorization","arguments":[{"name":"dcId","type":"number","description":"Number of a target data-centre"}],"description":"Returns data for copying authorization to another data-centre.","throws":[{"code":400,"name":"DC_ID_INVALID","description":"The provided DC ID is invalid"}],"available":"both"},{"name":"importAuthorization","id":3824129555,"returns":"auth.Authorization","arguments":[{"name":"id","type":"number","description":"User ID"},{"name":"bytes","type":"Buffer","description":"Authorization key"}],"description":"Logs in a user using a key transmitted from their native data-centre.","throws":[{"code":400,"name":"AUTH_BYTES_INVALID","description":"The provided authorization is invalid"},{"code":400,"name":"USER_ID_INVALID","description":"The provided user ID is invalid"}],"available":"both"},{"name":"bindTempAuthKey","id":3453233669,"returns":"boolean","arguments":[{"name":"permAuthKeyId","type":"Long","description":"Permanent auth_key_id to bind to"},{"name":"nonce","type":"Long","description":"Random long from {@link auth.bindTempAuthKey}"},{"name":"expiresAt","type":"number","description":"UNIX timestamp in seconds to invalidate temporary key, see {@link auth.bindTempAuthKey}"},{"name":"encryptedMessage","type":"Buffer","description":"See {@link auth.bindTempAuthKey}"}],"description":"Binds a temporary authorization key temp_auth_key_id to the permanent authorization key perm_auth_key_id. Each permanent key may only be bound to one temporary key at a time, binding a new temporary key overwrites the previous one.","throws":[{"code":400,"name":"ENCRYPTED_MESSAGE_INVALID","description":"Encrypted message is incorrect"},{"code":400,"name":"INPUT_REQUEST_TOO_LONG","description":"The request is too big"},{"code":400,"name":"TEMP_AUTH_KEY_ALREADY_BOUND","description":"The passed temporary key is already bound to another perm_auth_key_id"},{"code":400,"name":"TEMP_AUTH_KEY_EMPTY","description":"The request was not performed with a temporary authorization key"}],"available":"both"},{"name":"importBotAuthorization","id":1738800940,"returns":"auth.Authorization","arguments":[{"name":"flags","type":"number","description":"Reserved for future use"},{"name":"apiId","type":"number","description":"Application identifier (see. App configuration)"},{"name":"apiHash","type":"string","description":"Application identifier hash (see. App configuration)"},{"name":"botAuthToken","type":"string","description":"Bot token (see bots)"}],"description":"Login as a bot","throws":[{"code":400,"name":"ACCESS_TOKEN_EXPIRED","description":"Bot token expired"},{"code":400,"name":"ACCESS_TOKEN_INVALID","description":"The provided token is not valid"},{"code":400,"name":"API_ID_INVALID","description":"The api_id/api_hash combination is invalid"},{"code":401,"name":"AUTH_KEY_INVALID","description":"Auth key invalid"}],"available":"both"},{"name":"checkPassword","id":3515567382,"returns":"auth.Authorization","arguments":[{"name":"password","type":"InputCheckPasswordSRP","description":"The account's password (see SRP)"}],"description":"Try logging to an account protected by a 2FA password.","throws":[{"code":400,"name":"PASSWORD_HASH_INVALID","description":"The provided password isn't valid"},{"code":400,"name":"SRP_ID_INVALID","description":"Invalid SRP ID provided"},{"code":400,"name":"SRP_PASSWORD_CHANGED","description":"Password has changed"}],"available":"user"},{"name":"requestPasswordRecovery","id":3633822822,"returns":"auth.PasswordRecovery","arguments":[],"description":"Request recovery code of a 2FA password, only for accounts with a recovery email configured.","throws":[{"code":400,"name":"PASSWORD_EMPTY","description":"The provided password is empty"}],"available":"user"},{"name":"recoverPassword","id":923364464,"returns":"auth.Authorization","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"code","type":"string","description":"Code received via email"},{"name":"newSettings","type":"account.PasswordInputSettings","optional":true,"predicate":"flags.0"}],"description":"Reset the 2FA password using the recovery code sent using {@link auth.requestPasswordRecovery}.","throws":[{"code":400,"name":"CODE_EMPTY","description":"The provided code is empty"}],"available":"user"},{"name":"resendCode","id":1056025023,"returns":"auth.SentCode","arguments":[{"name":"phoneNumber","type":"string","description":"The phone number"},{"name":"phoneCodeHash","type":"string","description":"The phone code hash obtained from {@link auth.sendCode}"}],"description":"Resend the login code via another medium, the phone code type is determined by the return value of the previous auth.sendCode/auth.resendCode: see login for more info.","throws":[{"code":400,"name":"PHONE_CODE_EXPIRED","description":"The phone code you provided has expired, this may happen if it was sent to any chat on telegram (if the code is sent through a telegram chat (not the official account) to avoid it append or prepend to the code some chars)"},{"code":400,"name":"PHONE_CODE_HASH_EMPTY","description":"phone_code_hash is missing"},{"code":400,"name":"PHONE_NUMBER_INVALID","description":"The phone number is invalid"}],"available":"user"},{"name":"cancelCode","id":520357240,"returns":"boolean","arguments":[{"name":"phoneNumber","type":"string","description":"Phone number"},{"name":"phoneCodeHash","type":"string","description":"Phone code hash from {@link auth.sendCode}"}],"description":"Cancel the login verification code","throws":[{"code":400,"name":"PHONE_CODE_EXPIRED","description":"The phone code you provided has expired, this may happen if it was sent to any chat on telegram (if the code is sent through a telegram chat (not the official account) to avoid it append or prepend to the code some chars)"},{"code":400,"name":"PHONE_NUMBER_INVALID","description":"The phone number is invalid"}],"available":"user"},{"name":"dropTempAuthKeys","id":2387124616,"returns":"boolean","arguments":[{"name":"exceptAuthKeys","type":"Long[]","description":"The auth keys that shouldn't be dropped."}],"description":"Delete all temporary authorization keys except for the ones specified","available":"both"},{"name":"exportLoginToken","id":2981369111,"returns":"auth.LoginToken","arguments":[{"name":"apiId","type":"number","description":"Application identifier (see. App configuration)"},{"name":"apiHash","type":"string","description":"Application identifier hash (see. App configuration)"},{"name":"exceptIds","type":"number[]","description":"List of already logged-in user IDs, to prevent logging in twice with the same user"}],"description":"Generate a login token, for login via QR code.
\nThe generated login token should be encoded using base64url, then shown as a tg://login?token=base64encodedtoken URL in the QR code.","available":"user"},{"name":"importLoginToken","id":2511101156,"returns":"auth.LoginToken","arguments":[{"name":"token","type":"Buffer","description":"Login token"}],"description":"Login using a redirected login token, generated in case of DC mismatch during QR code login.","throws":[{"code":400,"name":"AUTH_TOKEN_EXPIRED","description":"The authorization token has expired"}],"available":"user"},{"name":"acceptLoginToken","id":3902057805,"returns":"Authorization","arguments":[{"name":"token","type":"Buffer","description":"Login token embedded in QR code, for more info, see login via QR code."}],"description":"Accept QR code login token, logging in the app that generated it.","throws":[{"code":400,"name":"AUTH_TOKEN_INVALIDX","description":"The specified auth token is invalid"}],"available":"user"},{"name":"checkRecoveryPassword","id":221691769,"returns":"boolean","arguments":[{"name":"code","type":"string"}]}],"unions":[{"type":"SentCode","subtypes":["auth.sentCode"],"description":"Contains info on a confirmation code message sent via SMS, phone call or Telegram."},{"type":"Authorization","subtypes":["auth.authorization","auth.authorizationSignUpRequired"],"description":"Oject contains info on user authorization."},{"type":"ExportedAuthorization","subtypes":["auth.exportedAuthorization"],"description":"Exported authorization"},{"type":"PasswordRecovery","subtypes":["auth.passwordRecovery"],"description":"Recovery info of a 2FA password, only for accounts with a recovery email configured."},{"type":"CodeType","subtypes":["auth.codeTypeSms","auth.codeTypeCall","auth.codeTypeFlashCall"],"description":"Type of verification code that will be sent next if you call the resendCode method"},{"type":"SentCodeType","subtypes":["auth.sentCodeTypeApp","auth.sentCodeTypeSms","auth.sentCodeTypeCall","auth.sentCodeTypeFlashCall"],"description":"Type of the verification code that was sent"},{"type":"LoginToken","subtypes":["auth.loginToken","auth.loginTokenMigrateTo","auth.loginTokenSuccess"],"description":"Login token (for QR code login)"}]},"contacts":{"classes":[{"name":"contactsNotModified","id":3075189202,"type":"contacts.Contacts","arguments":[],"description":"Contact list on the server is the same as the list on the client."},{"name":"contacts","id":3941105218,"type":"contacts.Contacts","arguments":[{"name":"contacts","type":"Contact[]","description":"Contact list"},{"name":"savedCount","type":"number","description":"Number of contacts that were saved successfully"},{"name":"users","type":"User[]","description":"User list"}],"description":"The current user's contact list and info on users."},{"name":"importedContacts","id":2010127419,"type":"contacts.ImportedContacts","arguments":[{"name":"imported","type":"ImportedContact[]","description":"List of succesfully imported contacts"},{"name":"popularInvites","type":"PopularContact[]","description":"Popular contacts"},{"name":"retryContacts","type":"Long[]","description":"List of contact ids that could not be imported due to system limitation and will need to be imported at a later date.
Parameter added in Layer 13"},{"name":"users","type":"User[]","description":"List of users"}],"description":"Info on succesfully imported contacts."},{"name":"blocked","id":182326673,"type":"contacts.Blocked","arguments":[{"name":"blocked","type":"PeerBlocked[]","description":"List of blocked users"},{"name":"chats","type":"Chat[]","description":"Blocked chats"},{"name":"users","type":"User[]","description":"List of users"}],"description":"Full list of blocked users."},{"name":"blockedSlice","id":3781575060,"type":"contacts.Blocked","arguments":[{"name":"count","type":"number","description":"Total number of elements in the list"},{"name":"blocked","type":"PeerBlocked[]","description":"List of blocked users"},{"name":"chats","type":"Chat[]","description":"Blocked chats"},{"name":"users","type":"User[]","description":"List of users"}],"description":"Incomplete list of blocked users."},{"name":"found","id":3004386717,"type":"contacts.Found","arguments":[{"name":"myResults","type":"Peer[]","description":"Personalized results"},{"name":"results","type":"Peer[]","description":"List of found user identifiers"},{"name":"chats","type":"Chat[]","description":"Found chats"},{"name":"users","type":"User[]","description":"List of users"}],"description":"Users found by name substring and auxiliary data."},{"name":"resolvedPeer","id":2131196633,"type":"contacts.ResolvedPeer","arguments":[{"name":"peer","type":"Peer","description":"The peer"},{"name":"chats","type":"Chat[]","description":"Chats"},{"name":"users","type":"User[]","description":"Users"}],"description":"Resolved peer"},{"name":"topPeersNotModified","id":3727060725,"type":"contacts.TopPeers","arguments":[],"description":"Top peer info hasn't changed"},{"name":"topPeers","id":1891070632,"type":"contacts.TopPeers","arguments":[{"name":"categories","type":"TopPeerCategoryPeers[]","description":"Top peers by top peer category"},{"name":"chats","type":"Chat[]","description":"Chats"},{"name":"users","type":"User[]","description":"Users"}],"description":"Top peers"},{"name":"topPeersDisabled","id":3039597469,"type":"contacts.TopPeers","arguments":[],"description":"Top peers disabled"}],"methods":[{"name":"getContactIDs","id":749357634,"returns":"number[]","arguments":[{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Get contact by telegram IDs","available":"user"},{"name":"getStatuses","id":3299038190,"returns":"ContactStatus[]","arguments":[],"description":"Returns the list of contact statuses.","available":"user"},{"name":"getContacts","id":3223553183,"returns":"contacts.Contacts","arguments":[{"name":"hash","type":"number","description":"If there already is a full contact list on the client, a hash of a the list of contact IDs in ascending order may be passed in this parameter. If the contact set was not changed, {@link contacts.contactsNotModified} will be returned."}],"description":"Returns the current user's contact list.","available":"user"},{"name":"importContacts","id":746589157,"returns":"contacts.ImportedContacts","arguments":[{"name":"contacts","type":"InputContact[]","description":"List of contacts to import"}],"description":"Imports contacts: saves a full list on the server, adds already registered contacts to the contact list, returns added contacts and their info.","available":"user"},{"name":"deleteContacts","id":157945344,"returns":"Updates","arguments":[{"name":"id","type":"InputUser[]","description":"User ID list"}],"description":"Deletes several contacts from the list.","available":"user"},{"name":"deleteByPhones","id":269745566,"returns":"boolean","arguments":[{"name":"phones","type":"string[]","description":"Phone numbers"}],"description":"Delete contacts by phone number","available":"user"},{"name":"block","id":1758204945,"returns":"boolean","arguments":[{"name":"id","type":"InputPeer","description":"User ID"}],"description":"Adds the user to the blacklist.","throws":[{"code":400,"name":"CONTACT_ID_INVALID","description":"The provided contact ID is invalid"},{"code":400,"name":"INPUT_USER_DEACTIVATED","description":"The specified user was deleted"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"unblock","id":3198573904,"returns":"boolean","arguments":[{"name":"id","type":"InputPeer","description":"User ID"}],"description":"Deletes the user from the blacklist.","throws":[{"code":400,"name":"CONTACT_ID_INVALID","description":"The provided contact ID is invalid"}],"available":"user"},{"name":"getBlocked","id":4118557967,"returns":"contacts.Blocked","arguments":[{"name":"offset","type":"number","description":"The number of list elements to be skipped"},{"name":"limit","type":"number","description":"The number of list elements to be returned"}],"description":"Returns the list of blocked users.","available":"user"},{"name":"search","id":301470424,"returns":"contacts.Found","arguments":[{"name":"q","type":"string","description":"Target substring"},{"name":"limit","type":"number","description":"Maximum number of users to be returned"}],"description":"Returns users found by username substring.","throws":[{"code":400,"name":"QUERY_TOO_SHORT","description":"The query string is too short"},{"code":400,"name":"SEARCH_QUERY_EMPTY","description":"The search query is empty"}],"available":"user"},{"name":"resolveUsername","id":4181511075,"returns":"contacts.ResolvedPeer","arguments":[{"name":"username","type":"string","description":"@username to resolve"}],"description":"Resolve a @username to get peer info","throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","description":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"CONNECTION_DEVICE_MODEL_EMPTY","description":"Device model empty"},{"code":400,"name":"CONNECTION_LAYER_INVALID","description":"Layer invalid"},{"code":400,"name":"USERNAME_INVALID","description":"The provided username is not valid"},{"code":400,"name":"USERNAME_NOT_OCCUPIED","description":"The provided username is not occupied"}],"available":"both"},{"name":"getTopPeers","id":3566742965,"returns":"contacts.TopPeers","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"correspondents","type":"true","optional":true,"predicate":"flags.0","description":"Users we've chatted most frequently with"},{"name":"botsPm","type":"true","optional":true,"predicate":"flags.1","description":"Most used bots"},{"name":"botsInline","type":"true","optional":true,"predicate":"flags.2","description":"Most used inline bots"},{"name":"phoneCalls","type":"true","optional":true,"predicate":"flags.3","description":"Most frequently called users"},{"name":"forwardUsers","type":"true","optional":true,"predicate":"flags.4","description":"Users to which the users often forwards messages to"},{"name":"forwardChats","type":"true","optional":true,"predicate":"flags.5","description":"Chats to which the users often forwards messages to"},{"name":"groups","type":"true","optional":true,"predicate":"flags.10","description":"Often-opened groups and supergroups"},{"name":"channels","type":"true","optional":true,"predicate":"flags.15","description":"Most frequently visited channels"},{"name":"offset","type":"number","description":"Offset for pagination"},{"name":"limit","type":"number","description":"Maximum number of results to return, see pagination"},{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Get most used peers","throws":[{"code":400,"name":"TYPES_EMPTY","description":"No top peer type was provided"}],"available":"user"},{"name":"resetTopPeerRating","id":451113900,"returns":"boolean","arguments":[{"name":"category","type":"TopPeerCategory","description":"Top peer category"},{"name":"peer","type":"InputPeer","description":"Peer whose rating should be reset"}],"description":"Reset rating of top peer","throws":[{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"resetSaved","id":2274703345,"returns":"boolean","arguments":[],"description":"Delete saved contacts","available":"user"},{"name":"getSaved","id":2196890527,"returns":"SavedContact[]","arguments":[],"description":"Get all contacts","throws":[{"code":403,"name":"TAKEOUT_REQUIRED","description":"A takeout session has to be initialized, first"}],"available":"user"},{"name":"toggleTopPeers","id":2232729050,"returns":"boolean","arguments":[{"name":"enabled","type":"boolean","description":"Enable/disable"}],"description":"Enable/disable top peers","available":"user"},{"name":"addContact","id":3908330448,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"addPhonePrivacyException","type":"true","optional":true,"predicate":"flags.0","description":"Allow the other user to see our phone number?"},{"name":"id","type":"InputUser","description":"Telegram ID of the other user"},{"name":"firstName","type":"string","description":"First name"},{"name":"lastName","type":"string","description":"Last name"},{"name":"phone","type":"string","description":"User's phone number"}],"description":"Add an existing telegram user as contact.","throws":[{"code":400,"name":"CONTACT_ID_INVALID","description":"The provided contact ID is invalid"},{"code":400,"name":"CONTACT_NAME_EMPTY","description":"Contact name empty"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"}],"available":"user"},{"name":"acceptContact","id":4164002319,"returns":"Updates","arguments":[{"name":"id","type":"InputUser","description":"The user to add as contact"}],"description":"If the {@link peerSettings} of a new user allow us to add them as contact, add that user as contact","throws":[{"code":400,"name":"CONTACT_ADD_MISSING","description":"Contact to add is missing"},{"code":400,"name":"CONTACT_ID_INVALID","description":"The provided contact ID is invalid"},{"code":400,"name":"CONTACT_REQ_MISSING","description":"Missing contact request"}],"available":"user"},{"name":"getLocated","id":3544759364,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"background","type":"true","optional":true,"predicate":"flags.1","description":"While the geolocation of the current user is public, clients should update it in the background every half-an-hour or so, while setting this flag.
Do this only if the new location is more than 1 KM away from the previous one, or if the previous location is unknown."},{"name":"geoPoint","type":"InputGeoPoint","description":"Geolocation"},{"name":"selfExpires","type":"number","optional":true,"predicate":"flags.0","description":"If set, the geolocation of the current user will be public for the specified number of seconds; pass 0x7fffffff to disable expiry, 0 to make the current geolocation private; if the flag isn't set, no changes will be applied."}],"description":"Get contacts near you","throws":[{"code":400,"name":"GEO_POINT_INVALID","description":"Invalid geoposition provided"},{"code":406,"name":"USERPIC_UPLOAD_REQUIRED","description":"You must have a profile picture to publish your geolocation"}],"available":"user"},{"name":"blockFromReplies","id":698914348,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"deleteMessage","type":"true","optional":true,"predicate":"flags.0","description":"Whether to delete the specified message as well"},{"name":"deleteHistory","type":"true","optional":true,"predicate":"flags.1","description":"Whether to delete all @replies messages from this user as well"},{"name":"reportSpam","type":"true","optional":true,"predicate":"flags.2","description":"Whether to also report this user for spam"},{"name":"msgId","type":"number","description":"ID of the message in the @replies chat"}],"description":"Stop getting notifications about thread replies of a certain user in @replies","available":"both"}],"unions":[{"type":"Contacts","subtypes":["contacts.contactsNotModified","contacts.contacts"],"description":"Info on the current user's contact list."},{"type":"ImportedContacts","subtypes":["contacts.importedContacts"],"description":"Object contains info on succesfully imported contacts."},{"type":"Blocked","subtypes":["contacts.blocked","contacts.blockedSlice"],"description":"Info on users from the current user's black list."},{"type":"Found","subtypes":["contacts.found"],"description":"Object contains info on users found by name substring and auxiliary data."},{"type":"ResolvedPeer","subtypes":["contacts.resolvedPeer"],"description":"Peer returned after resolving a @username"},{"type":"TopPeers","subtypes":["contacts.topPeersNotModified","contacts.topPeers","contacts.topPeersDisabled"],"description":"Top peers"}]},"updates":{"classes":[{"name":"state","id":2775329342,"type":"updates.State","arguments":[{"name":"pts","type":"number","description":"Number of events occured in a text box"},{"name":"qts","type":"number","description":"Position in a sequence of updates in secret chats. For further detailes refer to article secret chats
Parameter was added in eigth layer."},{"name":"date","type":"number","description":"Date of condition"},{"name":"seq","type":"number","description":"Number of sent updates"},{"name":"unreadCount","type":"number","description":"Number of unread messages"}],"description":"Updates state."},{"name":"differenceEmpty","id":1567990072,"type":"updates.Difference","arguments":[{"name":"date","type":"number","description":"Current date"},{"name":"seq","type":"number","description":"Number of sent updates"}],"description":"No events."},{"name":"difference","id":16030880,"type":"updates.Difference","arguments":[{"name":"newMessages","type":"Message[]","description":"List of new messages"},{"name":"newEncryptedMessages","type":"EncryptedMessage[]","description":"List of new encrypted secret chat messages"},{"name":"otherUpdates","type":"Update[]","description":"List of updates"},{"name":"chats","type":"Chat[]","description":"List of chats mentioned in events"},{"name":"users","type":"User[]","description":"List of users mentioned in events"},{"name":"state","type":"updates.State","description":"Current state"}],"description":"Full list of occurred events."},{"name":"differenceSlice","id":2835028353,"type":"updates.Difference","arguments":[{"name":"newMessages","type":"Message[]","description":"List of new messgaes"},{"name":"newEncryptedMessages","type":"EncryptedMessage[]","description":"New messages from the encrypted event sequence"},{"name":"otherUpdates","type":"Update[]","description":"List of updates"},{"name":"chats","type":"Chat[]","description":"List of chats mentioned in events"},{"name":"users","type":"User[]","description":"List of users mentioned in events"},{"name":"intermediateState","type":"updates.State","description":"Intermediary state"}],"description":"Incomplete list of occurred events."},{"name":"differenceTooLong","id":1258196845,"type":"updates.Difference","arguments":[{"name":"pts","type":"number","description":"The new state to use."}],"description":"The difference is too long, and the specified state must be used to refetch updates."},{"name":"channelDifferenceEmpty","id":1041346555,"type":"updates.ChannelDifference","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"final","type":"true","optional":true,"predicate":"flags.0","description":"Whether there are more updates that must be fetched (always false)"},{"name":"pts","type":"number","description":"The latest PTS"},{"name":"timeout","type":"number","optional":true,"predicate":"flags.1","description":"Clients are supposed to refetch the channel difference after timeout seconds have elapsed"}],"description":"There are no new updates"},{"name":"channelDifferenceTooLong","id":2763835134,"type":"updates.ChannelDifference","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"final","type":"true","optional":true,"predicate":"flags.0","description":"Whether there are more updates that must be fetched (always false)"},{"name":"timeout","type":"number","optional":true,"predicate":"flags.1","description":"Clients are supposed to refetch the channel difference after timeout seconds have elapsed"},{"name":"dialog","type":"Dialog","description":"Dialog containing the latest PTS that can be used to reset the channel state"},{"name":"messages","type":"Message[]","description":"The latest messages"},{"name":"chats","type":"Chat[]","description":"Chats from messages"},{"name":"users","type":"User[]","description":"Users from messages"}],"description":"The provided pts + limit < remote pts. Simply, there are too many updates to be fetched (more than limit), the client has to resolve the update gap in one of the following ways:"},{"name":"channelDifference","id":543450958,"type":"updates.ChannelDifference","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"final","type":"true","optional":true,"predicate":"flags.0","description":"Whether there are more updates to be fetched using getDifference, starting from the provided pts"},{"name":"pts","type":"number","description":"The PTS from which to start getting updates the next time"},{"name":"timeout","type":"number","optional":true,"predicate":"flags.1","description":"Clients are supposed to refetch the channel difference after timeout seconds have elapsed"},{"name":"newMessages","type":"Message[]","description":"New messages"},{"name":"otherUpdates","type":"Update[]","description":"Other updates"},{"name":"chats","type":"Chat[]","description":"Chats"},{"name":"users","type":"User[]","description":"Users"}],"description":"The new updates"}],"methods":[{"name":"getState","id":3990128682,"returns":"updates.State","arguments":[],"description":"Returns a current state of updates.","available":"both"},{"name":"getDifference","id":630429265,"returns":"updates.Difference","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"pts","type":"number","description":"PTS, see updates."},{"name":"ptsTotalLimit","type":"number","optional":true,"predicate":"flags.0","description":"For fast updating: if provided and pts + pts_total_limit < remote pts, {@link updates.differenceTooLong} will be returned.
Simply tells the server to not return the difference if it is bigger than pts_total_limit
If the remote pts is too big (> ~4000000), this field will default to 1000000"},{"name":"date","type":"number","description":"date, see updates."},{"name":"qts","type":"number","description":"QTS, see updates."}],"description":"Get new updates.","throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","description":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"CDN_METHOD_INVALID","description":"You can't call this method in a CDN DC"},{"code":400,"name":"DATE_EMPTY","description":"Date empty"},{"code":400,"name":"PERSISTENT_TIMESTAMP_EMPTY","description":"Persistent timestamp empty"},{"code":400,"name":"PERSISTENT_TIMESTAMP_INVALID","description":"Persistent timestamp invalid"}],"available":"both"},{"name":"getChannelDifference","id":51854712,"returns":"updates.ChannelDifference","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"force","type":"true","optional":true,"predicate":"flags.0","description":"Set to true to skip some possibly unneeded updates and reduce server-side load"},{"name":"channel","type":"InputChannel","description":"The channel"},{"name":"filter","type":"ChannelMessagesFilter","description":"Messsage filter"},{"name":"pts","type":"number","description":"Persistent timestamp (see updates)"},{"name":"limit","type":"number","description":"How many updates to fetch, max 100000
Ordinary (non-bot) users are supposed to pass 10-100"}],"description":"Returns the difference between the current state of updates of a certain channel and transmitted.","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":403,"name":"CHANNEL_PUBLIC_GROUP_NA","description":"channel/supergroup not available"},{"code":400,"name":"FROM_MESSAGE_BOT_DISABLED","description":"Bots can't use fromMessage min constructors"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"PERSISTENT_TIMESTAMP_EMPTY","description":"Persistent timestamp empty"},{"code":400,"name":"PERSISTENT_TIMESTAMP_INVALID","description":"Persistent timestamp invalid"},{"code":400,"name":"PINNED_DIALOGS_TOO_MUCH","description":"Too many pinned dialogs"},{"code":400,"name":"RANGES_INVALID","description":"Invalid range provided"}],"available":"both"}],"unions":[{"type":"State","subtypes":["updates.state"],"description":"Object contains info on state for further updates."},{"type":"Difference","subtypes":["updates.differenceEmpty","updates.difference","updates.differenceSlice","updates.differenceTooLong"],"description":"Occurred changes."},{"type":"ChannelDifference","subtypes":["updates.channelDifferenceEmpty","updates.channelDifferenceTooLong","updates.channelDifference"],"description":"Contains the difference (new messages) between our local channel state and the remote state"}]},"photos":{"classes":[{"name":"photos","id":2378853029,"type":"photos.Photos","arguments":[{"name":"photos","type":"Photo[]","description":"List of photos"},{"name":"users","type":"User[]","description":"List of mentioned users"}],"description":"Full list of photos with auxiliary data."},{"name":"photosSlice","id":352657236,"type":"photos.Photos","arguments":[{"name":"count","type":"number","description":"Total number of photos"},{"name":"photos","type":"Photo[]","description":"List of photos"},{"name":"users","type":"User[]","description":"List of mentioned users"}],"description":"Incomplete list of photos with auxiliary data."},{"name":"photo","id":539045032,"type":"photos.Photo","arguments":[{"name":"photo","type":"Photo","description":"Photo"},{"name":"users","type":"User[]","description":"Users"}],"description":"Photo with auxiliary data."}],"methods":[{"name":"updateProfilePhoto","id":1926525996,"returns":"photos.Photo","arguments":[{"name":"id","type":"InputPhoto","description":"Input photo"}],"description":"Installs a previously uploaded photo as a profile photo.","throws":[{"code":400,"name":"FILE_PARTS_INVALID","description":"The number of file parts is invalid"},{"code":400,"name":"IMAGE_PROCESS_FAILED","description":"Failure while processing image"},{"code":400,"name":"LOCATION_INVALID","description":"The provided location is invalid"},{"code":400,"name":"PHOTO_CROP_SIZE_SMALL","description":"Photo is too small"},{"code":400,"name":"PHOTO_EXT_INVALID","description":"The extension of the photo is invalid"},{"code":400,"name":"PHOTO_ID_INVALID","description":"Photo ID invalid"}],"available":"user"},{"name":"uploadProfilePhoto","id":2314407785,"returns":"photos.Photo","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"file","type":"InputFile","optional":true,"predicate":"flags.0","description":"File saved in parts by means of {@link upload.saveFilePart} method"},{"name":"video","type":"InputFile","optional":true,"predicate":"flags.1","description":"Animated profile picture video"},{"name":"videoStartTs","type":"Double","optional":true,"predicate":"flags.2","description":"Floating point UNIX timestamp in seconds, indicating the frame of the video that should be used as static preview."}],"description":"Updates current user profile photo.","throws":[{"code":400,"name":"FILE_PARTS_INVALID","description":"The number of file parts is invalid"},{"code":400,"name":"IMAGE_PROCESS_FAILED","description":"Failure while processing image"},{"code":400,"name":"PHOTO_CROP_FILE_MISSING","description":"Photo crop file missing"},{"code":400,"name":"PHOTO_CROP_SIZE_SMALL","description":"Photo is too small"},{"code":400,"name":"PHOTO_EXT_INVALID","description":"The extension of the photo is invalid"},{"code":400,"name":"PHOTO_FILE_MISSING","description":"Profile photo file missing"},{"code":400,"name":"VIDEO_FILE_INVALID","description":"The specified video file is invalid"}],"available":"user"},{"name":"deletePhotos","id":2278522671,"returns":"Long[]","arguments":[{"name":"id","type":"InputPhoto[]","description":"Input photos to delete"}],"description":"Deletes profile photos.","available":"user"},{"name":"getUserPhotos","id":2446144168,"returns":"photos.Photos","arguments":[{"name":"userId","type":"InputUser","description":"User ID"},{"name":"offset","type":"number","description":"Number of list elements to be skipped"},{"name":"maxId","type":"Long","description":"If a positive value was transferred, the method will return only photos with IDs less than the set one"},{"name":"limit","type":"number","description":"Number of list elements to be returned"}],"description":"Returns the list of user photos.","throws":[{"code":400,"name":"MAX_ID_INVALID","description":"The provided max ID is invalid"},{"code":400,"name":"USER_ID_INVALID","description":"The provided user ID is invalid"}],"available":"both"}],"unions":[{"type":"Photos","subtypes":["photos.photos","photos.photosSlice"],"description":"Object contains list of photos with auxiliary data."},{"type":"Photo","subtypes":["photos.photo"],"description":"Photo with auxiliary data."}]},"upload":{"classes":[{"name":"file","id":157948117,"type":"upload.File","arguments":[{"name":"type","type":"storage.FileType","description":"File type"},{"name":"mtime","type":"number","description":"Modification type"},{"name":"bytes","type":"Buffer","description":"Binary data, file content"}],"description":"File content."},{"name":"fileCdnRedirect","id":4052539972,"type":"upload.File","arguments":[{"name":"dcId","type":"number","description":"CDN DC ID"},{"name":"fileToken","type":"Buffer","description":"File token (see CDN files)"},{"name":"encryptionKey","type":"Buffer","description":"Encryption key (see CDN files)"},{"name":"encryptionIv","type":"Buffer","description":"Encryption IV (see CDN files)"},{"name":"fileHashes","type":"FileHash[]","description":"File hashes (see CDN files)"}],"description":"The file must be downloaded from a CDN DC."},{"name":"webFile","id":568808380,"type":"upload.WebFile","arguments":[{"name":"size","type":"number","description":"File size"},{"name":"mimeType","type":"string","description":"Mime type"},{"name":"fileType","type":"storage.FileType","description":"File type"},{"name":"mtime","type":"number","description":"Modified time"},{"name":"bytes","type":"Buffer","description":"Data"}],"description":"Represents a chunk of an HTTP webfile downloaded through telegram's secure MTProto servers"},{"name":"cdnFileReuploadNeeded","id":4004045934,"type":"upload.CdnFile","arguments":[{"name":"requestToken","type":"Buffer","description":"Request token (see CDN)"}],"description":"The file was cleared from the temporary RAM cache of the CDN and has to be reuploaded."},{"name":"cdnFile","id":2845821519,"type":"upload.CdnFile","arguments":[{"name":"bytes","type":"Buffer","description":"The data"}],"description":"Represent a chunk of a CDN file."}],"methods":[{"name":"saveFilePart","id":3003426337,"returns":"boolean","arguments":[{"name":"fileId","type":"Long","description":"Random file identifier created by the client"},{"name":"filePart","type":"number","description":"Numerical order of a part"},{"name":"bytes","type":"Buffer","description":"Binary data, contend of a part"}],"description":"Saves a part of file for futher sending to one of the methods.","throws":[{"code":400,"name":"FILE_PART_EMPTY","description":"The provided file part is empty"},{"code":400,"name":"FILE_PART_INVALID","description":"The file part number is invalid"}],"available":"both"},{"name":"getFile","id":2975505148,"returns":"upload.File","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"precise","type":"true","optional":true,"predicate":"flags.0","description":"Disable some checks on limit and offset values, useful for example to stream videos by keyframes"},{"name":"cdnSupported","type":"true","optional":true,"predicate":"flags.1","description":"Whether the current client supports CDN downloads"},{"name":"location","type":"InputFileLocation","description":"File location"},{"name":"offset","type":"number","description":"Number of bytes to be skipped"},{"name":"limit","type":"number","description":"Number of bytes to be returned"}],"description":"Returns content of a whole file or its part.","throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","description":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":406,"name":"FILEREF_UPGRADE_NEEDED","description":"The client has to be updated in order to support file references"},{"code":400,"name":"FILE_ID_INVALID","description":"The provided file id is invalid"},{"code":400,"name":"FILE_REFERENCE_*","description":"The file reference expired, it must be refreshed"},{"code":400,"name":"FILE_REFERENCE_EXPIRED","description":"File reference expired, it must be refetched as described in https://core.telegram.org/api/file_reference"},{"code":400,"name":"LIMIT_INVALID","description":"The provided limit is invalid"},{"code":400,"name":"LOCATION_INVALID","description":"The provided location is invalid"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"OFFSET_INVALID","description":"The provided offset is invalid"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"both"},{"name":"saveBigFilePart","id":3732629309,"returns":"boolean","arguments":[{"name":"fileId","type":"Long","description":"Random file id, created by the client"},{"name":"filePart","type":"number","description":"Part sequence number"},{"name":"fileTotalParts","type":"number","description":"Total number of parts"},{"name":"bytes","type":"Buffer","description":"Binary data, part contents"}],"description":"Saves a part of a large file (over 10Mb in size) to be later passed to one of the methods.","throws":[{"code":400,"name":"FILE_PARTS_INVALID","description":"The number of file parts is invalid"},{"code":400,"name":"FILE_PART_EMPTY","description":"The provided file part is empty"},{"code":400,"name":"FILE_PART_INVALID","description":"The file part number is invalid"},{"code":400,"name":"FILE_PART_SIZE_CHANGED","description":"Provided file part size has changed"},{"code":400,"name":"FILE_PART_SIZE_INVALID","description":"The provided file part size is invalid"},{"code":400,"name":"FILE_PART_TOO_BIG","description":"The uploaded file part is too big"}],"available":"both"},{"name":"getWebFile","id":619086221,"returns":"upload.WebFile","arguments":[{"name":"location","type":"InputWebFileLocation","description":"The file to download"},{"name":"offset","type":"number","description":"Number of bytes to be skipped"},{"name":"limit","type":"number","description":"Number of bytes to be returned"}],"description":"Returns content of an HTTP file or a part, by proxying the request through telegram.","throws":[{"code":400,"name":"LOCATION_INVALID","description":"The provided location is invalid"}],"available":"user"},{"name":"getCdnFile","id":536919235,"returns":"upload.CdnFile","arguments":[{"name":"fileToken","type":"Buffer","description":"File token"},{"name":"offset","type":"number","description":"Offset of chunk to download"},{"name":"limit","type":"number","description":"Length of chunk to download"}],"description":"Download a CDN file.","available":"user"},{"name":"reuploadCdnFile","id":2603046056,"returns":"FileHash[]","arguments":[{"name":"fileToken","type":"Buffer","description":"File token"},{"name":"requestToken","type":"Buffer","description":"Request token"}],"description":"Request a reupload of a certain file to a CDN DC.","throws":[{"code":400,"name":"RSA_DECRYPT_FAILED","description":"Internal RSA decryption failed"}],"available":"both"},{"name":"getCdnFileHashes","id":1302676017,"returns":"FileHash[]","arguments":[{"name":"fileToken","type":"Buffer","description":"File"},{"name":"offset","type":"number","description":"Offset from which to start getting hashes"}],"description":"Get SHA256 hashes for verifying downloaded CDN files","throws":[{"code":400,"name":"CDN_METHOD_INVALID","description":"You can't call this method in a CDN DC"},{"code":400,"name":"RSA_DECRYPT_FAILED","description":"Internal RSA decryption failed"}],"available":"both"},{"name":"getFileHashes","id":3338819889,"returns":"FileHash[]","arguments":[{"name":"location","type":"InputFileLocation","description":"File"},{"name":"offset","type":"number","description":"Offset from which to get file hashes"}],"description":"Get SHA256 hashes for verifying downloaded files","throws":[{"code":400,"name":"LOCATION_INVALID","description":"The provided location is invalid"}],"available":"both"}],"unions":[{"type":"File","subtypes":["upload.file","upload.fileCdnRedirect"],"description":"Contains info on file."},{"type":"WebFile","subtypes":["upload.webFile"],"description":"Remote file"},{"type":"CdnFile","subtypes":["upload.cdnFileReuploadNeeded","upload.cdnFile"],"description":"Represents the download status of a CDN file"}]},"account":{"classes":[{"name":"privacyRules","id":1352683077,"type":"account.PrivacyRules","arguments":[{"name":"rules","type":"PrivacyRule[]","description":"Privacy rules"},{"name":"chats","type":"Chat[]","description":"Chats to which the rules apply"},{"name":"users","type":"User[]","description":"Users to which the rules apply"}],"description":"Privacy rules"},{"name":"authorizations","id":307276766,"type":"account.Authorizations","arguments":[{"name":"authorizations","type":"Authorization[]","description":"Logged-in sessions"}],"description":"Logged-in sessions"},{"name":"password","id":408623183,"type":"account.Password","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"hasRecovery","type":"true","optional":true,"predicate":"flags.0","description":"Whether the user has a recovery method configured"},{"name":"hasSecureValues","type":"true","optional":true,"predicate":"flags.1","description":"Whether telegram passport is enabled"},{"name":"hasPassword","type":"true","optional":true,"predicate":"flags.2","description":"Whether the user has a password"},{"name":"currentAlgo","type":"PasswordKdfAlgo","optional":true,"predicate":"flags.2","description":"The KDF algorithm for SRP two-factor authentication of the current password"},{"name":"srpB","type":"Buffer","optional":true,"predicate":"flags.2","description":"Srp B param for SRP authorization"},{"name":"srpId","type":"Long","optional":true,"predicate":"flags.2","description":"Srp ID param for SRP authorization"},{"name":"hint","type":"string","optional":true,"predicate":"flags.3","description":"Text hint for the password"},{"name":"emailUnconfirmedPattern","type":"string","optional":true,"predicate":"flags.4","description":"A password recovery email with the specified pattern is still awaiting verification"},{"name":"newAlgo","type":"PasswordKdfAlgo","description":"The KDF algorithm for SRP two-factor authentication to use when creating new passwords"},{"name":"newSecureAlgo","type":"SecurePasswordKdfAlgo","description":"The KDF algorithm for telegram passport"},{"name":"secureRandom","type":"Buffer","description":"Secure random string"},{"name":"pendingResetDate","type":"number","optional":true,"predicate":"flags.5"}],"description":"Configuration for two-factor authorization"},{"name":"passwordSettings","id":2589733861,"type":"account.PasswordSettings","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"email","type":"string","optional":true,"predicate":"flags.0","description":"2FA Recovery email"},{"name":"secureSettings","type":"SecureSecretSettings","optional":true,"predicate":"flags.1","description":"Telegram passport settings"}],"description":"Private info associated to the password info (recovery email, telegram passport info & so on)"},{"name":"passwordInputSettings","id":3258394569,"type":"account.PasswordInputSettings","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"newAlgo","type":"PasswordKdfAlgo","optional":true,"predicate":"flags.0","description":"The SRP algorithm to use"},{"name":"newPasswordHash","type":"Buffer","optional":true,"predicate":"flags.0","description":"The computed password hash"},{"name":"hint","type":"string","optional":true,"predicate":"flags.0","description":"Text hint for the password"},{"name":"email","type":"string","optional":true,"predicate":"flags.1","description":"Password recovery email"},{"name":"newSecureSettings","type":"SecureSecretSettings","optional":true,"predicate":"flags.2","description":"Telegram passport settings"}],"description":"Settings for setting up a new password"},{"name":"tmpPassword","id":3680828724,"type":"account.TmpPassword","arguments":[{"name":"tmpPassword","type":"Buffer","description":"Temporary password"},{"name":"validUntil","type":"number","description":"Validity period"}],"description":"Temporary payment password"},{"name":"webAuthorizations","id":3981887996,"type":"account.WebAuthorizations","arguments":[{"name":"authorizations","type":"WebAuthorization[]","description":"Web authorization list"},{"name":"users","type":"User[]","description":"Users"}],"description":"Web authorizations"},{"name":"authorizationForm","id":2905480408,"type":"account.AuthorizationForm","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"requiredTypes","type":"SecureRequiredType[]","description":"Required Telegram Passport documents"},{"name":"values","type":"SecureValue[]","description":"Already submitted Telegram Passport documents"},{"name":"errors","type":"SecureValueError[]","description":"Telegram Passport errors"},{"name":"users","type":"User[]","description":"Info about the bot to which the form will be submitted"},{"name":"privacyPolicyUrl","type":"string","optional":true,"predicate":"flags.0","description":"URL of the service's privacy policy"}],"description":"Telegram Passport authorization form"},{"name":"sentEmailCode","id":2166326607,"type":"account.SentEmailCode","arguments":[{"name":"emailPattern","type":"string","description":"The email (to which the code was sent) must match this pattern"},{"name":"length","type":"number","description":"The length of the verification code"}],"description":"The sent email code"},{"name":"takeout","id":1304052993,"type":"account.Takeout","arguments":[{"name":"id","type":"Long","description":"Takeout ID"}],"description":"Takout info"},{"name":"wallPapersNotModified","id":471437699,"type":"account.WallPapers","arguments":[],"description":"No new wallpapers were found"},{"name":"wallPapers","id":1881892265,"type":"account.WallPapers","arguments":[{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"},{"name":"wallpapers","type":"WallPaper[]","description":"Wallpapers"}],"description":"Installed wallpapers"},{"name":"autoDownloadSettings","id":1674235686,"type":"account.AutoDownloadSettings","arguments":[{"name":"low","type":"AutoDownloadSettings","description":"Low data usage preset"},{"name":"medium","type":"AutoDownloadSettings","description":"Medium data usage preset"},{"name":"high","type":"AutoDownloadSettings","description":"High data usage preset"}],"description":"Media autodownload settings"},{"name":"themesNotModified","id":4095653410,"type":"account.Themes","arguments":[],"description":"No new themes were installed"},{"name":"themes","id":2137482273,"type":"account.Themes","arguments":[{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"},{"name":"themes","type":"Theme[]","description":"Themes"}],"description":"Installed themes"},{"name":"contentSettings","id":1474462241,"type":"account.ContentSettings","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"sensitiveEnabled","type":"true","optional":true,"predicate":"flags.0","description":"Whether viewing of sensitive (NSFW) content is enabled"},{"name":"sensitiveCanChange","type":"true","optional":true,"predicate":"flags.1","description":"Whether the current client can change the sensitive content settings to view NSFW content"}],"description":"Sensitive content settings"},{"name":"resetPasswordFailedWait","id":3816265825,"type":"account.ResetPasswordResult","arguments":[{"name":"retryDate","type":"number"}]},{"name":"resetPasswordRequestedWait","id":3924819069,"type":"account.ResetPasswordResult","arguments":[{"name":"untilDate","type":"number"}]},{"name":"resetPasswordOk","id":3911636542,"type":"account.ResetPasswordResult","arguments":[]}],"methods":[{"name":"registerDevice","id":1754754159,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"noMuted","type":"true","optional":true,"predicate":"flags.0","description":"Avoid receiving (silent and invisible background) notifications. Useful to save battery."},{"name":"tokenType","type":"number","description":"Device token type.
Possible values:
1 - APNS (device token for apple push)
2 - FCM (firebase token for google firebase)
3 - MPNS (channel URI for microsoft push)
4 - Simple push (endpoint for firefox's simple push API)
5 - Ubuntu phone (token for ubuntu push)
6 - Blackberry (token for blackberry push)
7 - Unused
8 - WNS (windows push)
9 - APNS VoIP (token for apple push VoIP)
10 - Web push (web push, see below)
11 - MPNS VoIP (token for microsoft push VoIP)
12 - Tizen (token for tizen push)

For 10 web push, the token must be a JSON-encoded object containing the keys described in PUSH updates"},{"name":"token","type":"string","description":"Device token"},{"name":"appSandbox","type":"boolean","description":"If {@link boolTrue} is transmitted, a sandbox-certificate will be used during transmission."},{"name":"secret","type":"Buffer","description":"For FCM and APNS VoIP, optional encryption key used to encrypt push notifications"},{"name":"otherUids","type":"number[]","description":"List of user identifiers of other users currently using the client"}],"description":"Register device to receive PUSH notifications","throws":[{"code":400,"name":"TOKEN_INVALID","description":"The provided token is invalid"}],"available":"user"},{"name":"unregisterDevice","id":813089983,"returns":"boolean","arguments":[{"name":"tokenType","type":"number","description":"Device token type.
Possible values:
1 - APNS (device token for apple push)
2 - FCM (firebase token for google firebase)
3 - MPNS (channel URI for microsoft push)
4 - Simple push (endpoint for firefox's simple push API)
5 - Ubuntu phone (token for ubuntu push)
6 - Blackberry (token for blackberry push)
7 - Unused
8 - WNS (windows push)
9 - APNS VoIP (token for apple push VoIP)
10 - Web push (web push, see below)
11 - MPNS VoIP (token for microsoft push VoIP)
12 - Tizen (token for tizen push)

For 10 web push, the token must be a JSON-encoded object containing the keys described in PUSH updates"},{"name":"token","type":"string","description":"Device token"},{"name":"otherUids","type":"number[]","description":"List of user identifiers of other users currently using the client"}],"description":"Deletes a device by its token, stops sending PUSH-notifications to it.","throws":[{"code":400,"name":"TOKEN_INVALID","description":"The provided token is invalid"}],"available":"user"},{"name":"updateNotifySettings","id":2227067795,"returns":"boolean","arguments":[{"name":"peer","type":"InputNotifyPeer","description":"Notification source"},{"name":"settings","type":"InputPeerNotifySettings","description":"Notification settings"}],"description":"Edits notification settings from a given user/group, from all users/all groups.","throws":[{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"SETTINGS_INVALID","description":"Invalid settings were provided"}],"available":"user"},{"name":"getNotifySettings","id":313765169,"returns":"PeerNotifySettings","arguments":[{"name":"peer","type":"InputNotifyPeer","description":"Notification source"}],"description":"Gets current notification settings for a given user/group, from all users/all groups.","throws":[{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"resetNotifySettings","id":3682473799,"returns":"boolean","arguments":[],"description":"Resets all notification settings from users and groups.","available":"user"},{"name":"updateProfile","id":2018596725,"returns":"User","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"firstName","type":"string","optional":true,"predicate":"flags.0","description":"New user first name"},{"name":"lastName","type":"string","optional":true,"predicate":"flags.1","description":"New user last name"},{"name":"about","type":"string","optional":true,"predicate":"flags.2","description":"New bio"}],"description":"Updates user profile.","throws":[{"code":400,"name":"ABOUT_TOO_LONG","description":"About string too long"},{"code":400,"name":"FIRSTNAME_INVALID","description":"The first name is invalid"}],"available":"user"},{"name":"updateStatus","id":1713919532,"returns":"boolean","arguments":[{"name":"offline","type":"boolean","description":"If {@link boolTrue} is transmitted, user status will change to {@link userStatusOffline}."}],"description":"Updates online user status.","available":"user"},{"name":"getWallPapers","id":2864387939,"returns":"account.WallPapers","arguments":[{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Returns a list of available wallpapers.","available":"user"},{"name":"reportPeer","id":3317316998,"returns":"boolean","arguments":[{"name":"peer","type":"InputPeer","description":"The peer to report"},{"name":"reason","type":"ReportReason","description":"The reason why this peer is being reported"},{"name":"message","type":"string"}],"description":"Report a peer for violation of telegram's Terms of Service","throws":[{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"}],"available":"user"},{"name":"checkUsername","id":655677548,"returns":"boolean","arguments":[{"name":"username","type":"string","description":"username
Accepted characters: A-z (case-insensitive), 0-9 and underscores.
Length: 5-32 characters."}],"description":"Validates a username and checks availability.","throws":[{"code":400,"name":"USERNAME_INVALID","description":"Unacceptable username"}],"available":"user"},{"name":"updateUsername","id":1040964988,"returns":"User","arguments":[{"name":"username","type":"string","description":"username or empty string if username is to be removed
Accepted characters: a-z (case-insensitive), 0-9 and underscores.
Length: 5-32 characters."}],"description":"Changes username for the current user.","throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","description":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"USERNAME_INVALID","description":"Unacceptable username"},{"code":400,"name":"USERNAME_NOT_MODIFIED","description":"Username is not different from the current username"},{"code":400,"name":"USERNAME_OCCUPIED","description":"Username is taken"}],"available":"user"},{"name":"getPrivacy","id":3671837008,"returns":"account.PrivacyRules","arguments":[{"name":"key","type":"InputPrivacyKey","description":"Peer category whose privacy settings should be fetched"}],"description":"Get privacy settings of current account","throws":[{"code":400,"name":"PRIVACY_KEY_INVALID","description":"The privacy key is invalid"}],"available":"user"},{"name":"setPrivacy","id":3388480744,"returns":"account.PrivacyRules","arguments":[{"name":"key","type":"InputPrivacyKey","description":"Peers to which the privacy rules apply"},{"name":"rules","type":"InputPrivacyRule[]","description":"New privacy rules"}],"description":"Change privacy settings of current account","throws":[{"code":400,"name":"PRIVACY_KEY_INVALID","description":"The privacy key is invalid"},{"code":400,"name":"PRIVACY_VALUE_INVALID","description":"The specified privacy rule combination is invalid"}],"available":"user"},{"name":"deleteAccount","id":1099779595,"returns":"boolean","arguments":[{"name":"reason","type":"string","description":"Why is the account being deleted, can be empty"}],"description":"Delete the user's account from the telegram servers. Can be used, for example, to delete the account of a user that provided the login code, but forgot the 2FA password and no recovery method is configured.","throws":[{"code":420,"name":"2FA_CONFIRM_WAIT_X","description":"Since this account is active and protected by a 2FA password, we will delete it in 1 week for security purposes. You can cancel this process at any time, you'll be able to reset your account in X seconds."}],"available":"user"},{"name":"getAccountTTL","id":150761757,"returns":"AccountDaysTTL","arguments":[],"description":"Get days to live of account","available":"user"},{"name":"setAccountTTL","id":608323678,"returns":"boolean","arguments":[{"name":"ttl","type":"AccountDaysTTL","description":"Time to live in days"}],"description":"Set account self-destruction period","throws":[{"code":400,"name":"TTL_DAYS_INVALID","description":"The provided TTL is invalid"}],"available":"user"},{"name":"sendChangePhoneCode","id":2186758885,"returns":"auth.SentCode","arguments":[{"name":"phoneNumber","type":"string","description":"New phone number"},{"name":"settings","type":"CodeSettings","description":"Phone code settings"}],"description":"Verify a new phone number to associate to the current account","throws":[{"code":400,"name":"PHONE_NUMBER_INVALID","description":"The phone number is invalid"}],"available":"user"},{"name":"changePhone","id":1891839707,"returns":"User","arguments":[{"name":"phoneNumber","type":"string","description":"New phone number"},{"name":"phoneCodeHash","type":"string","description":"Phone code hash received when calling {@link account.sendChangePhoneCode}"},{"name":"phoneCode","type":"string","description":"Phone code received when calling {@link account.sendChangePhoneCode}"}],"description":"Change the phone number of the current account","throws":[{"code":400,"name":"PHONE_CODE_EMPTY","description":"phone_code is missing"},{"code":400,"name":"PHONE_NUMBER_INVALID","description":"The phone number is invalid"}],"available":"user"},{"name":"updateDeviceLocked","id":954152242,"returns":"boolean","arguments":[{"name":"period","type":"number","description":"Inactivity period after which to start hiding message texts in PUSH notifications."}],"description":"When client-side passcode lock feature is enabled, will not show message texts in incoming PUSH notifications.","available":"user"},{"name":"getAuthorizations","id":3810574680,"returns":"account.Authorizations","arguments":[],"description":"Get logged-in sessions","available":"user"},{"name":"resetAuthorization","id":3749180348,"returns":"boolean","arguments":[{"name":"hash","type":"Long","description":"Session hash"}],"description":"Log out an active authorized session by its hash","throws":[{"code":406,"name":"FRESH_RESET_AUTHORISATION_FORBIDDEN","description":"You can't logout other sessions if less than 24 hours have passed since you logged on the current session"},{"code":400,"name":"HASH_INVALID","description":"The provided hash is invalid"}],"available":"user"},{"name":"getPassword","id":1418342645,"returns":"account.Password","arguments":[],"description":"Obtain configuration for two-factor authorization with password","available":"user"},{"name":"getPasswordSettings","id":2631199481,"returns":"account.PasswordSettings","arguments":[{"name":"password","type":"InputCheckPasswordSRP","description":"The password (see SRP)"}],"description":"Get private info associated to the password info (recovery email, telegram passport info & so on)","throws":[{"code":400,"name":"PASSWORD_HASH_INVALID","description":"The provided password hash is invalid"}],"available":"user"},{"name":"updatePasswordSettings","id":2778402863,"returns":"boolean","arguments":[{"name":"password","type":"InputCheckPasswordSRP","description":"The old password (see SRP)"},{"name":"newSettings","type":"account.PasswordInputSettings","description":"The new password (see SRP)"}],"description":"Set a new 2FA password","throws":[{"code":400,"name":"EMAIL_UNCONFIRMED","description":"Email unconfirmed"},{"code":400,"name":"EMAIL_UNCONFIRMED_X","description":"The provided email isn't confirmed, X is the length of the verification code that was just sent to the email: use {@link account.verifyEmail} to enter the received verification code and enable the recovery email."},{"code":400,"name":"NEW_SALT_INVALID","description":"The new salt is invalid"},{"code":400,"name":"NEW_SETTINGS_INVALID","description":"The new password settings are invalid"},{"code":400,"name":"PASSWORD_HASH_INVALID","description":"The old password hash is invalid"},{"code":400,"name":"SRP_ID_INVALID","description":"Invalid SRP ID provided"}],"available":"user"},{"name":"sendConfirmPhoneCode","id":457157256,"returns":"auth.SentCode","arguments":[{"name":"hash","type":"string","description":"The hash from the service notification, for more info click here »"},{"name":"settings","type":"CodeSettings","description":"Phone code settings"}],"description":"Send confirmation code to cancel account deletion, for more info click here »","throws":[{"code":400,"name":"HASH_INVALID","description":"The provided hash is invalid"}],"available":"user"},{"name":"confirmPhone","id":1596029123,"returns":"boolean","arguments":[{"name":"phoneCodeHash","type":"string","description":"Phone code hash, for more info click here »"},{"name":"phoneCode","type":"string","description":"SMS code, for more info click here »"}],"description":"Confirm a phone number to cancel account deletion, for more info click here »","throws":[{"code":400,"name":"CODE_HASH_INVALID","description":"Code hash invalid"},{"code":400,"name":"PHONE_CODE_EMPTY","description":"phone_code is missing"}],"available":"user"},{"name":"getTmpPassword","id":1151208273,"returns":"account.TmpPassword","arguments":[{"name":"password","type":"InputCheckPasswordSRP","description":"SRP password parameters"},{"name":"period","type":"number","description":"Time during which the temporary password will be valid, in seconds; should be between 60 and 86400"}],"description":"Get temporary payment password","throws":[{"code":400,"name":"PASSWORD_HASH_INVALID","description":"The provided password hash is invalid"},{"code":400,"name":"TMP_PASSWORD_DISABLED","description":"The temporary password is disabled"}],"available":"user"},{"name":"getWebAuthorizations","id":405695855,"returns":"account.WebAuthorizations","arguments":[],"description":"Get web login widget authorizations","available":"user"},{"name":"resetWebAuthorization","id":755087855,"returns":"boolean","arguments":[{"name":"hash","type":"Long","description":"{@link webAuthorization} hash"}],"description":"Log out an active web telegram login session","available":"user"},{"name":"resetWebAuthorizations","id":1747789204,"returns":"boolean","arguments":[],"description":"Reset all active web telegram login sessions","available":"user"},{"name":"getAllSecureValues","id":2995305597,"returns":"SecureValue[]","arguments":[],"description":"Get all saved Telegram Passport documents, for more info see the passport docs »","available":"user"},{"name":"getSecureValue","id":1936088002,"returns":"SecureValue[]","arguments":[{"name":"types","type":"SecureValueType[]","description":"Requested value types"}],"description":"Get saved Telegram Passport document, for more info see the passport docs »","available":"user"},{"name":"saveSecureValue","id":2308956957,"returns":"SecureValue","arguments":[{"name":"value","type":"InputSecureValue","description":"Secure value, for more info see the passport docs »"},{"name":"secureSecretId","type":"Long","description":"Passport secret hash, for more info see the passport docs »"}],"description":"Securely save Telegram Passport document, for more info see the passport docs »","available":"user"},{"name":"deleteSecureValue","id":3095444555,"returns":"boolean","arguments":[{"name":"types","type":"SecureValueType[]","description":"Document types to delete"}],"description":"Delete stored Telegram Passport documents, for more info see the passport docs »","available":"user"},{"name":"getAuthorizationForm","id":3094063329,"returns":"account.AuthorizationForm","arguments":[{"name":"botId","type":"number","description":"User identifier of the service's bot"},{"name":"scope","type":"string","description":"Telegram Passport element types requested by the service"},{"name":"publicKey","type":"string","description":"Service's public key"}],"description":"Returns a Telegram Passport authorization form for sharing data with a service","available":"user"},{"name":"acceptAuthorization","id":3875699860,"returns":"boolean","arguments":[{"name":"botId","type":"number","description":"Bot ID"},{"name":"scope","type":"string","description":"Telegram Passport element types requested by the service"},{"name":"publicKey","type":"string","description":"Service's public key"},{"name":"valueHashes","type":"SecureValueHash[]","description":"Types of values sent and their hashes"},{"name":"credentials","type":"SecureCredentialsEncrypted","description":"Encrypted values"}],"description":"Sends a Telegram Passport authorization form, effectively sharing data with the service","available":"user"},{"name":"sendVerifyPhoneCode","id":2778945273,"returns":"auth.SentCode","arguments":[{"name":"phoneNumber","type":"string","description":"The phone number to verify"},{"name":"settings","type":"CodeSettings","description":"Phone code settings"}],"description":"Send the verification phone code for telegram passport.","available":"user"},{"name":"verifyPhone","id":1305716726,"returns":"boolean","arguments":[{"name":"phoneNumber","type":"string","description":"Phone number"},{"name":"phoneCodeHash","type":"string","description":"Phone code hash received from the call to {@link account.sendVerifyPhoneCode}"},{"name":"phoneCode","type":"string","description":"Code received after the call to {@link account.sendVerifyPhoneCode}"}],"description":"Verify a phone number for telegram passport.","throws":[{"code":400,"name":"PHONE_CODE_EXPIRED","description":"The phone code you provided has expired, this may happen if it was sent to any chat on telegram (if the code is sent through a telegram chat (not the official account) to avoid it append or prepend to the code some chars)"}],"available":"user"},{"name":"sendVerifyEmailCode","id":1880182943,"returns":"account.SentEmailCode","arguments":[{"name":"email","type":"string","description":"The email where to send the code"}],"description":"Send the verification email code for telegram passport.","available":"user"},{"name":"verifyEmail","id":3971627483,"returns":"boolean","arguments":[{"name":"email","type":"string","description":"The email to verify"},{"name":"code","type":"string","description":"The verification code that was received"}],"description":"Verify an email address for telegram passport.","throws":[{"code":400,"name":"EMAIL_VERIFY_EXPIRED","description":"The verification email has expired"}],"available":"user"},{"name":"initTakeoutSession","id":4032514052,"returns":"account.Takeout","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"contacts","type":"true","optional":true,"predicate":"flags.0","description":"Whether to export contacts"},{"name":"messageUsers","type":"true","optional":true,"predicate":"flags.1","description":"Whether to export messages in private chats"},{"name":"messageChats","type":"true","optional":true,"predicate":"flags.2","description":"Whether to export messages in legacy groups"},{"name":"messageMegagroups","type":"true","optional":true,"predicate":"flags.3","description":"Whether to export messages in supergroups"},{"name":"messageChannels","type":"true","optional":true,"predicate":"flags.4","description":"Whether to export messages in channels"},{"name":"files","type":"true","optional":true,"predicate":"flags.5","description":"Whether to export files"},{"name":"fileMaxSize","type":"number","optional":true,"predicate":"flags.5","description":"Maximum size of files to export"}],"description":"Intialize account takeout session","throws":[{"code":420,"name":"TAKEOUT_INIT_DELAY_X","description":"Wait X seconds before initing takeout"}],"available":"user"},{"name":"finishTakeoutSession","id":489050862,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"success","type":"true","optional":true,"predicate":"flags.0","description":"Data exported successfully"}],"description":"Finish account takeout session","throws":[{"code":403,"name":"TAKEOUT_REQUIRED","description":"A takeout session has to be initialized, first"}],"available":"user"},{"name":"confirmPasswordEmail","id":2413762848,"returns":"boolean","arguments":[{"name":"code","type":"string","description":"The phone code that was received after setting a recovery email"}],"description":"Verify an email to use as 2FA recovery method.","throws":[{"code":400,"name":"CODE_INVALID","description":"Code invalid"},{"code":400,"name":"EMAIL_HASH_EXPIRED","description":"Email hash expired"}],"available":"user"},{"name":"resendPasswordEmail","id":2055154197,"returns":"boolean","arguments":[],"description":"Resend the code to verify an email to use as 2FA recovery method.","available":"user"},{"name":"cancelPasswordEmail","id":3251361206,"returns":"boolean","arguments":[],"description":"Cancel the code that was sent to verify an email to use as 2FA recovery method.","available":"user"},{"name":"getContactSignUpNotification","id":2668087080,"returns":"boolean","arguments":[],"description":"Whether the user will receive notifications when contacts sign up","available":"user"},{"name":"setContactSignUpNotification","id":3488890721,"returns":"boolean","arguments":[{"name":"silent","type":"boolean","description":"Whether to disable contact sign up notifications"}],"description":"Toggle contact sign up notifications","available":"user"},{"name":"getNotifyExceptions","id":1398240377,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"compareSound","type":"true","optional":true,"predicate":"flags.1","description":"If true, chats with non-default sound will also be returned"},{"name":"peer","type":"InputNotifyPeer","optional":true,"predicate":"flags.0","description":"If specified, only chats of the specified category will be returned"}],"description":"Returns list of chats with non-default notification settings","available":"user"},{"name":"getWallPaper","id":4237155306,"returns":"WallPaper","arguments":[{"name":"wallpaper","type":"InputWallPaper","description":"The wallpaper to get info about"}],"description":"Get info about a certain wallpaper","available":"user"},{"name":"uploadWallPaper","id":3716494945,"returns":"WallPaper","arguments":[{"name":"file","type":"InputFile","description":"The JPG/PNG wallpaper"},{"name":"mimeType","type":"string","description":"MIME type of uploaded wallpaper"},{"name":"settings","type":"WallPaperSettings","description":"Wallpaper settings"}],"description":"Create and upload a new wallpaper","available":"user"},{"name":"saveWallPaper","id":1817860919,"returns":"boolean","arguments":[{"name":"wallpaper","type":"InputWallPaper","description":"Wallpaper to save"},{"name":"unsave","type":"boolean","description":"Uninstall wallpaper?"},{"name":"settings","type":"WallPaperSettings","description":"Wallpaper settings"}],"description":"Install/uninstall wallpaper","available":"user"},{"name":"installWallPaper","id":4276967273,"returns":"boolean","arguments":[{"name":"wallpaper","type":"InputWallPaper","description":"Wallpaper to install"},{"name":"settings","type":"WallPaperSettings","description":"Wallpaper settings"}],"description":"Install wallpaper","available":"user"},{"name":"resetWallPapers","id":3141244932,"returns":"boolean","arguments":[],"description":"Delete installed wallpapers","available":"user"},{"name":"getAutoDownloadSettings","id":1457130303,"returns":"account.AutoDownloadSettings","arguments":[],"description":"Get media autodownload settings","available":"user"},{"name":"saveAutoDownloadSettings","id":1995661875,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"low","type":"true","optional":true,"predicate":"flags.0","description":"Whether to save settings in the low data usage preset"},{"name":"high","type":"true","optional":true,"predicate":"flags.1","description":"Whether to save settings in the high data usage preset"},{"name":"settings","type":"AutoDownloadSettings","description":"Media autodownload settings"}],"description":"Change media autodownload settings","available":"user"},{"name":"uploadTheme","id":473805619,"returns":"Document","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"file","type":"InputFile","description":"Theme file uploaded as described in files »"},{"name":"thumb","type":"InputFile","optional":true,"predicate":"flags.0","description":"Thumbnail"},{"name":"fileName","type":"string","description":"File name"},{"name":"mimeType","type":"string","description":"MIME type, must be application/x-tgtheme-{format}, where format depends on the client"}],"description":"Upload theme","throws":[{"code":400,"name":"THEME_FILE_INVALID","description":"Invalid theme file provided"}],"available":"user"},{"name":"createTheme","id":2217919007,"returns":"Theme","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"slug","type":"string","description":"Unique theme ID"},{"name":"title","type":"string","description":"Theme name"},{"name":"document","type":"InputDocument","optional":true,"predicate":"flags.2","description":"Theme file"},{"name":"settings","type":"InputThemeSettings","optional":true,"predicate":"flags.3","description":"Theme settings"}],"description":"Create a theme","available":"user"},{"name":"updateTheme","id":1555261397,"returns":"Theme","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"format","type":"string","description":"Theme format, a string that identifies the theming engines supported by the client"},{"name":"theme","type":"InputTheme","description":"Theme to update"},{"name":"slug","type":"string","optional":true,"predicate":"flags.0","description":"Unique theme ID"},{"name":"title","type":"string","optional":true,"predicate":"flags.1","description":"Theme name"},{"name":"document","type":"InputDocument","optional":true,"predicate":"flags.2","description":"Theme file"},{"name":"settings","type":"InputThemeSettings","optional":true,"predicate":"flags.3","description":"Theme settings"}],"description":"Update theme","available":"user"},{"name":"saveTheme","id":4065792108,"returns":"boolean","arguments":[{"name":"theme","type":"InputTheme","description":"Theme to save"},{"name":"unsave","type":"boolean","description":"Unsave"}],"description":"Save a theme","available":"user"},{"name":"installTheme","id":2061776695,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"dark","type":"true","optional":true,"predicate":"flags.0","description":"Whether to install the dark version"},{"name":"format","type":"string","optional":true,"predicate":"flags.1","description":"Theme format, a string that identifies the theming engines supported by the client"},{"name":"theme","type":"InputTheme","optional":true,"predicate":"flags.1","description":"Theme to install"}],"description":"Install a theme","available":"user"},{"name":"getTheme","id":2375906347,"returns":"Theme","arguments":[{"name":"format","type":"string","description":"Theme format, a string that identifies the theming engines supported by the client"},{"name":"theme","type":"InputTheme","description":"Theme"},{"name":"documentId","type":"Long","description":"Document ID"}],"description":"Get theme information","throws":[{"code":400,"name":"THEME_FORMAT_INVALID","description":"Invalid theme format provided"},{"code":400,"name":"THEME_INVALID","description":"Invalid theme provided"}],"available":"user"},{"name":"getThemes","id":676939512,"returns":"account.Themes","arguments":[{"name":"format","type":"string","description":"Theme format, a string that identifies the theming engines supported by the client"},{"name":"hash","type":"number","description":"Hash for pagination, for more info click here"}],"description":"Get installed themes","available":"user"},{"name":"setContentSettings","id":3044323691,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"sensitiveEnabled","type":"true","optional":true,"predicate":"flags.0","description":"Enable NSFW content"}],"description":"Set sensitive content settings (for viewing or hiding NSFW content)","available":"user"},{"name":"getContentSettings","id":2342210990,"returns":"account.ContentSettings","arguments":[],"description":"Get sensitive content settings","available":"user"},{"name":"getMultiWallPapers","id":1705865692,"returns":"WallPaper[]","arguments":[{"name":"wallpapers","type":"InputWallPaper[]","description":"Wallpapers to fetch info about"}],"description":"Get info about multiple wallpapers","available":"user"},{"name":"getGlobalPrivacySettings","id":3945483510,"returns":"GlobalPrivacySettings","arguments":[],"description":"Get global privacy settings","available":"both"},{"name":"setGlobalPrivacySettings","id":517647042,"returns":"GlobalPrivacySettings","arguments":[{"name":"settings","type":"GlobalPrivacySettings","description":"Global privacy settings"}],"description":"Set global privacy settings","available":"both"},{"name":"reportProfilePhoto","id":4203529973,"returns":"boolean","arguments":[{"name":"peer","type":"InputPeer"},{"name":"photoId","type":"InputPhoto"},{"name":"reason","type":"ReportReason"},{"name":"message","type":"string"}]},{"name":"resetPassword","id":2466827803,"returns":"account.ResetPasswordResult","arguments":[]},{"name":"declinePasswordReset","id":1284770294,"returns":"boolean","arguments":[]}],"unions":[{"type":"PrivacyRules","subtypes":["account.privacyRules"],"description":"Privacy rules"},{"type":"Authorizations","subtypes":["account.authorizations"],"description":"Logged-in sessions"},{"type":"Password","subtypes":["account.password"],"description":"Configuration for two-factor authorization"},{"type":"PasswordSettings","subtypes":["account.passwordSettings"],"description":"Private info associated to the password info (recovery email, telegram passport info & so on)"},{"type":"PasswordInputSettings","subtypes":["account.passwordInputSettings"],"description":"Constructor for setting up a new 2FA SRP password"},{"type":"TmpPassword","subtypes":["account.tmpPassword"],"description":"Temporary password"},{"type":"WebAuthorizations","subtypes":["account.webAuthorizations"],"description":"Web authorizations"},{"type":"AuthorizationForm","subtypes":["account.authorizationForm"],"description":"Authorization form"},{"type":"SentEmailCode","subtypes":["account.sentEmailCode"],"description":"The email code that was sent"},{"type":"Takeout","subtypes":["account.takeout"],"description":"Takeout info"},{"type":"WallPapers","subtypes":["account.wallPapersNotModified","account.wallPapers"],"description":"Wallpapers"},{"type":"AutoDownloadSettings","subtypes":["account.autoDownloadSettings"],"description":"Media autodownload settings"},{"type":"Themes","subtypes":["account.themesNotModified","account.themes"],"description":"Installed themes"},{"type":"ContentSettings","subtypes":["account.contentSettings"],"description":"Sensitive content settings"},{"type":"ResetPasswordResult","subtypes":["account.resetPasswordFailedWait","account.resetPasswordRequestedWait","account.resetPasswordOk"]}]},"channels":{"classes":[{"name":"channelParticipants","id":2595290799,"type":"channels.ChannelParticipants","arguments":[{"name":"count","type":"number","description":"Total number of participants that correspond to the given query"},{"name":"participants","type":"ChannelParticipant[]","description":"Participants"},{"name":"chats","type":"Chat[]"},{"name":"users","type":"User[]","description":"Users mentioned in participant info"}],"description":"Represents multiple channel participants"},{"name":"channelParticipantsNotModified","id":4028055529,"type":"channels.ChannelParticipants","arguments":[],"description":"No new participant info could be found"},{"name":"channelParticipant","id":3753378583,"type":"channels.ChannelParticipant","arguments":[{"name":"participant","type":"ChannelParticipant","description":"The channel participant"},{"name":"chats","type":"Chat[]"},{"name":"users","type":"User[]","description":"Users"}],"description":"Represents a channel participant"},{"name":"adminLogResults","id":3985307469,"type":"channels.AdminLogResults","arguments":[{"name":"events","type":"ChannelAdminLogEvent[]","description":"Admin log events"},{"name":"chats","type":"Chat[]","description":"Chats mentioned in events"},{"name":"users","type":"User[]","description":"Users mentioned in events"}],"description":"Admin log events"}],"methods":[{"name":"readHistory","id":3423619383,"returns":"boolean","arguments":[{"name":"channel","type":"InputChannel","description":"Channel/supergroup"},{"name":"maxId","type":"number","description":"ID of message up to which messages should be marked as read"}],"description":"Mark channel/supergroup history as read","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"}],"available":"user"},{"name":"deleteMessages","id":2227305806,"returns":"messages.AffectedMessages","arguments":[{"name":"channel","type":"InputChannel","description":"Channel/supergroup"},{"name":"id","type":"number[]","description":"IDs of messages to delete"}],"description":"Delete messages in a channel/supergroup","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":403,"name":"MESSAGE_DELETE_FORBIDDEN","description":"You can't delete one of the messages you tried to delete, most likely because it is a service message."},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"}],"available":"both"},{"name":"deleteUserHistory","id":3507345179,"returns":"messages.AffectedHistory","arguments":[{"name":"channel","type":"InputChannel","description":"Supergroup"},{"name":"userId","type":"InputUser","description":"User whose messages should be deleted"}],"description":"Delete all messages sent by a certain user in a supergroup","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"USER_ID_INVALID","description":"The provided user ID is invalid"}],"available":"user"},{"name":"reportSpam","id":4261967888,"returns":"boolean","arguments":[{"name":"channel","type":"InputChannel","description":"Supergroup"},{"name":"userId","type":"InputUser","description":"ID of the user that sent the spam messages"},{"name":"id","type":"number[]","description":"IDs of spam messages"}],"description":"Reports some messages from a user in a supergroup as spam; requires administrator rights in the supergroup","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"INPUT_USER_DEACTIVATED","description":"The specified user was deleted"},{"code":400,"name":"USER_ID_INVALID","description":"The provided user ID is invalid"}],"available":"user"},{"name":"getMessages","id":2911672867,"returns":"messages.Messages","arguments":[{"name":"channel","type":"InputChannel","description":"Channel/supergroup"},{"name":"id","type":"InputMessage[]","description":"IDs of messages to get"}],"description":"Get channel/supergroup messages","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"MESSAGE_IDS_EMPTY","description":"No message ids were provided"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"}],"available":"both"},{"name":"getParticipants","id":306054633,"returns":"channels.ChannelParticipants","arguments":[{"name":"channel","type":"InputChannel","description":"Channel"},{"name":"filter","type":"ChannelParticipantsFilter","description":"Which participant types to fetch"},{"name":"offset","type":"number","description":"Offset"},{"name":"limit","type":"number","description":"Limit"},{"name":"hash","type":"number","description":"Hash"}],"description":"Get the participants of a supergroup/channel","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"INPUT_CONSTRUCTOR_INVALID","description":"The provided constructor is invalid"}],"available":"both"},{"name":"getParticipant","id":2695589062,"returns":"channels.ChannelParticipant","arguments":[{"name":"channel","type":"InputChannel","description":"Channel/supergroup"},{"name":"participant","type":"InputPeer"}],"description":"Get info about a channel/supergroup participant","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"USER_ID_INVALID","description":"The provided user ID is invalid"},{"code":400,"name":"USER_NOT_PARTICIPANT","description":"You're not a member of this supergroup/channel"}],"available":"both"},{"name":"getChannels","id":176122811,"returns":"messages.Chats","arguments":[{"name":"id","type":"InputChannel[]","description":"IDs of channels/supergroups to get info about"}],"description":"Get info about channels/supergroups","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"}],"available":"both"},{"name":"getFullChannel","id":141781513,"returns":"messages.ChatFull","arguments":[{"name":"channel","type":"InputChannel","description":"The channel to get info about"}],"description":"Get full info about a channel","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":403,"name":"CHANNEL_PUBLIC_GROUP_NA","description":"channel/supergroup not available"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"}],"available":"both"},{"name":"createChannel","id":1029681423,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"broadcast","type":"true","optional":true,"predicate":"flags.0","description":"Whether to create a channel"},{"name":"megagroup","type":"true","optional":true,"predicate":"flags.1","description":"Whether to create a supergroup"},{"name":"forImport","type":"true","optional":true,"predicate":"flags.3"},{"name":"title","type":"string","description":"Channel title"},{"name":"about","type":"string","description":"Channel description"},{"name":"geoPoint","type":"InputGeoPoint","optional":true,"predicate":"flags.2","description":"Geogroup location"},{"name":"address","type":"string","optional":true,"predicate":"flags.2","description":"Geogroup address"}],"description":"Create a supergroup/channel.","throws":[{"code":400,"name":"CHANNELS_TOO_MUCH","description":"You have joined too many channels/supergroups"},{"code":400,"name":"CHAT_ABOUT_TOO_LONG","description":"Chat about too long"},{"code":400,"name":"CHAT_TITLE_EMPTY","description":"No chat title provided"},{"code":403,"name":"USER_RESTRICTED","description":"You're spamreported, you can't create channels or chats."}],"available":"user"},{"name":"editAdmin","id":3543959810,"returns":"Updates","arguments":[{"name":"channel","type":"InputChannel","description":"The supergroup/channel."},{"name":"userId","type":"InputUser","description":"The ID of the user whose admin rights should be modified"},{"name":"adminRights","type":"ChatAdminRights","description":"The admin rights"},{"name":"rank","type":"string","description":"Indicates the role (rank) of the admin in the group: just an arbitrary string"}],"description":"Modify the admin rights of a user in a supergroup/channel.","throws":[{"code":400,"name":"ADMINS_TOO_MUCH","description":"There are too many admins"},{"code":400,"name":"BOTS_TOO_MUCH","description":"There are too many bots in this chat/channel"},{"code":400,"name":"BOT_CHANNELS_NA","description":"Bots can't edit admin privileges"},{"code":400,"name":"BOT_GROUPS_BLOCKED","description":"This bot can't be added to groups"},{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":403,"name":"CHAT_ADMIN_INVITE_REQUIRED","description":"You do not have the rights to do this"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"},{"code":406,"name":"FRESH_CHANGE_ADMINS_FORBIDDEN","description":"You were just elected admin, you can't add or modify other admins yet"},{"code":400,"name":"INPUT_USER_DEACTIVATED","description":"The specified user was deleted"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":403,"name":"RIGHT_FORBIDDEN","description":"Your admin rights do not allow you to do this"},{"code":400,"name":"USERS_TOO_MUCH","description":"The maximum number of users has been exceeded (to create a chat, for example)"},{"code":400,"name":"USER_BLOCKED","description":"User blocked"},{"code":403,"name":"USER_CHANNELS_TOO_MUCH","description":"One of the users you tried to add is already in too many channels/supergroups"},{"code":400,"name":"USER_CREATOR","description":"You can't leave this channel, because you're its creator"},{"code":400,"name":"USER_ID_INVALID","description":"The provided user ID is invalid"},{"code":400,"name":"USER_NOT_MUTUAL_CONTACT","description":"The provided user is not a mutual contact"},{"code":403,"name":"USER_PRIVACY_RESTRICTED","description":"The user's privacy settings do not allow you to do this"},{"code":403,"name":"USER_RESTRICTED","description":"You're spamreported, you can't create channels or chats."}],"available":"both"},{"name":"editTitle","id":1450044624,"returns":"Updates","arguments":[{"name":"channel","type":"InputChannel","description":"Channel/supergroup"},{"name":"title","type":"string","description":"New name"}],"description":"Edit the name of a channel/supergroup","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"CHAT_NOT_MODIFIED","description":"The pinned message wasn't modified"},{"code":400,"name":"CHAT_TITLE_EMPTY","description":"No chat title provided"},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"}],"available":"both"},{"name":"editPhoto","id":4046346185,"returns":"Updates","arguments":[{"name":"channel","type":"InputChannel","description":"Channel/supergroup whose photo should be edited"},{"name":"photo","type":"InputChatPhoto","description":"New photo"}],"description":"Change the photo of a channel/supergroup","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"CHAT_NOT_MODIFIED","description":"The pinned message wasn't modified"},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"},{"code":400,"name":"PHOTO_CROP_SIZE_SMALL","description":"Photo is too small"},{"code":400,"name":"PHOTO_EXT_INVALID","description":"The extension of the photo is invalid"},{"code":400,"name":"PHOTO_INVALID","description":"Photo invalid"}],"available":"both"},{"name":"checkUsername","id":283557164,"returns":"boolean","arguments":[{"name":"channel","type":"InputChannel","description":"The channel/supergroup that will assigned the specified username"},{"name":"username","type":"string","description":"The username to check"}],"description":"Check if a username is free and can be assigned to a channel/supergroup","throws":[{"code":400,"name":"CHANNELS_ADMIN_PUBLIC_TOO_MUCH","description":"You're admin of too many public channels, make some channels private to change the username of this channel"},{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"},{"code":400,"name":"USERNAME_INVALID","description":"The provided username is not valid"}],"available":"user"},{"name":"updateUsername","id":890549214,"returns":"boolean","arguments":[{"name":"channel","type":"InputChannel","description":"Channel"},{"name":"username","type":"string","description":"New username"}],"description":"Change the username of a supergroup/channel","throws":[{"code":400,"name":"CHANNELS_ADMIN_PUBLIC_TOO_MUCH","description":"You're admin of too many public channels, make some channels private to change the username of this channel"},{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"CHAT_NOT_MODIFIED","description":"The pinned message wasn't modified"},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"},{"code":400,"name":"USERNAME_INVALID","description":"The provided username is not valid"},{"code":400,"name":"USERNAME_NOT_MODIFIED","description":"The username was not modified"},{"code":400,"name":"USERNAME_OCCUPIED","description":"The provided username is already occupied"}],"available":"user"},{"name":"joinChannel","id":615851205,"returns":"Updates","arguments":[{"name":"channel","type":"InputChannel","description":"Channel/supergroup to join"}],"description":"Join a channel/supergroup","throws":[{"code":400,"name":"CHANNELS_TOO_MUCH","description":"You have joined too many channels/supergroups"},{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"INVITE_HASH_EMPTY","description":"The invite hash is empty"},{"code":400,"name":"INVITE_HASH_EXPIRED","description":"The invite link has expired"},{"code":400,"name":"INVITE_HASH_INVALID","description":"The invite hash is invalid"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"USERS_TOO_MUCH","description":"The maximum number of users has been exceeded (to create a chat, for example)"},{"code":400,"name":"USER_ALREADY_PARTICIPANT","description":"The user is already in the group"},{"code":400,"name":"USER_CHANNELS_TOO_MUCH","description":"One of the users you tried to add is already in too many channels/supergroups"}],"available":"user"},{"name":"leaveChannel","id":4164332181,"returns":"Updates","arguments":[{"name":"channel","type":"InputChannel","description":"Channel/supergroup to leave"}],"description":"Leave a channel/supergroup","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":403,"name":"CHANNEL_PUBLIC_GROUP_NA","description":"channel/supergroup not available"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"USER_CREATOR","description":"You can't leave this channel, because you're its creator"},{"code":400,"name":"USER_NOT_PARTICIPANT","description":"You're not a member of this supergroup/channel"}],"available":"both"},{"name":"inviteToChannel","id":429865580,"returns":"Updates","arguments":[{"name":"channel","type":"InputChannel","description":"Channel/supergroup"},{"name":"users","type":"InputUser[]","description":"Users to invite"}],"description":"Invite users to a channel/supergroup","throws":[{"code":400,"name":"BOTS_TOO_MUCH","description":"There are too many bots in this chat/channel"},{"code":400,"name":"BOT_GROUPS_BLOCKED","description":"This bot can't be added to groups"},{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"CHAT_INVALID","description":"Invalid chat"},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"},{"code":400,"name":"INPUT_USER_DEACTIVATED","description":"The specified user was deleted"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"USERS_TOO_MUCH","description":"The maximum number of users has been exceeded (to create a chat, for example)"},{"code":400,"name":"USER_BANNED_IN_CHANNEL","description":"You're banned from sending messages in supergroups/channels"},{"code":400,"name":"USER_BLOCKED","description":"User blocked"},{"code":400,"name":"USER_BOT","description":"Bots can only be admins in channels."},{"code":403,"name":"USER_CHANNELS_TOO_MUCH","description":"One of the users you tried to add is already in too many channels/supergroups"},{"code":400,"name":"USER_ID_INVALID","description":"The provided user ID is invalid"},{"code":400,"name":"USER_KICKED","description":"This user was kicked from this supergroup/channel"},{"code":400,"name":"USER_NOT_MUTUAL_CONTACT","description":"The provided user is not a mutual contact"},{"code":403,"name":"USER_PRIVACY_RESTRICTED","description":"The user's privacy settings do not allow you to do this"}],"available":"user"},{"name":"deleteChannel","id":3222347747,"returns":"Updates","arguments":[{"name":"channel","type":"InputChannel","description":"Channel/supergroup to delete"}],"description":"Delete a channel/supergroup","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHANNEL_TOO_LARGE","description":"Channel is too large to be deleted; this error is issued when trying to delete channels with more than 1000 members (subject to change)"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"}],"available":"user"},{"name":"exportMessageLink","id":3862932971,"returns":"ExportedMessageLink","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"grouped","type":"true","optional":true,"predicate":"flags.0","description":"Whether to include other grouped media (for albums)"},{"name":"thread","type":"true","optional":true,"predicate":"flags.1","description":"Whether to also include a thread ID, if available, inside of the link"},{"name":"channel","type":"InputChannel","description":"Channel"},{"name":"id","type":"number","description":"Message ID"}],"description":"Get link and embed info of a message in a channel/supergroup","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"}],"available":"user"},{"name":"toggleSignatures","id":527021574,"returns":"Updates","arguments":[{"name":"channel","type":"InputChannel","description":"Channel"},{"name":"enabled","type":"boolean","description":"Value"}],"description":"Enable/disable message signatures in channels","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"}],"available":"user"},{"name":"getAdminedPublicChannels","id":4172297903,"returns":"messages.Chats","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"byLocation","type":"true","optional":true,"predicate":"flags.0","description":"Get geogroups"},{"name":"checkLimit","type":"true","optional":true,"predicate":"flags.1","description":"If set and the user has reached the limit of owned public channels/supergroups/geogroups, instead of returning the channel list one of the specified {@link channels.getAdminedPublicChannels} will be returned.
Useful to check if a new public channel can indeed be created, even before asking the user to enter a channel username to use in {@link channels.checkUsername}/{@link channels.updateUsername}."}],"description":"Get channels/supergroups/geogroups we're admin in. Usually called when the user exceeds the {@link config} for owned public channels/supergroups/geogroups, and the user is given the choice to remove one of their channels/supergroups/geogroups.","throws":[{"code":400,"name":"CHANNELS_ADMIN_LOCATED_TOO_MUCH","description":"Returned if both the check_limit and the by_location flags are set and the user has reached the limit of public geogroups"},{"code":400,"name":"CHANNELS_ADMIN_PUBLIC_TOO_MUCH","description":"Returned if the check_limit flag is set and the user has reached the limit of public channels/supergroups"}],"available":"user"},{"name":"editBanned","id":2531708289,"returns":"Updates","arguments":[{"name":"channel","type":"InputChannel","description":"The supergroup/channel."},{"name":"participant","type":"InputPeer"},{"name":"bannedRights","type":"ChatBannedRights","description":"The banned rights"}],"description":"Ban/unban/kick a user in a supergroup/channel.","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"PINNED_DIALOGS_TOO_MUCH","description":"Too many pinned dialogs"},{"code":400,"name":"USER_ADMIN_INVALID","description":"You're not an admin"},{"code":400,"name":"USER_ID_INVALID","description":"The provided user ID is invalid"}],"available":"both"},{"name":"getAdminLog","id":870184064,"returns":"channels.AdminLogResults","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"channel","type":"InputChannel","description":"Channel"},{"name":"q","type":"string","description":"Search query, can be empty"},{"name":"eventsFilter","type":"ChannelAdminLogEventsFilter","optional":true,"predicate":"flags.0","description":"Event filter"},{"name":"admins","type":"InputUser[]","optional":true,"predicate":"flags.1","description":"Only show events from these admins"},{"name":"maxId","type":"Long","description":"Maximum ID of message to return (see pagination)"},{"name":"minId","type":"Long","description":"Minimum ID of message to return (see pagination)"},{"name":"limit","type":"number","description":"Maximum number of results to return, see pagination"}],"description":"Get the admin log of a channel/supergroup","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","description":"You can't write in this chat"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"}],"available":"user"},{"name":"setStickers","id":3935085817,"returns":"boolean","arguments":[{"name":"channel","type":"InputChannel","description":"Supergroup"},{"name":"stickerset","type":"InputStickerSet","description":"The stickerset to associate"}],"description":"Associate a stickerset to the supergroup","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"PARTICIPANTS_TOO_FEW","description":"Not enough participants"}],"available":"both"},{"name":"readMessageContents","id":3937786936,"returns":"boolean","arguments":[{"name":"channel","type":"InputChannel","description":"Channel/supergroup"},{"name":"id","type":"number[]","description":"IDs of messages whose contents should be marked as read"}],"description":"Mark channel/supergroup message contents as read","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"}],"available":"user"},{"name":"deleteHistory","id":2939592002,"returns":"boolean","arguments":[{"name":"channel","type":"InputChannel","description":"Supergroup whose history must be deleted"},{"name":"maxId","type":"number","description":"ID of message up to which the history must be deleted"}],"description":"Delete the history of a supergroup","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"}],"available":"user"},{"name":"togglePreHistoryHidden","id":3938171212,"returns":"Updates","arguments":[{"name":"channel","type":"InputChannel","description":"Channel/supergroup"},{"name":"enabled","type":"boolean","description":"Hide/unhide"}],"description":"Hide/unhide message history for new channel/supergroup users","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"CHAT_ID_INVALID","description":"The provided chat id is invalid"},{"code":400,"name":"CHAT_LINK_EXISTS","description":"The chat is public, you can't hide the history to new users"},{"code":400,"name":"CHAT_NOT_MODIFIED","description":"The pinned message wasn't modified"}],"available":"user"},{"name":"getLeftChannels","id":2202135744,"returns":"messages.Chats","arguments":[{"name":"offset","type":"number","description":"Offset for pagination"}],"description":"Get a list of channels/supergroups we left","throws":[{"code":403,"name":"TAKEOUT_REQUIRED","description":"A takeout session has to be initialized, first"}],"available":"user"},{"name":"getGroupsForDiscussion","id":4124758904,"returns":"messages.Chats","arguments":[],"description":"Get all groups that can be used as discussion groups.","available":"user"},{"name":"setDiscussionGroup","id":1079520178,"returns":"boolean","arguments":[{"name":"broadcast","type":"InputChannel","description":"Channel"},{"name":"group","type":"InputChannel","description":"Discussion group to associate to the channel"}],"description":"Associate a group to a channel as discussion group for that channel","throws":[{"code":400,"name":"BROADCAST_ID_INVALID","description":"Broadcast ID invalid"},{"code":400,"name":"LINK_NOT_MODIFIED","description":"Discussion link not modified"},{"code":400,"name":"MEGAGROUP_ID_INVALID","description":"Invalid supergroup ID"}],"available":"user"},{"name":"editCreator","id":2402864415,"returns":"Updates","arguments":[{"name":"channel","type":"InputChannel","description":"Channel"},{"name":"userId","type":"InputUser","description":"New channel owner"},{"name":"password","type":"InputCheckPasswordSRP","description":"2FA password of account"}],"description":"Transfer channel ownership","throws":[{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"PASSWORD_HASH_INVALID","description":"The provided password hash is invalid"},{"code":400,"name":"PASSWORD_MISSING","description":"You must enable 2FA in order to transfer ownership of a channel"},{"code":400,"name":"PASSWORD_TOO_FRESH_X","description":"The password was modified less than 24 hours ago, try again in X seconds"},{"code":400,"name":"SESSION_TOO_FRESH_X","description":"This session was created less than 24 hours ago, try again in X seconds"},{"code":400,"name":"USER_ID_INVALID","description":"The provided user ID is invalid"}],"available":"user"},{"name":"editLocation","id":1491484525,"returns":"boolean","arguments":[{"name":"channel","type":"InputChannel","description":"Geogroup"},{"name":"geoPoint","type":"InputGeoPoint","description":"New geolocation"},{"name":"address","type":"string","description":"Address string"}],"description":"Edit location of geo group","throws":[{"code":400,"name":"MEGAGROUP_REQUIRED","description":"You can only use this method on a supergroup"}],"available":"user"},{"name":"toggleSlowMode","id":3990134512,"returns":"Updates","arguments":[{"name":"channel","type":"InputChannel","description":"The supergroup"},{"name":"seconds","type":"number","description":"Users will only be able to send one message every seconds seconds, 0 to disable the limitation"}],"description":"Toggle supergroup slow mode: if enabled, users will only be able to send one message every seconds seconds","throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"},{"code":400,"name":"CHAT_NOT_MODIFIED","description":"The pinned message wasn't modified"},{"code":400,"name":"INPUT_METHOD_INVALID_1192227_X","description":"Invalid method"},{"code":400,"name":"INPUT_METHOD_INVALID_1604042050_X","description":"Invalid method"},{"code":400,"name":"SECONDS_INVALID","description":"Invalid duration provided"}],"available":"user"},{"name":"getInactiveChannels","id":300429806,"returns":"messages.InactiveChats","arguments":[],"description":"Get inactive channels and supergroups","available":"user"},{"name":"convertToGigagroup","id":187239529,"returns":"Updates","arguments":[{"name":"channel","type":"InputChannel"}]}],"unions":[{"type":"ChannelParticipants","subtypes":["channels.channelParticipants","channels.channelParticipantsNotModified"],"description":"Channel/supergroup participants"},{"type":"ChannelParticipant","subtypes":["channels.channelParticipant"],"description":"Channel participant"},{"type":"AdminLogResults","subtypes":["channels.adminLogResults"],"description":"Admin log events"}]},"payments":{"classes":[{"name":"paymentForm","id":2366317589,"type":"payments.PaymentForm","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"canSaveCredentials","type":"true","optional":true,"predicate":"flags.2","description":"Whether the user can choose to save credentials."},{"name":"passwordMissing","type":"true","optional":true,"predicate":"flags.3","description":"Indicates that the user can save payment credentials, but only after setting up a 2FA password (currently the account doesn't have a 2FA password)"},{"name":"formId","type":"Long"},{"name":"botId","type":"number","description":"Bot ID"},{"name":"invoice","type":"Invoice","description":"Invoice"},{"name":"providerId","type":"number","description":"Payment provider ID."},{"name":"url","type":"string","description":"Payment form URL"},{"name":"nativeProvider","type":"string","optional":true,"predicate":"flags.4","description":"Payment provider name.
One of the following:
- stripe"},{"name":"nativeParams","type":"DataJSON","optional":true,"predicate":"flags.4","description":"Contains information about the payment provider, if available, to support it natively without the need for opening the URL.
A JSON object that can contain the following fields:

- apple_pay_merchant_id: Apple Pay merchant ID
- google_pay_public_key: Google Pay public key
- need_country: True, if the user country must be provided,
- need_zip: True, if the user ZIP/postal code must be provided,
- need_cardholder_name: True, if the cardholder name must be provided"},{"name":"savedInfo","type":"PaymentRequestedInfo","optional":true,"predicate":"flags.0","description":"Saved server-side order information"},{"name":"savedCredentials","type":"PaymentSavedCredentials","optional":true,"predicate":"flags.1","description":"Contains information about saved card credentials"},{"name":"users","type":"User[]","description":"Users"}],"description":"Payment form"},{"name":"validatedRequestedInfo","id":3510966403,"type":"payments.ValidatedRequestedInfo","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"id","type":"string","optional":true,"predicate":"flags.0","description":"ID"},{"name":"shippingOptions","type":"ShippingOption[]","optional":true,"predicate":"flags.1","description":"Shipping options"}],"description":"Validated user-provided info"},{"name":"paymentResult","id":1314881805,"type":"payments.PaymentResult","arguments":[{"name":"updates","type":"Updates","description":"Info about the payment"}],"description":"Payment result"},{"name":"paymentVerificationNeeded","id":3628142905,"type":"payments.PaymentResult","arguments":[{"name":"url","type":"string","description":"URL for additional payment credentials verification"}],"description":"Payment was not successful, additional verification is needed"},{"name":"paymentReceipt","id":280319440,"type":"payments.PaymentReceipt","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"date","type":"number","description":"Date of generation"},{"name":"botId","type":"number","description":"Bot ID"},{"name":"providerId","type":"number","description":"Provider ID"},{"name":"title","type":"string"},{"name":"description","type":"string"},{"name":"photo","type":"WebDocument","optional":true,"predicate":"flags.2"},{"name":"invoice","type":"Invoice","description":"Invoice"},{"name":"info","type":"PaymentRequestedInfo","optional":true,"predicate":"flags.0","description":"Info"},{"name":"shipping","type":"ShippingOption","optional":true,"predicate":"flags.1","description":"Selected shipping option"},{"name":"tipAmount","type":"Long","optional":true,"predicate":"flags.3"},{"name":"currency","type":"string","description":"Three-letter ISO 4217 currency code"},{"name":"totalAmount","type":"Long","description":"Total amount in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies)."},{"name":"credentialsTitle","type":"string","description":"Payment credential name"},{"name":"users","type":"User[]","description":"Users"}],"description":"Receipt"},{"name":"savedInfo","id":4220511292,"type":"payments.SavedInfo","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"hasSavedCredentials","type":"true","optional":true,"predicate":"flags.1","description":"Whether the user has some saved payment credentials"},{"name":"savedInfo","type":"PaymentRequestedInfo","optional":true,"predicate":"flags.0","description":"Saved server-side order information"}],"description":"Saved server-side order information"},{"name":"bankCardData","id":1042605427,"type":"payments.BankCardData","arguments":[{"name":"title","type":"string","description":"Credit card title"},{"name":"openUrls","type":"BankCardOpenUrl[]","description":"Info URL(s) provided by the card's bank(s)"}],"description":"Credit card info, provided by the card's bank(s)"}],"methods":[{"name":"getPaymentForm","id":2318613645,"returns":"payments.PaymentForm","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"peer","type":"InputPeer"},{"name":"msgId","type":"number","description":"Message ID of payment form"},{"name":"themeParams","type":"DataJSON","optional":true,"predicate":"flags.0"}],"description":"Get a payment form","throws":[{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"}],"available":"user"},{"name":"getPaymentReceipt","id":611897804,"returns":"payments.PaymentReceipt","arguments":[{"name":"peer","type":"InputPeer"},{"name":"msgId","type":"number","description":"Message ID of receipt"}],"description":"Get payment receipt","throws":[{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"}],"available":"user"},{"name":"validateRequestedInfo","id":3675271536,"returns":"payments.ValidatedRequestedInfo","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"save","type":"true","optional":true,"predicate":"flags.0","description":"Save order information to re-use it for future orders"},{"name":"peer","type":"InputPeer"},{"name":"msgId","type":"number","description":"Message ID of payment form"},{"name":"info","type":"PaymentRequestedInfo","description":"Requested order information"}],"description":"Submit requested order information for validation","throws":[{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"}],"available":"user"},{"name":"sendPaymentForm","id":818134173,"returns":"payments.PaymentResult","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"formId","type":"Long"},{"name":"peer","type":"InputPeer"},{"name":"msgId","type":"number","description":"Message ID of form"},{"name":"requestedInfoId","type":"string","optional":true,"predicate":"flags.0","description":"ID of saved and validated {@link payments.validatedRequestedInfo}"},{"name":"shippingOptionId","type":"string","optional":true,"predicate":"flags.1","description":"Chosen shipping option ID"},{"name":"credentials","type":"InputPaymentCredentials","description":"Payment credentials"},{"name":"tipAmount","type":"Long","optional":true,"predicate":"flags.2"}],"description":"Send compiled payment form","throws":[{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"}],"available":"user"},{"name":"getSavedInfo","id":578650699,"returns":"payments.SavedInfo","arguments":[],"description":"Get saved payment information","available":"user"},{"name":"clearSavedInfo","id":3627905217,"returns":"boolean","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"credentials","type":"true","optional":true,"predicate":"flags.0","description":"Remove saved payment credentials"},{"name":"info","type":"true","optional":true,"predicate":"flags.1","description":"Clear the last order settings saved by the user"}],"description":"Clear saved payment information","available":"user"},{"name":"getBankCardData","id":779736953,"returns":"payments.BankCardData","arguments":[{"name":"number","type":"string","description":"Credit card number"}],"description":"Get info about a credit card","available":"user"}],"unions":[{"type":"PaymentForm","subtypes":["payments.paymentForm"],"description":"Payment form"},{"type":"ValidatedRequestedInfo","subtypes":["payments.validatedRequestedInfo"],"description":"Validated requested info"},{"type":"PaymentResult","subtypes":["payments.paymentResult","payments.paymentVerificationNeeded"],"description":"Payment result"},{"type":"PaymentReceipt","subtypes":["payments.paymentReceipt"],"description":"Payment receipt"},{"type":"SavedInfo","subtypes":["payments.savedInfo"],"description":"Saved payment info"},{"type":"BankCardData","subtypes":["payments.bankCardData"],"description":"Credit card info, provided by the card's bank(s)"}]},"phone":{"classes":[{"name":"phoneCall","id":3968000320,"type":"phone.PhoneCall","arguments":[{"name":"phoneCall","type":"PhoneCall","description":"The VoIP phone call"},{"name":"users","type":"User[]","description":"VoIP phone call participants"}],"description":"A VoIP phone call"},{"name":"groupCall","id":2658302637,"type":"phone.GroupCall","arguments":[{"name":"call","type":"GroupCall"},{"name":"participants","type":"GroupCallParticipant[]"},{"name":"participantsNextOffset","type":"string"},{"name":"chats","type":"Chat[]"},{"name":"users","type":"User[]"}]},{"name":"groupParticipants","id":4101460406,"type":"phone.GroupParticipants","arguments":[{"name":"count","type":"number"},{"name":"participants","type":"GroupCallParticipant[]"},{"name":"nextOffset","type":"string"},{"name":"chats","type":"Chat[]"},{"name":"users","type":"User[]"},{"name":"version","type":"number"}]},{"name":"joinAsPeers","id":2951045695,"type":"phone.JoinAsPeers","arguments":[{"name":"peers","type":"Peer[]"},{"name":"chats","type":"Chat[]"},{"name":"users","type":"User[]"}]},{"name":"exportedGroupCallInvite","id":541839704,"type":"phone.ExportedGroupCallInvite","arguments":[{"name":"link","type":"string"}]}],"methods":[{"name":"getCallConfig","id":1430593449,"returns":"DataJSON","arguments":[],"description":"Get phone call configuration to be passed to libtgvoip's shared config","available":"user"},{"name":"requestCall","id":1124046573,"returns":"phone.PhoneCall","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"video","type":"true","optional":true,"predicate":"flags.0","description":"Whether to start a video call"},{"name":"userId","type":"InputUser","description":"Destination of the phone call"},{"name":"randomId","type":"number","description":"Random ID to avoid resending the same object"},{"name":"gAHash","type":"Buffer","description":"Parameter for E2E encryption key exchange »"},{"name":"protocol","type":"PhoneCallProtocol","description":"Phone call settings"}],"description":"Start a telegram phone call","throws":[{"code":400,"name":"CALL_PROTOCOL_FLAGS_INVALID","description":"Call protocol flags invalid"},{"code":400,"name":"PARTICIPANT_VERSION_OUTDATED","description":"The other participant does not use an up to date telegram client with support for calls"},{"code":400,"name":"USER_ID_INVALID","description":"The provided user ID is invalid"},{"code":403,"name":"USER_IS_BLOCKED","description":"You were blocked by this user"},{"code":403,"name":"USER_PRIVACY_RESTRICTED","description":"The user's privacy settings do not allow you to do this"}],"available":"user"},{"name":"acceptCall","id":1003664544,"returns":"phone.PhoneCall","arguments":[{"name":"peer","type":"InputPhoneCall","description":"The call to accept"},{"name":"gB","type":"Buffer","description":"Parameter for E2E encryption key exchange »"},{"name":"protocol","type":"PhoneCallProtocol","description":"Phone call settings"}],"description":"Accept incoming call","throws":[{"code":400,"name":"CALL_ALREADY_ACCEPTED","description":"The call was already accepted"},{"code":400,"name":"CALL_ALREADY_DECLINED","description":"The call was already declined"},{"code":400,"name":"CALL_PEER_INVALID","description":"The provided call peer object is invalid"},{"code":400,"name":"CALL_PROTOCOL_FLAGS_INVALID","description":"Call protocol flags invalid"}],"available":"user"},{"name":"confirmCall","id":788404002,"returns":"phone.PhoneCall","arguments":[{"name":"peer","type":"InputPhoneCall","description":"The phone call"},{"name":"gA","type":"Buffer","description":"Parameter for E2E encryption key exchange »"},{"name":"keyFingerprint","type":"Long","description":"Key fingerprint"},{"name":"protocol","type":"PhoneCallProtocol","description":"Phone call settings"}],"description":"Complete phone call E2E encryption key exchange »","throws":[{"code":400,"name":"CALL_ALREADY_DECLINED","description":"The call was already declined"},{"code":400,"name":"CALL_PEER_INVALID","description":"The provided call peer object is invalid"}],"available":"user"},{"name":"receivedCall","id":399855457,"returns":"boolean","arguments":[{"name":"peer","type":"InputPhoneCall","description":"The phone call we're currently in"}],"description":"Optional: notify the server that the user is currently busy in a call: this will automatically refuse all incoming phone calls until the current phone call is ended.","throws":[{"code":400,"name":"CALL_ALREADY_DECLINED","description":"The call was already declined"},{"code":400,"name":"CALL_PEER_INVALID","description":"The provided call peer object is invalid"}],"available":"user"},{"name":"discardCall","id":2999697856,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"video","type":"true","optional":true,"predicate":"flags.0","description":"Whether this is a video call"},{"name":"peer","type":"InputPhoneCall","description":"The phone call"},{"name":"duration","type":"number","description":"Call duration"},{"name":"reason","type":"PhoneCallDiscardReason","description":"Why was the call discarded"},{"name":"connectionId","type":"Long","description":"Preferred libtgvoip relay ID"}],"description":"Refuse or end running call","throws":[{"code":400,"name":"CALL_ALREADY_ACCEPTED","description":"The call was already accepted"},{"code":400,"name":"CALL_PEER_INVALID","description":"The provided call peer object is invalid"}],"available":"user"},{"name":"setCallRating","id":1508562471,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"userInitiative","type":"true","optional":true,"predicate":"flags.0","description":"Whether the user decided on their own initiative to rate the call"},{"name":"peer","type":"InputPhoneCall","description":"The call to rate"},{"name":"rating","type":"number","description":"Rating in 1-5 stars"},{"name":"comment","type":"string","description":"An additional comment"}],"description":"Rate a call","throws":[{"code":400,"name":"CALL_PEER_INVALID","description":"The provided call peer object is invalid"}],"available":"user"},{"name":"saveCallDebug","id":662363518,"returns":"boolean","arguments":[{"name":"peer","type":"InputPhoneCall","description":"Phone call"},{"name":"debug","type":"DataJSON","description":"Debug statistics obtained from libtgvoip"}],"description":"Send phone call debug data to server","throws":[{"code":400,"name":"CALL_PEER_INVALID","description":"The provided call peer object is invalid"},{"code":400,"name":"DATA_JSON_INVALID","description":"The provided JSON data is invalid"}],"available":"user"},{"name":"sendSignalingData","id":4286223235,"returns":"boolean","arguments":[{"name":"peer","type":"InputPhoneCall","description":"Phone call"},{"name":"data","type":"Buffer","description":"Signaling payload"}],"description":"Send VoIP signaling data","available":"both"},{"name":"createGroupCall","id":1221445336,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"peer","type":"InputPeer"},{"name":"randomId","type":"number"},{"name":"title","type":"string","optional":true,"predicate":"flags.0"},{"name":"scheduleDate","type":"number","optional":true,"predicate":"flags.1"}]},{"name":"joinGroupCall","id":2972909435,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"muted","type":"true","optional":true,"predicate":"flags.0"},{"name":"videoStopped","type":"true","optional":true,"predicate":"flags.2"},{"name":"call","type":"InputGroupCall"},{"name":"joinAs","type":"InputPeer"},{"name":"inviteHash","type":"string","optional":true,"predicate":"flags.1"},{"name":"params","type":"DataJSON"}]},{"name":"leaveGroupCall","id":1342404601,"returns":"Updates","arguments":[{"name":"call","type":"InputGroupCall"},{"name":"source","type":"number"}]},{"name":"inviteToGroupCall","id":2067345760,"returns":"Updates","arguments":[{"name":"call","type":"InputGroupCall"},{"name":"users","type":"InputUser[]"}]},{"name":"discardGroupCall","id":2054648117,"returns":"Updates","arguments":[{"name":"call","type":"InputGroupCall"}]},{"name":"toggleGroupCallSettings","id":1958458429,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"resetInviteHash","type":"true","optional":true,"predicate":"flags.1"},{"name":"call","type":"InputGroupCall"},{"name":"joinMuted","type":"boolean","optional":true,"predicate":"flags.0"}]},{"name":"getGroupCall","id":68699611,"returns":"phone.GroupCall","arguments":[{"name":"call","type":"InputGroupCall"},{"name":"limit","type":"number"}]},{"name":"getGroupParticipants","id":3310934187,"returns":"phone.GroupParticipants","arguments":[{"name":"call","type":"InputGroupCall"},{"name":"ids","type":"InputPeer[]"},{"name":"sources","type":"number[]"},{"name":"offset","type":"string"},{"name":"limit","type":"number"}]},{"name":"checkGroupCall","id":3046963575,"returns":"number[]","arguments":[{"name":"call","type":"InputGroupCall"},{"name":"sources","type":"number[]"}]},{"name":"toggleGroupCallRecord","id":3224004311,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"start","type":"true","optional":true,"predicate":"flags.0"},{"name":"call","type":"InputGroupCall"},{"name":"title","type":"string","optional":true,"predicate":"flags.1"}]},{"name":"editGroupCallParticipant","id":2770811583,"returns":"Updates","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"call","type":"InputGroupCall"},{"name":"participant","type":"InputPeer"},{"name":"muted","type":"boolean","optional":true,"predicate":"flags.0"},{"name":"volume","type":"number","optional":true,"predicate":"flags.1"},{"name":"raiseHand","type":"boolean","optional":true,"predicate":"flags.2"},{"name":"videoStopped","type":"boolean","optional":true,"predicate":"flags.3"},{"name":"videoPaused","type":"boolean","optional":true,"predicate":"flags.4"},{"name":"presentationPaused","type":"boolean","optional":true,"predicate":"flags.5"}]},{"name":"editGroupCallTitle","id":480685066,"returns":"Updates","arguments":[{"name":"call","type":"InputGroupCall"},{"name":"title","type":"string"}]},{"name":"getGroupCallJoinAs","id":4017889594,"returns":"phone.JoinAsPeers","arguments":[{"name":"peer","type":"InputPeer"}]},{"name":"exportGroupCallInvite","id":3869926527,"returns":"phone.ExportedGroupCallInvite","arguments":[{"name":"flags","type":"$FlagsBitField"},{"name":"canSelfUnmute","type":"true","optional":true,"predicate":"flags.0"},{"name":"call","type":"InputGroupCall"}]},{"name":"toggleGroupCallStartSubscription","id":563885286,"returns":"Updates","arguments":[{"name":"call","type":"InputGroupCall"},{"name":"subscribed","type":"boolean"}]},{"name":"startScheduledGroupCall","id":1451287362,"returns":"Updates","arguments":[{"name":"call","type":"InputGroupCall"}]},{"name":"saveDefaultGroupCallJoinAs","id":1465786252,"returns":"boolean","arguments":[{"name":"peer","type":"InputPeer"},{"name":"joinAs","type":"InputPeer"}]},{"name":"joinGroupCallPresentation","id":3421137860,"returns":"Updates","arguments":[{"name":"call","type":"InputGroupCall"},{"name":"params","type":"DataJSON"}]},{"name":"leaveGroupCallPresentation","id":475058500,"returns":"Updates","arguments":[{"name":"call","type":"InputGroupCall"}]}],"unions":[{"type":"PhoneCall","subtypes":["phone.phoneCall"],"description":"Phone call"},{"type":"GroupCall","subtypes":["phone.groupCall"]},{"type":"GroupParticipants","subtypes":["phone.groupParticipants"]},{"type":"JoinAsPeers","subtypes":["phone.joinAsPeers"]},{"type":"ExportedGroupCallInvite","subtypes":["phone.exportedGroupCallInvite"]}]},"stats":{"classes":[{"name":"broadcastStats","id":3187114900,"type":"stats.BroadcastStats","arguments":[{"name":"period","type":"StatsDateRangeDays","description":"Period in consideration"},{"name":"followers","type":"StatsAbsValueAndPrev","description":"Follower count change for period in consideration"},{"name":"viewsPerPost","type":"StatsAbsValueAndPrev","description":"total_viewcount/postcount, for posts posted during the period in consideration (views_per_post).
Note that in this case, current refers to the period in consideration (min_date till max_date), and prev refers to the previous period ((min_date - (max_date - min_date)) till min_date)."},{"name":"sharesPerPost","type":"StatsAbsValueAndPrev","description":"total_viewcount/postcount, for posts posted during the period in consideration (views_per_post).
Note that in this case, current refers to the period in consideration (min_date till max_date), and prev refers to the previous period ((min_date - (max_date - min_date)) till min_date)"},{"name":"enabledNotifications","type":"StatsPercentValue","description":"Percentage of subscribers with enabled notifications"},{"name":"growthGraph","type":"StatsGraph","description":"Channel growth graph (absolute subscriber count)"},{"name":"followersGraph","type":"StatsGraph","description":"Followers growth graph (relative subscriber count)"},{"name":"muteGraph","type":"StatsGraph","description":"Muted users graph (relative)"},{"name":"topHoursGraph","type":"StatsGraph","description":"Views per hour graph (absolute)"},{"name":"interactionsGraph","type":"StatsGraph","description":"Interactions graph (absolute)"},{"name":"ivInteractionsGraph","type":"StatsGraph","description":"IV interactions graph (absolute)"},{"name":"viewsBySourceGraph","type":"StatsGraph","description":"Views by source graph (absolute)"},{"name":"newFollowersBySourceGraph","type":"StatsGraph","description":"New followers by source graph (absolute)"},{"name":"languagesGraph","type":"StatsGraph","description":"Subscriber language graph (piechart)"},{"name":"recentMessageInteractions","type":"MessageInteractionCounters[]","description":"Recent message interactions"}],"description":"Channel statistics."},{"name":"megagroupStats","id":4018141462,"type":"stats.MegagroupStats","arguments":[{"name":"period","type":"StatsDateRangeDays","description":"Period in consideration"},{"name":"members","type":"StatsAbsValueAndPrev","description":"Member count change for period in consideration"},{"name":"messages","type":"StatsAbsValueAndPrev","description":"Message number change for period in consideration"},{"name":"viewers","type":"StatsAbsValueAndPrev","description":"Number of users that viewed messages, for range in consideration"},{"name":"posters","type":"StatsAbsValueAndPrev","description":"Number of users that posted messages, for range in consideration"},{"name":"growthGraph","type":"StatsGraph","description":"Supergroup growth graph (absolute subscriber count)"},{"name":"membersGraph","type":"StatsGraph","description":"Members growth (relative subscriber count)"},{"name":"newMembersBySourceGraph","type":"StatsGraph","description":"New members by source graph"},{"name":"languagesGraph","type":"StatsGraph","description":"Subscriber language graph (piechart)"},{"name":"messagesGraph","type":"StatsGraph","description":"Message activity graph (stacked bar graph, message type)"},{"name":"actionsGraph","type":"StatsGraph","description":"Group activity graph (deleted, modified messages, blocked users)"},{"name":"topHoursGraph","type":"StatsGraph","description":"Activity per hour graph (absolute)"},{"name":"weekdaysGraph","type":"StatsGraph","description":"Activity per day of week graph (absolute)"},{"name":"topPosters","type":"StatsGroupTopPoster[]","description":"Info about most active group members"},{"name":"topAdmins","type":"StatsGroupTopAdmin[]","description":"Info about most active group admins"},{"name":"topInviters","type":"StatsGroupTopInviter[]","description":"Info about most active group inviters"},{"name":"users","type":"User[]","description":"Info about users mentioned in statistics"}],"description":"Supergroup statistics"},{"name":"messageStats","id":2308567701,"type":"stats.MessageStats","arguments":[{"name":"viewsGraph","type":"StatsGraph","description":"Message view graph"}],"description":"Message statistics"}],"methods":[{"name":"getBroadcastStats","id":2873246746,"returns":"stats.BroadcastStats","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"dark","type":"true","optional":true,"predicate":"flags.0","description":"Whether to enable dark theme for graph colors"},{"name":"channel","type":"InputChannel","description":"The channel"}],"description":"Get channel statistics","throws":[{"code":400,"name":"BROADCAST_REQUIRED","description":"This method can only be called on a channel, please use stats.getMegagroupStats for supergroups"},{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"}],"available":"user"},{"name":"loadAsyncGraph","id":1646092192,"returns":"StatsGraph","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"token","type":"string","description":"Graph token from {@link statsGraphAsync} constructor"},{"name":"x","type":"Long","optional":true,"predicate":"flags.0","description":"Zoom value, if required"}],"description":"Load channel statistics graph asynchronously","throws":[{"code":400,"name":"GRAPH_INVALID_RELOAD","description":"Invalid graph token provided, please reload the stats and provide the updated token"},{"code":400,"name":"GRAPH_OUTDATED_RELOAD","description":"The graph is outdated, please get a new async token using stats.getBroadcastStats"}],"available":"user"},{"name":"getMegagroupStats","id":3705636359,"returns":"stats.MegagroupStats","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"dark","type":"true","optional":true,"predicate":"flags.0","description":"Whether to enable dark theme for graph colors"},{"name":"channel","type":"InputChannel","description":"Supergroup ID"}],"description":"Get supergroup statistics","available":"both"},{"name":"getMessagePublicForwards","id":1445996571,"returns":"messages.Messages","arguments":[{"name":"channel","type":"InputChannel","description":"Source channel"},{"name":"msgId","type":"number","description":"Source message ID"},{"name":"offsetRate","type":"number","description":"Initially 0, then set to the next_rate parameter of {@link messages.messagesSlice}"},{"name":"offsetPeer","type":"InputPeer","description":"Offsets for pagination, for more info click here"},{"name":"offsetId","type":"number","description":"Offsets for pagination, for more info click here"},{"name":"limit","type":"number","description":"Maximum number of results to return, see pagination"}],"description":"Obtains a list of messages, indicating to which other public channels was a channel message forwarded.
\nWill return a list of {@link message} with peer_id equal to the public channel to which this message was forwarded.","throws":[{"code":400,"name":"CHANNEL_INVALID","description":"The provided channel is invalid"},{"code":400,"name":"MESSAGE_ID_INVALID","description":"The provided message id is invalid"}],"available":"both"},{"name":"getMessageStats","id":3068175349,"returns":"stats.MessageStats","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"dark","type":"true","optional":true,"predicate":"flags.0","description":"Whether to enable dark theme for graph colors"},{"name":"channel","type":"InputChannel","description":"Channel ID"},{"name":"msgId","type":"number","description":"Message ID"}],"description":"Get message statistics","throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","description":"You must be an admin in this chat to do this"}],"available":"both"}],"unions":[{"type":"BroadcastStats","subtypes":["stats.broadcastStats"],"description":"Channel statistics"},{"type":"MegagroupStats","subtypes":["stats.megagroupStats"],"description":"Supergroup statistics"},{"type":"MessageStats","subtypes":["stats.messageStats"],"description":"Message statistics"}]},"stickers":{"classes":[{"name":"suggestedShortName","id":2248056895,"type":"stickers.SuggestedShortName","arguments":[{"name":"shortName","type":"string"}]}],"methods":[{"name":"createStickerSet","id":2418125671,"returns":"messages.StickerSet","arguments":[{"name":"flags","type":"$FlagsBitField","description":"Flags, see TL conditional fields"},{"name":"masks","type":"true","optional":true,"predicate":"flags.0","description":"Whether this is a mask stickerset"},{"name":"animated","type":"true","optional":true,"predicate":"flags.1","description":"Whether this is an animated stickerset"},{"name":"userId","type":"InputUser","description":"Stickerset owner"},{"name":"title","type":"string","description":"Stickerset name, 1-64 chars"},{"name":"shortName","type":"string","description":"Sticker set name. Can contain only English letters, digits and underscores. Must end with \"by\" ( is case insensitive); 1-64 characters"},{"name":"thumb","type":"InputDocument","optional":true,"predicate":"flags.2","description":"Thumbnail"},{"name":"stickers","type":"InputStickerSetItem[]","description":"Stickers"},{"name":"software","type":"string","optional":true,"predicate":"flags.3"}],"description":"Create a stickerset, bots only.","throws":[{"code":400,"name":"BOT_MISSING","description":"This method can only be run by a bot"},{"code":400,"name":"PACK_SHORT_NAME_INVALID","description":"Short pack name invalid"},{"code":400,"name":"PACK_SHORT_NAME_OCCUPIED","description":"A stickerpack with this name already exists"},{"code":400,"name":"PACK_TITLE_INVALID","description":"The stickerpack title is invalid"},{"code":400,"name":"PEER_ID_INVALID","description":"The provided peer id is invalid"},{"code":400,"name":"SHORTNAME_OCCUPY_FAILED","description":"An internal error occurred"},{"code":400,"name":"STICKERS_EMPTY","description":"No sticker provided"},{"code":400,"name":"STICKER_EMOJI_INVALID","description":"Sticker emoji invalid"},{"code":400,"name":"STICKER_FILE_INVALID","description":"Sticker file invalid"},{"code":400,"name":"STICKER_PNG_DIMENSIONS","description":"Sticker png dimensions invalid"},{"code":400,"name":"STICKER_PNG_NOPNG","description":"One of the specified stickers is not a valid PNG file"},{"code":400,"name":"USER_ID_INVALID","description":"The provided user ID is invalid"}],"available":"both"},{"name":"removeStickerFromSet","id":4151709521,"returns":"messages.StickerSet","arguments":[{"name":"sticker","type":"InputDocument","description":"The sticker to remove"}],"description":"Remove a sticker from the set where it belongs, bots only. The sticker set must have been created by the bot.","throws":[{"code":400,"name":"BOT_MISSING","description":"This method can only be run by a bot"},{"code":400,"name":"STICKER_INVALID","description":"The provided sticker is invalid"}],"available":"both"},{"name":"changeStickerPosition","id":4290172106,"returns":"messages.StickerSet","arguments":[{"name":"sticker","type":"InputDocument","description":"The sticker"},{"name":"position","type":"number","description":"The new position of the sticker, zero-based"}],"description":"Changes the absolute position of a sticker in the set to which it belongs; for bots only. The sticker set must have been created by the bot","throws":[{"code":400,"name":"BOT_MISSING","description":"This method can only be run by a bot"},{"code":400,"name":"STICKER_INVALID","description":"The provided sticker is invalid"}],"available":"both"},{"name":"addStickerToSet","id":2253651646,"returns":"messages.StickerSet","arguments":[{"name":"stickerset","type":"InputStickerSet","description":"The stickerset"},{"name":"sticker","type":"InputStickerSetItem","description":"The sticker"}],"description":"Add a sticker to a stickerset, bots only. The sticker set must have been created by the bot.","throws":[{"code":400,"name":"BOT_MISSING","description":"This method can only be run by a bot"},{"code":400,"name":"STICKERSET_INVALID","description":"The provided sticker set is invalid"}],"available":"both"},{"name":"setStickerSetThumb","id":2587250224,"returns":"messages.StickerSet","arguments":[{"name":"stickerset","type":"InputStickerSet","description":"Stickerset"},{"name":"thumb","type":"InputDocument","description":"Thumbnail"}],"description":"Set stickerset thumbnail","throws":[{"code":400,"name":"STICKERSET_INVALID","description":"The provided sticker set is invalid"}],"available":"both"},{"name":"checkShortName","id":676017721,"returns":"boolean","arguments":[{"name":"shortName","type":"string"}]},{"name":"suggestShortName","id":1303364867,"returns":"stickers.SuggestedShortName","arguments":[{"name":"title","type":"string"}]}],"unions":[{"type":"SuggestedShortName","subtypes":["stickers.suggestedShortName"]}]},"users":{"classes":[],"methods":[{"name":"getUsers","id":227648840,"returns":"User[]","arguments":[{"name":"id","type":"InputUser[]","description":"List of user identifiers"}],"description":"Returns basic user info according to their identifiers.","throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","description":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"CONNECTION_NOT_INITED","description":"Connection not initialized"},{"code":400,"name":"INPUT_LAYER_INVALID","description":"The provided layer is invalid"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"}],"available":"both"},{"name":"getFullUser","id":3392185777,"returns":"UserFull","arguments":[{"name":"id","type":"InputUser","description":"User ID"}],"description":"Returns extended user info by ID.","throws":[{"code":400,"name":"CHANNEL_PRIVATE","description":"You haven't joined this channel/supergroup"},{"code":400,"name":"MSG_ID_INVALID","description":"Invalid message ID provided"},{"code":400,"name":"USER_ID_INVALID","description":"The provided user ID is invalid"}],"available":"both"},{"name":"setSecureValueErrors","id":2429064373,"returns":"boolean","arguments":[{"name":"id","type":"InputUser","description":"The user"},{"name":"errors","type":"SecureValueError[]","description":"Errors"}],"description":"Notify the user that the sent passport data contains some errors The user will not be able to re-submit their Passport data to you until the errors are fixed (the contents of the field for which you returned the error must change).","throws":[{"code":400,"name":"USER_ID_INVALID","description":"The provided user ID is invalid"}],"available":"both"}],"unions":[]},"bots":{"classes":[],"methods":[{"name":"sendCustomRequest","id":2854709741,"returns":"DataJSON","arguments":[{"name":"customMethod","type":"string","description":"The method name"},{"name":"params","type":"DataJSON","description":"JSON-serialized method parameters"}],"description":"Sends a custom request; for bots only","throws":[{"code":400,"name":"METHOD_INVALID","description":"The specified method is invalid"},{"code":400,"name":"USER_BOT_INVALID","description":"This method can only be called by a bot"}],"available":"bot"},{"name":"answerWebhookJSONQuery","id":3860938573,"returns":"boolean","arguments":[{"name":"queryId","type":"Long","description":"Identifier of a custom query"},{"name":"data","type":"DataJSON","description":"JSON-serialized answer to the query"}],"description":"Answers a custom query; for bots only","throws":[{"code":400,"name":"QUERY_ID_INVALID","description":"The query ID is invalid"},{"code":400,"name":"USER_BOT_INVALID","description":"This method can only be called by a bot"}],"available":"bot"},{"name":"setBotCommands","id":85399130,"returns":"boolean","arguments":[{"name":"scope","type":"BotCommandScope"},{"name":"langCode","type":"string"},{"name":"commands","type":"BotCommand[]","description":"Bot commands"}],"description":"Set bot command list","available":"both"},{"name":"resetBotCommands","id":1032708345,"returns":"boolean","arguments":[{"name":"scope","type":"BotCommandScope"},{"name":"langCode","type":"string"}]},{"name":"getBotCommands","id":3813412310,"returns":"BotCommand[]","arguments":[{"name":"scope","type":"BotCommandScope"},{"name":"langCode","type":"string"}]}],"unions":[]},"langpack":{"classes":[],"methods":[{"name":"getLangPack","id":4075959050,"returns":"LangPackDifference","arguments":[{"name":"langPack","type":"string","description":"Language pack name"},{"name":"langCode","type":"string","description":"Language code"}],"description":"Get localization pack strings","throws":[{"code":400,"name":"LANG_PACK_INVALID","description":"The provided language pack is invalid"}],"available":"user"},{"name":"getStrings","id":4025104387,"returns":"LangPackString[]","arguments":[{"name":"langPack","type":"string","description":"Language pack name"},{"name":"langCode","type":"string","description":"Language code"},{"name":"keys","type":"string[]","description":"Strings to get"}],"description":"Get strings from a language pack","throws":[{"code":400,"name":"LANG_PACK_INVALID","description":"The provided language pack is invalid"}],"available":"user"},{"name":"getDifference","id":3449309861,"returns":"LangPackDifference","arguments":[{"name":"langPack","type":"string","description":"Language pack"},{"name":"langCode","type":"string","description":"Language code"},{"name":"fromVersion","type":"number","description":"Previous localization pack version"}],"description":"Get new strings in languagepack","throws":[{"code":400,"name":"LANG_PACK_INVALID","description":"The provided language pack is invalid"}],"available":"user"},{"name":"getLanguages","id":1120311183,"returns":"LangPackLanguage[]","arguments":[{"name":"langPack","type":"string","description":"Language pack"}],"description":"Get information about all languages in a localization pack","throws":[{"code":400,"name":"LANG_PACK_INVALID","description":"The provided language pack is invalid"}],"available":"user"},{"name":"getLanguage","id":1784243458,"returns":"LangPackLanguage","arguments":[{"name":"langPack","type":"string","description":"Language pack name"},{"name":"langCode","type":"string","description":"Language code"}],"description":"Get information about a language in a localization pack","available":"user"}],"unions":[]},"folders":{"classes":[],"methods":[{"name":"editPeerFolders","id":1749536939,"returns":"Updates","arguments":[{"name":"folderPeers","type":"InputFolderPeer[]","description":"New peer list"}],"description":"Edit peers in peer folder","throws":[{"code":400,"name":"FOLDER_ID_INVALID","description":"Invalid folder ID"}],"available":"user"},{"name":"deleteFolder","id":472471681,"returns":"Updates","arguments":[{"name":"folderId","type":"number","description":"Peer folder ID, for more info click here"}],"description":"Delete a peer folder","available":"user"}],"unions":[]}}} \ No newline at end of file diff --git a/packages/tl/scripts/.documentation.cache.json b/packages/tl/scripts/.documentation.cache.json new file mode 100644 index 00000000..d40aa211 --- /dev/null +++ b/packages/tl/scripts/.documentation.cache.json @@ -0,0 +1 @@ +{"updated":"05.11.2021, 20:20:41 (layer 134)","classes":{"error":{"comment":"Error.","arguments":{"code":"Error code","text":"Message"}},"inputPeerEmpty":{"comment":"An empty constructor, no user or chat is defined.","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputPeerSelf":{"comment":"Defines the current user.","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputPeerChat":{"comment":"Defines a chat for further interaction.","arguments":{"chatId":"Chat idientifier"}},"inputPeerUser":{"comment":"Defines a user for further interaction.","arguments":{"userId":"User identifier","accessHash":"access_hash value from the {@link user} constructor"}},"inputPeerChannel":{"comment":"Defines a channel for further interaction.","arguments":{"channelId":"Channel identifier","accessHash":"access_hash value from the {@link channel} constructor"}},"inputPeerUserFromMessage":{"comment":"Defines a min user that was seen in a certain message of a certain chat.","arguments":{"peer":"The chat where the user was seen","msgId":"The message ID","userId":"The identifier of the user that was seen"}},"inputPeerChannelFromMessage":{"comment":"Defines a min channel that was seen in a certain message of a certain chat.","arguments":{"peer":"The chat where the channel's message was seen","msgId":"The message ID","channelId":"The identifier of the channel that was seen"}},"inputUserEmpty":{"comment":"Empty constructor, does not define a user.","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputUserSelf":{"comment":"Defines the current user.","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputUser":{"comment":"Defines a user for further interaction.","arguments":{"userId":"User identifier","accessHash":"access_hash value from the {@link user} constructor"}},"inputUserFromMessage":{"comment":"Defines a min user that was seen in a certain message of a certain chat.","arguments":{"peer":"The chat where the user was seen","msgId":"The message ID","userId":"The identifier of the user that was seen"}},"inputPhoneContact":{"comment":"Phone contact. The client_id is just an arbitrary contact ID: it should be set, for example, to an incremental number when using {@link contacts.importContacts}, in order to retry importing only the contacts that weren't imported successfully.","arguments":{"clientId":"User identifier on the client","phone":"Phone number","firstName":"Contact's first name","lastName":"Contact's last name"}},"inputFile":{"comment":"Defines a file saved in parts using the method {@link upload.saveFilePart}.","arguments":{"id":"Random file identifier created by the client","parts":"Number of parts saved","name":"Full name of the file","md5Checksum":"In case the file's md5-hash was passed, contents of the file will be checked prior to use"}},"inputFileBig":{"comment":"Assigns a big file (over 10Mb in size), saved in part using the method {@link upload.saveBigFilePart}.","arguments":{"id":"Random file id, created by the client","parts":"Number of parts saved","name":"Full file name"}},"inputMediaEmpty":{"comment":"Empty media content of a message.","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputMediaUploadedPhoto":{"comment":"Photo","arguments":{"flags":"Flags, see TL conditional fields","file":"The uploaded file","stickers":"Attached mask stickers","ttlSeconds":"Time to live in seconds of self-destructing photo"}},"inputMediaPhoto":{"comment":"Forwarded photo","arguments":{"flags":"Flags, see TL conditional fields","id":"Photo to be forwarded","ttlSeconds":"Time to live in seconds of self-destructing photo"}},"inputMediaGeoPoint":{"comment":"Map.","arguments":{"geoPoint":"GeoPoint"}},"inputMediaContact":{"comment":"Phonebook contact","arguments":{"phoneNumber":"Phone number","firstName":"Contact's first name","lastName":"Contact's last name","vcard":"Contact vcard"}},"inputMediaUploadedDocument":{"comment":"New document","arguments":{"flags":"Flags, see TL conditional fields","nosoundVideo":"Whether the specified document is a video file with no audio tracks (a GIF animation (even as MPEG4), for example)","forceFile":"Force the media file to be uploaded as document","file":"The uploaded file","thumb":"Thumbnail of the document, uploaded as for the file","mimeType":"MIME type of document","attributes":"Attributes that specify the type of the document (video, audio, voice, sticker, etc.)","stickers":"Attached stickers","ttlSeconds":"Time to live in seconds of self-destructing document"}},"inputMediaDocument":{"comment":"Forwarded document","arguments":{"flags":"Flags, see TL conditional fields","id":"The document to be forwarded.","ttlSeconds":"Time to live of self-destructing document","query":"Text query or emoji that was used by the user to find this sticker or GIF: used to improve search result relevance."}},"inputMediaVenue":{"comment":"Can be used to send a venue geolocation.","arguments":{"geoPoint":"Geolocation","title":"Venue name","address":"Physical address of the venue","provider":"Venue provider: currently only \"foursquare\" and \"gplaces\" need to be supported","venueId":"Venue ID in the provider's database","venueType":"Venue type in the provider's database"}},"inputMediaPhotoExternal":{"comment":"New photo that will be uploaded by the server using the specified URL","arguments":{"flags":"Flags, see TL conditional fields","url":"URL of the photo","ttlSeconds":"Self-destruct time to live of photo"}},"inputMediaDocumentExternal":{"comment":"Document that will be downloaded by the telegram servers","arguments":{"flags":"Flags, see TL conditional fields","url":"URL of the document","ttlSeconds":"Self-destruct time to live of document"}},"inputMediaGame":{"comment":"A game","arguments":{"id":"The game to forward"}},"inputMediaInvoice":{"comment":"Generated invoice of a bot payment","arguments":{"flags":"Flags, see TL conditional fields","title":"Product name, 1-32 characters","description":"Product description, 1-255 characters","photo":"URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for.","invoice":"The actual invoice","payload":"Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes.","provider":"Payments provider token, obtained via Botfather","providerData":"JSON-encoded data about the invoice, which will be shared with the payment provider. A detailed description of required fields should be provided by the payment provider.","startParam":"Start parameter"}},"inputMediaGeoLive":{"comment":"Live geolocation","arguments":{"flags":"Flags, see TL conditional fields","stopped":"Whether sending of the geolocation was stopped","geoPoint":"Current geolocation","heading":"For live locations, a direction in which the location moves, in degrees; 1-360.","period":"Validity period of the current location","proximityNotificationRadius":"For live locations, a maximum distance to another chat member for proximity alerts, in meters (0-100000)"}},"inputMediaPoll":{"comment":"A poll","arguments":{"flags":"Flags, see TL conditional fields","poll":"The poll to send","correctAnswers":"Correct answer IDs (for quiz polls)","solution":"Explanation of quiz solution","solutionEntities":"Message entities for styled text"}},"inputMediaDice":{"comment":"Send a dice-based animated sticker","arguments":{"emoticon":"The emoji, for now \"🏀\", \"🎲\" and \"🎯\" are supported"}},"inputChatPhotoEmpty":{"comment":"Empty constructor, remove group photo.","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputChatUploadedPhoto":{"comment":"New photo to be set as group profile photo.","arguments":{"flags":"Flags, see TL conditional fields","file":"File saved in parts using the method {@link upload.saveFilePart}","video":"Square video for animated profile picture","videoStartTs":"Timestamp that should be shown as static preview to the user (seconds)"}},"inputChatPhoto":{"comment":"Existing photo to be set as a chat profile photo.","arguments":{"id":"Existing photo"}},"inputGeoPointEmpty":{"comment":"Empty GeoPoint constructor.","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputGeoPoint":{"comment":"Defines a GeoPoint by its coordinates.","arguments":{"flags":"Flags, see TL conditional fields","lat":"Latitide","long":"Longtitude","accuracyRadius":"The estimated horizontal accuracy of the location, in meters; as defined by the sender."}},"inputPhotoEmpty":{"comment":"Empty constructor.","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputPhoto":{"comment":"Defines a photo for further interaction.","arguments":{"id":"Photo identifier","accessHash":"access_hash value from the {@link photo} constructor","fileReference":"File reference"}},"inputFileLocation":{"comment":"DEPRECATED location of a photo","arguments":{"volumeId":"Server volume","localId":"File identifier","secret":"Check sum to access the file","fileReference":"File reference"}},"inputEncryptedFileLocation":{"comment":"Location of encrypted secret chat file.","arguments":{"id":"File ID, id parameter value from {@link encryptedFile}","accessHash":"Checksum, access_hash parameter value from {@link encryptedFile}"}},"inputDocumentFileLocation":{"comment":"Document location (video, voice, audio, basically every type except photo)","arguments":{"id":"Document ID","accessHash":"access_hash parameter from the {@link document} constructor","fileReference":"File reference","thumbSize":"Thumbnail size to download the thumbnail"}},"inputSecureFileLocation":{"comment":"Location of encrypted telegram passport file.","arguments":{"id":"File ID, id parameter value from {@link secureFile}","accessHash":"Checksum, access_hash parameter value from {@link secureFile}"}},"inputTakeoutFileLocation":{"comment":"Empty constructor for takeout","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputPhotoFileLocation":{"comment":"Use this object to download a photo with {@link upload.getFile} method","arguments":{"id":"Photo ID, obtained from the {@link photo} object","accessHash":"Photo's access hash, obtained from the {@link photo} object","fileReference":"File reference","thumbSize":"The PhotoSize to download: must be set to the type field of the desired PhotoSize object of the {@link photo}"}},"inputPhotoLegacyFileLocation":{"comment":"DEPRECATED legacy photo file location","arguments":{"id":"Photo ID","accessHash":"Access hash","fileReference":"File reference","volumeId":"Volume ID","localId":"Local ID","secret":"Secret"}},"inputPeerPhotoFileLocation":{"comment":"Location of profile photo of channel/group/supergroup/user","arguments":{"flags":"Flags, see TL conditional fields","big":"Whether to download the high-quality version of the picture","peer":"The peer whose profile picture should be downloaded","photoId":"Photo ID"}},"inputStickerSetThumb":{"comment":"Location of stickerset thumbnail (see files)","arguments":{"stickerset":"Sticker set","thumbVersion":"Thumbnail version"}},"inputGroupCallStream":{"comment":"Chunk of a livestream","arguments":{"flags":"Flags, see TL conditional fields","call":"Livestream info","timeMs":"Timestamp in milliseconds","scale":"Specifies the duration of the video segment to fetch in milliseconds, by bitshifting 1000 to the right scale times: duration_ms := 1000 >> scale","videoChannel":"Selected video channel","videoQuality":"Selected video quality (0 = lowest, 1 = medium, 2 = best)"}},"peerUser":{"comment":"Chat partner","arguments":{"userId":"User identifier"}},"peerChat":{"comment":"Group.","arguments":{"chatId":"Group identifier"}},"peerChannel":{"comment":"Channel/supergroup","arguments":{"channelId":"Channel ID"}},"storage.fileUnknown":{"comment":"Unknown type.","arguments":{"gigagroup":"Is this a broadcast group?"}},"storage.filePartial":{"comment":"Part of a bigger file.","arguments":{"gigagroup":"Is this a broadcast group?"}},"storage.fileJpeg":{"comment":"JPEG image. MIME type: image/jpeg.","arguments":{"gigagroup":"Is this a broadcast group?"}},"storage.fileGif":{"comment":"GIF image. MIME type: image/gif.","arguments":{"gigagroup":"Is this a broadcast group?"}},"storage.filePng":{"comment":"PNG image. MIME type: image/png.","arguments":{"gigagroup":"Is this a broadcast group?"}},"storage.filePdf":{"comment":"PDF document image. MIME type: application/pdf.","arguments":{"gigagroup":"Is this a broadcast group?"}},"storage.fileMp3":{"comment":"Mp3 audio. MIME type: audio/mpeg.","arguments":{"gigagroup":"Is this a broadcast group?"}},"storage.fileMov":{"comment":"Quicktime video. MIME type: video/quicktime.","arguments":{"gigagroup":"Is this a broadcast group?"}},"storage.fileMp4":{"comment":"MPEG-4 video. MIME type: video/mp4.","arguments":{"gigagroup":"Is this a broadcast group?"}},"storage.fileWebp":{"comment":"WEBP image. MIME type: image/webp.","arguments":{"gigagroup":"Is this a broadcast group?"}},"userEmpty":{"comment":"Empty constructor, non-existent user.","arguments":{"id":"User identifier or 0"}},"user":{"comment":"Indicates info about a certain user","arguments":{"flags":"Flags, see TL conditional fields","self":"Whether this user indicates the currently logged in user","contact":"Whether this user is a contact","mutualContact":"Whether this user is a mutual contact","deleted":"Whether the account of this user was deleted","bot":"Is this user a bot?","botChatHistory":"Can the bot see all messages in groups?","botNochats":"Can the bot be added to groups?","verified":"Whether this user is verified","restricted":"Access to this user must be restricted for the reason specified in restriction_reason","min":"See min","botInlineGeo":"Whether the bot can request our geolocation in inline mode","support":"Whether this is an official support user","scam":"This may be a scam user","applyMinPhoto":"If set, the profile picture for this user should be refetched","fake":"If set, this user was reported by many users as a fake or scam user: be careful when interacting with them.","id":"ID of the user","accessHash":"Access hash of the user","firstName":"First name","lastName":"Last name","username":"Username","phone":"Phone number","photo":"Profile picture of user","status":"Online status of user","botInfoVersion":"Version of the {@link userFull}, incremented every time it changes","restrictionReason":"Contains the reason why access to this user must be restricted.","botInlinePlaceholder":"Inline placeholder for this inline bot","langCode":"Language code of the user"}},"userProfilePhotoEmpty":{"comment":"Profile photo has not been set, or was hidden.","arguments":{"gigagroup":"Is this a broadcast group?"}},"userProfilePhoto":{"comment":"User profile photo.","arguments":{"flags":"Flags, see TL conditional fields","hasVideo":"Whether an animated profile picture is available for this user","photoId":"Identifier of the respective photo
Parameter added in Layer 2","strippedThumb":"Stripped thumbnail","dcId":"DC ID where the photo is stored"}},"userStatusEmpty":{"comment":"User status has not been set yet.","arguments":{"gigagroup":"Is this a broadcast group?"}},"userStatusOnline":{"comment":"Online status of the user.","arguments":{"expires":"Time to expiration of the current online status"}},"userStatusOffline":{"comment":"The user's offline status.","arguments":{"wasOnline":"Time the user was last seen online"}},"userStatusRecently":{"comment":"Online status: last seen recently","arguments":{"gigagroup":"Is this a broadcast group?"}},"userStatusLastWeek":{"comment":"Online status: last seen last week","arguments":{"gigagroup":"Is this a broadcast group?"}},"userStatusLastMonth":{"comment":"Online status: last seen last month","arguments":{"gigagroup":"Is this a broadcast group?"}},"chatEmpty":{"comment":"Empty constructor, group doesn't exist","arguments":{"id":"Group identifier"}},"chat":{"comment":"Info about a group","arguments":{"flags":"Flags, see TL conditional fields","creator":"Whether the current user is the creator of the group","kicked":"Whether the current user was kicked from the group","left":"Whether the current user has left the group","deactivated":"Whether the group was migrated","callActive":"Whether a group call is currently active","callNotEmpty":"Whether there's anyone in the group call","id":"ID of the group","title":"Title","photo":"Chat photo","participantsCount":"Participant count","date":"Date of creation of the group","version":"Used in basic groups to reorder updates and make sure that all of them were received.","migratedTo":"Means this chat was upgraded to a supergroup","adminRights":"Admin rights of the user in the group","defaultBannedRights":"Default banned rights of all users in the group"}},"chatForbidden":{"comment":"A group to which the user has no access. E.g., because the user was kicked from the group.","arguments":{"id":"User identifier","title":"Group name"}},"channel":{"comment":"Channel/supergroup info","arguments":{"flags":"Flags, see TL conditional fields","creator":"Whether the current user is the creator of this channel","left":"Whether the current user has left this channel","broadcast":"Is this a channel?","verified":"Is this channel verified by telegram?","megagroup":"Is this a supergroup?","restricted":"Whether viewing/writing in this channel for a reason (see restriction_reason","signatures":"Whether signatures are enabled (channels)","min":"See min","scam":"This channel/supergroup is probably a scam","hasLink":"Whether this channel has a private join link","hasGeo":"Whether this channel has a geo position","slowmodeEnabled":"Whether slow mode is enabled for groups to prevent flood in chat","callActive":"Whether a group call or livestream is currently active","callNotEmpty":"Whether there's anyone in the group call or livestream","fake":"If set, this supergroup/channel was reported by many users as a fake or scam: be careful when interacting with it.","gigagroup":"Whether this supergroup is a gigagroup","id":"ID of the channel","accessHash":"Access hash","title":"Title","username":"Username","photo":"Profile photo","date":"Date when the user joined the supergroup/channel, or if the user isn't a member, its creation date","restrictionReason":"Contains the reason why access to this channel must be restricted.","adminRights":"Admin rights of the user in this channel (see rights)","bannedRights":"Banned rights of the user in this channel (see rights)","defaultBannedRights":"Default chat rights (see rights)","participantsCount":"Participant count"}},"channelForbidden":{"comment":"Indicates a channel/supergroup we can't access because we were banned, or for some other reason.","arguments":{"flags":"Flags, see TL conditional fields","broadcast":"Is this a channel","megagroup":"Is this a supergroup","id":"Channel ID","accessHash":"Access hash","title":"Title","untilDate":"The ban is valid until the specified date"}},"chatFull":{"comment":"Detailed chat info","arguments":{"flags":"Flags, see TL conditional fields","canSetUsername":"Can we change the username of this chat","hasScheduled":"Whether scheduled messages are available","id":"ID of the chat","about":"About string for this chat","participants":"Participant list","chatPhoto":"Chat photo","notifySettings":"Notification settings","exportedInvite":"Chat invite","botInfo":"Info about bots that are in this chat","pinnedMsgId":"Message ID of the last pinned message","folderId":"Peer folder ID, for more info click here","call":"Group call information","ttlPeriod":"Time-To-Live of messages sent by the current user to this chat","groupcallDefaultJoinAs":"When using {@link phone.getGroupCallJoinAs} to get a list of peers that can be used to join a group call, this field indicates the peer that should be selected by default.","themeEmoticon":"Emoji representing a specific chat theme"}},"channelFull":{"comment":"Full info about a channel/supergroup","arguments":{"flags":"Flags, see TL conditional fields","canViewParticipants":"Can we vew the participant list?","canSetUsername":"Can we set the channel's username?","canSetStickers":"Can we {@link channels.setStickers} a stickerpack to the supergroup?","hiddenPrehistory":"Is the history before we joined hidden to us?","canSetLocation":"Can we set the geolocation of this group (for geogroups)","hasScheduled":"Whether scheduled messages are available","canViewStats":"Can the user view channel/supergroup statistics","blocked":"Whether any anonymous admin of this supergroup was blocked: if set, you won't receive messages from anonymous group admins in discussion replies via @replies","id":"ID of the channel","about":"Info about the channel","participantsCount":"Number of participants of the channel","adminsCount":"Number of channel admins","kickedCount":"Number of users kicked from the channel","bannedCount":"Number of users banned from the channel","onlineCount":"Number of users currently online","readInboxMaxId":"Position up to which all incoming messages are read.","readOutboxMaxId":"Position up to which all outgoing messages are read.","unreadCount":"Count of unread messages","chatPhoto":"Channel picture","notifySettings":"Notification settings","exportedInvite":"Invite link","botInfo":"Info about bots in the channel/supergrup","migratedFromChatId":"The chat ID from which this group was migrated","migratedFromMaxId":"The message ID in the original chat at which this group was migrated","pinnedMsgId":"Message ID of the last pinned message","stickerset":"Associated stickerset","availableMinId":"Identifier of a maximum unavailable message in a channel due to hidden history.","folderId":"Peer folder ID, for more info click here","linkedChatId":"ID of the linked discussion chat for channels","location":"Location of the geo group","slowmodeSeconds":"If specified, users in supergroups will only be able to send one message every slowmode_seconds seconds","slowmodeNextSendDate":"Indicates when the user will be allowed to send another message in the supergroup (UNIX timestamp in seconds)","statsDc":"If set, specifies the DC to use for fetching channel statistics","pts":"Latest PTS for this channel","call":"Livestream or group call information","ttlPeriod":"Time-To-Live of messages in this channel or supergroup","pendingSuggestions":"A list of suggested actions for the supergroup admin, see here for more info ».","groupcallDefaultJoinAs":"When using {@link phone.getGroupCallJoinAs} to get a list of peers that can be used to join a group call, this field indicates the peer that should be selected by default.","themeEmoticon":"Emoji representing a specific chat theme"}},"chatParticipant":{"comment":"Group member.","arguments":{"userId":"Member user ID","inviterId":"ID of the user that added the member to the group","date":"Date added to the group"}},"chatParticipantCreator":{"comment":"Represents the creator of the group","arguments":{"userId":"ID of the user that created the group"}},"chatParticipantAdmin":{"comment":"Chat admin","arguments":{"userId":"ID of a group member that is admin","inviterId":"ID of the user that added the member to the group","date":"Date when the user was added"}},"chatParticipantsForbidden":{"comment":"Info on members is unavailable","arguments":{"flags":"Flags, see TL conditional fields","chatId":"Group ID","selfParticipant":"Info about the group membership of the current user"}},"chatParticipants":{"comment":"Group members.","arguments":{"chatId":"Group identifier","participants":"List of group members","version":"Group version number"}},"chatPhotoEmpty":{"comment":"Group photo is not set.","arguments":{"gigagroup":"Is this a broadcast group?"}},"chatPhoto":{"comment":"Group profile photo.","arguments":{"flags":"Flags, see TL conditional fields","hasVideo":"Whether the user has an animated profile picture","photoId":"Photo ID","strippedThumb":"Stripped thumbnail","dcId":"DC where this photo is stored"}},"messageEmpty":{"comment":"Empty constructor, non-existent message.","arguments":{"flags":"Flags, see TL conditional fields","id":"Message identifier","peerId":"Peer ID, the chat where this message was sent"}},"message":{"comment":"A message","arguments":{"flags":"Flags, see TL conditional fields","out":"Is this an outgoing message","mentioned":"Whether we were mentioned in this message","mediaUnread":"Whether there are unread media attachments in this message","silent":"Whether this is a silent message (no notification triggered)","post":"Whether this is a channel post","fromScheduled":"Whether this is a scheduled message","legacy":"This is a legacy message: it has to be refetched with the new layer","editHide":"Whether the message should be shown as not modified to the user, even if an edit date is present","pinned":"Whether this message is pinned","id":"ID of the message","fromId":"ID of the sender of the message","peerId":"Peer ID, the chat where this message was sent","fwdFrom":"Info about forwarded messages","viaBotId":"ID of the inline bot that generated the message","replyTo":"Reply information","date":"Date of the message","message":"The message","media":"Media attachment","replyMarkup":"Reply markup (bot/inline keyboards)","entities":"Message entities for styled text","views":"View count for channel posts","forwards":"Forward counter","replies":"Info about post comments (for channels) or message replies (for groups)","editDate":"Last edit date of this message","postAuthor":"Name of the author of this message for channel posts (with signatures enabled)","groupedId":"Multiple media messages sent using {@link messages.sendMultiMedia} with the same grouped ID indicate an album or media group","restrictionReason":"Contains the reason why access to this message must be restricted.","ttlPeriod":"Time To Live of the message, once message.date+message.ttl_period === time(), the message will be deleted on the server, and must be deleted locally as well."}},"messageService":{"comment":"Indicates a service message","arguments":{"flags":"Flags, see TL conditional fields","out":"Whether the message is outgoing","mentioned":"Whether we were mentioned in the message","mediaUnread":"Whether the message contains unread media","silent":"Whether the message is silent","post":"Whether it's a channel post","legacy":"This is a legacy message: it has to be refetched with the new layer","id":"Message ID","fromId":"ID of the sender of this message","peerId":"Sender of service message","replyTo":"Reply (thread) information","date":"Message date","action":"Event connected with the service message","ttlPeriod":"Time To Live of the message, once message.date+message.ttl_period === time(), the message will be deleted on the server, and must be deleted locally as well."}},"messageMediaEmpty":{"comment":"Empty constructor.","arguments":{"gigagroup":"Is this a broadcast group?"}},"messageMediaPhoto":{"comment":"Attached photo.","arguments":{"flags":"Flags, see TL conditional fields","photo":"Photo","ttlSeconds":"Time to live in seconds of self-destructing photo"}},"messageMediaGeo":{"comment":"Attached map.","arguments":{"geo":"GeoPoint"}},"messageMediaContact":{"comment":"Attached contact.","arguments":{"phoneNumber":"Phone number","firstName":"Contact's first name","lastName":"Contact's last name","vcard":"VCARD of contact","userId":"User identifier or 0, if the user with the given phone number is not registered"}},"messageMediaUnsupported":{"comment":"Current version of the client does not support this media type.","arguments":{"gigagroup":"Is this a broadcast group?"}},"messageMediaDocument":{"comment":"Document (video, audio, voice, sticker, any media type except photo)","arguments":{"flags":"Flags, see TL conditional fields","document":"Attached document","ttlSeconds":"Time to live of self-destructing document"}},"messageMediaWebPage":{"comment":"Preview of webpage","arguments":{"webpage":"Webpage preview"}},"messageMediaVenue":{"comment":"Venue","arguments":{"geo":"Geolocation of venue","title":"Venue name","address":"Address","provider":"Venue provider: currently only \"foursquare\" and \"gplaces\" need to be supported","venueId":"Venue ID in the provider's database","venueType":"Venue type in the provider's database"}},"messageMediaGame":{"comment":"Telegram game","arguments":{"game":"Game"}},"messageMediaInvoice":{"comment":"Invoice","arguments":{"flags":"Flags, see TL conditional fields","shippingAddressRequested":"Whether the shipping address was requested","test":"Whether this is an example invoice","title":"Product name, 1-32 characters","description":"Product description, 1-255 characters","photo":"URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for.","receiptMsgId":"Message ID of receipt: if set, clients should change the text of the first {@link keyboardButtonBuy} button always attached to the {@link message} to a localized version of the word Receipt","currency":"Three-letter ISO 4217 currency code","totalAmount":"Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).","startParam":"Unique bot deep-linking parameter that can be used to generate this invoice"}},"messageMediaGeoLive":{"comment":"Indicates a live geolocation","arguments":{"flags":"Flags, see TL conditional fields","geo":"Geolocation","heading":"For live locations, a direction in which the location moves, in degrees; 1-360","period":"Validity period of provided geolocation","proximityNotificationRadius":"For live locations, a maximum distance to another chat member for proximity alerts, in meters (0-100000)."}},"messageMediaPoll":{"comment":"Poll","arguments":{"poll":"The poll","results":"The results of the poll"}},"messageMediaDice":{"comment":"Dice-based animated sticker","arguments":{"value":"Dice value","emoticon":"The emoji, for now \"🏀\", \"🎲\" and \"🎯\" are supported"}},"messageActionEmpty":{"comment":"Empty constructor.","arguments":{"gigagroup":"Is this a broadcast group?"}},"messageActionChatCreate":{"comment":"Group created","arguments":{"title":"Group name","users":"List of group members"}},"messageActionChatEditTitle":{"comment":"Group name changed.","arguments":{"title":"New group name"}},"messageActionChatEditPhoto":{"comment":"Group profile changed","arguments":{"photo":"New group pofile photo"}},"messageActionChatDeletePhoto":{"comment":"Group profile photo removed.","arguments":{"gigagroup":"Is this a broadcast group?"}},"messageActionChatAddUser":{"comment":"New member in the group","arguments":{"users":"Users that were invited to the chat"}},"messageActionChatDeleteUser":{"comment":"User left the group.","arguments":{"userId":"Leaving user ID"}},"messageActionChatJoinedByLink":{"comment":"A user joined the chat via an invite link","arguments":{"inviterId":"ID of the user that created the invite link"}},"messageActionChannelCreate":{"comment":"The channel was created","arguments":{"title":"Original channel/supergroup title"}},"messageActionChatMigrateTo":{"comment":"Indicates the chat was migrated to the specified supergroup","arguments":{"channelId":"The supergroup it was migrated to"}},"messageActionChannelMigrateFrom":{"comment":"Indicates the channel was migrated from the specified chat","arguments":{"title":"The old chat tite","chatId":"The old chat ID"}},"messageActionPinMessage":{"comment":"A message was pinned","arguments":{"gigagroup":"Is this a broadcast group?"}},"messageActionHistoryClear":{"comment":"Chat history was cleared","arguments":{"gigagroup":"Is this a broadcast group?"}},"messageActionGameScore":{"comment":"Someone scored in a game","arguments":{"gameId":"Game ID","score":"Score"}},"messageActionPaymentSentMe":{"comment":"A user just sent a payment to me (a bot)","arguments":{"flags":"Flags, see TL conditional fields","currency":"Three-letter ISO 4217 currency code","totalAmount":"Price of the product in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).","payload":"Bot specified invoice payload","info":"Order info provided by the user","shippingOptionId":"Identifier of the shipping option chosen by the user","charge":"Provider payment identifier"}},"messageActionPaymentSent":{"comment":"A payment was sent","arguments":{"currency":"Three-letter ISO 4217 currency code","totalAmount":"Price of the product in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies)."}},"messageActionPhoneCall":{"comment":"A phone call","arguments":{"flags":"Flags, see TL conditional fields","video":"Is this a video call?","callId":"Call ID","reason":"If the call has ended, the reason why it ended","duration":"Duration of the call in seconds"}},"messageActionScreenshotTaken":{"comment":"A screenshot of the chat was taken","arguments":{"gigagroup":"Is this a broadcast group?"}},"messageActionCustomAction":{"comment":"Custom action (most likely not supported by the current layer, an upgrade might be needed)","arguments":{"message":"Action message"}},"messageActionBotAllowed":{"comment":"The domain name of the website on which the user has logged in. More about Telegram Login »","arguments":{"domain":"The domain name of the website on which the user has logged in."}},"messageActionSecureValuesSentMe":{"comment":"Secure telegram passport values were received","arguments":{"values":"Vector with information about documents and other Telegram Passport elements that were shared with the bot","credentials":"Encrypted credentials required to decrypt the data"}},"messageActionSecureValuesSent":{"comment":"Request for secure telegram passport values was sent","arguments":{"types":"Secure value types"}},"messageActionContactSignUp":{"comment":"A contact just signed up to telegram","arguments":{"gigagroup":"Is this a broadcast group?"}},"messageActionGeoProximityReached":{"comment":"A user of the chat is now in proximity of another user","arguments":{"fromId":"The user or chat that is now in proximity of to_id","toId":"The user or chat that subscribed to live geolocation proximity alerts","distance":"Distance, in meters (0-100000)"}},"messageActionGroupCall":{"comment":"The group call has ended","arguments":{"flags":"Flags, see TL conditional fields","call":"Group call","duration":"Group call duration"}},"messageActionInviteToGroupCall":{"comment":"A set of users was invited to the group call","arguments":{"call":"The group call","users":"The invited users"}},"messageActionSetMessagesTTL":{"comment":"The Time-To-Live of messages in this chat was changed.","arguments":{"period":"New Time-To-Live"}},"messageActionGroupCallScheduled":{"comment":"A group call was scheduled","arguments":{"call":"The group call","scheduleDate":"When is this group call scheduled to start"}},"messageActionSetChatTheme":{"comment":"The chat theme was changed","arguments":{"emoticon":"The emoji that identifies a chat theme"}},"dialog":{"comment":"Chat","arguments":{"flags":"Flags, see TL conditional fields","pinned":"Is the dialog pinned","unreadMark":"Whether the chat was manually marked as unread","peer":"The chat","topMessage":"The latest message ID","readInboxMaxId":"Position up to which all incoming messages are read.","readOutboxMaxId":"Position up to which all outgoing messages are read.","unreadCount":"Number of unread messages","unreadMentionsCount":"Number of unread mentions","notifySettings":"Notification settings","pts":"PTS","draft":"Message draft","folderId":"Peer folder ID, for more info click here"}},"dialogFolder":{"comment":"Dialog in folder","arguments":{"flags":"Flags, see TL conditional fields","pinned":"Is this folder pinned","folder":"The folder","peer":"Peer in folder","topMessage":"Latest message ID of dialog","unreadMutedPeersCount":"Number of unread muted peers in folder","unreadUnmutedPeersCount":"Number of unread unmuted peers in folder","unreadMutedMessagesCount":"Number of unread messages from muted peers in folder","unreadUnmutedMessagesCount":"Number of unread messages from unmuted peers in folder"}},"photoEmpty":{"comment":"Empty constructor, non-existent photo","arguments":{"id":"Photo identifier"}},"photo":{"comment":"Photo","arguments":{"flags":"Flags, see TL conditional fields","hasStickers":"Whether the photo has mask stickers attached to it","id":"ID","accessHash":"Access hash","fileReference":"file reference","date":"Date of upload","sizes":"Available sizes for download","videoSizes":"For animated profiles, the MPEG4 videos","dcId":"DC ID to use for download"}},"photoSizeEmpty":{"comment":"Empty constructor. Image with this thumbnail is unavailable.","arguments":{"type":"Thumbnail type (see. {@link photoSize})"}},"photoSize":{"comment":"Image description.","arguments":{"type":"Thumbnail type","w":"Image width","h":"Image height","size":"File size"}},"photoCachedSize":{"comment":"Description of an image and its content.","arguments":{"type":"Thumbnail type","w":"Image width","h":"Image height","bytes":"Binary data, file content"}},"photoStrippedSize":{"comment":"A low-resolution compressed JPG payload","arguments":{"type":"Thumbnail type","bytes":"Thumbnail data, see here for more info on decompression »"}},"photoSizeProgressive":{"comment":"Progressively encoded photosize","arguments":{"type":"Photosize type","w":"Photo width","h":"Photo height","sizes":"Sizes of progressive JPEG file prefixes, which can be used to preliminarily show the image."}},"photoPathSize":{"comment":"Messages with animated stickers can have a compressed svg (< 300 bytes) to show the outline of the sticker before fetching the actual lottie animation.","arguments":{"type":"Always j","bytes":"Compressed SVG path payload, see here for decompression instructions"}},"geoPointEmpty":{"comment":"Empty constructor.","arguments":{"gigagroup":"Is this a broadcast group?"}},"geoPoint":{"comment":"GeoPoint.","arguments":{"flags":"Flags, see TL conditional fields","long":"Longtitude","lat":"Latitude","accessHash":"Access hash","accuracyRadius":"The estimated horizontal accuracy of the location, in meters; as defined by the sender."}},"auth.sentCode":{"comment":"Contains info about a sent verification code.","arguments":{"flags":"Flags, see TL conditional fields","type":"Phone code type","phoneCodeHash":"Phone code hash, to be stored and later re-used with {@link auth.signIn}","nextType":"Phone code type that will be sent next, if the phone code is not received within timeout seconds: to send it use {@link auth.resendCode}","timeout":"Timeout for reception of the phone code"}},"auth.authorization":{"comment":"Contains user authorization info.","arguments":{"flags":"Flags, see TL conditional fields","tmpSessions":"Temporary passport sessions","user":"Info on authorized user"}},"auth.authorizationSignUpRequired":{"comment":"An account with this phone number doesn't exist on telegram: the user has to enter basic information and sign up","arguments":{"flags":"Flags, see TL conditional fields","termsOfService":"Telegram's terms of service: the user must read and accept the terms of service before signing up to telegram"}},"auth.exportedAuthorization":{"comment":"Data for copying of authorization between data centres.","arguments":{"id":"current user identifier","bytes":"authorizes key"}},"inputNotifyPeer":{"comment":"Notifications generated by a certain user or group.","arguments":{"peer":"User or group"}},"inputNotifyUsers":{"comment":"Notifications generated by all users.","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputNotifyChats":{"comment":"Notifications generated by all groups.","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputNotifyBroadcasts":{"comment":"All channels","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputPeerNotifySettings":{"comment":"Notification settings.","arguments":{"flags":"Flags, see TL conditional fields","showPreviews":"If the text of the message shall be displayed in notification","silent":"Peer was muted?","muteUntil":"Date until which all notifications shall be switched off","sound":"Name of an audio file for notification"}},"peerNotifySettings":{"comment":"Notification settings.","arguments":{"flags":"Flags, see TL conditional fields","showPreviews":"Display text in notifications","silent":"Mute peer?","muteUntil":"Mute all notifications until this date","sound":"Audio file name for notifications"}},"peerSettings":{"comment":"Peer settings","arguments":{"flags":"Flags, see TL conditional fields","reportSpam":"Whether we can still report the user for spam","addContact":"Whether we can add the user as contact","blockContact":"Whether we can block the user","shareContact":"Whether we can share the user's contact","needContactsException":"Whether a special exception for contacts is needed","reportGeo":"Whether we can report a geo group is irrelevant for this location","autoarchived":"Whether this peer was automatically archived according to {@link globalPrivacySettings}","inviteMembers":"Whether we can invite members to a group or channel","geoDistance":"Distance in meters between us and this peer"}},"wallPaper":{"comment":"Wallpaper settings.","arguments":{"id":"Identifier","flags":"Flags, see TL conditional fields","creator":"Creator of the wallpaper","default":"Whether this is the default wallpaper","pattern":"Pattern","dark":"Dark mode","accessHash":"Access hash","slug":"Unique wallpaper ID","document":"The actual wallpaper","settings":"Wallpaper settings"}},"wallPaperNoFile":{"comment":"Wallpaper with no file access hash, used for example when deleting (unsave=true) wallpapers using {@link account.saveWallPaper}, specifying just the wallpaper ID.\nAlso used for some default wallpapers which contain only colours.","arguments":{"id":"Wallpaper ID","flags":"Flags, see TL conditional fields","default":"Whether this is the default wallpaper","dark":"Dark mode","settings":"Wallpaper settings"}},"inputReportReasonSpam":{"comment":"Report for spam","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputReportReasonViolence":{"comment":"Report for violence","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputReportReasonPornography":{"comment":"Report for pornography","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputReportReasonChildAbuse":{"comment":"Report for child abuse","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputReportReasonOther":{"comment":"Other","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputReportReasonCopyright":{"comment":"Report for copyrighted content","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputReportReasonGeoIrrelevant":{"comment":"Report an irrelevant geo group","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputReportReasonFake":{"comment":"Report for impersonation","arguments":{"gigagroup":"Is this a broadcast group?"}},"userFull":{"comment":"Extended user info","arguments":{"flags":"Flags, see TL conditional fields","blocked":"Whether you have blocked this user","phoneCallsAvailable":"Whether this user can make VoIP calls","phoneCallsPrivate":"Whether this user's privacy settings allow you to call them","canPinMessage":"Whether you can pin messages in the chat with this user, you can do this only for a chat with yourself","hasScheduled":"Whether scheduled messages are available","videoCallsAvailable":"Whether the user can receive video calls","user":"Remaining user info","about":"Bio of the user","settings":"Peer settings","profilePhoto":"Profile photo","notifySettings":"Notification settings","botInfo":"For bots, info about the bot (bot commands, etc)","pinnedMsgId":"Message ID of the last pinned message","commonChatsCount":"Chats in common with this user","folderId":"Peer folder ID, for more info click here","ttlPeriod":"Time To Live of all messages in this chat; once a message is this many seconds old, it must be deleted.","themeEmoticon":"Emoji associated with chat theme"}},"contact":{"comment":"A contact of the current user that is registered in the system.","arguments":{"userId":"User identifier","mutual":"Current user is in the user's contact list"}},"importedContact":{"comment":"Successfully imported contact.","arguments":{"userId":"User identifier","clientId":"The contact's client identifier (passed to one of the InputContact constructors)"}},"contactStatus":{"comment":"Contact status: online / offline.","arguments":{"userId":"User identifier","status":"Online status"}},"contacts.contactsNotModified":{"comment":"Contact list on the server is the same as the list on the client.","arguments":{"gigagroup":"Is this a broadcast group?"}},"contacts.contacts":{"comment":"The current user's contact list and info on users.","arguments":{"contacts":"Contact list","savedCount":"Number of contacts that were saved successfully","users":"User list"}},"contacts.importedContacts":{"comment":"Info on succesfully imported contacts.","arguments":{"imported":"List of succesfully imported contacts","popularInvites":"Popular contacts","retryContacts":"List of contact ids that could not be imported due to system limitation and will need to be imported at a later date.
Parameter added in Layer 13","users":"List of users"}},"contacts.blocked":{"comment":"Full list of blocked users.","arguments":{"blocked":"List of blocked users","chats":"Blocked chats","users":"List of users"}},"contacts.blockedSlice":{"comment":"Incomplete list of blocked users.","arguments":{"count":"Total number of elements in the list","blocked":"List of blocked users","chats":"Blocked chats","users":"List of users"}},"messages.dialogs":{"comment":"Full list of chats with messages and auxiliary data.","arguments":{"dialogs":"List of chats","messages":"List of last messages from each chat","chats":"List of groups mentioned in the chats","users":"List of users mentioned in messages and groups"}},"messages.dialogsSlice":{"comment":"Incomplete list of dialogs with messages and auxiliary data.","arguments":{"count":"Total number of dialogs","dialogs":"List of dialogs","messages":"List of last messages from dialogs","chats":"List of chats mentioned in dialogs","users":"List of users mentioned in messages and chats"}},"messages.dialogsNotModified":{"comment":"Dialogs haven't changed","arguments":{"count":"Number of dialogs found server-side by the query"}},"messages.messages":{"comment":"Full list of messages with auxilary data.","arguments":{"messages":"List of messages","chats":"List of chats mentioned in dialogs","users":"List of users mentioned in messages and chats"}},"messages.messagesSlice":{"comment":"Incomplete list of messages and auxiliary data.","arguments":{"flags":"Flags, see TL conditional fields","inexact":"If set, indicates that the results may be inexact","count":"Total number of messages in the list","nextRate":"Rate to use in the offset_rate parameter in the next call to {@link messages.searchGlobal}","offsetIdOffset":"Indicates the absolute position of messages[0] within the total result set with count count.
This is useful, for example, if the result was fetched using offset_id, and we need to display a progress/total counter (like photo 134 of 200, for all media in a chat, we could simply use photo ${offset_id_offset} of ${count}.","messages":"List of messages","chats":"List of chats mentioned in messages","users":"List of users mentioned in messages and chats"}},"messages.channelMessages":{"comment":"Channel messages","arguments":{"flags":"Flags, see TL conditional fields","inexact":"If set, returned results may be inexact","pts":"Event count after generation","count":"Total number of results were found server-side (may not be all included here)","offsetIdOffset":"Indicates the absolute position of messages[0] within the total result set with count count.
This is useful, for example, if the result was fetched using offset_id, and we need to display a progress/total counter (like photo 134 of 200, for all media in a chat, we could simply use photo ${offset_id_offset} of ${count}.","messages":"Found messages","chats":"Chats","users":"Users"}},"messages.messagesNotModified":{"comment":"No new messages matching the query were found","arguments":{"count":"Number of results found server-side by the given query"}},"messages.chats":{"comment":"List of chats with auxiliary data.","arguments":{"chats":"List of chats"}},"messages.chatsSlice":{"comment":"Partial list of chats, more would have to be fetched with pagination","arguments":{"count":"Total number of results that were found server-side (not all are included in chats)","chats":"Chats"}},"messages.chatFull":{"comment":"Extended info on chat and auxiliary data.","arguments":{"fullChat":"Extended info on a chat","chats":"List containing basic info on chat","users":"List of users mentioned above"}},"messages.affectedHistory":{"comment":"Affected part of communication history with the user or in a chat.","arguments":{"pts":"Number of events occured in a text box","ptsCount":"Number of affected events","offset":"If a parameter contains positive value, it is necessary to repeat the method call using the given value; during the proceeding of all the history the value itself shall gradually decrease"}},"inputMessagesFilterEmpty":{"comment":"Filter is absent.","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputMessagesFilterPhotos":{"comment":"Filter for messages containing photos.","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputMessagesFilterVideo":{"comment":"Filter for messages containing videos.","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputMessagesFilterPhotoVideo":{"comment":"Filter for messages containing photos or videos.","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputMessagesFilterDocument":{"comment":"Filter for messages containing documents.","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputMessagesFilterUrl":{"comment":"Return only messages containing URLs","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputMessagesFilterGif":{"comment":"Return only messages containing gifs","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputMessagesFilterVoice":{"comment":"Return only messages containing voice notes","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputMessagesFilterMusic":{"comment":"Return only messages containing audio files","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputMessagesFilterChatPhotos":{"comment":"Return only chat photo changes","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputMessagesFilterPhoneCalls":{"comment":"Return only phone calls","arguments":{"flags":"Flags, see TL conditional fields","missed":"Return only missed phone calls"}},"inputMessagesFilterRoundVoice":{"comment":"Return only round videos and voice notes","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputMessagesFilterRoundVideo":{"comment":"Return only round videos","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputMessagesFilterMyMentions":{"comment":"Return only messages where the current user was mentioned.","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputMessagesFilterGeo":{"comment":"Return only messages containing geolocations","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputMessagesFilterContacts":{"comment":"Return only messages containing contacts","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputMessagesFilterPinned":{"comment":"Fetch only pinned messages","arguments":{"gigagroup":"Is this a broadcast group?"}},"updateNewMessage":{"comment":"New message in a private chat or in a legacy group.","arguments":{"message":"Message","pts":"New quantity of actions in a message box","ptsCount":"Number of generated events"}},"updateMessageID":{"comment":"Sent message with random_id client identifier was assigned an identifier.","arguments":{"id":"id identifier of a respective Message","randomId":"Previuosly transferred client random_id identifier"}},"updateDeleteMessages":{"comment":"Messages were deleted.","arguments":{"messages":"List of identifiers of deleted messages","pts":"New quality of actions in a message box","ptsCount":"Number of generated events"}},"updateUserTyping":{"comment":"The user is preparing a message; typing, recording, uploading, etc. This update is valid for 6 seconds. If no repeated update received after 6 seconds, it should be considered that the user stopped doing whatever he's been doing.","arguments":{"userId":"User id","action":"Action type
Param added in Layer 17."}},"updateChatUserTyping":{"comment":"The user is preparing a message in a group; typing, recording, uploading, etc. This update is valid for 6 seconds. If no repeated update received after 6 seconds, it should be considered that the user stopped doing whatever he's been doing.","arguments":{"chatId":"Group id","fromId":"Peer that started typing (can be the chat itself, in case of anonymous admins).","action":"Type of action
Parameter added in Layer 17."}},"updateChatParticipants":{"comment":"Composition of chat participants changed.","arguments":{"participants":"Updated chat participants"}},"updateUserStatus":{"comment":"Contact status update.","arguments":{"userId":"User identifier","status":"New status"}},"updateUserName":{"comment":"Changes the user's first name, last name and username.","arguments":{"userId":"User identifier","firstName":"New first name. Corresponds to the new value of real_first_name field of the {@link userFull} constructor.","lastName":"New last name. Corresponds to the new value of real_last_name field of the {@link userFull} constructor.","username":"New username.
Parameter added in Layer 18."}},"updateUserPhoto":{"comment":"Change of contact's profile photo.","arguments":{"userId":"User identifier","date":"Date of photo update.","photo":"New profile photo","previous":"({@link boolTrue}), if one of the previously used photos is set a profile photo."}},"updateNewEncryptedMessage":{"comment":"New encrypted message.","arguments":{"message":"Message","qts":"New qts value, see updates » for more info."}},"updateEncryptedChatTyping":{"comment":"Interlocutor is typing a message in an encrypted chat. Update period is 6 second. If upon this time there is no repeated update, it shall be considered that the interlocutor stopped typing.","arguments":{"chatId":"Chat ID"}},"updateEncryption":{"comment":"Change of state in an encrypted chat.","arguments":{"chat":"Encrypted chat","date":"Date of change"}},"updateEncryptedMessagesRead":{"comment":"Communication history in an encrypted chat was marked as read.","arguments":{"chatId":"Chat ID","maxDate":"Maximum value of data for read messages","date":"Time when messages were read"}},"updateChatParticipantAdd":{"comment":"New group member.","arguments":{"chatId":"Group ID","userId":"ID of the new member","inviterId":"ID of the user, who added member to the group","date":"When was the participant added","version":"Chat version number"}},"updateChatParticipantDelete":{"comment":"A member has left the group.","arguments":{"chatId":"Group ID","userId":"ID of the user","version":"Used in basic groups to reorder updates and make sure that all of them was received."}},"updateDcOptions":{"comment":"Changes in the data center configuration options.","arguments":{"dcOptions":"New connection options"}},"updateNotifySettings":{"comment":"Changes in notification settings.","arguments":{"peer":"Nofication source","notifySettings":"New notification settings"}},"updateServiceNotification":{"comment":"The app must show the message to the user upon receiving this update. In case the popup parameter was passed, the text message must be displayed in a popup alert immediately upon receipt. It is recommended to handle the text as you would an ordinary message in terms of highlighting links, etc. The message must also be stored locally as part of the message history with the user id 777000 (Telegram Notifications).\n\nA service message for the user.","arguments":{"flags":"Flags, see TL conditional fields","popup":"(boolTrue) if the message must be displayed in a popup.","inboxDate":"When was the notification received
The message must also be stored locally as part of the message history with the user id 777000 (Telegram Notifications).","type":"String, identical in format and contents to the type field in API errors. Describes type of service message. It is acceptable to ignore repeated messages of the same type within a short period of time (15 minutes).","message":"Message text","media":"Media content (optional)","entities":"Message entities for styled text"}},"updatePrivacy":{"comment":"Privacy rules were changed","arguments":{"key":"Peers to which the privacy rules apply","rules":"New privacy rules"}},"updateUserPhone":{"comment":"A user's phone number was changed","arguments":{"userId":"User ID","phone":"New phone number"}},"updateReadHistoryInbox":{"comment":"Incoming messages were read","arguments":{"flags":"Flags, see TL conditional fields","folderId":"Peer folder ID, for more info click here","peer":"Peer","maxId":"Maximum ID of messages read","stillUnreadCount":"Number of messages that are still unread","pts":"Event count after generation","ptsCount":"Number of events that were generated"}},"updateReadHistoryOutbox":{"comment":"Outgoing messages were read","arguments":{"peer":"Peer","maxId":"Maximum ID of read outgoing messages","pts":"Event count after generation","ptsCount":"Number of events that were generated"}},"updateWebPage":{"comment":"An instant view webpage preview was generated","arguments":{"webpage":"Webpage preview","pts":"Event count after generation","ptsCount":"Number of events that were generated"}},"updateReadMessagesContents":{"comment":"Contents of messages in the common message box were read","arguments":{"messages":"IDs of read messages","pts":"Event count after generation","ptsCount":"Number of events that were generated"}},"updateChannelTooLong":{"comment":"There are new updates in the specified channel, the client must fetch them.\nIf the difference is too long or if the channel isn't currently in the states, start fetching from the specified pts.","arguments":{"flags":"Flags, see TL conditional fields","channelId":"The channel","pts":"The PTS."}},"updateChannel":{"comment":"A new channel is available","arguments":{"channelId":"Channel ID"}},"updateNewChannelMessage":{"comment":"A new message was sent in a channel/supergroup","arguments":{"message":"New message","pts":"Event count after generation","ptsCount":"Number of events that were generated"}},"updateReadChannelInbox":{"comment":"Incoming messages in a channel/supergroup were read","arguments":{"flags":"Flags, see TL conditional fields","folderId":"Peer folder ID, for more info click here","channelId":"Channel/supergroup ID","maxId":"Position up to which all incoming messages are read.","stillUnreadCount":"Count of messages weren't read yet","pts":"Event count after generation"}},"updateDeleteChannelMessages":{"comment":"Some messages in a supergroup/channel were deleted","arguments":{"channelId":"Channel ID","messages":"IDs of messages that were deleted","pts":"Event count after generation","ptsCount":"Number of events that were generated"}},"updateChannelMessageViews":{"comment":"The view counter of a message in a channel has changed","arguments":{"channelId":"Channel ID","id":"ID of the message","views":"New view counter"}},"updateChatParticipantAdmin":{"comment":"Admin permissions of a user in a legacy group were changed","arguments":{"chatId":"Chat ID","userId":"ID of the (de)admined user","isAdmin":"Whether the user was rendered admin","version":"Used in basic groups to reorder updates and make sure that all of them was received."}},"updateNewStickerSet":{"comment":"A new stickerset was installed","arguments":{"stickerset":"The installed stickerset"}},"updateStickerSetsOrder":{"comment":"The order of stickersets was changed","arguments":{"flags":"Flags, see TL conditional fields","masks":"Whether the updated stickers are mask stickers","order":"New sticker order by sticker ID"}},"updateStickerSets":{"comment":"Installed stickersets have changed, the client should refetch them using {@link messages.getAllStickers}","arguments":{"gigagroup":"Is this a broadcast group?"}},"updateSavedGifs":{"comment":"The saved gif list has changed, the client should refetch it using {@link messages.getSavedGifs}","arguments":{"gigagroup":"Is this a broadcast group?"}},"updateBotInlineQuery":{"comment":"An incoming inline query","arguments":{"flags":"Flags, see TL conditional fields","queryId":"Query ID","userId":"User that sent the query","query":"Text of query","geo":"Attached geolocation","peerType":"Type of the chat from which the inline query was sent.","offset":"Offset to navigate through results"}},"updateBotInlineSend":{"comment":"The result of an inline query that was chosen by a user and sent to their chat partner. Please see our documentation on the feedback collecting for details on how to enable these updates for your bot.","arguments":{"flags":"Flags, see TL conditional fields","userId":"The user that chose the result","query":"The query that was used to obtain the result","geo":"Optional. Sender location, only for bots that require user location","id":"The unique identifier for the result that was chosen","msgId":"Identifier of the sent inline message. Available only if there is an inline keyboard attached to the message. Will be also received in callback queries and can be used to edit the message."}},"updateEditChannelMessage":{"comment":"A message was edited in a channel/supergroup","arguments":{"message":"The new message","pts":"Event count after generation","ptsCount":"Number of events that were generated"}},"updateBotCallbackQuery":{"comment":"A callback button was pressed, and the button data was sent to the bot that created the button","arguments":{"flags":"Flags, see TL conditional fields","queryId":"Query ID","userId":"ID of the user that pressed the button","peer":"Chat where the inline keyboard was sent","msgId":"Message ID","chatInstance":"Global identifier, uniquely corresponding to the chat to which the message with the callback button was sent. Useful for high scores in games.","data":"Callback data","gameShortName":"Short name of a Game to be returned, serves as the unique identifier for the game"}},"updateEditMessage":{"comment":"A message was edited","arguments":{"message":"The new edited message","pts":"PTS","ptsCount":"PTS count"}},"updateInlineBotCallbackQuery":{"comment":"This notification is received by bots when a button is pressed","arguments":{"flags":"Flags, see TL conditional fields","queryId":"Query ID","userId":"ID of the user that pressed the button","msgId":"ID of the inline message with the button","chatInstance":"Global identifier, uniquely corresponding to the chat to which the message with the callback button was sent. Useful for high scores in games.","data":"Data associated with the callback button. Be aware that a bad client can send arbitrary data in this field.","gameShortName":"Short name of a Game to be returned, serves as the unique identifier for the game"}},"updateReadChannelOutbox":{"comment":"Outgoing messages in a channel/supergroup were read","arguments":{"channelId":"Channel/supergroup ID","maxId":"Position up to which all outgoing messages are read."}},"updateDraftMessage":{"comment":"Notifies a change of a message draft.","arguments":{"peer":"The peer to which the draft is associated","draft":"The draft"}},"updateReadFeaturedStickers":{"comment":"Some featured stickers were marked as read","arguments":{"gigagroup":"Is this a broadcast group?"}},"updateRecentStickers":{"comment":"The recent sticker list was updated","arguments":{"gigagroup":"Is this a broadcast group?"}},"updateConfig":{"comment":"The server-side configuration has changed; the client should re-fetch the config using {@link help.getConfig}","arguments":{"gigagroup":"Is this a broadcast group?"}},"updatePtsChanged":{"comment":"Common message box sequence PTS has changed, state has to be refetched using updates.getState","arguments":{"gigagroup":"Is this a broadcast group?"}},"updateChannelWebPage":{"comment":"A webpage preview of a link in a channel/supergroup message was generated","arguments":{"channelId":"Channel/supergroup ID","webpage":"Generated webpage preview","pts":"Event count after generation","ptsCount":"Number of events that were generated"}},"updateDialogPinned":{"comment":"A dialog was pinned/unpinned","arguments":{"flags":"Flags, see TL conditional fields","pinned":"Whether the dialog was pinned","folderId":"Peer folder ID, for more info click here","peer":"The dialog"}},"updatePinnedDialogs":{"comment":"Pinned dialogs were updated","arguments":{"flags":"Flags, see TL conditional fields","folderId":"Peer folder ID, for more info click here","order":"New order of pinned dialogs"}},"updateBotWebhookJSON":{"comment":"A new incoming event; for bots only","arguments":{"data":"The event"}},"updateBotWebhookJSONQuery":{"comment":"A new incoming query; for bots only","arguments":{"queryId":"Query identifier","data":"Query data","timeout":"Query timeout"}},"updateBotShippingQuery":{"comment":"This object contains information about an incoming shipping query.","arguments":{"queryId":"Unique query identifier","userId":"User who sent the query","payload":"Bot specified invoice payload","shippingAddress":"User specified shipping address"}},"updateBotPrecheckoutQuery":{"comment":"This object contains information about an incoming pre-checkout query.","arguments":{"flags":"Flags, see TL conditional fields","queryId":"Unique query identifier","userId":"User who sent the query","payload":"Bot specified invoice payload","info":"Order info provided by the user","shippingOptionId":"Identifier of the shipping option chosen by the user","currency":"Three-letter ISO 4217 currency code","totalAmount":"Total amount in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies)."}},"updatePhoneCall":{"comment":"An incoming phone call","arguments":{"phoneCall":"Phone call"}},"updateLangPackTooLong":{"comment":"A language pack has changed, the client should manually fetch the changed strings using {@link langpack.getDifference}","arguments":{"langCode":"Language code"}},"updateLangPack":{"comment":"Language pack updated","arguments":{"difference":"Changed strings"}},"updateFavedStickers":{"comment":"The list of favorited stickers was changed, the client should call {@link messages.getFavedStickers} to refetch the new list","arguments":{"gigagroup":"Is this a broadcast group?"}},"updateChannelReadMessagesContents":{"comment":"The specified channel/supergroup messages were read","arguments":{"channelId":"Channel/supergroup ID","messages":"IDs of messages that were read"}},"updateContactsReset":{"comment":"All contacts were deleted","arguments":{"gigagroup":"Is this a broadcast group?"}},"updateChannelAvailableMessages":{"comment":"The history of a channel/supergroup was hidden.","arguments":{"channelId":"Channel/supergroup ID","availableMinId":"Identifier of a maximum unavailable message in a channel due to hidden history."}},"updateDialogUnreadMark":{"comment":"The manual unread mark of a chat was changed","arguments":{"flags":"Flags, see TL conditional fields","unread":"Was the chat marked or unmarked as read","peer":"The dialog"}},"updateMessagePoll":{"comment":"The results of a poll have changed","arguments":{"flags":"Flags, see TL conditional fields","pollId":"Poll ID","poll":"If the server knows the client hasn't cached this poll yet, the poll itself","results":"New poll results"}},"updateChatDefaultBannedRights":{"comment":"Default banned rights in a normal chat were updated","arguments":{"peer":"The chat","defaultBannedRights":"New default banned rights","version":"Version"}},"updateFolderPeers":{"comment":"The peer list of a peer folder was updated","arguments":{"folderPeers":"New peer list","pts":"Event count after generation","ptsCount":"Number of events that were generated"}},"updatePeerSettings":{"comment":"Settings of a certain peer have changed","arguments":{"peer":"The peer","settings":"Associated peer settings"}},"updatePeerLocated":{"comment":"List of peers near you was updated","arguments":{"peers":"Geolocated peer list update"}},"updateNewScheduledMessage":{"comment":"A message was added to the schedule queue of a chat","arguments":{"message":"Message"}},"updateDeleteScheduledMessages":{"comment":"Some scheduled messages were deleted from the schedule queue of a chat","arguments":{"peer":"Peer","messages":"Deleted scheduled messages"}},"updateTheme":{"comment":"A cloud theme was updated","arguments":{"theme":"Theme"}},"updateGeoLiveViewed":{"comment":"Live geo position message was viewed","arguments":{"peer":"The user that viewed the live geo position","msgId":"Message ID of geo position message"}},"updateLoginToken":{"comment":"A login token (for login via QR code) was accepted.","arguments":{"gigagroup":"Is this a broadcast group?"}},"updateMessagePollVote":{"comment":"A specific user has voted in a poll","arguments":{"pollId":"Poll ID","userId":"User ID","options":"Chosen option(s)","qts":"New qts value, see updates » for more info."}},"updateDialogFilter":{"comment":"A new folder was added","arguments":{"flags":"Flags, see TL conditional fields","id":"Folder ID","filter":"Folder info"}},"updateDialogFilterOrder":{"comment":"New folder order","arguments":{"order":"Ordered folder IDs"}},"updateDialogFilters":{"comment":"Clients should update folder info","arguments":{"gigagroup":"Is this a broadcast group?"}},"updatePhoneCallSignalingData":{"comment":"Incoming phone call signaling payload","arguments":{"phoneCallId":"Phone call ID","data":"Signaling payload"}},"updateChannelMessageForwards":{"comment":"The forward counter of a message in a channel has changed","arguments":{"channelId":"Channel ID","id":"ID of the message","forwards":"New forward counter"}},"updateReadChannelDiscussionInbox":{"comment":"Incoming comments in a discussion thread were marked as read","arguments":{"flags":"Flags, see TL conditional fields","channelId":"Discussion group ID","topMsgId":"ID of the group message that started the thread (message in linked discussion group)","readMaxId":"Message ID of latest read incoming message for this thread","broadcastId":"If set, contains the ID of the channel that contains the post that started the comment thread in the discussion group (channel_id)","broadcastPost":"If set, contains the ID of the channel post that started the the comment thread"}},"updateReadChannelDiscussionOutbox":{"comment":"Outgoing comments in a discussion thread were marked as read","arguments":{"channelId":"Supergroup ID","topMsgId":"ID of the group message that started the thread","readMaxId":"Message ID of latest read outgoing message for this thread"}},"updatePeerBlocked":{"comment":"A peer was blocked","arguments":{"peerId":"The blocked peer","blocked":"Whether the peer was blocked or unblocked"}},"updateChannelUserTyping":{"comment":"A user is typing in a supergroup, channel or message thread","arguments":{"flags":"Flags, see TL conditional fields","channelId":"Channel ID","topMsgId":"Thread ID","fromId":"The peer that is typing","action":"Whether the user is typing, sending a media or doing something else"}},"updatePinnedMessages":{"comment":"Some messages were pinned in a chat","arguments":{"flags":"Flags, see TL conditional fields","pinned":"Whether the messages were pinned or unpinned","peer":"Peer","messages":"Message IDs","pts":"Event count after generation","ptsCount":"Number of events that were generated"}},"updatePinnedChannelMessages":{"comment":"Messages were pinned/unpinned in a channel/supergroup","arguments":{"flags":"Flags, see TL conditional fields","pinned":"Whether the messages were pinned or unpinned","channelId":"Channel ID","messages":"Messages","pts":"Event count after generation","ptsCount":"Number of events that were generated"}},"updateChat":{"comment":"A new chat is available","arguments":{"chatId":"Chat ID"}},"updateGroupCallParticipants":{"comment":"The participant list of a certain group call has changed","arguments":{"call":"Group call","participants":"New participant list","version":"Version"}},"updateGroupCall":{"comment":"A new groupcall was started","arguments":{"chatId":"The channel/supergroup where this group call or livestream takes place","call":"Info about the group call or livestream"}},"updatePeerHistoryTTL":{"comment":"The Time-To-Live for messages sent by the current user in a specific chat has changed","arguments":{"flags":"Flags, see TL conditional fields","peer":"The chat","ttlPeriod":"The new Time-To-Live"}},"updateChatParticipant":{"comment":"A user has joined or left a specific chat","arguments":{"flags":"Flags, see TL conditional fields","chatId":"Chat ID","date":"When did this event occur","actorId":"User that triggered the change (inviter, admin that kicked the user, or the even the user_id itself)","userId":"User that was affected by the change","prevParticipant":"Previous participant info (empty if this participant just joined)","newParticipant":"New participant info (empty if this participant just left)","invite":"The invite that was used to join the group","qts":"New qts value, see updates » for more info."}},"updateChannelParticipant":{"comment":"A participant has left, joined, was banned or admined in a channel or supergroup.","arguments":{"flags":"Flags, see TL conditional fields","channelId":"Channel ID","date":"Date of the event","actorId":"User that triggered the change (inviter, admin that kicked the user, or the even the user_id itself)","userId":"User that was affected by the change","prevParticipant":"Previous participant status","newParticipant":"New participant status","invite":"Chat invite used to join the channel/supergroup","qts":"New qts value, see updates » for more info."}},"updateBotStopped":{"comment":"A bot was stopped or re-started.","arguments":{"userId":"The bot ID","date":"When did this action occur","stopped":"Whether the bot was stopped or started","qts":"New qts value, see updates » for more info."}},"updateGroupCallConnection":{"comment":"New WebRTC parameters","arguments":{"flags":"Flags, see TL conditional fields","presentation":"Are these parameters related to the screen capture session currently in progress?","params":"WebRTC parameters"}},"updateBotCommands":{"comment":"The command set of a certain bot in a certain chat has changed.","arguments":{"peer":"The affected chat","botId":"ID of the bot that changed its command set","commands":"New bot commands"}},"updates.state":{"comment":"Updates state.","arguments":{"pts":"Number of events occured in a text box","qts":"Position in a sequence of updates in secret chats. For further detailes refer to article secret chats
Parameter was added in eigth layer.","date":"Date of condition","seq":"Number of sent updates","unreadCount":"Number of unread messages"}},"updates.differenceEmpty":{"comment":"No events.","arguments":{"date":"Current date","seq":"Number of sent updates"}},"updates.difference":{"comment":"Full list of occurred events.","arguments":{"newMessages":"List of new messages","newEncryptedMessages":"List of new encrypted secret chat messages","otherUpdates":"List of updates","chats":"List of chats mentioned in events","users":"List of users mentioned in events","state":"Current state"}},"updates.differenceSlice":{"comment":"Incomplete list of occurred events.","arguments":{"newMessages":"List of new messgaes","newEncryptedMessages":"New messages from the encrypted event sequence","otherUpdates":"List of updates","chats":"List of chats mentioned in events","users":"List of users mentioned in events","intermediateState":"Intermediary state"}},"updates.differenceTooLong":{"comment":"The difference is too long, and the specified state must be used to refetch updates.","arguments":{"pts":"The new state to use."}},"updatesTooLong":{"comment":"Too many updates, it is necessary to execute {@link updates.getDifference}.","arguments":{"gigagroup":"Is this a broadcast group?"}},"updateShortMessage":{"comment":"Info about a message sent to (received from) another user","arguments":{"flags":"Flags, see TL conditional fields","out":"Whether the message is outgoing","mentioned":"Whether we were mentioned in the message","mediaUnread":"Whether there are some unread mentions in this message","silent":"If true, the message is a silent message, no notifications should be triggered","id":"The message ID","userId":"The ID of the sender (if outgoing will be the ID of the destination) of the message","message":"The message","pts":"PTS","ptsCount":"PTS count","date":"date","fwdFrom":"Info about a forwarded message","viaBotId":"Info about the inline bot used to generate this message","replyTo":"Reply and thread information","entities":"Entities for styled text","ttlPeriod":"Time To Live of the message, once message.date+message.ttl_period === time(), the message will be deleted on the server, and must be deleted locally as well."}},"updateShortChatMessage":{"comment":"Shortened constructor containing info on one new incoming text message from a chat","arguments":{"flags":"Flags, see TL conditional fields","out":"Whether the message is outgoing","mentioned":"Whether we were mentioned in this message","mediaUnread":"Whether the message contains some unread mentions","silent":"If true, the message is a silent message, no notifications should be triggered","id":"ID of the message","fromId":"ID of the sender of the message","chatId":"ID of the chat where the message was sent","message":"Message","pts":"PTS","ptsCount":"PTS count","date":"date","fwdFrom":"Info about a forwarded message","viaBotId":"Info about the inline bot used to generate this message","replyTo":"Reply (thread) information","entities":"Entities for styled text","ttlPeriod":"Time To Live of the message, once updateShortChatMessage.date+updateShortChatMessage.ttl_period === time(), the message will be deleted on the server, and must be deleted locally as well."}},"updateShort":{"comment":"Shortened constructor containing info on one update not requiring auxiliary data","arguments":{"update":"Update","date":"Date of event"}},"updatesCombined":{"comment":"Constructor for a group of updates.","arguments":{"updates":"List of updates","users":"List of users mentioned in updates","chats":"List of chats mentioned in updates","date":"Current date","seqStart":"Value seq for the earliest update in a group","seq":"Value seq for the latest update in a group"}},"updates":{"comment":"Full constructor of updates","arguments":{"updates":"List of updates","users":"List of users mentioned in updates","chats":"List of chats mentioned in updates","date":"Current date","seq":"Total number of sent updates"}},"updateShortSentMessage":{"comment":"Shortened constructor containing info on one outgoing message to a contact (the destination chat has to be extracted from the method call that returned this object).","arguments":{"flags":"Flags, see TL conditional fields","out":"Whether the message is outgoing","id":"ID of the sent message","pts":"PTS","ptsCount":"PTS count","date":"date","media":"Attached media","entities":"Entities for styled text","ttlPeriod":"Time To Live of the message, once message.date+message.ttl_period === time(), the message will be deleted on the server, and must be deleted locally as well."}},"photos.photos":{"comment":"Full list of photos with auxiliary data.","arguments":{"photos":"List of photos","users":"List of mentioned users"}},"photos.photosSlice":{"comment":"Incomplete list of photos with auxiliary data.","arguments":{"count":"Total number of photos","photos":"List of photos","users":"List of mentioned users"}},"photos.photo":{"comment":"Photo with auxiliary data.","arguments":{"photo":"Photo","users":"Users"}},"upload.file":{"comment":"File content.","arguments":{"type":"File type","mtime":"Modification type","bytes":"Binary data, file content"}},"upload.fileCdnRedirect":{"comment":"The file must be downloaded from a CDN DC.","arguments":{"dcId":"CDN DC ID","fileToken":"File token (see CDN files)","encryptionKey":"Encryption key (see CDN files)","encryptionIv":"Encryption IV (see CDN files)","fileHashes":"File hashes (see CDN files)"}},"dcOption":{"comment":"Data centre","arguments":{"flags":"Flags, see TL conditional fields","ipv6":"Whether the specified IP is an IPv6 address","mediaOnly":"Whether this DC should only be used to download or upload files","tcpoOnly":"Whether this DC only supports connection with transport obfuscation","cdn":"Whether this is a CDN DC.","static":"If set, this IP should be used when connecting through a proxy","id":"DC ID","ipAddress":"IP address of DC","port":"Port","secret":"If the tcpo_only flag is set, specifies the secret to use when connecting using transport obfuscation"}},"config":{"comment":"Current configuration","arguments":{"flags":"Flags, see TL conditional fields","phonecallsEnabled":"Whether phone calls can be used","defaultP2pContacts":"Whether the client should use P2P by default for phone calls with contacts","preloadFeaturedStickers":"Whether the client should preload featured stickers","ignorePhoneEntities":"Whether the client should ignore phone entities","revokePmInbox":"Whether incoming private messages can be deleted for both participants","blockedMode":"Indicates that telegram is probably censored by governments/ISPs in the current region","pfsEnabled":"Whether pfs was used","date":"Current date at the server","expires":"Expiration date of this config: when it expires it'll have to be refetched using {@link help.getConfig}","testMode":"Whether we're connected to the test DCs","thisDc":"ID of the DC that returned the reply","dcOptions":"DC IP list","dcTxtDomainName":"Domain name for fetching encrypted DC list from DNS TXT record","chatSizeMax":"Maximum member count for normal groups","megagroupSizeMax":"Maximum member count for supergroups","forwardedCountMax":"Maximum number of messages that can be forwarded at once using {@link messages.forwardMessages}.","onlineUpdatePeriodMs":"The client should {@link account.updateStatus} every N milliseconds","offlineBlurTimeoutMs":"Delay before offline status needs to be sent to the server","offlineIdleTimeoutMs":"Time without any user activity after which it should be treated offline","onlineCloudTimeoutMs":"If we are offline, but were online from some other client in last online_cloud_timeout_ms milliseconds after we had gone offline, then delay offline notification for notify_cloud_delay_ms milliseconds.","notifyCloudDelayMs":"If we are offline, but online from some other client then delay sending the offline notification for notify_cloud_delay_ms milliseconds.","notifyDefaultDelayMs":"If some other client is online, then delay notification for notification_default_delay_ms milliseconds","pushChatPeriodMs":"Not for client use","pushChatLimit":"Not for client use","savedGifsLimit":"Maximum count of saved gifs","editTimeLimit":"Only messages with age smaller than the one specified can be edited","revokeTimeLimit":"Only channel/supergroup messages with age smaller than the specified can be deleted","revokePmTimeLimit":"Only private messages with age smaller than the specified can be deleted","ratingEDecay":"Exponential decay rate for computing top peer rating","stickersRecentLimit":"Maximum number of recent stickers","stickersFavedLimit":"Maximum number of faved stickers","channelsReadMediaPeriod":"Indicates that round videos (video notes) and voice messages sent in channels and older than the specified period must be marked as read","tmpSessions":"Temporary passport sessions","pinnedDialogsCountMax":"Maximum count of pinned dialogs","pinnedInfolderCountMax":"Maximum count of dialogs per folder","callReceiveTimeoutMs":"Maximum allowed outgoing ring time in VoIP calls: if the user we're calling doesn't reply within the specified time (in milliseconds), we should hang up the call","callRingTimeoutMs":"Maximum allowed incoming ring time in VoIP calls: if the current user doesn't reply within the specified time (in milliseconds), the call will be automatically refused","callConnectTimeoutMs":"VoIP connection timeout: if the instance of libtgvoip on the other side of the call doesn't connect to our instance of libtgvoip within the specified time (in milliseconds), the call must be aborted","callPacketTimeoutMs":"If during a VoIP call a packet isn't received for the specified period of time, the call must be aborted","meUrlPrefix":"The domain to use to parse in-app links.
For example t.me indicates that t.me/username links should parsed to @username, t.me/addsticker/name should be parsed to the appropriate stickerset and so on...","autoupdateUrlPrefix":"URL to use to auto-update the current app","gifSearchUsername":"Username of the bot to use to search for GIFs","venueSearchUsername":"Username of the bot to use to search for venues","imgSearchUsername":"Username of the bot to use for image search","staticMapsProvider":"ID of the map provider to use for venues","captionLengthMax":"Maximum length of caption (length in utf8 codepoints)","messageLengthMax":"Maximum length of messages (length in utf8 codepoints)","webfileDcId":"DC ID to use to download webfiles","suggestedLangCode":"Suggested language code","langPackVersion":"Language pack version","baseLangPackVersion":"Basic language pack version"}},"nearestDc":{"comment":"Nearest data centre, according to geo-ip.","arguments":{"country":"Country code determined by geo-ip","thisDc":"Number of current data centre","nearestDc":"Number of nearest data centre"}},"help.appUpdate":{"comment":"An update is available for the application.","arguments":{"flags":"Flags, see TL conditional fields","canNotSkip":"Unskippable, the new info must be shown to the user (with a popup or something else)","id":"Update ID","version":"New version name","text":"Text description of the update","entities":"Message entities for styled text","document":"Application binary","url":"Application download URL","sticker":"Associated sticker"}},"help.noAppUpdate":{"comment":"No updates are available for the application.","arguments":{"gigagroup":"Is this a broadcast group?"}},"help.inviteText":{"comment":"Text of a text message with an invitation to install Telegram.","arguments":{"message":"Text of the message"}},"encryptedChatEmpty":{"comment":"Empty constructor.","arguments":{"id":"Chat ID"}},"encryptedChatWaiting":{"comment":"Chat waiting for approval of second participant.","arguments":{"id":"Chat ID","accessHash":"Checking sum depending on user ID","date":"Date of chat creation","adminId":"Chat creator ID","participantId":"ID of second chat participant"}},"encryptedChatRequested":{"comment":"Request to create an encrypted chat.","arguments":{"flags":"Flags, see TL conditional fields","folderId":"Peer folder ID, for more info click here","id":"Chat ID","accessHash":"Check sum depending on user ID","date":"Chat creation date","adminId":"Chat creator ID","participantId":"ID of second chat participant","gA":"A = g ^ a mod p, see Wikipedia"}},"encryptedChat":{"comment":"Encrypted chat","arguments":{"id":"Chat ID","accessHash":"Check sum dependant on the user ID","date":"Date chat was created","adminId":"Chat creator ID","participantId":"ID of the second chat participant","gAOrB":"B = g ^ b mod p, if the currently authorized user is the chat's creator,
or A = g ^ a mod p otherwise
See Wikipedia for more info","keyFingerprint":"64-bit fingerprint of received key"}},"encryptedChatDiscarded":{"comment":"Discarded or deleted chat.","arguments":{"flags":"Flags, see TL conditional fields","historyDeleted":"Whether both users of this secret chat should also remove all of its messages","id":"Chat ID"}},"inputEncryptedChat":{"comment":"Creates an encrypted chat.","arguments":{"chatId":"Chat ID","accessHash":"Checking sum from constructor {@link encryptedChat}, {@link encryptedChatWaiting} or {@link encryptedChatRequested}"}},"encryptedFileEmpty":{"comment":"Empty constructor, unexisitng file.","arguments":{"gigagroup":"Is this a broadcast group?"}},"encryptedFile":{"comment":"Encrypted file.","arguments":{"id":"File ID","accessHash":"Checking sum depending on user ID","size":"File size in bytes","dcId":"Number of data centre","keyFingerprint":"32-bit fingerprint of key used for file encryption"}},"inputEncryptedFileEmpty":{"comment":"Empty constructor.","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputEncryptedFileUploaded":{"comment":"Sets new encrypted file saved by parts using upload.saveFilePart method.","arguments":{"id":"Random file ID created by clien","parts":"Number of saved parts","md5Checksum":"In case md5-HASH of the (already encrypted) file was transmitted, file content will be checked prior to use","keyFingerprint":"32-bit fingerprint of the key used to encrypt a file"}},"inputEncryptedFile":{"comment":"Sets forwarded encrypted file for attachment.","arguments":{"id":"File ID, value of id parameter from {@link encryptedFile}","accessHash":"Checking sum, value of access_hash parameter from {@link encryptedFile}"}},"inputEncryptedFileBigUploaded":{"comment":"Assigns a new big encrypted file (over 10Mb in size), saved in parts using the method {@link upload.saveBigFilePart}.","arguments":{"id":"Random file id, created by the client","parts":"Number of saved parts","keyFingerprint":"32-bit imprint of the key used to encrypt the file"}},"encryptedMessage":{"comment":"Encrypted message.","arguments":{"randomId":"Random message ID, assigned by the author of message","chatId":"ID of encrypted chat","date":"Date of sending","bytes":"TL-serialising of DecryptedMessage type, encrypted with the key creatied at stage of chat initialization","file":"Attached encrypted file"}},"encryptedMessageService":{"comment":"Encrypted service message","arguments":{"randomId":"Random message ID, assigned by the author of message","chatId":"ID of encrypted chat","date":"Date of sending","bytes":"TL-serialising of DecryptedMessage type, encrypted with the key creatied at stage of chat initialization"}},"messages.dhConfigNotModified":{"comment":"Configuring parameters did not change.","arguments":{"random":"Random sequence of bytes of assigned length"}},"messages.dhConfig":{"comment":"New set of configuring parameters.","arguments":{"g":"New value prime, see Wikipedia","p":"New value primitive root, see Wikipedia","version":"Vestion of set of parameters","random":"Random sequence of bytes of assigned length"}},"messages.sentEncryptedMessage":{"comment":"Message without file attachemts sent to an encrypted file.","arguments":{"date":"Date of sending"}},"messages.sentEncryptedFile":{"comment":"Message with a file enclosure sent to a protected chat","arguments":{"date":"Sending date","file":"Attached file"}},"inputDocumentEmpty":{"comment":"Empty constructor.","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputDocument":{"comment":"Defines a video for subsequent interaction.","arguments":{"id":"Document ID","accessHash":"access_hash parameter from the {@link document} constructor","fileReference":"File reference"}},"documentEmpty":{"comment":"Empty constructor, document doesn't exist.","arguments":{"id":"Document ID or 0"}},"document":{"comment":"Document","arguments":{"flags":"Flags, see TL conditional fields","id":"Document ID","accessHash":"Check sum, dependant on document ID","fileReference":"File reference","date":"Creation date","mimeType":"MIME type","size":"Size","thumbs":"Thumbnails","videoThumbs":"Video thumbnails","dcId":"DC ID","attributes":"Attributes"}},"help.support":{"comment":"Info on support user.","arguments":{"phoneNumber":"Phone number","user":"User"}},"notifyPeer":{"comment":"Notifications generated by a certain user or group.","arguments":{"peer":"user or group"}},"notifyUsers":{"comment":"Notifications generated by all users.","arguments":{"gigagroup":"Is this a broadcast group?"}},"notifyChats":{"comment":"Notifications generated by all groups.","arguments":{"gigagroup":"Is this a broadcast group?"}},"notifyBroadcasts":{"comment":"Channel notification settings","arguments":{"gigagroup":"Is this a broadcast group?"}},"sendMessageTypingAction":{"comment":"User is typing.","arguments":{"gigagroup":"Is this a broadcast group?"}},"sendMessageCancelAction":{"comment":"Invalidate all previous action updates. E.g. when user deletes entered text or aborts a video upload.","arguments":{"gigagroup":"Is this a broadcast group?"}},"sendMessageRecordVideoAction":{"comment":"User is recording a video.","arguments":{"gigagroup":"Is this a broadcast group?"}},"sendMessageUploadVideoAction":{"comment":"User is uploading a video.","arguments":{"progress":"Progress percentage"}},"sendMessageRecordAudioAction":{"comment":"User is recording a voice message.","arguments":{"gigagroup":"Is this a broadcast group?"}},"sendMessageUploadAudioAction":{"comment":"User is uploading a voice message.","arguments":{"progress":"Progress percentage"}},"sendMessageUploadPhotoAction":{"comment":"User is uploading a photo.","arguments":{"progress":"Progress percentage"}},"sendMessageUploadDocumentAction":{"comment":"User is uploading a file.","arguments":{"progress":"Progress percentage"}},"sendMessageGeoLocationAction":{"comment":"User is selecting a location to share.","arguments":{"gigagroup":"Is this a broadcast group?"}},"sendMessageChooseContactAction":{"comment":"User is selecting a contact to share.","arguments":{"gigagroup":"Is this a broadcast group?"}},"sendMessageGamePlayAction":{"comment":"User is playing a game","arguments":{"gigagroup":"Is this a broadcast group?"}},"sendMessageRecordRoundAction":{"comment":"User is recording a round video to share","arguments":{"gigagroup":"Is this a broadcast group?"}},"sendMessageUploadRoundAction":{"comment":"User is uploading a round video","arguments":{"progress":"Progress percentage"}},"speakingInGroupCallAction":{"comment":"User is currently speaking in the group call","arguments":{"gigagroup":"Is this a broadcast group?"}},"sendMessageHistoryImportAction":{"comment":"Chat history is being imported","arguments":{"progress":"Progress percentage"}},"sendMessageChooseStickerAction":{"comment":"User is choosing a sticker","arguments":{"gigagroup":"Is this a broadcast group?"}},"sendMessageEmojiInteraction":{"comment":"User has clicked on an animated emoji triggering a reaction, click here for more info ».","arguments":{"emoticon":"Emoji","msgId":"Message ID of the animated emoji that was clicked","interaction":"A JSON object with interaction info, click here for more info »"}},"sendMessageEmojiInteractionSeen":{"comment":"User is watching an animated emoji reaction triggered by another user, click here for more info ».","arguments":{"emoticon":"Emoji"}},"contacts.found":{"comment":"Users found by name substring and auxiliary data.","arguments":{"myResults":"Personalized results","results":"List of found user identifiers","chats":"Found chats","users":"List of users"}},"inputPrivacyKeyStatusTimestamp":{"comment":"Whether we can see the exact last online timestamp of the user","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputPrivacyKeyChatInvite":{"comment":"Whether the user can be invited to chats","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputPrivacyKeyPhoneCall":{"comment":"Whether the user will accept phone calls","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputPrivacyKeyPhoneP2P":{"comment":"Whether the user allows P2P communication during VoIP calls","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputPrivacyKeyForwards":{"comment":"Whether messages forwarded from this user will be anonymous","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputPrivacyKeyProfilePhoto":{"comment":"Whether people will be able to see the user's profile picture","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputPrivacyKeyPhoneNumber":{"comment":"Whether people will be able to see the user's phone number","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputPrivacyKeyAddedByPhone":{"comment":"Whether people can add you to their contact list by your phone number","arguments":{"gigagroup":"Is this a broadcast group?"}},"privacyKeyStatusTimestamp":{"comment":"Whether we can see the last online timestamp","arguments":{"gigagroup":"Is this a broadcast group?"}},"privacyKeyChatInvite":{"comment":"Whether the user can be invited to chats","arguments":{"gigagroup":"Is this a broadcast group?"}},"privacyKeyPhoneCall":{"comment":"Whether the user accepts phone calls","arguments":{"gigagroup":"Is this a broadcast group?"}},"privacyKeyPhoneP2P":{"comment":"Whether P2P connections in phone calls are allowed","arguments":{"gigagroup":"Is this a broadcast group?"}},"privacyKeyForwards":{"comment":"Whether messages forwarded from the user will be anonymously forwarded","arguments":{"gigagroup":"Is this a broadcast group?"}},"privacyKeyProfilePhoto":{"comment":"Whether the profile picture of the user is visible","arguments":{"gigagroup":"Is this a broadcast group?"}},"privacyKeyPhoneNumber":{"comment":"Whether the user allows us to see their phone number","arguments":{"gigagroup":"Is this a broadcast group?"}},"privacyKeyAddedByPhone":{"comment":"Whether people can add you to their contact list by your phone number","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputPrivacyValueAllowContacts":{"comment":"Allow only contacts","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputPrivacyValueAllowAll":{"comment":"Allow all users","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputPrivacyValueAllowUsers":{"comment":"Allow only certain users","arguments":{"users":"Allowed users"}},"inputPrivacyValueDisallowContacts":{"comment":"Disallow only contacts","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputPrivacyValueDisallowAll":{"comment":"Disallow all","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputPrivacyValueDisallowUsers":{"comment":"Disallow only certain users","arguments":{"users":"Users to disallow"}},"inputPrivacyValueAllowChatParticipants":{"comment":"Allow only participants of certain chats","arguments":{"chats":"Allowed chat IDs"}},"inputPrivacyValueDisallowChatParticipants":{"comment":"Disallow only participants of certain chats","arguments":{"chats":"Disallowed chat IDs"}},"privacyValueAllowContacts":{"comment":"Allow all contacts","arguments":{"gigagroup":"Is this a broadcast group?"}},"privacyValueAllowAll":{"comment":"Allow all users","arguments":{"gigagroup":"Is this a broadcast group?"}},"privacyValueAllowUsers":{"comment":"Allow only certain users","arguments":{"users":"Allowed users"}},"privacyValueDisallowContacts":{"comment":"Disallow only contacts","arguments":{"gigagroup":"Is this a broadcast group?"}},"privacyValueDisallowAll":{"comment":"Disallow all users","arguments":{"gigagroup":"Is this a broadcast group?"}},"privacyValueDisallowUsers":{"comment":"Disallow only certain users","arguments":{"users":"Disallowed users"}},"privacyValueAllowChatParticipants":{"comment":"Allow all participants of certain chats","arguments":{"chats":"Allowed chats"}},"privacyValueDisallowChatParticipants":{"comment":"Disallow only participants of certain chats","arguments":{"chats":"Disallowed chats"}},"account.privacyRules":{"comment":"Privacy rules","arguments":{"rules":"Privacy rules","chats":"Chats to which the rules apply","users":"Users to which the rules apply"}},"accountDaysTTL":{"comment":"Time to live in days of the current account","arguments":{"days":"This account will self-destruct in the specified number of days"}},"documentAttributeImageSize":{"comment":"Defines the width and height of an image uploaded as document","arguments":{"w":"Width of image","h":"Height of image"}},"documentAttributeAnimated":{"comment":"Defines an animated GIF","arguments":{"gigagroup":"Is this a broadcast group?"}},"documentAttributeSticker":{"comment":"Defines a sticker","arguments":{"flags":"Flags, see TL conditional fields","mask":"Whether this is a mask sticker","alt":"Alternative emoji representation of sticker","stickerset":"Associated stickerset","maskCoords":"Mask coordinates (if this is a mask sticker, attached to a photo)"}},"documentAttributeVideo":{"comment":"Defines a video","arguments":{"flags":"Flags, see TL conditional fields","roundMessage":"Whether this is a round video","supportsStreaming":"Whether the video supports streaming","duration":"Duration in seconds","w":"Video width","h":"Video height"}},"documentAttributeAudio":{"comment":"Represents an audio file","arguments":{"flags":"Flags, see TL conditional fields","voice":"Whether this is a voice message","duration":"Duration in seconds","title":"Name of song","performer":"Performer","waveform":"Waveform"}},"documentAttributeFilename":{"comment":"A simple document with a file name","arguments":{"fileName":"The file name"}},"documentAttributeHasStickers":{"comment":"Whether the current document has stickers attached","arguments":{"gigagroup":"Is this a broadcast group?"}},"messages.stickersNotModified":{"comment":"No new stickers were found for the given query","arguments":{"gigagroup":"Is this a broadcast group?"}},"messages.stickers":{"comment":"Found stickers","arguments":{"hash":"Hash for pagination, for more info click here","stickers":"Stickers"}},"stickerPack":{"comment":"A stickerpack is a group of stickers associated to the same emoji.\nIt is not a sticker pack the way it is usually intended, you may be looking for a StickerSet.","arguments":{"emoticon":"Emoji","documents":"Stickers"}},"messages.allStickersNotModified":{"comment":"Info about all installed stickers hasn't changed","arguments":{"gigagroup":"Is this a broadcast group?"}},"messages.allStickers":{"comment":"Info about all installed stickers","arguments":{"hash":"Hash for pagination, for more info click here","sets":"All stickersets"}},"messages.affectedMessages":{"comment":"Events affected by operation","arguments":{"pts":"Event count after generation","ptsCount":"Number of events that were generated"}},"webPageEmpty":{"comment":"No preview is available for the webpage","arguments":{"id":"Preview ID"}},"webPagePending":{"comment":"A preview of the webpage is currently being generated","arguments":{"id":"ID of preview","date":"When was the processing started"}},"webPage":{"comment":"Webpage preview","arguments":{"flags":"Flags, see TL conditional fields","id":"Preview ID","url":"URL of previewed webpage","displayUrl":"Webpage URL to be displayed to the user","hash":"Hash for pagination, for more info click here","type":"Type of the web page. Can be: article, photo, audio, video, document, profile, app, or something else","siteName":"Short name of the site (e.g., Google Docs, App Store)","title":"Title of the content","description":"Content description","photo":"Image representing the content","embedUrl":"URL to show in the embedded preview","embedType":"MIME type of the embedded preview, (e.g., text/html or video/mp4)","embedWidth":"Width of the embedded preview","embedHeight":"Height of the embedded preview","duration":"Duration of the content, in seconds","author":"Author of the content","document":"Preview of the content as a media file","cachedPage":"Page contents in instant view format","attributes":"Webpage attributes"}},"webPageNotModified":{"comment":"The preview of the webpage hasn't changed","arguments":{"flags":"Flags, see TL conditional fields","cachedPageViews":"Page view count"}},"authorization":{"comment":"Logged-in session","arguments":{"flags":"Flags, see TL conditional fields","current":"Whether this is the current session","officialApp":"Whether the session is from an official app","passwordPending":"Whether the session is still waiting for a 2FA password","hash":"Identifier","deviceModel":"Device model","platform":"Platform","systemVersion":"System version","apiId":"API ID","appName":"App name","appVersion":"App version","dateCreated":"When was the session created","dateActive":"When was the session last active","ip":"Last known IP","country":"Country determined from IP","region":"Region determined from IP"}},"account.authorizations":{"comment":"Logged-in sessions","arguments":{"authorizations":"Logged-in sessions"}},"account.password":{"comment":"Configuration for two-factor authorization","arguments":{"flags":"Flags, see TL conditional fields","hasRecovery":"Whether the user has a recovery method configured","hasSecureValues":"Whether telegram passport is enabled","hasPassword":"Whether the user has a password","currentAlgo":"The KDF algorithm for SRP two-factor authentication of the current password","srpB":"Srp B param for SRP authorization","srpId":"Srp ID param for SRP authorization","hint":"Text hint for the password","emailUnconfirmedPattern":"A password recovery email with the specified pattern is still awaiting verification","newAlgo":"The KDF algorithm for SRP two-factor authentication to use when creating new passwords","newSecureAlgo":"The KDF algorithm for telegram passport","secureRandom":"Secure random string","pendingResetDate":"The 2FA password will be automatically removed at this date, unless the user cancels the operation"}},"account.passwordSettings":{"comment":"Private info associated to the password info (recovery email, telegram passport info & so on)","arguments":{"flags":"Flags, see TL conditional fields","email":"2FA Recovery email","secureSettings":"Telegram passport settings"}},"account.passwordInputSettings":{"comment":"Settings for setting up a new password","arguments":{"flags":"Flags, see TL conditional fields","newAlgo":"The SRP algorithm to use","newPasswordHash":"The computed password hash","hint":"Text hint for the password","email":"Password recovery email","newSecureSettings":"Telegram passport settings"}},"auth.passwordRecovery":{"comment":"Recovery info of a 2FA password, only for accounts with a recovery email configured.","arguments":{"emailPattern":"The email to which the recovery code was sent must match this pattern."}},"receivedNotifyMessage":{"comment":"Message ID, for which PUSH-notifications were cancelled.","arguments":{"id":"Message ID, for which PUSH-notifications were canceled","flags":"Reserved for future use"}},"chatInviteExported":{"comment":"Exported chat invite","arguments":{"flags":"Flags, see TL conditional fields","revoked":"Whether this chat invite was revoked","permanent":"Whether this chat invite has no expiration","link":"Chat invitation link","adminId":"ID of the admin that created this chat invite","date":"When was this chat invite created","startDate":"When was this chat invite last modified","expireDate":"When does this chat invite expire","usageLimit":"Maximum number of users that can join using this link","usage":"How many users joined using this link"}},"chatInviteAlready":{"comment":"The user has already joined this chat","arguments":{"chat":"The chat connected to the invite"}},"chatInvite":{"comment":"Chat invite info","arguments":{"flags":"Flags, see TL conditional fields","channel":"Whether this is a channel/supergroup or a normal group","broadcast":"Whether this is a channel","public":"Whether this is a public channel/supergroup","megagroup":"Whether this is a supergroup","title":"Chat/supergroup/channel title","photo":"Chat/supergroup/channel photo","participantsCount":"Participant count","participants":"A few of the participants that are in the group"}},"chatInvitePeek":{"comment":"A chat invitation that also allows peeking into the group to read messages without joining it.","arguments":{"chat":"Chat information","expires":"Read-only anonymous access to this group will be revoked at this date"}},"inputStickerSetEmpty":{"comment":"Empty constructor","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputStickerSetID":{"comment":"Stickerset by ID","arguments":{"id":"ID","accessHash":"Access hash"}},"inputStickerSetShortName":{"comment":"Stickerset by short name, from tg://addstickers?set=short_name","arguments":{"shortName":"From tg://addstickers?set=short_name"}},"inputStickerSetAnimatedEmoji":{"comment":"Animated emojis stickerset","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputStickerSetDice":{"comment":"Used for fetching animated dice stickers","arguments":{"emoticon":"The emoji, for now \"🏀\", \"🎲\" and \"🎯\" are supported"}},"inputStickerSetAnimatedEmojiAnimations":{"comment":"Animated emoji reaction stickerset (contains animations to play when a user clicks on a given animated emoji)","arguments":{"gigagroup":"Is this a broadcast group?"}},"stickerSet":{"comment":"Represents a stickerset (stickerpack)","arguments":{"flags":"Flags, see TL conditional fields","archived":"Whether this stickerset was archived (due to too many saved stickers in the current account)","official":"Is this stickerset official","masks":"Is this a mask stickerset","animated":"Is this an animated stickerpack","installedDate":"When was this stickerset installed","id":"ID of the stickerset","accessHash":"Access hash of stickerset","title":"Title of stickerset","shortName":"Short name of stickerset to use in tg://addstickers?set=short_name","thumbs":"Stickerset thumbnail","thumbDcId":"DC ID of thumbnail","thumbVersion":"Thumbnail version","count":"Number of stickers in pack","hash":"Hash"}},"messages.stickerSet":{"comment":"Stickerset and stickers inside it","arguments":{"set":"The stickerset","packs":"Emoji info for stickers","documents":"Stickers in stickerset"}},"botCommand":{"comment":"Describes a bot command that can be used in a chat","arguments":{"command":"/command name","description":"Description of the command"}},"botInfo":{"comment":"Info about bots (available bot commands, etc)","arguments":{"userId":"ID of the bot","description":"Description of the bot","commands":"Bot commands that can be used in the chat"}},"keyboardButton":{"comment":"Bot keyboard button","arguments":{"text":"Button text"}},"keyboardButtonUrl":{"comment":"URL button","arguments":{"text":"Button label","url":"URL"}},"keyboardButtonCallback":{"comment":"Callback button","arguments":{"flags":"Flags, see TL conditional fields","requiresPassword":"Whether the user should verify their identity by entering their 2FA SRP parameters to the {@link messages.getBotCallbackAnswer} method. NOTE: telegram and the bot WILL NOT have access to the plaintext password, thanks to SRP. This button is mainly used by the official @botfather bot, for verifying the user's identity before transferring ownership of a bot to another user.","text":"Button text","data":"Callback data"}},"keyboardButtonRequestPhone":{"comment":"Button to request a user's phone number","arguments":{"text":"Button text"}},"keyboardButtonRequestGeoLocation":{"comment":"Button to request a user's geolocation","arguments":{"text":"Button text"}},"keyboardButtonSwitchInline":{"comment":"Button to force a user to switch to inline mode Pressing the button will prompt the user to select one of their chats, open that chat and insert the bot‘s username and the specified inline query in the input field.","arguments":{"flags":"Flags, see TL conditional fields","samePeer":"If set, pressing the button will insert the bot‘s username and the specified inline query in the current chat's input field.","text":"Button label","query":"The inline query to use"}},"keyboardButtonGame":{"comment":"Button to start a game","arguments":{"text":"Button text"}},"keyboardButtonBuy":{"comment":"Button to buy a product","arguments":{"text":"Button text"}},"keyboardButtonUrlAuth":{"comment":"Button to request a user to authorize via URL using Seamless Telegram Login. When the user clicks on such a button, {@link messages.requestUrlAuth} should be called, providing the button_id and the ID of the container message. The returned {@link urlAuthResultRequest} object will contain more details about the authorization request (request_write_access if the bot would like to send messages to the user along with the username of the bot which will be used for user authorization). Finally, the user can choose to call {@link messages.acceptUrlAuth} to get a {@link urlAuthResultAccepted} with the URL to open instead of the url of this constructor, or a {@link urlAuthResultDefault}, in which case the url of this constructor must be opened, instead. If the user refuses the authorization request but still wants to open the link, the url of this constructor must be used.","arguments":{"flags":"Flags, see TL conditional fields","text":"Button label","fwdText":"New text of the button in forwarded messages.","url":"An HTTP URL to be opened with user authorization data added to the query string when the button is pressed. If the user refuses to provide authorization data, the original URL without information about the user will be opened. The data added is the same as described in Receiving authorization data.

NOTE: Services must always check the hash of the received data to verify the authentication and the integrity of the data as described in Checking authorization.","buttonId":"ID of the button to pass to {@link messages.requestUrlAuth}"}},"inputKeyboardButtonUrlAuth":{"comment":"Button to request a user to {@link messages.acceptUrlAuth} via URL using Seamless Telegram Login.","arguments":{"flags":"Flags, see TL conditional fields","requestWriteAccess":"Set this flag to request the permission for your bot to send messages to the user.","text":"Button text","fwdText":"New text of the button in forwarded messages.","url":"An HTTP URL to be opened with user authorization data added to the query string when the button is pressed. If the user refuses to provide authorization data, the original URL without information about the user will be opened. The data added is the same as described in Receiving authorization data.
NOTE: You must always check the hash of the received data to verify the authentication and the integrity of the data as described in Checking authorization.","bot":"Username of a bot, which will be used for user authorization. See Setting up a bot for more details. If not specified, the current bot's username will be assumed. The url's domain must be the same as the domain linked with the bot. See Linking your domain to the bot for more details."}},"keyboardButtonRequestPoll":{"comment":"A button that allows the user to create and send a poll when pressed; available only in private","arguments":{"flags":"Flags, see TL conditional fields","quiz":"If set, only quiz polls can be sent","text":"Button text"}},"keyboardButtonRow":{"comment":"Inline keyboard row","arguments":{"buttons":"Bot or inline keyboard buttons"}},"replyKeyboardHide":{"comment":"Hide sent bot keyboard","arguments":{"flags":"Flags, see TL conditional fields","selective":"Use this flag if you want to remove the keyboard for specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.

Example: A user votes in a poll, bot returns confirmation message in reply to the vote and removes the keyboard for that user, while still showing the keyboard with poll options to users who haven't voted yet"}},"replyKeyboardForceReply":{"comment":"Force the user to send a reply","arguments":{"flags":"Flags, see TL conditional fields","singleUse":"Requests clients to hide the keyboard as soon as it's been used. The keyboard will still be available, but clients will automatically display the usual letter-keyboard in the chat – the user can press a special button in the input field to see the custom keyboard again.","selective":"Use this parameter if you want to show the keyboard to specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.
Example: A user requests to change the bot‘s language, bot replies to the request with a keyboard to select the new language. Other users in the group don’t see the keyboard.","placeholder":"The placeholder to be shown in the input field when the keyboard is active; 1-64 characters."}},"replyKeyboardMarkup":{"comment":"Bot keyboard","arguments":{"flags":"Flags, see TL conditional fields","resize":"Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). If not set, the custom keyboard is always of the same height as the app's standard keyboard.","singleUse":"Requests clients to hide the keyboard as soon as it's been used. The keyboard will still be available, but clients will automatically display the usual letter-keyboard in the chat – the user can press a special button in the input field to see the custom keyboard again.","selective":"Use this parameter if you want to show the keyboard to specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.

Example: A user requests to change the bot‘s language, bot replies to the request with a keyboard to select the new language. Other users in the group don’t see the keyboard.","rows":"Button row","placeholder":"The placeholder to be shown in the input field when the keyboard is active; 1-64 characters."}},"replyInlineMarkup":{"comment":"Bot or inline keyboard","arguments":{"rows":"Bot or inline keyboard rows"}},"messageEntityUnknown":{"comment":"Unknown message entity","arguments":{"offset":"Offset of message entity within message (in UTF-8 codepoints)","length":"Length of message entity within message (in UTF-8 codepoints)"}},"messageEntityMention":{"comment":"Message entity mentioning the current user","arguments":{"offset":"Offset of message entity within message (in UTF-8 codepoints)","length":"Length of message entity within message (in UTF-8 codepoints)"}},"messageEntityHashtag":{"comment":"#hashtag message entity","arguments":{"offset":"Offset of message entity within message (in UTF-8 codepoints)","length":"Length of message entity within message (in UTF-8 codepoints)"}},"messageEntityBotCommand":{"comment":"Message entity representing a bot /command","arguments":{"offset":"Offset of message entity within message (in UTF-8 codepoints)","length":"Length of message entity within message (in UTF-8 codepoints)"}},"messageEntityUrl":{"comment":"Message entity representing an in-text url: https://google.com; for text urls, use {@link messageEntityTextUrl}.","arguments":{"offset":"Offset of message entity within message (in UTF-8 codepoints)","length":"Length of message entity within message (in UTF-8 codepoints)"}},"messageEntityEmail":{"comment":"Message entity representing an email@example.com.","arguments":{"offset":"Offset of message entity within message (in UTF-8 codepoints)","length":"Length of message entity within message (in UTF-8 codepoints)"}},"messageEntityBold":{"comment":"Message entity representing bold text.","arguments":{"offset":"Offset of message entity within message (in UTF-8 codepoints)","length":"Length of message entity within message (in UTF-8 codepoints)"}},"messageEntityItalic":{"comment":"Message entity representing italic text.","arguments":{"offset":"Offset of message entity within message (in UTF-8 codepoints)","length":"Length of message entity within message (in UTF-8 codepoints)"}},"messageEntityCode":{"comment":"Message entity representing a codeblock.","arguments":{"offset":"Offset of message entity within message (in UTF-8 codepoints)","length":"Length of message entity within message (in UTF-8 codepoints)"}},"messageEntityPre":{"comment":"Message entity representing a preformatted codeblock, allowing the user to specify a programming language for the codeblock.","arguments":{"offset":"Offset of message entity within message (in UTF-8 codepoints)","length":"Length of message entity within message (in UTF-8 codepoints)","language":"Programming language of the code"}},"messageEntityTextUrl":{"comment":"Message entity representing a text url: for in-text urls like https://google.com use {@link messageEntityUrl}.","arguments":{"offset":"Offset of message entity within message (in UTF-8 codepoints)","length":"Length of message entity within message (in UTF-8 codepoints)","url":"The actual URL"}},"messageEntityMentionName":{"comment":"Message entity representing a user mention: for creating a mention use {@link inputMessageEntityMentionName}.","arguments":{"offset":"Offset of message entity within message (in UTF-8 codepoints)","length":"Length of message entity within message (in UTF-8 codepoints)","userId":"Identifier of the user that was mentioned"}},"inputMessageEntityMentionName":{"comment":"Message entity that can be used to create a user user mention: received mentions use the {@link messageEntityMentionName} constructor, instead.","arguments":{"offset":"Offset of message entity within message (in UTF-8 codepoints)","length":"Length of message entity within message (in UTF-8 codepoints)","userId":"Identifier of the user that was mentioned"}},"messageEntityPhone":{"comment":"Message entity representing a phone number.","arguments":{"offset":"Offset of message entity within message (in UTF-8 codepoints)","length":"Length of message entity within message (in UTF-8 codepoints)"}},"messageEntityCashtag":{"comment":"Message entity representing a $cashtag.","arguments":{"offset":"Offset of message entity within message (in UTF-8 codepoints)","length":"Length of message entity within message (in UTF-8 codepoints)"}},"messageEntityUnderline":{"comment":"Message entity representing underlined text.","arguments":{"offset":"Offset of message entity within message (in UTF-8 codepoints)","length":"Length of message entity within message (in UTF-8 codepoints)"}},"messageEntityStrike":{"comment":"Message entity representing strikethrough text.","arguments":{"offset":"Offset of message entity within message (in UTF-8 codepoints)","length":"Length of message entity within message (in UTF-8 codepoints)"}},"messageEntityBlockquote":{"comment":"Message entity representing a block quote.","arguments":{"offset":"Offset of message entity within message (in UTF-8 codepoints)","length":"Length of message entity within message (in UTF-8 codepoints)"}},"messageEntityBankCard":{"comment":"Indicates a credit card number","arguments":{"offset":"Offset of message entity within message (in UTF-8 codepoints)","length":"Length of message entity within message (in UTF-8 codepoints)"}},"inputChannelEmpty":{"comment":"Represents the absence of a channel","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputChannel":{"comment":"Represents a channel","arguments":{"channelId":"Channel ID","accessHash":"Access hash taken from the {@link channel} constructor"}},"inputChannelFromMessage":{"comment":"Defines a min channel that was seen in a certain message of a certain chat.","arguments":{"peer":"The chat where the channel was seen","msgId":"The message ID in the chat where the channel was seen","channelId":"The channel ID"}},"contacts.resolvedPeer":{"comment":"Resolved peer","arguments":{"peer":"The peer","chats":"Chats","users":"Users"}},"messageRange":{"comment":"Indicates a range of chat messages","arguments":{"minId":"Start of range (message ID)","maxId":"End of range (message ID)"}},"updates.channelDifferenceEmpty":{"comment":"There are no new updates","arguments":{"flags":"Flags, see TL conditional fields","final":"Whether there are more updates that must be fetched (always false)","pts":"The latest PTS","timeout":"Clients are supposed to refetch the channel difference after timeout seconds have elapsed"}},"updates.channelDifferenceTooLong":{"comment":"The provided pts + limit < remote pts. Simply, there are too many updates to be fetched (more than limit), the client has to resolve the update gap in one of the following ways:","arguments":{"flags":"Flags, see TL conditional fields","final":"Whether there are more updates that must be fetched (always false)","timeout":"Clients are supposed to refetch the channel difference after timeout seconds have elapsed","dialog":"Dialog containing the latest PTS that can be used to reset the channel state","messages":"The latest messages","chats":"Chats from messages","users":"Users from messages"}},"updates.channelDifference":{"comment":"The new updates","arguments":{"flags":"Flags, see TL conditional fields","final":"Whether there are more updates to be fetched using getDifference, starting from the provided pts","pts":"The PTS from which to start getting updates the next time","timeout":"Clients are supposed to refetch the channel difference after timeout seconds have elapsed","newMessages":"New messages","otherUpdates":"Other updates","chats":"Chats","users":"Users"}},"channelMessagesFilterEmpty":{"comment":"No filter","arguments":{"gigagroup":"Is this a broadcast group?"}},"channelMessagesFilter":{"comment":"Filter for getting only certain types of channel messages","arguments":{"flags":"Flags, see TL conditional fields","excludeNewMessages":"Whether to exclude new messages from the search","ranges":"A range of messages to fetch"}},"channelParticipant":{"comment":"Channel/supergroup participant","arguments":{"userId":"Pariticipant user ID","date":"Date joined"}},"channelParticipantSelf":{"comment":"Myself","arguments":{"userId":"User ID","inviterId":"User that invited me to the channel/supergroup","date":"When did I join the channel/supergroup"}},"channelParticipantCreator":{"comment":"Channel/supergroup creator","arguments":{"flags":"Flags, see TL conditional fields","userId":"User ID","adminRights":"Creator admin rights","rank":"The role (rank) of the group creator in the group: just an arbitrary string, admin by default"}},"channelParticipantAdmin":{"comment":"Admin","arguments":{"flags":"Flags, see TL conditional fields","canEdit":"Can this admin promote other admins with the same permissions?","self":"Is this the current user","userId":"Admin user ID","inviterId":"User that invited the admin to the channel/group","promotedBy":"User that promoted the user to admin","date":"When did the user join","adminRights":"Admin rights","rank":"The role (rank) of the admin in the group: just an arbitrary string, admin by default"}},"channelParticipantBanned":{"comment":"Banned/kicked user","arguments":{"flags":"Flags, see TL conditional fields","left":"Whether the user has left the group","peer":"The banned peer","kickedBy":"User was kicked by the specified admin","date":"When did the user join the group","bannedRights":"Banned rights"}},"channelParticipantLeft":{"comment":"A participant that left the channel/supergroup","arguments":{"peer":"The peer that left"}},"channelParticipantsRecent":{"comment":"Fetch only recent participants","arguments":{"gigagroup":"Is this a broadcast group?"}},"channelParticipantsAdmins":{"comment":"Fetch only admin participants","arguments":{"gigagroup":"Is this a broadcast group?"}},"channelParticipantsKicked":{"comment":"Fetch only kicked participants","arguments":{"q":"Optional filter for searching kicked participants by name (otherwise empty)"}},"channelParticipantsBots":{"comment":"Fetch only bot participants","arguments":{"gigagroup":"Is this a broadcast group?"}},"channelParticipantsBanned":{"comment":"Fetch only banned participants","arguments":{"q":"Optional filter for searching banned participants by name (otherwise empty)"}},"channelParticipantsSearch":{"comment":"Query participants by name","arguments":{"q":"Search query"}},"channelParticipantsContacts":{"comment":"Fetch only participants that are also contacts","arguments":{"q":"Optional search query for searching contact participants by name"}},"channelParticipantsMentions":{"comment":"This filter is used when looking for supergroup members to mention.\nThis filter will automatically remove anonymous admins, and return even non-participant users that replied to a specific thread through the comment section of a channel.","arguments":{"flags":"Flags, see TL conditional fields","q":"Filter by user name or username","topMsgId":"Look only for users that posted in this thread"}},"channels.channelParticipants":{"comment":"Represents multiple channel participants","arguments":{"count":"Total number of participants that correspond to the given query","participants":"Participants","chats":"Mentioned chats","users":"Users mentioned in participant info"}},"channels.channelParticipantsNotModified":{"comment":"No new participant info could be found","arguments":{"gigagroup":"Is this a broadcast group?"}},"channels.channelParticipant":{"comment":"Represents a channel participant","arguments":{"participant":"The channel participant","chats":"Mentioned chats","users":"Users"}},"help.termsOfService":{"comment":"Info about the latest telegram Terms Of Service","arguments":{"flags":"Flags, see TL conditional fields","popup":"Whether a prompt must be showed to the user, in order to accept the new terms.","id":"ID of the new terms","text":"Text of the new terms","entities":"Message entities for styled text","minAgeConfirm":"Minimum age required to sign up to telegram, the user must confirm that they is older than the minimum age."}},"messages.savedGifsNotModified":{"comment":"No new saved gifs were found","arguments":{"gigagroup":"Is this a broadcast group?"}},"messages.savedGifs":{"comment":"Saved gifs","arguments":{"hash":"Hash for pagination, for more info click here","gifs":"List of saved gifs"}},"inputBotInlineMessageMediaAuto":{"comment":"A media","arguments":{"flags":"Flags, see TL conditional fields","message":"Caption","entities":"Message entities for styled text","replyMarkup":"Inline keyboard"}},"inputBotInlineMessageText":{"comment":"Simple text message","arguments":{"flags":"Flags, see TL conditional fields","noWebpage":"Disable webpage preview","message":"Message","entities":"Message entities for styled text","replyMarkup":"Inline keyboard"}},"inputBotInlineMessageMediaGeo":{"comment":"Geolocation","arguments":{"flags":"Flags, see TL conditional fields","geoPoint":"Geolocation","heading":"For live locations, a direction in which the location moves, in degrees; 1-360","period":"Validity period","proximityNotificationRadius":"For live locations, a maximum distance to another chat member for proximity alerts, in meters (0-100000)","replyMarkup":"Reply markup for bot/inline keyboards"}},"inputBotInlineMessageMediaVenue":{"comment":"Venue","arguments":{"flags":"Flags, see TL conditional fields","geoPoint":"Geolocation","title":"Venue name","address":"Address","provider":"Venue provider: currently only \"foursquare\" and \"gplaces\" need to be supported","venueId":"Venue ID in the provider's database","venueType":"Venue type in the provider's database","replyMarkup":"Inline keyboard"}},"inputBotInlineMessageMediaContact":{"comment":"A contact","arguments":{"flags":"Flags, see TL conditional fields","phoneNumber":"Phone number","firstName":"First name","lastName":"Last name","vcard":"VCard info","replyMarkup":"Inline keyboard"}},"inputBotInlineMessageGame":{"comment":"A game","arguments":{"flags":"Flags, see TL conditional fields","replyMarkup":"Inline keyboard"}},"inputBotInlineMessageMediaInvoice":{"comment":"An invoice","arguments":{"flags":"Flags, see TL conditional fields","title":"Product name, 1-32 characters","description":"Product description, 1-255 characters","photo":"Invoice photo","invoice":"The invoice","payload":"Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes.","provider":"Payments provider token, obtained via Botfather","providerData":"A JSON-serialized object for data about the invoice, which will be shared with the payment provider. A detailed description of the required fields should be provided by the payment provider.","replyMarkup":"Inline keyboard"}},"inputBotInlineResult":{"comment":"An inline bot result","arguments":{"flags":"Flags, see TL conditional fields","id":"ID of result","type":"Result type (see bot API docs)","title":"Result title","description":"Result description","url":"URL of result","thumb":"Thumbnail for result","content":"Result contents","sendMessage":"Message to send when the result is selected"}},"inputBotInlineResultPhoto":{"comment":"Photo","arguments":{"id":"Result ID","type":"Result type (see bot API docs)","photo":"Photo to send","sendMessage":"Message to send when the result is selected"}},"inputBotInlineResultDocument":{"comment":"Document (media of any type except for photos)","arguments":{"flags":"Flags, see TL conditional fields","id":"Result ID","type":"Result type (see bot API docs)","title":"Result title","description":"Result description","document":"Document to send","sendMessage":"Message to send when the result is selected"}},"inputBotInlineResultGame":{"comment":"Game","arguments":{"id":"Result ID","shortName":"Game short name","sendMessage":"Message to send when the result is selected"}},"botInlineMessageMediaAuto":{"comment":"Send whatever media is attached to the {@link botInlineMediaResult}","arguments":{"flags":"Flags, see TL conditional fields","message":"Caption","entities":"Message entities for styled text","replyMarkup":"Inline keyboard"}},"botInlineMessageText":{"comment":"Send a simple text message","arguments":{"flags":"Flags, see TL conditional fields","noWebpage":"Disable webpage preview","message":"The message","entities":"Message entities for styled text","replyMarkup":"Inline keyboard"}},"botInlineMessageMediaGeo":{"comment":"Send a geolocation","arguments":{"flags":"Flags, see TL conditional fields","geo":"Geolocation","heading":"For live locations, a direction in which the location moves, in degrees; 1-360.","period":"Validity period","proximityNotificationRadius":"For live locations, a maximum distance to another chat member for proximity alerts, in meters (0-100000).","replyMarkup":"Inline keyboard"}},"botInlineMessageMediaVenue":{"comment":"Send a venue","arguments":{"flags":"Flags, see TL conditional fields","geo":"Geolocation of venue","title":"Venue name","address":"Address","provider":"Venue provider: currently only \"foursquare\" and \"gplaces\" need to be supported","venueId":"Venue ID in the provider's database","venueType":"Venue type in the provider's database","replyMarkup":"Inline keyboard"}},"botInlineMessageMediaContact":{"comment":"Send a contact","arguments":{"flags":"Flags, see TL conditional fields","phoneNumber":"Phone number","firstName":"First name","lastName":"Last name","vcard":"VCard info","replyMarkup":"Inline keyboard"}},"botInlineMessageMediaInvoice":{"comment":"Send an invoice","arguments":{"flags":"Flags, see TL conditional fields","shippingAddressRequested":"Set this flag if you require the user's shipping address to complete the order","test":"Test invoice","title":"Product name, 1-32 characters","description":"Product description, 1-255 characters","photo":"Product photo","currency":"Three-letter ISO 4217 currency code","totalAmount":"Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).","replyMarkup":"Inline keyboard"}},"botInlineResult":{"comment":"Generic result","arguments":{"flags":"Flags, see TL conditional fields","id":"Result ID","type":"Result type (see bot API docs)","title":"Result title","description":"Result description","url":"URL of article or webpage","thumb":"Thumbnail for the result","content":"Content of the result","sendMessage":"Message to send"}},"botInlineMediaResult":{"comment":"Media result","arguments":{"flags":"Flags, see TL conditional fields","id":"Result ID","type":"Result type (see bot API docs)","photo":"If type is photo, the photo to send","document":"If type is document, the document to send","title":"Result title","description":"Description","sendMessage":"Depending on the type and on the constructor, contains the caption of the media or the content of the message to be sent instead of the media"}},"messages.botResults":{"comment":"Result of a query to an inline bot","arguments":{"flags":"Flags, see TL conditional fields","gallery":"Whether the result is a picture gallery","queryId":"Query ID","nextOffset":"The next offset to use when navigating through results","switchPm":"Whether the bot requested the user to message them in private","results":"The results","cacheTime":"Caching validity of the results","users":"Users mentioned in the results"}},"exportedMessageLink":{"comment":"Link to a message in a supergroup/channel","arguments":{"link":"URL","html":"Embed code"}},"messageFwdHeader":{"comment":"Info about a forwarded message","arguments":{"flags":"Flags, see TL conditional fields","imported":"Whether this message was imported from a foreign chat service, click here for more info »","fromId":"The ID of the user that originally sent the message","fromName":"The name of the user that originally sent the message","date":"When was the message originally sent","channelPost":"ID of the channel message that was forwarded","postAuthor":"For channels and if signatures are enabled, author of the channel message","savedFromPeer":"Only for messages forwarded to the current user (inputPeerSelf), full info about the user/channel that originally sent the message","savedFromMsgId":"Only for messages forwarded to the current user (inputPeerSelf), ID of the message that was forwarded from the original user/channel","psaType":"PSA type"}},"auth.codeTypeSms":{"comment":"Type of verification code that will be sent next if you call the resendCode method: SMS code","arguments":{"gigagroup":"Is this a broadcast group?"}},"auth.codeTypeCall":{"comment":"Type of verification code that will be sent next if you call the resendCode method: SMS code","arguments":{"gigagroup":"Is this a broadcast group?"}},"auth.codeTypeFlashCall":{"comment":"Type of verification code that will be sent next if you call the resendCode method: SMS code","arguments":{"gigagroup":"Is this a broadcast group?"}},"auth.sentCodeTypeApp":{"comment":"The code was sent through the telegram app","arguments":{"length":"Length of the code in bytes"}},"auth.sentCodeTypeSms":{"comment":"The code was sent via SMS","arguments":{"length":"Length of the code in bytes"}},"auth.sentCodeTypeCall":{"comment":"The code will be sent via a phone call: a synthesized voice will tell the user which verification code to input.","arguments":{"length":"Length of the verification code"}},"auth.sentCodeTypeFlashCall":{"comment":"The code will be sent via a flash phone call, that will be closed immediately. The phone code will then be the phone number itself, just make sure that the phone number matches the specified pattern.","arguments":{"pattern":"pattern to match"}},"messages.botCallbackAnswer":{"comment":"Callback answer sent by the bot in response to a button press","arguments":{"flags":"Flags, see TL conditional fields","alert":"Whether an alert should be shown to the user instead of a toast notification","hasUrl":"Whether an URL is present","nativeUi":"Whether to show games in WebView or in native UI.","message":"Alert to show","url":"URL to open","cacheTime":"For how long should this answer be cached"}},"messages.messageEditData":{"comment":"Message edit data for media","arguments":{"flags":"Flags, see TL conditional fields","caption":"Media caption, if the specified media's caption can be edited"}},"inputBotInlineMessageID":{"comment":"Represents a sent inline message from the perspective of a bot (legacy constructor)","arguments":{"dcId":"DC ID to use when working with this inline message","id":"ID of message, contains both the (32-bit, legacy) owner ID and the message ID, used only for Bot API backwards compatibility with 32-bit user ID.","accessHash":"Access hash of message"}},"inputBotInlineMessageID64":{"comment":"Represents a sent inline message from the perspective of a bot","arguments":{"dcId":"DC ID to use when working with this inline message","ownerId":"ID of the owner of this message","id":"ID of message","accessHash":"Access hash of message"}},"inlineBotSwitchPM":{"comment":"The bot requested the user to message them in private","arguments":{"text":"Text for the button that switches the user to a private chat with the bot and sends the bot a start message with the parameter start_parameter (can be empty)","startParam":"The parameter for the /start parameter"}},"messages.peerDialogs":{"comment":"Dialog info of multiple peers","arguments":{"dialogs":"Dialog info","messages":"Messages mentioned in dialog info","chats":"Chats","users":"Users","state":"Current update state of dialog"}},"topPeer":{"comment":"Top peer","arguments":{"peer":"Peer","rating":"Rating as computed in top peer rating »"}},"topPeerCategoryBotsPM":{"comment":"Most used bots","arguments":{"gigagroup":"Is this a broadcast group?"}},"topPeerCategoryBotsInline":{"comment":"Most used inline bots","arguments":{"gigagroup":"Is this a broadcast group?"}},"topPeerCategoryCorrespondents":{"comment":"Users we've chatted most frequently with","arguments":{"gigagroup":"Is this a broadcast group?"}},"topPeerCategoryGroups":{"comment":"Often-opened groups and supergroups","arguments":{"gigagroup":"Is this a broadcast group?"}},"topPeerCategoryChannels":{"comment":"Most frequently visited channels","arguments":{"gigagroup":"Is this a broadcast group?"}},"topPeerCategoryPhoneCalls":{"comment":"Most frequently called users","arguments":{"gigagroup":"Is this a broadcast group?"}},"topPeerCategoryForwardUsers":{"comment":"Users to which the users often forwards messages to","arguments":{"gigagroup":"Is this a broadcast group?"}},"topPeerCategoryForwardChats":{"comment":"Chats to which the users often forwards messages to","arguments":{"gigagroup":"Is this a broadcast group?"}},"topPeerCategoryPeers":{"comment":"Top peer category","arguments":{"category":"Top peer category of peers","count":"Count of peers","peers":"Peers"}},"contacts.topPeersNotModified":{"comment":"Top peer info hasn't changed","arguments":{"gigagroup":"Is this a broadcast group?"}},"contacts.topPeers":{"comment":"Top peers","arguments":{"categories":"Top peers by top peer category","chats":"Chats","users":"Users"}},"contacts.topPeersDisabled":{"comment":"Top peers disabled","arguments":{"gigagroup":"Is this a broadcast group?"}},"draftMessageEmpty":{"comment":"Empty draft","arguments":{"flags":"Flags, see TL conditional fields","date":"When was the draft last updated"}},"draftMessage":{"comment":"Represents a message draft.","arguments":{"flags":"Flags, see TL conditional fields","noWebpage":"Whether no webpage preview will be generated","replyToMsgId":"The message this message will reply to","message":"The draft","entities":"Message entities for styled text.","date":"Date of last update of the draft."}},"messages.featuredStickersNotModified":{"comment":"Featured stickers haven't changed","arguments":{"count":"Total number of featured stickers"}},"messages.featuredStickers":{"comment":"Featured stickersets","arguments":{"hash":"Hash for pagination, for more info click here","count":"Total number of featured stickers","sets":"Featured stickersets","unread":"IDs of new featured stickersets"}},"messages.recentStickersNotModified":{"comment":"No new recent sticker was found","arguments":{"gigagroup":"Is this a broadcast group?"}},"messages.recentStickers":{"comment":"Recently used stickers","arguments":{"hash":"Hash for pagination, for more info click here","packs":"Emojis associated to stickers","stickers":"Recent stickers","dates":"When was each sticker last used"}},"messages.archivedStickers":{"comment":"Archived stickersets","arguments":{"count":"Number of archived stickers","sets":"Archived stickersets"}},"messages.stickerSetInstallResultSuccess":{"comment":"The stickerset was installed successfully","arguments":{"gigagroup":"Is this a broadcast group?"}},"messages.stickerSetInstallResultArchive":{"comment":"The stickerset was installed, but since there are too many stickersets some were archived","arguments":{"sets":"Archived stickersets"}},"stickerSetCovered":{"comment":"Stickerset, with a specific sticker as preview","arguments":{"set":"Stickerset","cover":"Preview"}},"stickerSetMultiCovered":{"comment":"Stickerset, with a specific stickers as preview","arguments":{"set":"Stickerset","covers":"Preview stickers"}},"maskCoords":{"comment":"The n position indicates where the mask should be placed:\n\nPosition on a photo where a mask should be placed","arguments":{"n":"Part of the face, relative to which the mask should be placed","x":"Shift by X-axis measured in widths of the mask scaled to the face size, from left to right. (For example, -1.0 will place the mask just to the left of the default mask position)","y":"Shift by Y-axis measured in widths of the mask scaled to the face size, from left to right. (For example, -1.0 will place the mask just to the left of the default mask position)","zoom":"Mask scaling coefficient. (For example, 2.0 means a doubled size)"}},"inputStickeredMediaPhoto":{"comment":"A photo with stickers attached","arguments":{"id":"The photo"}},"inputStickeredMediaDocument":{"comment":"A document with stickers attached","arguments":{"id":"The document"}},"game":{"comment":"Indicates an already sent game","arguments":{"flags":"Flags, see TL conditional fields","id":"ID of the game","accessHash":"Access hash of the game","shortName":"Short name for the game","title":"Title of the game","description":"Game description","photo":"Game preview","document":"Optional attached document"}},"inputGameID":{"comment":"Indicates an already sent game","arguments":{"id":"game ID from Game constructor","accessHash":"access hash from Game constructor"}},"inputGameShortName":{"comment":"Game by short name","arguments":{"botId":"The bot that provides the game","shortName":"The game's short name"}},"highScore":{"comment":"Game highscore","arguments":{"pos":"Position in highscore list","userId":"User ID","score":"Score"}},"messages.highScores":{"comment":"Highscores in a game","arguments":{"scores":"Highscores","users":"Users, associated to the highscores"}},"textEmpty":{"comment":"Empty rich text element","arguments":{"gigagroup":"Is this a broadcast group?"}},"textPlain":{"comment":"Plain text","arguments":{"text":"Text"}},"textBold":{"comment":"Bold text","arguments":{"text":"Text"}},"textItalic":{"comment":"Italic text","arguments":{"text":"Text"}},"textUnderline":{"comment":"Underlined text","arguments":{"text":"Text"}},"textStrike":{"comment":"Strikethrough text","arguments":{"text":"Text"}},"textFixed":{"comment":"fixed-width rich text","arguments":{"text":"Text"}},"textUrl":{"comment":"Link","arguments":{"text":"Text of link","url":"Webpage HTTP URL","webpageId":"If a preview was already generated for the page, the page ID"}},"textEmail":{"comment":"Rich text email link","arguments":{"text":"Link text","email":"Email address"}},"textConcat":{"comment":"Concatenation of rich texts","arguments":{"texts":"Concatenated rich texts"}},"textSubscript":{"comment":"Subscript text","arguments":{"text":"Text"}},"textSuperscript":{"comment":"Superscript text","arguments":{"text":"Text"}},"textMarked":{"comment":"Highlighted text","arguments":{"text":"Text"}},"textPhone":{"comment":"Rich text linked to a phone number","arguments":{"text":"Text","phone":"Phone number"}},"textImage":{"comment":"Inline image","arguments":{"documentId":"Document ID","w":"Width","h":"Height"}},"textAnchor":{"comment":"Text linking to another section of the page","arguments":{"text":"Text","name":"Section name"}},"pageBlockUnsupported":{"comment":"Unsupported IV element","arguments":{"gigagroup":"Is this a broadcast group?"}},"pageBlockTitle":{"comment":"Title","arguments":{"text":"Title"}},"pageBlockSubtitle":{"comment":"Subtitle","arguments":{"text":"Text"}},"pageBlockAuthorDate":{"comment":"Author and date of creation of article","arguments":{"author":"Author name","publishedDate":"Date of pubblication"}},"pageBlockHeader":{"comment":"Page header","arguments":{"text":"Contents"}},"pageBlockSubheader":{"comment":"Subheader","arguments":{"text":"Subheader"}},"pageBlockParagraph":{"comment":"A paragraph","arguments":{"text":"Text"}},"pageBlockPreformatted":{"comment":"Preformatted (
 text)","arguments":{"text":"Text","language":"Programming language of preformatted text"}},"pageBlockFooter":{"comment":"Page footer","arguments":{"text":"Contents"}},"pageBlockDivider":{"comment":"An empty block separating a page","arguments":{"gigagroup":"Is this a broadcast group?"}},"pageBlockAnchor":{"comment":"Link to section within the page itself (like anchor)","arguments":{"name":"Name of target section"}},"pageBlockList":{"comment":"Unordered list of IV blocks","arguments":{"items":"List of blocks in an IV page"}},"pageBlockBlockquote":{"comment":"Quote (equivalent to the HTML 
)","arguments":{"text":"Quote contents","caption":"Caption"}},"pageBlockPullquote":{"comment":"Pullquote","arguments":{"text":"Text","caption":"Caption"}},"pageBlockPhoto":{"comment":"A photo","arguments":{"flags":"Flags, see TL conditional fields","photoId":"Photo ID","caption":"Caption","url":"HTTP URL of page the photo leads to when clicked","webpageId":"ID of preview of the page the photo leads to when clicked"}},"pageBlockVideo":{"comment":"Video","arguments":{"flags":"Flags, see TL conditional fields","autoplay":"Whether the video is set to autoplay","loop":"Whether the video is set to loop","videoId":"Video ID","caption":"Caption"}},"pageBlockCover":{"comment":"A page cover","arguments":{"cover":"Cover"}},"pageBlockEmbed":{"comment":"An embedded webpage","arguments":{"flags":"Flags, see TL conditional fields","fullWidth":"Whether the block should be full width","allowScrolling":"Whether scrolling should be allowed","url":"Web page URL, if available","html":"HTML-markup of the embedded page","posterPhotoId":"Poster photo, if available","w":"Block width, if known","h":"Block height, if known","caption":"Caption"}},"pageBlockEmbedPost":{"comment":"An embedded post","arguments":{"url":"Web page URL","webpageId":"ID of generated webpage preview","authorPhotoId":"ID of the author's photo","author":"Author name","date":"Creation date","blocks":"Post contents","caption":"Caption"}},"pageBlockCollage":{"comment":"Collage of media","arguments":{"items":"Media elements","caption":"Caption"}},"pageBlockSlideshow":{"comment":"Slideshow","arguments":{"items":"Slideshow items","caption":"Caption"}},"pageBlockChannel":{"comment":"Reference to a telegram channel","arguments":{"channel":"The channel/supergroup/chat"}},"pageBlockAudio":{"comment":"Audio","arguments":{"audioId":"Audio ID (to be fetched from the container {@link page} constructor","caption":"Audio caption"}},"pageBlockKicker":{"comment":"Kicker","arguments":{"text":"Contents"}},"pageBlockTable":{"comment":"Table","arguments":{"flags":"Flags, see TL conditional fields","bordered":"Does the table have a visible border?","striped":"Is the table striped?","title":"Title","rows":"Table rows"}},"pageBlockOrderedList":{"comment":"Ordered list of IV blocks","arguments":{"items":"List items"}},"pageBlockDetails":{"comment":"A collapsible details block","arguments":{"flags":"Flags, see TL conditional fields","open":"Whether the block is open by default","blocks":"Block contents","title":"Always visible heading for the block"}},"pageBlockRelatedArticles":{"comment":"Related articles","arguments":{"title":"Title","articles":"Related articles"}},"pageBlockMap":{"comment":"A map","arguments":{"geo":"Location of the map center","zoom":"Map zoom level; 13-20","w":"Map width in pixels before applying scale; 16-102","h":"Map height in pixels before applying scale; 16-1024","caption":"Caption"}},"phoneCallDiscardReasonMissed":{"comment":"The phone call was missed","arguments":{"gigagroup":"Is this a broadcast group?"}},"phoneCallDiscardReasonDisconnect":{"comment":"The phone call was disconnected","arguments":{"gigagroup":"Is this a broadcast group?"}},"phoneCallDiscardReasonHangup":{"comment":"The phone call was ended normally","arguments":{"gigagroup":"Is this a broadcast group?"}},"phoneCallDiscardReasonBusy":{"comment":"The phone call was discared because the user is busy in another call","arguments":{"gigagroup":"Is this a broadcast group?"}},"dataJSON":{"comment":"Represents a json-encoded object","arguments":{"data":"JSON-encoded object"}},"labeledPrice":{"comment":"This object represents a portion of the price for goods or services.","arguments":{"label":"Portion label","amount":"Price of the product in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies)."}},"invoice":{"comment":"Invoice","arguments":{"flags":"Flags, see TL conditional fields","test":"Test invoice","nameRequested":"Set this flag if you require the user's full name to complete the order","phoneRequested":"Set this flag if you require the user's phone number to complete the order","emailRequested":"Set this flag if you require the user's email address to complete the order","shippingAddressRequested":"Set this flag if you require the user's shipping address to complete the order","flexible":"Set this flag if the final price depends on the shipping method","phoneToProvider":"Set this flag if user's phone number should be sent to provider","emailToProvider":"Set this flag if user's email address should be sent to provider","currency":"Three-letter ISO 4217 currency code","prices":"Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)","maxTipAmount":"The maximum accepted amount for tips in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).","suggestedTipAmounts":"A vector of suggested amounts of tips in the smallest units of the currency (integer, not float/double). At most 4 suggested tip amounts can be specified. The suggested tip amounts must be positive, passed in a strictly increased order and must not exceed max_tip_amount."}},"paymentCharge":{"comment":"Payment identifier","arguments":{"id":"Telegram payment identifier","providerChargeId":"Provider payment identifier"}},"postAddress":{"comment":"Shipping address","arguments":{"streetLine1":"First line for the address","streetLine2":"Second line for the address","city":"City","state":"State, if applicable (empty otherwise)","countryIso2":"ISO 3166-1 alpha-2 country code","postCode":"Address post code"}},"paymentRequestedInfo":{"comment":"Order info provided by the user","arguments":{"flags":"Flags, see TL conditional fields","name":"User's full name","phone":"User's phone number","email":"User's email address","shippingAddress":"User's shipping address"}},"paymentSavedCredentialsCard":{"comment":"Saved credit card","arguments":{"id":"Card ID","title":"Title"}},"webDocument":{"comment":"Remote document","arguments":{"url":"Document URL","accessHash":"Access hash","size":"File size","mimeType":"MIME type","attributes":"Attributes for media types"}},"webDocumentNoProxy":{"comment":"Remote document that can be downloaded without proxying through telegram","arguments":{"url":"Document URL","size":"File size","mimeType":"MIME type","attributes":"Attributes for media types"}},"inputWebDocument":{"comment":"The document","arguments":{"url":"Remote document URL to be downloaded using the appropriate method","size":"Remote file size","mimeType":"Mime type","attributes":"Attributes for media types"}},"inputWebFileLocation":{"comment":"Location of a remote HTTP(s) file","arguments":{"url":"HTTP URL of file","accessHash":"Access hash"}},"inputWebFileGeoPointLocation":{"comment":"Geolocation","arguments":{"geoPoint":"Geolocation","accessHash":"Access hash","w":"Map width in pixels before applying scale; 16-1024","h":"Map height in pixels before applying scale; 16-1024","zoom":"Map zoom level; 13-20","scale":"Map scale; 1-3"}},"upload.webFile":{"comment":"Represents a chunk of an HTTP webfile downloaded through telegram's secure MTProto servers","arguments":{"size":"File size","mimeType":"Mime type","fileType":"File type","mtime":"Modified time","bytes":"Data"}},"payments.paymentForm":{"comment":"Payment form","arguments":{"flags":"Flags, see TL conditional fields","canSaveCredentials":"Whether the user can choose to save credentials.","passwordMissing":"Indicates that the user can save payment credentials, but only after setting up a 2FA password (currently the account doesn't have a 2FA password)","formId":"Form ID","botId":"Bot ID","invoice":"Invoice","providerId":"Payment provider ID.","url":"Payment form URL","nativeProvider":"Payment provider name.
One of the following:
- stripe","nativeParams":"Contains information about the payment provider, if available, to support it natively without the need for opening the URL.
A JSON object that can contain the following fields:

- apple_pay_merchant_id: Apple Pay merchant ID
- google_pay_public_key: Google Pay public key
- need_country: True, if the user country must be provided,
- need_zip: True, if the user ZIP/postal code must be provided,
- need_cardholder_name: True, if the cardholder name must be provided
","savedInfo":"Saved server-side order information","savedCredentials":"Contains information about saved card credentials","users":"Users"}},"payments.validatedRequestedInfo":{"comment":"Validated user-provided info","arguments":{"flags":"Flags, see TL conditional fields","id":"ID","shippingOptions":"Shipping options"}},"payments.paymentResult":{"comment":"Payment result","arguments":{"updates":"Info about the payment"}},"payments.paymentVerificationNeeded":{"comment":"Payment was not successful, additional verification is needed","arguments":{"url":"URL for additional payment credentials verification"}},"payments.paymentReceipt":{"comment":"Receipt","arguments":{"flags":"Flags, see TL conditional fields","date":"Date of generation","botId":"Bot ID","providerId":"Provider ID","title":"Title","description":"Description","photo":"Photo","invoice":"Invoice","info":"Info","shipping":"Selected shipping option","tipAmount":"Tipped amount","currency":"Three-letter ISO 4217 currency code","totalAmount":"Total amount in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).","credentialsTitle":"Payment credential name","users":"Users"}},"payments.savedInfo":{"comment":"Saved server-side order information","arguments":{"flags":"Flags, see TL conditional fields","hasSavedCredentials":"Whether the user has some saved payment credentials","savedInfo":"Saved server-side order information"}},"inputPaymentCredentialsSaved":{"comment":"Saved payment credentials","arguments":{"id":"Credential ID","tmpPassword":"Temporary password"}},"inputPaymentCredentials":{"comment":"Payment credentials","arguments":{"flags":"Flags, see TL conditional fields","save":"Save payment credential for future use","data":"Payment credentials"}},"inputPaymentCredentialsApplePay":{"comment":"Apple pay payment credentials","arguments":{"paymentData":"Payment data"}},"inputPaymentCredentialsGooglePay":{"comment":"Google Pay payment credentials","arguments":{"paymentToken":"Payment token"}},"account.tmpPassword":{"comment":"Temporary payment password","arguments":{"tmpPassword":"Temporary password","validUntil":"Validity period"}},"shippingOption":{"comment":"Shipping option","arguments":{"id":"Option ID","title":"Title","prices":"List of price portions"}},"inputStickerSetItem":{"comment":"Sticker in a stickerset","arguments":{"flags":"Flags, see TL conditional fields","document":"The sticker","emoji":"Associated emoji","maskCoords":"Coordinates for mask sticker"}},"inputPhoneCall":{"comment":"Phone call","arguments":{"id":"Call ID","accessHash":"Access hash"}},"phoneCallEmpty":{"comment":"Empty constructor","arguments":{"id":"Call ID"}},"phoneCallWaiting":{"comment":"Incoming phone call","arguments":{"flags":"Flags, see TL conditional fields","video":"Is this a video call","id":"Call ID","accessHash":"Access hash","date":"Date","adminId":"Admin ID","participantId":"Participant ID","protocol":"Phone call protocol info","receiveDate":"When was the phone call received"}},"phoneCallRequested":{"comment":"Requested phone call","arguments":{"flags":"Flags, see TL conditional fields","video":"Whether this is a video call","id":"Phone call ID","accessHash":"Access hash","date":"When was the phone call created","adminId":"ID of the creator of the phone call","participantId":"ID of the other participant of the phone call","gAHash":"Parameter for key exchange","protocol":"Call protocol info to be passed to libtgvoip"}},"phoneCallAccepted":{"comment":"An accepted phone call","arguments":{"flags":"Flags, see TL conditional fields","video":"Whether this is a video call","id":"ID of accepted phone call","accessHash":"Access hash of phone call","date":"When was the call accepted","adminId":"ID of the call creator","participantId":"ID of the other user in the call","gB":"B parameter for secure E2E phone call key exchange","protocol":"Protocol to use for phone call"}},"phoneCall":{"comment":"Phone call","arguments":{"flags":"Flags, see TL conditional fields","p2pAllowed":"Whether P2P connection to the other peer is allowed","video":"Whether this is a video call","id":"Call ID","accessHash":"Access hash","date":"Date of creation of the call","adminId":"User ID of the creator of the call","participantId":"User ID of the other participant in the call","gAOrB":"Parameter for key exchange","keyFingerprint":"Key fingerprint","protocol":"Call protocol info to be passed to libtgvoip","connections":"List of endpoints the user can connect to to exchange call data","startDate":"When was the call actually started"}},"phoneCallDiscarded":{"comment":"Indicates a discarded phone call","arguments":{"flags":"Flags, see TL conditional fields","needRating":"Whether the server required the user to {@link phone.setCallRating} the call","needDebug":"Whether the server required the client to {@link phone.saveCallDebug} the libtgvoip call debug data","video":"Whether the call was a video call","id":"Call ID","reason":"Why was the phone call discarded","duration":"Duration of the phone call in seconds"}},"phoneConnection":{"comment":"Identifies an endpoint that can be used to connect to the other user in a phone call","arguments":{"id":"Endpoint ID","ip":"IP address of endpoint","ipv6":"IPv6 address of endpoint","port":"Port ID","peerTag":"Our peer tag"}},"phoneConnectionWebrtc":{"comment":"WebRTC connection parameters","arguments":{"flags":"Flags, see TL conditional fields","turn":"Whether this is a TURN endpoint","stun":"Whether this is a STUN endpoint","id":"Endpoint ID","ip":"IP address","ipv6":"IPv6 address","port":"Port","username":"Username","password":"Password"}},"phoneCallProtocol":{"comment":"Protocol info for libtgvoip","arguments":{"flags":"Flags, see TL conditional fields","udpP2p":"Whether to allow P2P connection to the other participant","udpReflector":"Whether to allow connection to the other participants through the reflector servers","minLayer":"Minimum layer for remote libtgvoip","maxLayer":"Maximum layer for remote libtgvoip","libraryVersions":"When using {@link phone.requestCall} and {@link phone.acceptCall}, specify all library versions supported by the client.
The server will merge and choose the best library version supported by both peers, returning only the best value in the result of the callee's {@link phone.acceptCall} and in the {@link phoneCallAccepted} update received by the caller."}},"phone.phoneCall":{"comment":"A VoIP phone call","arguments":{"phoneCall":"The VoIP phone call","users":"VoIP phone call participants"}},"upload.cdnFileReuploadNeeded":{"comment":"The file was cleared from the temporary RAM cache of the CDN and has to be reuploaded.","arguments":{"requestToken":"Request token (see CDN)"}},"upload.cdnFile":{"comment":"Represent a chunk of a CDN file.","arguments":{"bytes":"The data"}},"cdnPublicKey":{"comment":"Public key to use only during handshakes to CDN DCs.","arguments":{"dcId":"CDN DC ID","publicKey":"RSA public key"}},"cdnConfig":{"comment":"Configuration for CDN file downloads.","arguments":{"publicKeys":"Vector of public keys to use only during handshakes to CDN DCs."}},"langPackString":{"comment":"Translated localization string","arguments":{"key":"Language key","value":"Value"}},"langPackStringPluralized":{"comment":"A language pack string which has different forms based on the number of some object it mentions. See https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html for more info","arguments":{"flags":"Flags, see TL conditional fields","key":"Localization key","zeroValue":"Value for zero objects","oneValue":"Value for one object","twoValue":"Value for two objects","fewValue":"Value for a few objects","manyValue":"Value for many objects","otherValue":"Default value"}},"langPackStringDeleted":{"comment":"Deleted localization string","arguments":{"key":"Localization key"}},"langPackDifference":{"comment":"Changes to the app's localization pack","arguments":{"langCode":"Language code","fromVersion":"Previous version number","version":"New version number","strings":"Localized strings"}},"langPackLanguage":{"comment":"Identifies a localization pack","arguments":{"flags":"Flags, see TL conditional fields","official":"Whether the language pack is official","rtl":"Is this a localization pack for an RTL language","beta":"Is this a beta localization pack?","name":"Language name","nativeName":"Language name in the language itself","langCode":"Language code (pack identifier)","baseLangCode":"Identifier of a base language pack; may be empty. If a string is missed in the language pack, then it should be fetched from base language pack. Unsupported in custom language packs","pluralCode":"A language code to be used to apply plural forms. See https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html for more info","stringsCount":"Total number of non-deleted strings from the language pack","translatedCount":"Total number of translated strings from the language pack","translationsUrl":"Link to language translation interface; empty for custom local language packs"}},"channelAdminLogEventActionChangeTitle":{"comment":"Channel/supergroup title was changed","arguments":{"prevValue":"Previous title","newValue":"New title"}},"channelAdminLogEventActionChangeAbout":{"comment":"The description was changed","arguments":{"prevValue":"Previous description","newValue":"New description"}},"channelAdminLogEventActionChangeUsername":{"comment":"Channel/supergroup username was changed","arguments":{"prevValue":"Old username","newValue":"New username"}},"channelAdminLogEventActionChangePhoto":{"comment":"The channel/supergroup's picture was changed","arguments":{"prevPhoto":"Previous picture","newPhoto":"New picture"}},"channelAdminLogEventActionToggleInvites":{"comment":"Invites were enabled/disabled","arguments":{"newValue":"New value"}},"channelAdminLogEventActionToggleSignatures":{"comment":"Channel signatures were enabled/disabled","arguments":{"newValue":"New value"}},"channelAdminLogEventActionUpdatePinned":{"comment":"A message was pinned","arguments":{"message":"The message that was pinned"}},"channelAdminLogEventActionEditMessage":{"comment":"A message was edited","arguments":{"prevMessage":"Old message","newMessage":"New message"}},"channelAdminLogEventActionDeleteMessage":{"comment":"A message was deleted","arguments":{"message":"The message that was deleted"}},"channelAdminLogEventActionParticipantJoin":{"comment":"A user has joined the group (in the case of big groups, info of the user that has joined isn't shown)","arguments":{"gigagroup":"Is this a broadcast group?"}},"channelAdminLogEventActionParticipantLeave":{"comment":"A user left the channel/supergroup (in the case of big groups, info of the user that has joined isn't shown)","arguments":{"gigagroup":"Is this a broadcast group?"}},"channelAdminLogEventActionParticipantInvite":{"comment":"A user was invited to the group","arguments":{"participant":"The user that was invited"}},"channelAdminLogEventActionParticipantToggleBan":{"comment":"The banned rights of a user were changed","arguments":{"prevParticipant":"Old banned rights of user","newParticipant":"New banned rights of user"}},"channelAdminLogEventActionParticipantToggleAdmin":{"comment":"The admin rights of a user were changed","arguments":{"prevParticipant":"Previous admin rights","newParticipant":"New admin rights"}},"channelAdminLogEventActionChangeStickerSet":{"comment":"The supergroup's stickerset was changed","arguments":{"prevStickerset":"Previous stickerset","newStickerset":"New stickerset"}},"channelAdminLogEventActionTogglePreHistoryHidden":{"comment":"The hidden prehistory setting was {@link channels.togglePreHistoryHidden}","arguments":{"newValue":"New value"}},"channelAdminLogEventActionDefaultBannedRights":{"comment":"The default banned rights were modified","arguments":{"prevBannedRights":"Previous global banned rights","newBannedRights":"New glboal banned rights."}},"channelAdminLogEventActionStopPoll":{"comment":"A poll was stopped","arguments":{"message":"The poll that was stopped"}},"channelAdminLogEventActionChangeLinkedChat":{"comment":"The linked chat was changed","arguments":{"prevValue":"Previous linked chat","newValue":"New linked chat"}},"channelAdminLogEventActionChangeLocation":{"comment":"The geo group location was changed","arguments":{"prevValue":"Previous location","newValue":"New location"}},"channelAdminLogEventActionToggleSlowMode":{"comment":"{@link channels.toggleSlowMode}","arguments":{"prevValue":"Previous slow mode value","newValue":"New slow mode value"}},"channelAdminLogEventActionStartGroupCall":{"comment":"A group call was started","arguments":{"call":"Group call"}},"channelAdminLogEventActionDiscardGroupCall":{"comment":"A group call was terminated","arguments":{"call":"The group call that was terminated"}},"channelAdminLogEventActionParticipantMute":{"comment":"A group call participant was muted","arguments":{"participant":"The participant that was muted"}},"channelAdminLogEventActionParticipantUnmute":{"comment":"A group call participant was unmuted","arguments":{"participant":"The participant that was unmuted"}},"channelAdminLogEventActionToggleGroupCallSetting":{"comment":"Group call settings were changed","arguments":{"joinMuted":"Whether all users are muted by default upon joining"}},"channelAdminLogEventActionParticipantJoinByInvite":{"comment":"A user joined the supergroup/channel using a specific invite link","arguments":{"invite":"The invite link used to join the supergroup/channel"}},"channelAdminLogEventActionExportedInviteDelete":{"comment":"A chat invite was deleted","arguments":{"invite":"The deleted chat invite"}},"channelAdminLogEventActionExportedInviteRevoke":{"comment":"A specific invite link was revoked","arguments":{"invite":"The invite link that was revoked"}},"channelAdminLogEventActionExportedInviteEdit":{"comment":"A chat invite was edited","arguments":{"prevInvite":"Previous chat invite information","newInvite":"New chat invite information"}},"channelAdminLogEventActionParticipantVolume":{"comment":"channelAdminLogEvent.user_id has set the volume of participant.peer to participant.volume","arguments":{"participant":"The participant whose volume was changed"}},"channelAdminLogEventActionChangeHistoryTTL":{"comment":"The Time-To-Live of messages in this chat was changed","arguments":{"prevValue":"Previous value","newValue":"New value"}},"channelAdminLogEvent":{"comment":"Admin log event","arguments":{"id":"Event ID","date":"Date","userId":"User ID","action":"Action"}},"channels.adminLogResults":{"comment":"Admin log events","arguments":{"events":"Admin log events","chats":"Chats mentioned in events","users":"Users mentioned in events"}},"channelAdminLogEventsFilter":{"comment":"Filter only certain admin log events","arguments":{"flags":"Flags, see TL conditional fields","join":"{@link channelAdminLogEventActionParticipantJoin}","leave":"{@link channelAdminLogEventActionParticipantLeave}","invite":"{@link channelAdminLogEventActionParticipantInvite}","ban":"{@link channelAdminLogEventActionParticipantToggleBan}","unban":"{@link channelAdminLogEventActionParticipantToggleBan}","kick":"{@link channelAdminLogEventActionParticipantToggleBan}","unkick":"{@link channelAdminLogEventActionParticipantToggleBan}","promote":"{@link channelAdminLogEventActionParticipantToggleAdmin}","demote":"{@link channelAdminLogEventActionParticipantToggleAdmin}","info":"Info change events (when {@link channelAdminLogEventActionChangeAbout}, {@link channelAdminLogEventActionChangeLinkedChat}, {@link channelAdminLogEventActionChangeLocation}, {@link channelAdminLogEventActionChangePhoto}, {@link channelAdminLogEventActionChangeStickerSet}, {@link channelAdminLogEventActionChangeTitle} or {@link channelAdminLogEventActionChangeUsername} data of a channel gets modified)","settings":"Settings change events ({@link channelAdminLogEventActionToggleInvites}, {@link channelAdminLogEventActionTogglePreHistoryHidden}, {@link channelAdminLogEventActionToggleSignatures}, {@link channelAdminLogEventActionDefaultBannedRights})","pinned":"{@link channelAdminLogEventActionUpdatePinned}","edit":"{@link channelAdminLogEventActionEditMessage}","delete":"{@link channelAdminLogEventActionDeleteMessage}","groupCall":"Group call events","invites":"Invite events"}},"popularContact":{"comment":"Popular contact","arguments":{"clientId":"Contact identifier","importers":"How many people imported this contact"}},"messages.favedStickersNotModified":{"comment":"No new favorited stickers were found","arguments":{"gigagroup":"Is this a broadcast group?"}},"messages.favedStickers":{"comment":"Favorited stickers","arguments":{"hash":"Hash for pagination, for more info click here","packs":"Emojis associated to stickers","stickers":"Favorited stickers"}},"recentMeUrlUnknown":{"comment":"Unknown t.me url","arguments":{"url":"URL"}},"recentMeUrlUser":{"comment":"Recent t.me link to a user","arguments":{"url":"URL","userId":"User ID"}},"recentMeUrlChat":{"comment":"Recent t.me link to a chat","arguments":{"url":"t.me URL","chatId":"Chat ID"}},"recentMeUrlChatInvite":{"comment":"Recent t.me invite link to a chat","arguments":{"url":"t.me URL","chatInvite":"Chat invitation"}},"recentMeUrlStickerSet":{"comment":"Recent t.me stickerset installation URL","arguments":{"url":"t.me URL","set":"Stickerset"}},"help.recentMeUrls":{"comment":"Recent t.me URLs","arguments":{"urls":"URLs","chats":"Chats","users":"Users"}},"inputSingleMedia":{"comment":"A single media in an album or grouped media sent with {@link messages.sendMultiMedia}.","arguments":{"flags":"Flags, see TL conditional fields","media":"The media","randomId":"Unique client media ID required to prevent message resending","message":"A caption for the media","entities":"Message entities for styled text"}},"webAuthorization":{"comment":"Represents a bot logged in using the Telegram login widget","arguments":{"hash":"Authorization hash","botId":"Bot ID","domain":"The domain name of the website on which the user has logged in.","browser":"Browser user-agent","platform":"Platform","dateCreated":"When was the web session created","dateActive":"When was the web session last active","ip":"IP address","region":"Region, determined from IP address"}},"account.webAuthorizations":{"comment":"Web authorizations","arguments":{"authorizations":"Web authorization list","users":"Users"}},"inputMessageID":{"comment":"Message by ID","arguments":{"id":"Message ID"}},"inputMessageReplyTo":{"comment":"Message to which the specified message replies to","arguments":{"id":"ID of the message that replies to the message we need"}},"inputMessagePinned":{"comment":"Pinned message","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputMessageCallbackQuery":{"comment":"Used by bots for fetching information about the message that originated a callback query","arguments":{"id":"Message ID","queryId":"Callback query ID"}},"inputDialogPeer":{"comment":"A peer","arguments":{"peer":"Peer"}},"inputDialogPeerFolder":{"comment":"All peers in a peer folder","arguments":{"folderId":"Peer folder ID, for more info click here"}},"dialogPeer":{"comment":"Peer","arguments":{"peer":"Peer"}},"dialogPeerFolder":{"comment":"Peer folder","arguments":{"folderId":"Peer folder ID, for more info click here"}},"messages.foundStickerSetsNotModified":{"comment":"No further results were found","arguments":{"gigagroup":"Is this a broadcast group?"}},"messages.foundStickerSets":{"comment":"Found stickersets","arguments":{"hash":"Hash for pagination, for more info click here","sets":"Found stickersets"}},"fileHash":{"comment":"SHA256 Hash of an uploaded file, to be checked for validity after download","arguments":{"offset":"Offset from where to start computing SHA-256 hash","limit":"Length","hash":"SHA-256 Hash of file chunk, to be checked for validity after download"}},"inputClientProxy":{"comment":"Info about an MTProxy used to connect.","arguments":{"address":"Proxy address","port":"Proxy port"}},"help.termsOfServiceUpdateEmpty":{"comment":"No changes were made to telegram's terms of service","arguments":{"expires":"New TOS updates will have to be queried using {@link help.getTermsOfServiceUpdate} in expires seconds"}},"help.termsOfServiceUpdate":{"comment":"Info about an update of telegram's terms of service. If the terms of service are declined, then the {@link account.deleteAccount} method should be called with the reason \"Decline ToS update\"","arguments":{"expires":"New TOS updates will have to be queried using {@link help.getTermsOfServiceUpdate} in expires seconds","termsOfService":"New terms of service"}},"inputSecureFileUploaded":{"comment":"Uploaded secure file, for more info see the passport docs »","arguments":{"id":"Secure file ID","parts":"Secure file part count","md5Checksum":"MD5 hash of encrypted uploaded file, to be checked server-side","fileHash":"File hash","secret":"Secret"}},"inputSecureFile":{"comment":"Preuploaded passport file, for more info see the passport docs »","arguments":{"id":"Secure file ID","accessHash":"Secure file access hash"}},"secureFileEmpty":{"comment":"Empty constructor","arguments":{"gigagroup":"Is this a broadcast group?"}},"secureFile":{"comment":"Secure passport file, for more info see the passport docs »","arguments":{"id":"ID","accessHash":"Access hash","size":"File size","dcId":"DC ID","date":"Date of upload","fileHash":"File hash","secret":"Secret"}},"secureData":{"comment":"Secure passport data, for more info see the passport docs »","arguments":{"data":"Data","dataHash":"Data hash","secret":"Secret"}},"securePlainPhone":{"comment":"Phone number to use in telegram passport: it must be verified, first ».","arguments":{"phone":"Phone number"}},"securePlainEmail":{"comment":"Email address to use in telegram passport: it must be verified, first ».","arguments":{"email":"Email address"}},"secureValueTypePersonalDetails":{"comment":"Personal details","arguments":{"gigagroup":"Is this a broadcast group?"}},"secureValueTypePassport":{"comment":"Passport","arguments":{"gigagroup":"Is this a broadcast group?"}},"secureValueTypeDriverLicense":{"comment":"Driver's license","arguments":{"gigagroup":"Is this a broadcast group?"}},"secureValueTypeIdentityCard":{"comment":"Identity card","arguments":{"gigagroup":"Is this a broadcast group?"}},"secureValueTypeInternalPassport":{"comment":"Internal passport","arguments":{"gigagroup":"Is this a broadcast group?"}},"secureValueTypeAddress":{"comment":"Address","arguments":{"gigagroup":"Is this a broadcast group?"}},"secureValueTypeUtilityBill":{"comment":"Utility bill","arguments":{"gigagroup":"Is this a broadcast group?"}},"secureValueTypeBankStatement":{"comment":"Bank statement","arguments":{"gigagroup":"Is this a broadcast group?"}},"secureValueTypeRentalAgreement":{"comment":"Rental agreement","arguments":{"gigagroup":"Is this a broadcast group?"}},"secureValueTypePassportRegistration":{"comment":"Internal registration passport","arguments":{"gigagroup":"Is this a broadcast group?"}},"secureValueTypeTemporaryRegistration":{"comment":"Temporary registration","arguments":{"gigagroup":"Is this a broadcast group?"}},"secureValueTypePhone":{"comment":"Phone","arguments":{"gigagroup":"Is this a broadcast group?"}},"secureValueTypeEmail":{"comment":"Email","arguments":{"gigagroup":"Is this a broadcast group?"}},"secureValue":{"comment":"Secure value","arguments":{"flags":"Flags, see TL conditional fields","type":"Secure passport value type","data":"Encrypted Telegram Passport element data","frontSide":"Encrypted passport file with the front side of the document","reverseSide":"Encrypted passport file with the reverse side of the document","selfie":"Encrypted passport file with a selfie of the user holding the document","translation":"Array of encrypted passport files with translated versions of the provided documents","files":"Array of encrypted passport files with photos the of the documents","plainData":"Plaintext verified passport data","hash":"Data hash"}},"inputSecureValue":{"comment":"Secure value, for more info see the passport docs »","arguments":{"flags":"Flags, see TL conditional fields","type":"Secure passport value type","data":"Encrypted Telegram Passport element data","frontSide":"Encrypted passport file with the front side of the document","reverseSide":"Encrypted passport file with the reverse side of the document","selfie":"Encrypted passport file with a selfie of the user holding the document","translation":"Array of encrypted passport files with translated versions of the provided documents","files":"Array of encrypted passport files with photos the of the documents","plainData":"Plaintext verified passport data"}},"secureValueHash":{"comment":"Secure value hash","arguments":{"type":"Secure value type","hash":"Hash"}},"secureValueErrorData":{"comment":"Represents an issue in one of the data fields that was provided by the user. The error is considered resolved when the field's value changes.","arguments":{"type":"The section of the user's Telegram Passport which has the error, one of {@link secureValueTypePersonalDetails}, {@link secureValueTypePassport}, {@link secureValueTypeDriverLicense}, {@link secureValueTypeIdentityCard}, {@link secureValueTypeInternalPassport}, {@link secureValueTypeAddress}","dataHash":"Data hash","field":"Name of the data field which has the error","text":"Error message"}},"secureValueErrorFrontSide":{"comment":"Represents an issue with the front side of a document. The error is considered resolved when the file with the front side of the document changes.","arguments":{"type":"One of {@link secureValueTypePassport}, {@link secureValueTypeDriverLicense}, {@link secureValueTypeIdentityCard}, {@link secureValueTypeInternalPassport}","fileHash":"File hash","text":"Error message"}},"secureValueErrorReverseSide":{"comment":"Represents an issue with the reverse side of a document. The error is considered resolved when the file with reverse side of the document changes.","arguments":{"type":"One of {@link secureValueTypeDriverLicense}, {@link secureValueTypeIdentityCard}","fileHash":"File hash","text":"Error message"}},"secureValueErrorSelfie":{"comment":"Represents an issue with the selfie with a document. The error is considered resolved when the file with the selfie changes.","arguments":{"type":"One of {@link secureValueTypePassport}, {@link secureValueTypeDriverLicense}, {@link secureValueTypeIdentityCard}, {@link secureValueTypeInternalPassport}","fileHash":"File hash","text":"Error message"}},"secureValueErrorFile":{"comment":"Represents an issue with a document scan. The error is considered resolved when the file with the document scan changes.","arguments":{"type":"One of {@link secureValueTypeUtilityBill}, {@link secureValueTypeBankStatement}, {@link secureValueTypeRentalAgreement}, {@link secureValueTypePassportRegistration}, {@link secureValueTypeTemporaryRegistration}","fileHash":"File hash","text":"Error message"}},"secureValueErrorFiles":{"comment":"Represents an issue with a list of scans. The error is considered resolved when the list of files containing the scans changes.","arguments":{"type":"One of {@link secureValueTypeUtilityBill}, {@link secureValueTypeBankStatement}, {@link secureValueTypeRentalAgreement}, {@link secureValueTypePassportRegistration}, {@link secureValueTypeTemporaryRegistration}","fileHash":"File hash","text":"Error message"}},"secureValueError":{"comment":"Secure value error","arguments":{"type":"Type of element which has the issue","hash":"Hash","text":"Error message"}},"secureValueErrorTranslationFile":{"comment":"Represents an issue with one of the files that constitute the translation of a document. The error is considered resolved when the file changes.","arguments":{"type":"One of {@link secureValueTypePersonalDetails}, {@link secureValueTypePassport}, {@link secureValueTypeDriverLicense}, {@link secureValueTypeIdentityCard}, {@link secureValueTypeInternalPassport}, {@link secureValueTypeUtilityBill}, {@link secureValueTypeBankStatement}, {@link secureValueTypeRentalAgreement}, {@link secureValueTypePassportRegistration}, {@link secureValueTypeTemporaryRegistration}","fileHash":"File hash","text":"Error message"}},"secureValueErrorTranslationFiles":{"comment":"Represents an issue with the translated version of a document. The error is considered resolved when a file with the document translation changes.","arguments":{"type":"One of {@link secureValueTypePersonalDetails}, {@link secureValueTypePassport}, {@link secureValueTypeDriverLicense}, {@link secureValueTypeIdentityCard}, {@link secureValueTypeInternalPassport}, {@link secureValueTypeUtilityBill}, {@link secureValueTypeBankStatement}, {@link secureValueTypeRentalAgreement}, {@link secureValueTypePassportRegistration}, {@link secureValueTypeTemporaryRegistration}","fileHash":"Hash","text":"Error message"}},"secureCredentialsEncrypted":{"comment":"Encrypted credentials required to decrypt telegram passport data.","arguments":{"data":"Encrypted JSON-serialized data with unique user's payload, data hashes and secrets required for EncryptedPassportElement decryption and authentication, as described in decrypting data »","hash":"Data hash for data authentication as described in decrypting data »","secret":"Secret, encrypted with the bot's public RSA key, required for data decryption as described in decrypting data »"}},"account.authorizationForm":{"comment":"Telegram Passport authorization form","arguments":{"flags":"Flags, see TL conditional fields","requiredTypes":"Required Telegram Passport documents","values":"Already submitted Telegram Passport documents","errors":"Telegram Passport errors","users":"Info about the bot to which the form will be submitted","privacyPolicyUrl":"URL of the service's privacy policy"}},"account.sentEmailCode":{"comment":"The sent email code","arguments":{"emailPattern":"The email (to which the code was sent) must match this pattern","length":"The length of the verification code"}},"help.deepLinkInfoEmpty":{"comment":"Deep link info empty","arguments":{"gigagroup":"Is this a broadcast group?"}},"help.deepLinkInfo":{"comment":"Deep linking info","arguments":{"flags":"Flags, see TL conditional fields","updateApp":"An update of the app is required to parse this link","message":"Message to show to the user","entities":"Message entities for styled text"}},"savedPhoneContact":{"comment":"Saved contact","arguments":{"phone":"Phone number","firstName":"First name","lastName":"Last name","date":"Date added"}},"account.takeout":{"comment":"Takout info","arguments":{"id":"Takeout ID"}},"passwordKdfAlgoUnknown":{"comment":"Unknown KDF (most likely, the client is outdated and does not support the specified KDF algorithm)","arguments":{"gigagroup":"Is this a broadcast group?"}},"passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow":{"comment":"This key derivation algorithm defines that SRP 2FA login must be used","arguments":{"salt1":"One of two salts used by the derivation function (see SRP 2FA login)","salt2":"One of two salts used by the derivation function (see SRP 2FA login)","g":"Base (see SRP 2FA login)","p":"2048-bit modulus (see SRP 2FA login)"}},"securePasswordKdfAlgoUnknown":{"comment":"Unknown KDF algo (most likely the client has to be updated)","arguments":{"gigagroup":"Is this a broadcast group?"}},"securePasswordKdfAlgoPBKDF2HMACSHA512iter100000":{"comment":"PBKDF2 with SHA512 and 100000 iterations KDF algo","arguments":{"salt":"Salt"}},"securePasswordKdfAlgoSHA512":{"comment":"SHA512 KDF algo","arguments":{"salt":"Salt"}},"secureSecretSettings":{"comment":"Secure settings","arguments":{"secureAlgo":"Secure KDF algo","secureSecret":"Secure secret","secureSecretId":"Secret ID"}},"inputCheckPasswordEmpty":{"comment":"There is no password","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputCheckPasswordSRP":{"comment":"Constructor for checking the validity of a 2FA SRP password (see SRP)","arguments":{"srpId":"SRP ID","A":"A parameter (see SRP)","M1":"M1 parameter (see SRP)"}},"secureRequiredType":{"comment":"Required type","arguments":{"flags":"Flags, see TL conditional fields","nativeNames":"Native names","selfieRequired":"Is a selfie required","translationRequired":"Is a translation required","type":"Secure value type"}},"secureRequiredTypeOneOf":{"comment":"One of","arguments":{"types":"Secure required value types"}},"help.passportConfigNotModified":{"comment":"Password configuration not modified","arguments":{"gigagroup":"Is this a broadcast group?"}},"help.passportConfig":{"comment":"Telegram passport configuration","arguments":{"hash":"Hash for pagination, for more info click here","countriesLangs":"Localization"}},"inputAppEvent":{"comment":"Event that occured in the application.","arguments":{"time":"Client's exact timestamp for the event","type":"Type of event","peer":"Arbitrary numeric value for more convenient selection of certain event types, or events referring to a certain object","data":"Details of the event"}},"jsonObjectValue":{"comment":"JSON key: value pair","arguments":{"key":"Key","value":"Value"}},"jsonNull":{"comment":"null JSON value","arguments":{"gigagroup":"Is this a broadcast group?"}},"jsonBool":{"comment":"JSON boolean value","arguments":{"value":"Value"}},"jsonNumber":{"comment":"JSON numeric value","arguments":{"value":"Value"}},"jsonString":{"comment":"JSON string","arguments":{"value":"Value"}},"jsonArray":{"comment":"JSON array","arguments":{"value":"JSON values"}},"jsonObject":{"comment":"JSON object value","arguments":{"value":"Values"}},"pageTableCell":{"comment":"Table cell","arguments":{"flags":"Flags, see TL conditional fields","header":"Is this element part of the column header","alignCenter":"Horizontally centered block","alignRight":"Right-aligned block","valignMiddle":"Vertically centered block","valignBottom":"Block vertically-alligned to the bottom","text":"Content","colspan":"For how many columns should this cell extend","rowspan":"For how many rows should this cell extend"}},"pageTableRow":{"comment":"Table row","arguments":{"cells":"Table cells"}},"pageCaption":{"comment":"Page caption","arguments":{"text":"Caption","credit":"Credits"}},"pageListItemText":{"comment":"List item","arguments":{"text":"Text"}},"pageListItemBlocks":{"comment":"List item","arguments":{"blocks":"Blocks"}},"pageListOrderedItemText":{"comment":"Ordered list of text items","arguments":{"num":"Number of element within ordered list","text":"Text"}},"pageListOrderedItemBlocks":{"comment":"Ordered list of IV blocks","arguments":{"num":"Number of element within ordered list","blocks":"Item contents"}},"pageRelatedArticle":{"comment":"Related article","arguments":{"flags":"Flags, see TL conditional fields","url":"URL of article","webpageId":"Webpage ID of generated IV preview","title":"Title","description":"Description","photoId":"ID of preview photo","author":"Author name","publishedDate":"Date of pubblication"}},"page":{"comment":"Instant view page","arguments":{"flags":"Flags, see TL conditional fields","part":"Indicates that not full page preview is available to the client and it will need to fetch full Instant View from the server using {@link messages.getWebPagePreview}.","rtl":"Whether the page contains RTL text","v2":"Whether this is an IV v2 page","url":"Original page HTTP URL","blocks":"Page elements (like with HTML elements, only as TL constructors)","photos":"Photos in page","documents":"Media in page","views":"Viewcount"}},"help.supportName":{"comment":"Localized name for telegram support","arguments":{"name":"Localized name"}},"help.userInfoEmpty":{"comment":"Internal use","arguments":{"gigagroup":"Is this a broadcast group?"}},"help.userInfo":{"comment":"Internal use","arguments":{"message":"Info","entities":"Message entities for styled text","author":"Author","date":"Date"}},"pollAnswer":{"comment":"A possible answer of a poll","arguments":{"text":"Textual representation of the answer","option":"The param that has to be passed to {@link messages.sendVote}."}},"poll":{"comment":"Poll","arguments":{"id":"ID of the poll","flags":"Flags, see TL conditional fields","closed":"Whether the poll is closed and doesn't accept any more answers","publicVoters":"Whether cast votes are publicly visible to all users (non-anonymous poll)","multipleChoice":"Whether multiple options can be chosen as answer","quiz":"Whether this is a quiz (with wrong and correct answers, results shown in the return type)","question":"The question of the poll","answers":"The possible answers, vote using {@link messages.sendVote}.","closePeriod":"Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with close_date.","closeDate":"Point in time (UNIX timestamp in seconds) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future; can't be used together with close_period."}},"pollAnswerVoters":{"comment":"A poll answer, and how users voted on it","arguments":{"flags":"Flags, see TL conditional fields","chosen":"Whether we have chosen this answer","correct":"For quizes, whether the option we have chosen is correct","option":"The param that has to be passed to {@link messages.sendVote}.","voters":"How many users voted for this option"}},"pollResults":{"comment":"Results of poll","arguments":{"flags":"Flags, see TL conditional fields","min":"Similar to min objects, used for poll constructors that are the same for all users so they don't have option chosen by the current user (you can use {@link messages.getPollResults} to get the full poll results).","results":"Poll results","totalVoters":"Total number of people that voted in the poll","recentVoters":"IDs of the last users that recently voted in the poll","solution":"Explanation of quiz solution","solutionEntities":"Message entities for styled text in quiz solution"}},"chatOnlines":{"comment":"Number of online users in a chat","arguments":{"onlines":"Number of online users"}},"statsURL":{"comment":"URL with chat statistics","arguments":{"url":"Chat statistics"}},"chatAdminRights":{"comment":"Represents the rights of an admin in a channel/supergroup.","arguments":{"flags":"Flags, see TL conditional fields","changeInfo":"If set, allows the admin to modify the description of the channel/supergroup","postMessages":"If set, allows the admin to post messages in the channel","editMessages":"If set, allows the admin to also edit messages from other admins in the channel","deleteMessages":"If set, allows the admin to also delete messages from other admins in the channel","banUsers":"If set, allows the admin to ban users from the channel/supergroup","inviteUsers":"If set, allows the admin to invite users in the channel/supergroup","pinMessages":"If set, allows the admin to pin messages in the channel/supergroup","addAdmins":"If set, allows the admin to add other admins with the same (or more limited) permissions in the channel/supergroup","anonymous":"Whether this admin is anonymous","manageCall":"If set, allows the admin to change group call/livestream settings","other":"Set this flag if none of the other flags are set, but you stil want the user to be an admin."}},"chatBannedRights":{"comment":"Represents the rights of a normal user in a supergroup/channel/chat. In this case, the flags are inverted: if set, a flag does not allow a user to do X.","arguments":{"flags":"Flags, see TL conditional fields","viewMessages":"If set, does not allow a user to view messages in a supergroup/channel/chat","sendMessages":"If set, does not allow a user to send messages in a supergroup/chat","sendMedia":"If set, does not allow a user to send any media in a supergroup/chat","sendStickers":"If set, does not allow a user to send stickers in a supergroup/chat","sendGifs":"If set, does not allow a user to send gifs in a supergroup/chat","sendGames":"If set, does not allow a user to send games in a supergroup/chat","sendInline":"If set, does not allow a user to use inline bots in a supergroup/chat","embedLinks":"If set, does not allow a user to embed links in the messages of a supergroup/chat","sendPolls":"If set, does not allow a user to send stickers in a supergroup/chat","changeInfo":"If set, does not allow any user to change the description of a supergroup/chat","inviteUsers":"If set, does not allow any user to invite users in a supergroup/chat","pinMessages":"If set, does not allow any user to pin messages in a supergroup/chat","untilDate":"Validity of said permissions (it is considered forever any value less then 30 seconds or more then 366 days)."}},"inputWallPaper":{"comment":"Wallpaper","arguments":{"id":"Wallpaper ID","accessHash":"Access hash"}},"inputWallPaperSlug":{"comment":"Wallpaper by slug (a unique ID)","arguments":{"slug":"Unique wallpaper ID"}},"inputWallPaperNoFile":{"comment":"Wallpaper with no file access hash, used for example when deleting (unsave=true) wallpapers using {@link account.saveWallPaper}, specifying just the wallpaper ID.","arguments":{"id":"Wallpaper ID"}},"account.wallPapersNotModified":{"comment":"No new wallpapers were found","arguments":{"gigagroup":"Is this a broadcast group?"}},"account.wallPapers":{"comment":"Installed wallpapers","arguments":{"hash":"Hash for pagination, for more info click here","wallpapers":"Wallpapers"}},"codeSettings":{"comment":"Example implementations: telegram for android, tdlib.\n\nSettings used by telegram servers for sending the confirm code.","arguments":{"flags":"Flags, see TL conditional fields","allowFlashcall":"Whether to allow phone verification via phone calls.","currentNumber":"Pass true if the phone number is used on the current device. Ignored if allow_flashcall is not set.","allowAppHash":"If a token that will be included in eventually sent SMSs is required: required in newer versions of android, to use the android SMS receiver APIs"}},"wallPaperSettings":{"comment":"Wallpaper settings","arguments":{"flags":"Flags, see TL conditional fields","blur":"If set, the wallpaper must be downscaled to fit in 450x450 square and then box-blurred with radius 12","motion":"If set, the background needs to be slightly moved when device is rotated","backgroundColor":"If set, a PNG pattern is to be combined with the color chosen by the user: the main color of the background in RGB24 format","secondBackgroundColor":"If set, a PNG pattern is to be combined with the first and second background colors (RGB24 format) in a top-bottom gradient","thirdBackgroundColor":"If set, a PNG pattern is to be combined with the first, second and third background colors (RGB24 format) in a freeform gradient","fourthBackgroundColor":"If set, a PNG pattern is to be combined with the first, second, third and fourth background colors (RGB24 format) in a freeform gradient","intensity":"Intensity of the pattern when it is shown above the main background color, 0-100","rotation":"Clockwise rotation angle of the gradient, in degrees; 0-359. Should be always divisible by 45"}},"autoDownloadSettings":{"comment":"Autodownload settings","arguments":{"flags":"Flags, see TL conditional fields","disabled":"Disable automatic media downloads?","videoPreloadLarge":"Whether to preload the first seconds of videos larger than the specified limit","audioPreloadNext":"Whether to preload the next audio track when you're listening to music","phonecallsLessData":"Whether to enable data saving mode in phone calls","photoSizeMax":"Maximum size of photos to preload","videoSizeMax":"Maximum size of videos to preload","fileSizeMax":"Maximum size of other files to preload","videoUploadMaxbitrate":"Maximum suggested bitrate for uploading videos"}},"account.autoDownloadSettings":{"comment":"Media autodownload settings","arguments":{"low":"Low data usage preset","medium":"Medium data usage preset","high":"High data usage preset"}},"emojiKeyword":{"comment":"Emoji keyword","arguments":{"keyword":"Keyword","emoticons":"Emojis associated to keyword"}},"emojiKeywordDeleted":{"comment":"Deleted emoji keyword","arguments":{"keyword":"Keyword","emoticons":"Emojis that were associated to keyword"}},"emojiKeywordsDifference":{"comment":"Changes to emoji keywords","arguments":{"langCode":"Language code for keywords","fromVersion":"Previous emoji keyword list version","version":"Current version of emoji keyword list","keywords":"Emojis associated to keywords"}},"emojiURL":{"comment":"An HTTP URL which can be used to automatically log in into translation platform and suggest new emoji replacements. The URL will be valid for 30 seconds after generation","arguments":{"url":"An HTTP URL which can be used to automatically log in into translation platform and suggest new emoji replacements. The URL will be valid for 30 seconds after generation"}},"emojiLanguage":{"comment":"Emoji language","arguments":{"langCode":"Language code"}},"folder":{"comment":"Folder","arguments":{"flags":"Flags, see TL conditional fields","autofillNewBroadcasts":"Automatically add new channels to this folder","autofillPublicGroups":"Automatically add joined new public supergroups to this folder","autofillNewCorrespondents":"Automatically add new private chats to this folder","id":"Folder ID","title":"Folder title","photo":"Folder picture"}},"inputFolderPeer":{"comment":"Peer in a folder","arguments":{"peer":"Peer","folderId":"Peer folder ID, for more info click here"}},"folderPeer":{"comment":"Peer in a folder","arguments":{"peer":"Folder peer info","folderId":"Peer folder ID, for more info click here"}},"messages.searchCounter":{"comment":"Indicates how many results would be found by a {@link messages.search} call with the same parameters","arguments":{"flags":"Flags, see TL conditional fields","inexact":"If set, the results may be inexact","filter":"Provided message filter","count":"Number of results that were found server-side"}},"urlAuthResultRequest":{"comment":"Details about the authorization request, for more info click here »","arguments":{"flags":"Flags, see TL conditional fields","requestWriteAccess":"Whether the bot would like to send messages to the user","bot":"Username of a bot, which will be used for user authorization. If not specified, the current bot's username will be assumed. The url's domain must be the same as the domain linked with the bot. See Linking your domain to the bot for more details.","domain":"The domain name of the website on which the user will log in."}},"urlAuthResultAccepted":{"comment":"Details about an accepted authorization request, for more info click here »","arguments":{"url":"The URL name of the website on which the user has logged in."}},"urlAuthResultDefault":{"comment":"Details about an accepted authorization request, for more info click here »","arguments":{"gigagroup":"Is this a broadcast group?"}},"channelLocationEmpty":{"comment":"No location (normal supergroup)","arguments":{"gigagroup":"Is this a broadcast group?"}},"channelLocation":{"comment":"Geographical location of supergroup (geogroups)","arguments":{"geoPoint":"Geographical location of supergrup","address":"Textual description of the address"}},"peerLocated":{"comment":"Peer geolocated nearby","arguments":{"peer":"Peer","expires":"Validity period of current data","distance":"Distance from the peer in meters"}},"peerSelfLocated":{"comment":"Current peer","arguments":{"expires":"Expiry of geolocation info for current peer"}},"restrictionReason":{"comment":"Contains the reason why access to a certain object must be restricted. Clients are supposed to deny access to the channel if the platform field is equal to all or to the current platform (ios, android, wp, etc.). Platforms can be concatenated (ios-android, ios-wp), unknown platforms are to be ignored. The text is the error message that should be shown to the user.\n\nRestriction reason.","arguments":{"platform":"Platform identifier (ios, android, wp, all, etc.), can be concatenated with a dash as separator (android-ios, ios-wp, etc)","reason":"Restriction reason (porno, terms, etc.)","text":"Error message to be shown to the user"}},"inputTheme":{"comment":"Theme","arguments":{"id":"ID","accessHash":"Access hash"}},"inputThemeSlug":{"comment":"Theme by theme ID","arguments":{"slug":"Unique theme ID"}},"theme":{"comment":"Theme","arguments":{"flags":"Flags, see TL conditional fields","creator":"Whether the current user is the creator of this theme","default":"Whether this is the default theme","forChat":"Whether this theme is meant to be used as a chat theme","id":"Theme ID","accessHash":"Theme access hash","slug":"Unique theme ID","title":"Theme name","document":"Theme","settings":"Theme settings","installsCount":"Installation count"}},"account.themesNotModified":{"comment":"No new themes were installed","arguments":{"gigagroup":"Is this a broadcast group?"}},"account.themes":{"comment":"Installed themes","arguments":{"hash":"Hash for pagination, for more info click here","themes":"Themes"}},"auth.loginToken":{"comment":"Login token (for QR code login)","arguments":{"expires":"Expiry date of QR code","token":"Token to render in QR code"}},"auth.loginTokenMigrateTo":{"comment":"Repeat the query to the specified DC","arguments":{"dcId":"DC ID","token":"Token to use for login"}},"auth.loginTokenSuccess":{"comment":"Login via token (QR code) succeded!","arguments":{"authorization":"Authorization info"}},"account.contentSettings":{"comment":"Sensitive content settings","arguments":{"flags":"Flags, see TL conditional fields","sensitiveEnabled":"Whether viewing of sensitive (NSFW) content is enabled","sensitiveCanChange":"Whether the current client can change the sensitive content settings to view NSFW content"}},"messages.inactiveChats":{"comment":"Inactive chat list","arguments":{"dates":"When was the chat last active","chats":"Chat list","users":"Users mentioned in the chat list"}},"baseThemeClassic":{"comment":"Classic theme","arguments":{"gigagroup":"Is this a broadcast group?"}},"baseThemeDay":{"comment":"Day theme","arguments":{"gigagroup":"Is this a broadcast group?"}},"baseThemeNight":{"comment":"Night theme","arguments":{"gigagroup":"Is this a broadcast group?"}},"baseThemeTinted":{"comment":"Tinted theme","arguments":{"gigagroup":"Is this a broadcast group?"}},"baseThemeArctic":{"comment":"Arctic theme","arguments":{"gigagroup":"Is this a broadcast group?"}},"inputThemeSettings":{"comment":"Theme settings","arguments":{"flags":"Flags, see TL conditional fields","messageColorsAnimated":"If set, the freeform gradient fill needs to be animated on every sent message","baseTheme":"Default theme on which this theme is based","accentColor":"Accent color, ARGB format","outboxAccentColor":"Accent color of outgoing messages in ARGB format","messageColors":"The fill to be used as a background for outgoing messages, in RGB24 format.
If just one or two equal colors are provided, describes a solid fill of a background.
If two different colors are provided, describes the top and bottom colors of a 0-degree gradient.
If three or four colors are provided, describes a freeform gradient fill of a background.","wallpaper":"Wallpaper","wallpaperSettings":"Wallpaper settings"}},"themeSettings":{"comment":"Theme settings","arguments":{"flags":"Flags, see TL conditional fields","messageColorsAnimated":"If set, the freeform gradient fill needs to be animated on every sent message.","baseTheme":"Base theme","accentColor":"Accent color, ARGB format","outboxAccentColor":"Accent color of outgoing messages in ARGB format","messageColors":"The fill to be used as a background for outgoing messages, in RGB24 format.
If just one or two equal colors are provided, describes a solid fill of a background.
If two different colors are provided, describes the top and bottom colors of a 0-degree gradient.
If three or four colors are provided, describes a freeform gradient fill of a background.","wallpaper":"Wallpaper"}},"webPageAttributeTheme":{"comment":"Page theme","arguments":{"flags":"Flags, see TL conditional fields","documents":"Theme files","settings":"Theme settings"}},"messageUserVote":{"comment":"How a user voted in a poll","arguments":{"userId":"User ID","option":"The option chosen by the user","date":"When did the user cast the vote"}},"messageUserVoteInputOption":{"comment":"How a user voted in a poll (reduced constructor, returned if an option was provided to {@link messages.getPollVotes})","arguments":{"userId":"The user that voted for the queried option","date":"When did the user cast the vote"}},"messageUserVoteMultiple":{"comment":"How a user voted in a multiple-choice poll","arguments":{"userId":"User ID","options":"Options chosen by the user","date":"When did the user cast their votes"}},"messages.votesList":{"comment":"How users voted in a poll","arguments":{"flags":"Flags, see TL conditional fields","count":"Total number of votes for all options (or only for the chosen option, if provided to {@link messages.getPollVotes})","votes":"Vote info for each user","users":"Info about users that voted in the poll","nextOffset":"Offset to use with the next {@link messages.getPollVotes} request, empty string if no more results are available."}},"bankCardOpenUrl":{"comment":"Credit card info URL provided by the bank","arguments":{"url":"Info URL","name":"Bank name"}},"payments.bankCardData":{"comment":"Credit card info, provided by the card's bank(s)","arguments":{"title":"Credit card title","openUrls":"Info URL(s) provided by the card's bank(s)"}},"dialogFilter":{"comment":"Dialog filter AKA folder","arguments":{"flags":"Flags, see TL conditional fields","contacts":"Whether to include all contacts in this folder","nonContacts":"Whether to include all non-contacts in this folder","groups":"Whether to include all groups in this folder","broadcasts":"Whether to include all channels in this folder","bots":"Whether to include all bots in this folder","excludeMuted":"Whether to exclude muted chats from this folder","excludeRead":"Whether to exclude read chats from this folder","excludeArchived":"Whether to exclude archived chats from this folder","id":"Folder ID","title":"Folder name","emoticon":"Folder emoticon","pinnedPeers":"Pinned chats, folders can have unlimited pinned chats","includePeers":"Include the following chats in this folder","excludePeers":"Exclude the following chats from this folder"}},"dialogFilterSuggested":{"comment":"Suggested folders","arguments":{"filter":"Folder info","description":"Folder description"}},"statsDateRangeDays":{"comment":"Channel statistics date range","arguments":{"minDate":"Initial date","maxDate":"Final date"}},"statsAbsValueAndPrev":{"comment":"Statistics value couple; initial and final value for period of time currently in consideration","arguments":{"current":"Current value","previous":"Previous value"}},"statsPercentValue":{"comment":"Channel statistics percentage.\nCompute the percentage simply by doing part * total / 100","arguments":{"part":"Partial value","total":"Total value"}},"statsGraphAsync":{"comment":"This channel statistics graph must be generated asynchronously using {@link stats.loadAsyncGraph} to reduce server load","arguments":{"token":"Token to use for fetching the async graph"}},"statsGraphError":{"comment":"An error occurred while generating the statistics graph","arguments":{"error":"The error"}},"statsGraph":{"comment":"Channel statistics graph","arguments":{"flags":"Flags, see TL conditional fields","json":"Statistics data","zoomToken":"Zoom token"}},"messageInteractionCounters":{"comment":"Message interaction counters","arguments":{"msgId":"Message ID","views":"Views","forwards":"Number of times this message was forwarded"}},"stats.broadcastStats":{"comment":"Channel statistics.","arguments":{"period":"Period in consideration","followers":"Follower count change for period in consideration","viewsPerPost":"total_viewcount/postcount, for posts posted during the period in consideration (views_per_post).
Note that in this case, current refers to the period in consideration (min_date till max_date), and prev refers to the previous period ((min_date - (max_date - min_date)) till min_date).","sharesPerPost":"total_viewcount/postcount, for posts posted during the period in consideration (views_per_post).
Note that in this case, current refers to the period in consideration (min_date till max_date), and prev refers to the previous period ((min_date - (max_date - min_date)) till min_date)","enabledNotifications":"Percentage of subscribers with enabled notifications","growthGraph":"Channel growth graph (absolute subscriber count)","followersGraph":"Followers growth graph (relative subscriber count)","muteGraph":"Muted users graph (relative)","topHoursGraph":"Views per hour graph (absolute)","interactionsGraph":"Interactions graph (absolute)","ivInteractionsGraph":"IV interactions graph (absolute)","viewsBySourceGraph":"Views by source graph (absolute)","newFollowersBySourceGraph":"New followers by source graph (absolute)","languagesGraph":"Subscriber language graph (piechart)","recentMessageInteractions":"Recent message interactions"}},"help.promoDataEmpty":{"comment":"No PSA/MTProxy info is available","arguments":{"expires":"Re-fetch PSA/MTProxy info after the specified number of seconds"}},"help.promoData":{"comment":"MTProxy/Public Service Announcement information","arguments":{"flags":"Flags, see TL conditional fields","proxy":"MTProxy-related channel","expires":"Expiry of PSA/MTProxy info","peer":"MTProxy/PSA peer","chats":"Chat info","users":"User info","psaType":"PSA type","psaMessage":"PSA message"}},"videoSize":{"comment":"Animated profile picture in MPEG4 format","arguments":{"flags":"Flags, see TL conditional fields","type":"u for animated profile pictures, and v for trimmed and downscaled video previews","w":"Video width","h":"Video height","size":"File size","videoStartTs":"Timestamp that should be shown as static preview to the user (seconds)"}},"statsGroupTopPoster":{"comment":"Information about an active user in a supergroup","arguments":{"userId":"User ID","messages":"Number of messages for statistics period in consideration","avgChars":"Average number of characters per message"}},"statsGroupTopAdmin":{"comment":"Information about an active admin in a supergroup","arguments":{"userId":"User ID","deleted":"Number of deleted messages for statistics period in consideration","kicked":"Number of kicked users for statistics period in consideration","banned":"Number of banned users for statistics period in consideration"}},"statsGroupTopInviter":{"comment":"Information about an active supergroup inviter","arguments":{"userId":"User ID","invitations":"Number of invitations for statistics period in consideration"}},"stats.megagroupStats":{"comment":"Supergroup statistics","arguments":{"period":"Period in consideration","members":"Member count change for period in consideration","messages":"Message number change for period in consideration","viewers":"Number of users that viewed messages, for range in consideration","posters":"Number of users that posted messages, for range in consideration","growthGraph":"Supergroup growth graph (absolute subscriber count)","membersGraph":"Members growth (relative subscriber count)","newMembersBySourceGraph":"New members by source graph","languagesGraph":"Subscriber language graph (piechart)","messagesGraph":"Message activity graph (stacked bar graph, message type)","actionsGraph":"Group activity graph (deleted, modified messages, blocked users)","topHoursGraph":"Activity per hour graph (absolute)","weekdaysGraph":"Activity per day of week graph (absolute)","topPosters":"Info about most active group members","topAdmins":"Info about most active group admins","topInviters":"Info about most active group inviters","users":"Info about users mentioned in statistics"}},"globalPrivacySettings":{"comment":"Global privacy settings","arguments":{"flags":"Flags, see TL conditional fields","archiveAndMuteNewNoncontactPeers":"Whether to archive and mute new chats from non-contacts"}},"help.countryCode":{"comment":"Country code and phone number pattern of a specific country","arguments":{"flags":"Flags, see TL conditional fields","countryCode":"ISO country code","prefixes":"Possible phone prefixes","patterns":"Phone patterns: for example, XXX XXX XXX"}},"help.country":{"comment":"Name, ISO code, localized name and phone codes/patterns of a specific country","arguments":{"flags":"Flags, see TL conditional fields","hidden":"Whether this country should not be shown in the list","iso2":"ISO code of country","defaultName":"Name of the country in the country's language","name":"Name of the country in the user's language, if different from the original name","countryCodes":"Phone codes/patterns"}},"help.countriesListNotModified":{"comment":"The country list has not changed","arguments":{"gigagroup":"Is this a broadcast group?"}},"help.countriesList":{"comment":"Name, ISO code, localized name and phone codes/patterns of all available countries","arguments":{"countries":"Name, ISO code, localized name and phone codes/patterns of all available countries","hash":"Hash for pagination, for more info click here"}},"messageViews":{"comment":"View, forward counter + info about replies of a specific message","arguments":{"flags":"Flags, see TL conditional fields","views":"Viewcount of message","forwards":"Forward count of message","replies":"Reply and thread information of message"}},"messages.messageViews":{"comment":"View, forward counter + info about replies","arguments":{"views":"View, forward counter + info about replies","chats":"Chats mentioned in constructor","users":"Users mentioned in constructor"}},"messages.discussionMessage":{"comment":"Information about a message thread","arguments":{"flags":"Flags, see TL conditional fields","messages":"Discussion messages","maxId":"Message ID of latest reply in this thread","readInboxMaxId":"Message ID of latest read incoming message in this thread","readOutboxMaxId":"Message ID of latest read outgoing message in this thread","unreadCount":"Number of unread messages","chats":"Chats mentioned in constructor","users":"Users mentioned in constructor"}},"messageReplyHeader":{"comment":"Message replies and thread information","arguments":{"flags":"Flags, see TL conditional fields","replyToMsgId":"ID of message to which this message is replying","replyToPeerId":"For replies sent in channel discussion threads of which the current user is not a member, the discussion group ID","replyToTopId":"ID of the message that started this message thread"}},"messageReplies":{"comment":"Info about the comment section of a channel post, or a simple message thread","arguments":{"flags":"Flags, see TL conditional fields","comments":"Whether this constructor contains information about the comment section of a channel post, or a simple message thread","replies":"Contains the total number of replies in this thread or comment section.","repliesPts":"PTS of the message that started this thread.","recentRepliers":"For channel post comments, contains information about the last few comment posters for a specific thread, to show a small list of commenter profile pictures in client previews.","channelId":"For channel post comments, contains the ID of the associated discussion supergroup","maxId":"ID of the latest message in this thread or comment section.","readMaxId":"Contains the ID of the latest read message in this thread or comment section."}},"peerBlocked":{"comment":"Information about a blocked peer","arguments":{"peerId":"Peer ID","date":"When was the peer blocked"}},"stats.messageStats":{"comment":"Message statistics","arguments":{"viewsGraph":"Message view graph"}},"groupCallDiscarded":{"comment":"An ended group call","arguments":{"id":"Group call ID","accessHash":"Group call access hash","duration":"Group call duration"}},"groupCall":{"comment":"Info about a group call or livestream","arguments":{"flags":"Flags, see TL conditional fields","joinMuted":"Whether the user should be muted upon joining the call","canChangeJoinMuted":"Whether the current user can change the value of the join_muted flag using {@link phone.toggleGroupCallSettings}","joinDateAsc":"Specifies the ordering to use when locally sorting by date and displaying in the UI group call participants.","scheduleStartSubscribed":"Whether we subscribed to the scheduled call","canStartVideo":"Whether you can start streaming video into the call","recordVideoActive":"Whether the group call is currently being recorded","id":"Group call ID","accessHash":"Group call access hash","participantsCount":"Participant count","title":"Group call title","streamDcId":"DC ID to be used for livestream chunks","recordStartDate":"When was the recording started","scheduleDate":"When is the call scheduled to start","unmutedVideoCount":"Number of people currently streaming video into the call","unmutedVideoLimit":"Maximum number of people allowed to stream video into the call","version":"Version"}},"inputGroupCall":{"comment":"Points to a specific group call","arguments":{"id":"Group call ID","accessHash":"Group call access hash"}},"groupCallParticipant":{"comment":"Info about a group call participant","arguments":{"flags":"Flags, see TL conditional fields","muted":"Whether the participant is muted","left":"Whether the participant has left","canSelfUnmute":"Whether the participant can unmute themselves","justJoined":"Whether the participant has just joined","versioned":"If set, and {@link updateGroupCallParticipants}.version < locally stored call.version, info about this participant should be ignored. If (...), and {@link updateGroupCallParticipants}.version > call.version+1, the participant list should be refetched using {@link phone.getGroupParticipants}.","min":"If not set, the volume and muted_by_you fields can be safely used to overwrite locally cached information; otherwise, volume will contain valid information only if volume_by_admin is set both in the cache and in the received constructor.","mutedByYou":"Whether this participant was muted by the current user","volumeByAdmin":"Whether our volume can only changed by an admin","self":"Whether this participant is the current user","videoJoined":"Whether this participant is currently broadcasting video","peer":"Peer information","date":"When did this participant join the group call","activeDate":"When was this participant last active in the group call","source":"Source ID","volume":"Volume, if not set the volume is set to 100%.","about":"Info about this participant","raiseHandRating":"Specifies the UI visualization order of peers with raised hands: peers with a higher rating should be showed first in the list.","video":"Info about the video stream the participant is currently broadcasting","presentation":"Info about the screen sharing stream the participant is currently broadcasting"}},"phone.groupCall":{"comment":"Contains info about a group call, and partial info about its participants.","arguments":{"call":"Info about the group call","participants":"A partial list of participants.","participantsNextOffset":"Next offset to use when fetching the remaining participants using {@link phone.getGroupParticipants}","chats":"Chats mentioned in the participants vector","users":"Users mentioned in the participants vector"}},"phone.groupParticipants":{"comment":"Info about the participants of a group call or livestream","arguments":{"count":"Number of participants","participants":"List of participants","nextOffset":"If not empty, the specified list of participants is partial, and more participants can be fetched specifying this parameter as offset in {@link phone.getGroupParticipants}.","chats":"Mentioned chats","users":"Mentioned users","version":"Version info"}},"inlineQueryPeerTypeSameBotPM":{"comment":"The inline query was sent in a private chat with the bot itself","arguments":{"gigagroup":"Is this a broadcast group?"}},"inlineQueryPeerTypePM":{"comment":"The inline query was sent in a private chat","arguments":{"gigagroup":"Is this a broadcast group?"}},"inlineQueryPeerTypeChat":{"comment":"The inline query was sent in a chat","arguments":{"gigagroup":"Is this a broadcast group?"}},"inlineQueryPeerTypeMegagroup":{"comment":"The inline query was sent in a supergroup","arguments":{"gigagroup":"Is this a broadcast group?"}},"inlineQueryPeerTypeBroadcast":{"comment":"The inline query was sent in a channel","arguments":{"gigagroup":"Is this a broadcast group?"}},"messages.historyImport":{"comment":"ID of a specific chat import session, click here for more info ».","arguments":{"id":"History import ID"}},"messages.historyImportParsed":{"comment":"Contains information about a chat export file generated by a foreign chat app, click here for more info.\nIf neither the pm or group flags are set, the specified chat export was generated from a chat of unknown type.","arguments":{"flags":"Flags, see TL conditional fields","pm":"The chat export file was generated from a private chat.","group":"The chat export file was generated from a group chat.","title":"Title of the chat."}},"messages.affectedFoundMessages":{"comment":"Messages found and affected by changes","arguments":{"pts":"Event count after generation","ptsCount":"Number of events that were generated","offset":"If bigger than zero, the request must be repeated to remove more messages","messages":"Affected message IDs"}},"chatInviteImporter":{"comment":"When and which user joined the chat using a chat invite","arguments":{"userId":"The user","date":"When did the user join"}},"messages.exportedChatInvites":{"comment":"Info about chat invites exported by a certain admin.","arguments":{"count":"Number of invites exported by the admin","invites":"Exported invites","users":"Info about the admin"}},"messages.exportedChatInvite":{"comment":"Info about a chat invite","arguments":{"invite":"Info about the chat invite","users":"Mentioned users"}},"messages.exportedChatInviteReplaced":{"comment":"The specified chat invite was replaced with another one","arguments":{"invite":"The replaced chat invite","newInvite":"The invite that replaces the previous invite","users":"Mentioned users"}},"messages.chatInviteImporters":{"comment":"Info about the users that joined the chat using a specific chat invite","arguments":{"count":"Number of users that joined","importers":"The users that joined","users":"The users that joined"}},"chatAdminWithInvites":{"comment":"Info about chat invites generated by admins.","arguments":{"adminId":"The admin","invitesCount":"Number of invites generated by the admin","revokedInvitesCount":"Number of revoked invites"}},"messages.chatAdminsWithInvites":{"comment":"Info about chat invites generated by admins.","arguments":{"admins":"Info about chat invites generated by admins.","users":"Mentioned users"}},"messages.checkedHistoryImportPeer":{"comment":"Contains a confirmation text to be shown to the user, upon importing chat history, click here for more info ».","arguments":{"confirmText":"A confirmation text to be shown to the user, upon importing chat history »."}},"phone.joinAsPeers":{"comment":"A list of peers that can be used to join a group call, presenting yourself as a specific user/channel.","arguments":{"peers":"Peers","chats":"Chats mentioned in the peers vector","users":"Users mentioned in the peers vector"}},"phone.exportedGroupCallInvite":{"comment":"An invite to a group call or livestream","arguments":{"link":"Invite link"}},"groupCallParticipantVideoSourceGroup":{"comment":"Describes a group of video synchronization source identifiers","arguments":{"semantics":"SDP semantics","sources":"Source IDs"}},"groupCallParticipantVideo":{"comment":"Info about a video stream","arguments":{"flags":"Flags, see TL conditional fields","paused":"Whether the stream is currently paused","endpoint":"Endpoint","sourceGroups":"Source groups","audioSource":"Audio source ID"}},"stickers.suggestedShortName":{"comment":"A suggested short name for a stickerpack","arguments":{"shortName":"Suggested short name"}},"botCommandScopeDefault":{"comment":"The commands will be valid in all dialogs","arguments":{"gigagroup":"Is this a broadcast group?"}},"botCommandScopeUsers":{"comment":"The specified bot commands will only be valid in all private chats with users.","arguments":{"gigagroup":"Is this a broadcast group?"}},"botCommandScopeChats":{"comment":"The specified bot commands will be valid in all groups and supergroups.","arguments":{"gigagroup":"Is this a broadcast group?"}},"botCommandScopeChatAdmins":{"comment":"The specified bot commands will be valid only for chat administrators, in all groups and supergroups.","arguments":{"gigagroup":"Is this a broadcast group?"}},"botCommandScopePeer":{"comment":"The specified bot commands will be valid only in a specific dialog.","arguments":{"peer":"The dialog"}},"botCommandScopePeerAdmins":{"comment":"The specified bot commands will be valid for all admins of the specified group or supergroup.","arguments":{"peer":"The chat"}},"botCommandScopePeerUser":{"comment":"The specified bot commands will be valid only for a specific user in the specified group or supergroup.","arguments":{"peer":"The chat","userId":"The user"}},"account.resetPasswordFailedWait":{"comment":"You recently requested a password reset that was canceled, please wait until the specified date before requesting another reset.","arguments":{"retryDate":"Wait until this date before requesting another reset."}},"account.resetPasswordRequestedWait":{"comment":"You successfully requested a password reset, please wait until the specified date before finalizing the reset.","arguments":{"untilDate":"Wait until this date before finalizing the reset."}},"account.resetPasswordOk":{"comment":"The 2FA password was reset successfully.","arguments":{"gigagroup":"Is this a broadcast group?"}},"sponsoredMessage":{"comment":"A sponsored message","arguments":{"flags":"Flags, see TL conditional fields","randomId":"Message ID","fromId":"ID of the sender of the message","startParam":"Parameter for the bot start message if the sponsored chat is a chat with a bot.","message":"Sponsored message","entities":"Message entities for styled text"}},"messages.sponsoredMessages":{"comment":"A set of sponsored messages associated to a channel","arguments":{"messages":"Sponsored messages","chats":"Chats mentioned in the sponsored messages","users":"Users mentioned in the sponsored messages"}},"chatTheme":{"comment":"A chat theme","arguments":{"emoticon":"Emoji, identifying this specific chat theme","theme":"Theme","darkTheme":"Dark mode theme"}},"account.chatThemesNotModified":{"comment":"The available chat themes were not modified","arguments":{"gigagroup":"Is this a broadcast group?"}},"account.chatThemes":{"comment":"Available chat themes","arguments":{"hash":"Hash for pagination, for more info click here","themes":"Available chat themes"}},"updateMessageReactions":{"comment":"New message reactions are available","arguments":{"peer":"Peer","msgId":"Message ID","reactions":"Reactions"}},"messageReactions":{"comment":"Message reactions","arguments":{"flags":"Flags, see TL conditional fields","min":"Similar to min objects, used for message reaction constructors that are the same for all users so they don't have the reactions sent by the current user (you can use {@link messages.getMessagesReactions} to get the full reaction info).","results":"Reactions"}},"reactionCount":{"comment":"Reactions","arguments":{"flags":"Flags, see TL conditional fields","chosen":"Whether the current user sent this reaction","reaction":"Reaction (a UTF8 emoji)","count":"NUmber of users that reacted with this emoji"}},"messageReactionsList":{"arguments":{"flags":"Flags, see TL conditional fields","count":"Total number of reactions","reactions":"Reactions","users":"Users that reacted like this","nextOffset":"Next offset to use when fetching reactions using {@link messages.getMessageReactionsList}"}},"messageUserReaction":{"comment":"Message reaction","arguments":{"userId":"ID of user that reacted this way","reaction":"Reaction (UTF8 emoji)"}}},"methods":{"invokeAfterMsg":{"comment":"Invokes a query after successfull completion of one of the previous queries.","arguments":{"msgId":"Message identifier on which a current query depends","query":"The query itself"},"available":"both"},"invokeAfterMsgs":{"comment":"Invokes a query after a successfull completion of previous queries","arguments":{"msgIds":"List of messages on which a current query depends","query":"The query itself"},"available":"both"},"initConnection":{"comment":"Initialize connection","arguments":{"flags":"Flags, see TL conditional fields","apiId":"Application identifier (see. App configuration)","deviceModel":"Device model","systemVersion":"Operation system version","appVersion":"Application version","systemLangCode":"Code for the language used on the device's OS, ISO 639-1 standard","langPack":"Language pack to use","langCode":"Code for the language used on the client, ISO 639-1 standard","proxy":"Info about an MTProto proxy","params":"Additional initConnection parameters.
For now, only the tz_offset field is supported, for specifying timezone offset in seconds.","query":"The query itself"},"throws":[{"code":400,"name":"CONNECTION_LAYER_INVALID","comment":"Layer invalid."}],"available":"both"},"invokeWithLayer":{"comment":"Invoke the specified query using the specified API layer","arguments":{"layer":"The layer to use","query":"The query"},"throws":[{"code":400,"name":"AUTH_BYTES_INVALID","comment":"The provided authorization is invalid."},{"code":400,"name":"CDN_METHOD_INVALID","comment":"You can't call this method in a CDN DC."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"CONNECTION_API_ID_INVALID","comment":"The provided API id is invalid."},{"code":400,"name":"CONNECTION_NOT_INITED","comment":"Connection not initialized."},{"code":400,"name":"INPUT_LAYER_INVALID","comment":"The provided layer is invalid."},{"code":400,"name":"INVITE_HASH_EXPIRED","comment":"The invite link has expired."}],"available":"both"},"invokeWithoutUpdates":{"comment":"Invoke a request without subscribing the used connection for updates (this is enabled by default for file queries).","arguments":{"query":"The query"},"available":"both"},"invokeWithMessagesRange":{"comment":"Invoke with the given message range","arguments":{"range":"Message range","query":"Query"},"available":"both"},"invokeWithTakeout":{"comment":"Invoke a method within a takeout session","arguments":{"takeoutId":"Takeout session ID","query":"Query"},"available":"both"},"auth.sendCode":{"comment":"Send the verification code for login","arguments":{"phoneNumber":"Phone number in international format","apiId":"Application identifier (see App configuration)","apiHash":"Application secret hash (see App configuration)","settings":"Settings for the code type to send"},"throws":[{"code":400,"name":"API_ID_INVALID","comment":"API ID invalid."},{"code":400,"name":"API_ID_PUBLISHED_FLOOD","comment":"This API id was published somewhere, you can't use it now."},{"code":401,"name":"AUTH_KEY_PERM_EMPTY","comment":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":303,"name":"NETWORK_MIGRATE_X","comment":"Repeat the query to data-center X."},{"code":303,"name":"PHONE_MIGRATE_X","comment":"Repeat the query to data-center X."},{"code":400,"name":"PHONE_NUMBER_APP_SIGNUP_FORBIDDEN","comment":"You can't sign up using this app."},{"code":400,"name":"PHONE_NUMBER_BANNED","comment":"The provided phone number is banned from telegram."},{"code":400,"name":"PHONE_NUMBER_FLOOD","comment":"You asked for the code too many times."},{"code":400,"name":"PHONE_NUMBER_INVALID","comment":"Invalid phone number."},{"code":406,"name":"PHONE_PASSWORD_FLOOD","comment":"You have tried logging in too many times."},{"code":400,"name":"PHONE_PASSWORD_PROTECTED","comment":"This phone is password protected."},{"code":400,"name":"SMS_CODE_CREATE_FAILED","comment":"An error occurred while creating the SMS code."}],"available":"user"},"auth.signUp":{"comment":"Registers a validated phone number in the system.","arguments":{"phoneNumber":"Phone number in the international format","phoneCodeHash":"SMS-message ID","firstName":"New user first name","lastName":"New user last name"},"throws":[{"code":400,"name":"FIRSTNAME_INVALID","comment":"Invalid first name."},{"code":400,"name":"LASTNAME_INVALID","comment":"Invalid last name."},{"code":400,"name":"PHONE_CODE_EMPTY","comment":"phone_code from a SMS is empty."},{"code":400,"name":"PHONE_CODE_EXPIRED","comment":"SMS expired."},{"code":400,"name":"PHONE_CODE_INVALID","comment":"Invalid SMS code was sent."},{"code":400,"name":"PHONE_NUMBER_FLOOD","comment":"You asked for the code too many times."},{"code":400,"name":"PHONE_NUMBER_INVALID","comment":"Invalid phone number."},{"code":400,"name":"PHONE_NUMBER_OCCUPIED","comment":"The phone number is already in use."}],"available":"user"},"auth.signIn":{"comment":"Signs in a user with a validated phone number.","arguments":{"phoneNumber":"Phone number in the international format","phoneCodeHash":"SMS-message ID, obtained from {@link auth.sendCode}","phoneCode":"Valid numerical code from the SMS-message"},"throws":[{"code":400,"name":"PHONE_CODE_EMPTY","comment":"phone_code from the SMS is empty."},{"code":400,"name":"PHONE_CODE_EXPIRED","comment":"SMS expired."},{"code":400,"name":"PHONE_CODE_INVALID","comment":"Invalid SMS code was sent."},{"code":400,"name":"PHONE_NUMBER_INVALID","comment":"Invalid phone number."},{"code":400,"name":"PHONE_NUMBER_UNOCCUPIED","comment":"The code is valid but no user with the given number is registered."}],"available":"user"},"auth.logOut":{"comment":"Logs out the user.","available":"both","arguments":{"gigagroup":"Is this a broadcast group?"}},"auth.resetAuthorizations":{"comment":"After calling this method it is necessary to reregister the current device using the method {@link account.registerDevice}\n\nTerminates all user's authorized sessions except for the current one.","throws":[{"code":406,"name":"FRESH_RESET_AUTHORISATION_FORBIDDEN","comment":"You can't logout other sessions if less than 24 hours have passed since you logged on the current session."}],"available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"auth.exportAuthorization":{"comment":"Returns data for copying authorization to another data-centre.","arguments":{"dcId":"Number of a target data-centre"},"throws":[{"code":400,"name":"DC_ID_INVALID","comment":"The provided DC ID is invalid."}],"available":"both"},"auth.importAuthorization":{"comment":"Logs in a user using a key transmitted from their native data-centre.","arguments":{"id":"User ID","bytes":"Authorization key"},"throws":[{"code":400,"name":"AUTH_BYTES_INVALID","comment":"The provided authorization is invalid."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"both"},"auth.bindTempAuthKey":{"comment":"For more information, see Perfect Forward Secrecy.\n\nBinds a temporary authorization key temp_auth_key_id to the permanent authorization key perm_auth_key_id. Each permanent key may only be bound to one temporary key at a time, binding a new temporary key overwrites the previous one.","arguments":{"permAuthKeyId":"Permanent auth_key_id to bind to","nonce":"Random long from Binding message contents","expiresAt":"UNIX timestamp in seconds to invalidate temporary key, see Binding message contents","encryptedMessage":"See Generating encrypted_message"},"throws":[{"code":400,"name":"ENCRYPTED_MESSAGE_INVALID","comment":"Encrypted message is incorrect."},{"code":400,"name":"TEMP_AUTH_KEY_ALREADY_BOUND","comment":"The passed temporary key is already bound to another perm_auth_key_id."},{"code":400,"name":"TEMP_AUTH_KEY_EMPTY","comment":"The request was not performed with a temporary authorization key."}],"available":"both"},"auth.importBotAuthorization":{"comment":"Login as a bot","arguments":{"flags":"Reserved for future use","apiId":"Application identifier (see. App configuration)","apiHash":"Application identifier hash (see. App configuration)","botAuthToken":"Bot token (see bots)"},"throws":[{"code":400,"name":"ACCESS_TOKEN_EXPIRED","comment":"Bot token expired."},{"code":400,"name":"ACCESS_TOKEN_INVALID","comment":"The provided token is not valid."},{"code":400,"name":"API_ID_INVALID","comment":"The api_id/api_hash combination is invalid."},{"code":400,"name":"API_ID_PUBLISHED_FLOOD","comment":"This API id was published somewhere, you can't use it now."},{"code":401,"name":"AUTH_KEY_INVALID","comment":"Auth key invalid."}],"available":"both"},"auth.checkPassword":{"comment":"Try logging to an account protected by a 2FA password.","arguments":{"password":"The account's password (see SRP)"},"throws":[{"code":400,"name":"PASSWORD_HASH_INVALID","comment":"The provided password isn't valid."},{"code":400,"name":"SRP_ID_INVALID","comment":"Invalid SRP ID provided."},{"code":400,"name":"SRP_PASSWORD_CHANGED","comment":"Password has changed."}],"available":"user"},"auth.requestPasswordRecovery":{"comment":"Request recovery code of a 2FA password, only for accounts with a recovery email configured.","throws":[{"code":400,"name":"PASSWORD_EMPTY","comment":"The provided password is empty."},{"code":400,"name":"PASSWORD_RECOVERY_NA","comment":"No email was set, can't recover password via email."}],"available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"auth.recoverPassword":{"comment":"Reset the 2FA password using the recovery code sent using {@link auth.requestPasswordRecovery}.","arguments":{"flags":"Flags, see TL conditional fields","code":"Code received via email","newSettings":"New password"},"throws":[{"code":400,"name":"CODE_EMPTY","comment":"The provided code is empty."},{"code":400,"name":"NEW_SETTINGS_INVALID","comment":"The new settings are invalid."}],"available":"user"},"auth.resendCode":{"comment":"Resend the login code via another medium, the phone code type is determined by the return value of the previous auth.sendCode/auth.resendCode: see login for more info.","arguments":{"phoneNumber":"The phone number","phoneCodeHash":"The phone code hash obtained from {@link auth.sendCode}"},"throws":[{"code":400,"name":"PHONE_CODE_EXPIRED","comment":"The phone code you provided has expired, this may happen if it was sent to any chat on telegram (if the code is sent through a telegram chat (not the official account) to avoid it append or prepend to the code some chars)."},{"code":400,"name":"PHONE_CODE_HASH_EMPTY","comment":"phone_code_hash is missing."},{"code":400,"name":"PHONE_NUMBER_INVALID","comment":"The phone number is invalid."},{"code":406,"name":"SEND_CODE_UNAVAILABLE","comment":"Returned when all available options for this type of number were already used (e.g. flash-call, then SMS, then this error might be returned to trigger a second resend)."}],"available":"user"},"auth.cancelCode":{"comment":"Cancel the login verification code","arguments":{"phoneNumber":"Phone number","phoneCodeHash":"Phone code hash from {@link auth.sendCode}"},"throws":[{"code":400,"name":"PHONE_CODE_EXPIRED","comment":"The phone code you provided has expired, this may happen if it was sent to any chat on telegram (if the code is sent through a telegram chat (not the official account) to avoid it append or prepend to the code some chars)."},{"code":400,"name":"PHONE_NUMBER_INVALID","comment":"The phone number is invalid."}],"available":"user"},"auth.dropTempAuthKeys":{"comment":"Delete all temporary authorization keys except for the ones specified","arguments":{"exceptAuthKeys":"The auth keys that shouldn't be dropped."},"available":"both"},"auth.exportLoginToken":{"comment":"For more info, see login via QR code.\n\nGenerate a login token, for login via QR code.\nThe generated login token should be encoded using base64url, then shown as a tg://login?token=base64encodedtoken URL in the QR code.","arguments":{"apiId":"Application identifier (see. App configuration)","apiHash":"Application identifier hash (see. App configuration)","exceptIds":"List of already logged-in user IDs, to prevent logging in twice with the same user"},"throws":[{"code":400,"name":"API_ID_INVALID","comment":"API ID invalid."}],"available":"user"},"auth.importLoginToken":{"comment":"For more info, see login via QR code.\n\nLogin using a redirected login token, generated in case of DC mismatch during QR code login.","arguments":{"token":"Login token"},"throws":[{"code":400,"name":"AUTH_TOKEN_ALREADY_ACCEPTED","comment":"The specified auth token was already accepted."},{"code":400,"name":"AUTH_TOKEN_EXPIRED","comment":"The authorization token has expired."},{"code":400,"name":"AUTH_TOKEN_INVALID","comment":"The specified auth token is invalid."},{"code":400,"name":"AUTH_TOKEN_INVALIDX","comment":"The specified auth token is invalid."}],"available":"user"},"auth.acceptLoginToken":{"comment":"For more info, see login via QR code.\n\nReturns info about the new session.\n\nAccept QR code login token, logging in the app that generated it.","arguments":{"token":"Login token embedded in QR code, for more info, see login via QR code."},"throws":[{"code":400,"name":"AUTH_TOKEN_INVALIDX","comment":"The specified auth token is invalid."}],"available":"user"},"auth.checkRecoveryPassword":{"comment":"Check if the 2FA recovery code sent using {@link auth.requestPasswordRecovery} is valid, before passing it to {@link auth.recoverPassword}.","arguments":{"code":"Code received via email"},"throws":[{"code":400,"name":"PASSWORD_RECOVERY_EXPIRED","comment":"The recovery code has expired."}],"available":"user"},"account.registerDevice":{"comment":"Register device to receive PUSH notifications","arguments":{"flags":"Flags, see TL conditional fields","noMuted":"Avoid receiving (silent and invisible background) notifications. Useful to save battery.","tokenType":"Device token type.
Possible values:
1 - APNS (device token for apple push)
2 - FCM (firebase token for google firebase)
3 - MPNS (channel URI for microsoft push)
4 - Simple push (endpoint for firefox's simple push API)
5 - Ubuntu phone (token for ubuntu push)
6 - Blackberry (token for blackberry push)
7 - Unused
8 - WNS (windows push)
9 - APNS VoIP (token for apple push VoIP)
10 - Web push (web push, see below)
11 - MPNS VoIP (token for microsoft push VoIP)
12 - Tizen (token for tizen push)

For 10 web push, the token must be a JSON-encoded object containing the keys described in PUSH updates","token":"Device token","appSandbox":"If {@link boolTrue} is transmitted, a sandbox-certificate will be used during transmission.","secret":"For FCM and APNS VoIP, optional encryption key used to encrypt push notifications","otherUids":"List of user identifiers of other users currently using the client"},"throws":[{"code":400,"name":"TOKEN_INVALID","comment":"The provided token is invalid."},{"code":400,"name":"WEBPUSH_AUTH_INVALID","comment":"The specified web push authentication secret is invalid."},{"code":400,"name":"WEBPUSH_KEY_INVALID","comment":"The specified web push elliptic curve Diffie-Hellman public key is invalid."},{"code":400,"name":"WEBPUSH_TOKEN_INVALID","comment":"The specified web push token is invalid."}],"available":"user"},"account.unregisterDevice":{"comment":"Deletes a device by its token, stops sending PUSH-notifications to it.","arguments":{"tokenType":"Device token type.
Possible values:
1 - APNS (device token for apple push)
2 - FCM (firebase token for google firebase)
3 - MPNS (channel URI for microsoft push)
4 - Simple push (endpoint for firefox's simple push API)
5 - Ubuntu phone (token for ubuntu push)
6 - Blackberry (token for blackberry push)
7 - Unused
8 - WNS (windows push)
9 - APNS VoIP (token for apple push VoIP)
10 - Web push (web push, see below)
11 - MPNS VoIP (token for microsoft push VoIP)
12 - Tizen (token for tizen push)

For 10 web push, the token must be a JSON-encoded object containing the keys described in PUSH updates","token":"Device token","otherUids":"List of user identifiers of other users currently using the client"},"throws":[{"code":400,"name":"TOKEN_INVALID","comment":"The provided token is invalid."}],"available":"user"},"account.updateNotifySettings":{"comment":"Edits notification settings from a given user/group, from all users/all groups.","arguments":{"peer":"Notification source","settings":"Notification settings"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"SETTINGS_INVALID","comment":"Invalid settings were provided."}],"available":"user"},"account.getNotifySettings":{"comment":"Gets current notification settings for a given user/group, from all users/all groups.","arguments":{"peer":"Notification source"},"throws":[{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"account.resetNotifySettings":{"comment":"Resets all notification settings from users and groups.","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"account.updateProfile":{"comment":"Updates user profile.","arguments":{"flags":"Flags, see TL conditional fields","firstName":"New user first name","lastName":"New user last name","about":"New bio"},"throws":[{"code":400,"name":"ABOUT_TOO_LONG","comment":"About string too long."},{"code":400,"name":"FIRSTNAME_INVALID","comment":"The first name is invalid."}],"available":"user"},"account.updateStatus":{"comment":"Updates online user status.","arguments":{"offline":"If {@link boolTrue} is transmitted, user status will change to {@link userStatusOffline}."},"available":"user"},"account.getWallPapers":{"comment":"Returns a list of available wallpapers.","arguments":{"hash":"Hash for pagination, for more info click here"},"available":"user"},"account.reportPeer":{"comment":"Report a peer for violation of telegram's Terms of Service","arguments":{"peer":"The peer to report","reason":"The reason why this peer is being reported","message":"Comment for report moderation"},"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"account.checkUsername":{"comment":"Validates a username and checks availability.","arguments":{"username":"username
Accepted characters: A-z (case-insensitive), 0-9 and underscores.
Length: 5-32 characters."},"throws":[{"code":400,"name":"USERNAME_INVALID","comment":"Unacceptable username."}],"available":"user"},"account.updateUsername":{"comment":"Changes username for the current user.","arguments":{"username":"username or empty string if username is to be removed
Accepted characters: a-z (case-insensitive), 0-9 and underscores.
Length: 5-32 characters."},"throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","comment":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"USERNAME_INVALID","comment":"Unacceptable username."},{"code":400,"name":"USERNAME_NOT_MODIFIED","comment":"Username is not different from the current username."},{"code":400,"name":"USERNAME_OCCUPIED","comment":"Username is taken."}],"available":"user"},"account.getPrivacy":{"comment":"Get privacy settings of current account","arguments":{"key":"Peer category whose privacy settings should be fetched"},"throws":[{"code":400,"name":"PRIVACY_KEY_INVALID","comment":"The privacy key is invalid."}],"available":"user"},"account.setPrivacy":{"comment":"Change privacy settings of current account","arguments":{"key":"Peers to which the privacy rules apply","rules":"New privacy rules"},"throws":[{"code":400,"name":"PRIVACY_KEY_INVALID","comment":"The privacy key is invalid."},{"code":400,"name":"PRIVACY_TOO_LONG","comment":"Too many privacy rules were specified, the current limit is 1000."},{"code":400,"name":"PRIVACY_VALUE_INVALID","comment":"The specified privacy rule combination is invalid."}],"available":"user"},"account.deleteAccount":{"comment":"Delete the user's account from the telegram servers. Can be used, for example, to delete the account of a user that provided the login code, but forgot the 2FA password and no recovery method is configured.","arguments":{"reason":"Why is the account being deleted, can be empty"},"throws":[{"code":420,"name":"2FA_CONFIRM_WAIT_X","comment":"Since this account is active and protected by a 2FA password, we will delete it in 1 week for security purposes. You can cancel this process at any time, you'll be able to reset your account in X seconds."}],"available":"user"},"account.getAccountTTL":{"comment":"Get days to live of account","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"account.setAccountTTL":{"comment":"Set account self-destruction period","arguments":{"ttl":"Time to live in days"},"throws":[{"code":400,"name":"TTL_DAYS_INVALID","comment":"The provided TTL is invalid."}],"available":"user"},"account.sendChangePhoneCode":{"comment":"Verify a new phone number to associate to the current account","arguments":{"phoneNumber":"New phone number","settings":"Phone code settings"},"throws":[{"code":406,"name":"FRESH_CHANGE_PHONE_FORBIDDEN","comment":"You can't change phone number right after logging in, please wait at least 24 hours."},{"code":400,"name":"PHONE_NUMBER_BANNED","comment":"The provided phone number is banned from telegram."},{"code":400,"name":"PHONE_NUMBER_INVALID","comment":"The phone number is invalid."},{"code":400,"name":"PHONE_NUMBER_OCCUPIED","comment":"The phone number is already in use."}],"available":"user"},"account.changePhone":{"comment":"Change the phone number of the current account","arguments":{"phoneNumber":"New phone number","phoneCodeHash":"Phone code hash received when calling {@link account.sendChangePhoneCode}","phoneCode":"Phone code received when calling {@link account.sendChangePhoneCode}"},"throws":[{"code":400,"name":"PHONE_CODE_EMPTY","comment":"phone_code is missing."},{"code":400,"name":"PHONE_NUMBER_INVALID","comment":"The phone number is invalid."},{"code":400,"name":"PHONE_NUMBER_OCCUPIED","comment":"The phone number is already in use."}],"available":"user"},"account.updateDeviceLocked":{"comment":"When client-side passcode lock feature is enabled, will not show message texts in incoming PUSH notifications.","arguments":{"period":"Inactivity period after which to start hiding message texts in PUSH notifications."},"available":"user"},"account.getAuthorizations":{"comment":"Get logged-in sessions","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"account.resetAuthorization":{"comment":"Log out an active authorized session by its hash","arguments":{"hash":"Session hash"},"throws":[{"code":406,"name":"FRESH_RESET_AUTHORISATION_FORBIDDEN","comment":"You can't logout other sessions if less than 24 hours have passed since you logged on the current session."},{"code":400,"name":"HASH_INVALID","comment":"The provided hash is invalid."}],"available":"user"},"account.getPassword":{"comment":"Obtain configuration for two-factor authorization with password","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"account.getPasswordSettings":{"comment":"Get private info associated to the password info (recovery email, telegram passport info & so on)","arguments":{"password":"The password (see SRP)"},"throws":[{"code":400,"name":"PASSWORD_HASH_INVALID","comment":"The provided password hash is invalid."}],"available":"user"},"account.updatePasswordSettings":{"comment":"Set a new 2FA password","arguments":{"password":"The old password (see SRP)","newSettings":"The new password (see SRP)"},"throws":[{"code":400,"name":"EMAIL_UNCONFIRMED","comment":"Email unconfirmed."},{"code":400,"name":"EMAIL_UNCONFIRMED_X","comment":"The provided email isn't confirmed, X is the length of the verification code that was just sent to the email: use {@link account.verifyEmail} to enter the received verification code and enable the recovery email."},{"code":400,"name":"NEW_SALT_INVALID","comment":"The new salt is invalid."},{"code":400,"name":"NEW_SETTINGS_INVALID","comment":"The new password settings are invalid."},{"code":400,"name":"PASSWORD_HASH_INVALID","comment":"The old password hash is invalid."},{"code":400,"name":"SRP_ID_INVALID","comment":"Invalid SRP ID provided."},{"code":400,"name":"SRP_PASSWORD_CHANGED","comment":"Password has changed."}],"available":"user"},"account.sendConfirmPhoneCode":{"comment":"Send confirmation code to cancel account deletion, for more info click here »","arguments":{"hash":"The hash from the service notification, for more info click here »","settings":"Phone code settings"},"throws":[{"code":400,"name":"HASH_INVALID","comment":"The provided hash is invalid."}],"available":"user"},"account.confirmPhone":{"comment":"Confirm a phone number to cancel account deletion, for more info click here »","arguments":{"phoneCodeHash":"Phone code hash, for more info click here »","phoneCode":"SMS code, for more info click here »"},"throws":[{"code":400,"name":"CODE_HASH_INVALID","comment":"Code hash invalid."},{"code":400,"name":"PHONE_CODE_EMPTY","comment":"phone_code is missing."}],"available":"user"},"account.getTmpPassword":{"comment":"Get temporary payment password","arguments":{"password":"SRP password parameters","period":"Time during which the temporary password will be valid, in seconds; should be between 60 and 86400"},"throws":[{"code":400,"name":"PASSWORD_HASH_INVALID","comment":"The provided password hash is invalid."},{"code":400,"name":"TMP_PASSWORD_DISABLED","comment":"The temporary password is disabled."}],"available":"user"},"account.getWebAuthorizations":{"comment":"Get web login widget authorizations","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"account.resetWebAuthorization":{"comment":"Log out an active web telegram login session","arguments":{"hash":"{@link webAuthorization} hash"},"throws":[{"code":400,"name":"HASH_INVALID","comment":"The provided hash is invalid."}],"available":"user"},"account.resetWebAuthorizations":{"comment":"Reset all active web telegram login sessions","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"account.getAllSecureValues":{"comment":"Get all saved Telegram Passport documents, for more info see the passport docs »","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"account.getSecureValue":{"comment":"Get saved Telegram Passport document, for more info see the passport docs »","arguments":{"types":"Requested value types"},"available":"user"},"account.saveSecureValue":{"comment":"Securely save Telegram Passport document, for more info see the passport docs »","arguments":{"value":"Secure value, for more info see the passport docs »","secureSecretId":"Passport secret hash, for more info see the passport docs »"},"throws":[{"code":400,"name":"PASSWORD_REQUIRED","comment":"A 2FA password must be configured to use Telegram Passport."}],"available":"user"},"account.deleteSecureValue":{"comment":"Delete stored Telegram Passport documents, for more info see the passport docs »","arguments":{"types":"Document types to delete"},"available":"user"},"account.getAuthorizationForm":{"comment":"Returns a Telegram Passport authorization form for sharing data with a service","arguments":{"botId":"User identifier of the service's bot","scope":"Telegram Passport element types requested by the service","publicKey":"Service's public key"},"throws":[{"code":400,"name":"PUBLIC_KEY_REQUIRED","comment":"A public key is required."}],"available":"user"},"account.acceptAuthorization":{"comment":"Sends a Telegram Passport authorization form, effectively sharing data with the service","arguments":{"botId":"Bot ID","scope":"Telegram Passport element types requested by the service","publicKey":"Service's public key","valueHashes":"Types of values sent and their hashes","credentials":"Encrypted values"},"available":"user"},"account.sendVerifyPhoneCode":{"comment":"Send the verification phone code for telegram passport.","arguments":{"phoneNumber":"The phone number to verify","settings":"Phone code settings"},"throws":[{"code":400,"name":"PHONE_NUMBER_INVALID","comment":"The phone number is invalid."}],"available":"user"},"account.verifyPhone":{"comment":"Verify a phone number for telegram passport.","arguments":{"phoneNumber":"Phone number","phoneCodeHash":"Phone code hash received from the call to {@link account.sendVerifyPhoneCode}","phoneCode":"Code received after the call to {@link account.sendVerifyPhoneCode}"},"throws":[{"code":400,"name":"PHONE_CODE_EMPTY","comment":"phone_code is missing."},{"code":400,"name":"PHONE_CODE_EXPIRED","comment":"The phone code you provided has expired, this may happen if it was sent to any chat on telegram (if the code is sent through a telegram chat (not the official account) to avoid it append or prepend to the code some chars)."},{"code":400,"name":"PHONE_NUMBER_INVALID","comment":"The phone number is invalid."}],"available":"user"},"account.sendVerifyEmailCode":{"comment":"Send the verification email code for telegram passport.","arguments":{"email":"The email where to send the code"},"throws":[{"code":400,"name":"EMAIL_INVALID","comment":"The specified email is invalid."}],"available":"user"},"account.verifyEmail":{"comment":"Verify an email address for telegram passport.","arguments":{"email":"The email to verify","code":"The verification code that was received"},"throws":[{"code":400,"name":"EMAIL_INVALID","comment":"The specified email is invalid."},{"code":400,"name":"EMAIL_VERIFY_EXPIRED","comment":"The verification email has expired."}],"available":"user"},"account.initTakeoutSession":{"comment":"Initialize account takeout session","arguments":{"flags":"Flags, see TL conditional fields","contacts":"Whether to export contacts","messageUsers":"Whether to export messages in private chats","messageChats":"Whether to export messages in legacy groups","messageMegagroups":"Whether to export messages in supergroups","messageChannels":"Whether to export messages in channels","files":"Whether to export files","fileMaxSize":"Maximum size of files to export"},"throws":[{"code":420,"name":"TAKEOUT_INIT_DELAY_X","comment":"Wait X seconds before initing takeout."}],"available":"user"},"account.finishTakeoutSession":{"comment":"Finish account takeout session","arguments":{"flags":"Flags, see TL conditional fields","success":"Data exported successfully"},"throws":[{"code":403,"name":"TAKEOUT_REQUIRED","comment":"A takeout session has to be initialized, first."}],"available":"user"},"account.confirmPasswordEmail":{"comment":"Verify an email to use as 2FA recovery method.","arguments":{"code":"The phone code that was received after setting a recovery email"},"throws":[{"code":400,"name":"CODE_INVALID","comment":"Code invalid."},{"code":400,"name":"EMAIL_HASH_EXPIRED","comment":"Email hash expired."}],"available":"user"},"account.resendPasswordEmail":{"comment":"Resend the code to verify an email to use as 2FA recovery method.","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"account.cancelPasswordEmail":{"comment":"Cancel the code that was sent to verify an email to use as 2FA recovery method.","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"account.getContactSignUpNotification":{"comment":"Whether the user will receive notifications when contacts sign up","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"account.setContactSignUpNotification":{"comment":"Toggle contact sign up notifications","arguments":{"silent":"Whether to disable contact sign up notifications"},"available":"user"},"account.getNotifyExceptions":{"comment":"Returns list of chats with non-default notification settings","arguments":{"flags":"Flags, see TL conditional fields","compareSound":"If true, chats with non-default sound will also be returned","peer":"If specified, only chats of the specified category will be returned"},"available":"user"},"account.getWallPaper":{"comment":"Get info about a certain wallpaper","arguments":{"wallpaper":"The wallpaper to get info about"},"throws":[{"code":400,"name":"WALLPAPER_INVALID","comment":"The specified wallpaper is invalid."}],"available":"user"},"account.uploadWallPaper":{"comment":"Create and upload a new wallpaper","arguments":{"file":"The JPG/PNG wallpaper","mimeType":"MIME type of uploaded wallpaper","settings":"Wallpaper settings"},"throws":[{"code":400,"name":"WALLPAPER_FILE_INVALID","comment":"The specified wallpaper file is invalid."},{"code":400,"name":"WALLPAPER_MIME_INVALID","comment":"The specified wallpaper MIME type is invalid."}],"available":"user"},"account.saveWallPaper":{"comment":"Install/uninstall wallpaper","arguments":{"wallpaper":"Wallpaper to save","unsave":"Uninstall wallpaper?","settings":"Wallpaper settings"},"throws":[{"code":400,"name":"WALLPAPER_INVALID","comment":"The specified wallpaper is invalid."}],"available":"user"},"account.installWallPaper":{"comment":"Install wallpaper","arguments":{"wallpaper":"Wallpaper to install","settings":"Wallpaper settings"},"throws":[{"code":400,"name":"WALLPAPER_INVALID","comment":"The specified wallpaper is invalid."}],"available":"user"},"account.resetWallPapers":{"comment":"Delete installed wallpapers","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"account.getAutoDownloadSettings":{"comment":"Get media autodownload settings","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"account.saveAutoDownloadSettings":{"comment":"Change media autodownload settings","arguments":{"flags":"Flags, see TL conditional fields","low":"Whether to save settings in the low data usage preset","high":"Whether to save settings in the high data usage preset","settings":"Media autodownload settings"},"available":"user"},"account.uploadTheme":{"comment":"Upload theme","arguments":{"flags":"Flags, see TL conditional fields","file":"Theme file uploaded as described in files »","thumb":"Thumbnail","fileName":"File name","mimeType":"MIME type, must be application/x-tgtheme-{format}, where format depends on the client"},"throws":[{"code":400,"name":"THEME_FILE_INVALID","comment":"Invalid theme file provided."}],"available":"user"},"account.createTheme":{"comment":"Create a theme","arguments":{"flags":"Flags, see TL conditional fields","slug":"Unique theme ID","title":"Theme name","document":"Theme file","settings":"Theme settings"},"throws":[{"code":400,"name":"THEME_MIME_INVALID","comment":"The theme's MIME type is invalid."}],"available":"user"},"account.updateTheme":{"comment":"Update theme","arguments":{"flags":"Flags, see TL conditional fields","format":"Theme format, a string that identifies the theming engines supported by the client","theme":"Theme to update","slug":"Unique theme ID","title":"Theme name","document":"Theme file","settings":"Theme settings"},"throws":[{"code":400,"name":"THEME_INVALID","comment":"Invalid theme provided."}],"available":"user"},"account.saveTheme":{"comment":"Save a theme","arguments":{"theme":"Theme to save","unsave":"Unsave"},"available":"user"},"account.installTheme":{"comment":"Install a theme","arguments":{"flags":"Flags, see TL conditional fields","dark":"Whether to install the dark version","format":"Theme format, a string that identifies the theming engines supported by the client","theme":"Theme to install"},"available":"user"},"account.getTheme":{"comment":"Get theme information","arguments":{"format":"Theme format, a string that identifies the theming engines supported by the client","theme":"Theme","documentId":"Document ID"},"throws":[{"code":400,"name":"THEME_FORMAT_INVALID","comment":"Invalid theme format provided."},{"code":400,"name":"THEME_INVALID","comment":"Invalid theme provided."}],"available":"user"},"account.getThemes":{"comment":"Get installed themes","arguments":{"format":"Theme format, a string that identifies the theming engines supported by the client","hash":"Hash for pagination, for more info click here"},"available":"user"},"account.setContentSettings":{"comment":"Set sensitive content settings (for viewing or hiding NSFW content)","arguments":{"flags":"Flags, see TL conditional fields","sensitiveEnabled":"Enable NSFW content"},"throws":[{"code":403,"name":"SENSITIVE_CHANGE_FORBIDDEN","comment":"You can't change your sensitive content settings."}],"available":"user"},"account.getContentSettings":{"comment":"Get sensitive content settings","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"account.getMultiWallPapers":{"comment":"Get info about multiple wallpapers","arguments":{"wallpapers":"Wallpapers to fetch info about"},"available":"user"},"account.getGlobalPrivacySettings":{"comment":"Get global privacy settings","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"account.setGlobalPrivacySettings":{"comment":"Set global privacy settings","arguments":{"settings":"Global privacy settings"},"throws":[{"code":400,"name":"AUTOARCHIVE_NOT_AVAILABLE","comment":"The autoarchive setting is not available at this time: please check the value of the autoarchive_setting_available field in client config » before calling this method."}],"available":"user"},"account.reportProfilePhoto":{"comment":"Report a profile photo of a dialog","arguments":{"peer":"The dialog","photoId":"Dialog photo ID","reason":"Report reason","message":"Comment for report moderation"},"available":"user"},"account.resetPassword":{"comment":"Initiate a 2FA password reset: can only be used if the user is already logged-in, see here for more info »","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"account.declinePasswordReset":{"comment":"Abort a pending 2FA password reset, see here for more info »","throws":[{"code":400,"name":"RESET_REQUEST_MISSING","comment":"No password reset is in progress."}],"available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"account.getChatThemes":{"comment":"Get all available chat themes","arguments":{"hash":"Hash for pagination, for more info click here"},"available":"user"},"users.getUsers":{"comment":"Returns basic user info according to their identifiers.","arguments":{"id":"List of user identifiers"},"throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","comment":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CONNECTION_NOT_INITED","comment":"Connection not initialized."},{"code":400,"name":"INPUT_LAYER_INVALID","comment":"The provided layer is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"both"},"users.getFullUser":{"comment":"Returns extended user info by ID.","arguments":{"id":"User ID"},"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"both"},"users.setSecureValueErrors":{"comment":"Use this if the data submitted by the user doesn't satisfy the standards your service requires for any reason. For example, if a birthday date seems invalid, a submitted document is blurry, a scan shows evidence of tampering, etc. Supply some details in the error message to make sure the user knows how to correct the issues.\n\nNotify the user that the sent passport data contains some errors The user will not be able to re-submit their Passport data to you until the errors are fixed (the contents of the field for which you returned the error must change).","arguments":{"id":"The user","errors":"Errors"},"throws":[{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"both"},"contacts.getContactIDs":{"comment":"Get contact by telegram IDs","arguments":{"hash":"Hash for pagination, for more info click here"},"available":"user"},"contacts.getStatuses":{"comment":"Returns the list of contact statuses.","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"contacts.getContacts":{"comment":"Returns the current user's contact list.","arguments":{"hash":"If there already is a full contact list on the client, a hash of a the list of contact IDs in ascending order may be passed in this parameter. If the contact set was not changed, {@link contacts.contactsNotModified} will be returned."},"available":"user"},"contacts.importContacts":{"comment":"Use {@link contacts.addContact} to add Telegram contacts without actually using their phone number.\n\nImports contacts: saves a full list on the server, adds already registered contacts to the contact list, returns added contacts and their info.","arguments":{"contacts":"List of contacts to import"},"available":"user"},"contacts.deleteContacts":{"comment":"Deletes several contacts from the list.","arguments":{"id":"User ID list"},"available":"user"},"contacts.deleteByPhones":{"comment":"Delete contacts by phone number","arguments":{"phones":"Phone numbers"},"available":"user"},"contacts.block":{"comment":"Adds the user to the blacklist.","arguments":{"id":"User ID"},"throws":[{"code":400,"name":"CONTACT_ID_INVALID","comment":"The provided contact ID is invalid."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"contacts.unblock":{"comment":"Deletes the user from the blacklist.","arguments":{"id":"User ID"},"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CONTACT_ID_INVALID","comment":"The provided contact ID is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"contacts.getBlocked":{"comment":"Returns the list of blocked users.","arguments":{"offset":"The number of list elements to be skipped","limit":"The number of list elements to be returned"},"available":"user"},"contacts.search":{"comment":"Returns users found by username substring.","arguments":{"q":"Target substring","limit":"Maximum number of users to be returned"},"throws":[{"code":400,"name":"QUERY_TOO_SHORT","comment":"The query string is too short."},{"code":400,"name":"SEARCH_QUERY_EMPTY","comment":"The search query is empty."}],"available":"user"},"contacts.resolveUsername":{"comment":"Resolve a @username to get peer info","arguments":{"username":"@username to resolve"},"throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","comment":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"CONNECTION_LAYER_INVALID","comment":"Layer invalid."},{"code":400,"name":"USERNAME_INVALID","comment":"The provided username is not valid."},{"code":400,"name":"USERNAME_NOT_OCCUPIED","comment":"The provided username is not occupied."}],"available":"both"},"contacts.getTopPeers":{"comment":"Get most used peers","arguments":{"flags":"Flags, see TL conditional fields","correspondents":"Users we've chatted most frequently with","botsPm":"Most used bots","botsInline":"Most used inline bots","phoneCalls":"Most frequently called users","forwardUsers":"Users to which the users often forwards messages to","forwardChats":"Chats to which the users often forwards messages to","groups":"Often-opened groups and supergroups","channels":"Most frequently visited channels","offset":"Offset for pagination","limit":"Maximum number of results to return, see pagination","hash":"Hash for pagination, for more info click here"},"throws":[{"code":400,"name":"TYPES_EMPTY","comment":"No top peer type was provided."}],"available":"user"},"contacts.resetTopPeerRating":{"comment":"Reset rating of top peer","arguments":{"category":"Top peer category","peer":"Peer whose rating should be reset"},"throws":[{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"contacts.resetSaved":{"comment":"Delete saved contacts","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"contacts.getSaved":{"comment":"Get all contacts","throws":[{"code":403,"name":"TAKEOUT_REQUIRED","comment":"A takeout session has to be initialized, first."}],"available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"contacts.toggleTopPeers":{"comment":"Enable/disable top peers","arguments":{"enabled":"Enable/disable"},"available":"user"},"contacts.addContact":{"comment":"Use {@link contacts.importContacts} to add contacts by phone number, without knowing their Telegram ID.\n\nAdd an existing telegram user as contact.","arguments":{"flags":"Flags, see TL conditional fields","addPhonePrivacyException":"Allow the other user to see our phone number?","id":"Telegram ID of the other user","firstName":"First name","lastName":"Last name","phone":"User's phone number"},"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CONTACT_ID_INVALID","comment":"The provided contact ID is invalid."},{"code":400,"name":"CONTACT_NAME_EMPTY","comment":"Contact name empty."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"user"},"contacts.acceptContact":{"comment":"If the {@link peerSettings} of a new user allow us to add them as contact, add that user as contact","arguments":{"id":"The user to add as contact"},"throws":[{"code":400,"name":"CONTACT_ADD_MISSING","comment":"Contact to add is missing."},{"code":400,"name":"CONTACT_ID_INVALID","comment":"The provided contact ID is invalid."},{"code":400,"name":"CONTACT_REQ_MISSING","comment":"Missing contact request."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"user"},"contacts.getLocated":{"comment":"Get contacts near you","arguments":{"flags":"Flags, see TL conditional fields","background":"While the geolocation of the current user is public, clients should update it in the background every half-an-hour or so, while setting this flag.
Do this only if the new location is more than 1 KM away from the previous one, or if the previous location is unknown.","geoPoint":"Geolocation","selfExpires":"If set, the geolocation of the current user will be public for the specified number of seconds; pass 0x7fffffff to disable expiry, 0 to make the current geolocation private; if the flag isn't set, no changes will be applied."},"throws":[{"code":400,"name":"GEO_POINT_INVALID","comment":"Invalid geoposition provided."},{"code":406,"name":"USERPIC_PRIVACY_REQUIRED","comment":"You need to disable privacy settings for your profile picture in order to make your geolocation public."},{"code":406,"name":"USERPIC_UPLOAD_REQUIRED","comment":"You must have a profile picture to publish your geolocation."}],"available":"user"},"contacts.blockFromReplies":{"comment":"Stop getting notifications about thread replies of a certain user in @replies","arguments":{"flags":"Flags, see TL conditional fields","deleteMessage":"Whether to delete the specified message as well","deleteHistory":"Whether to delete all @replies messages from this user as well","reportSpam":"Whether to also report this user for spam","msgId":"ID of the message in the @replies chat"},"available":"user"},"messages.getMessages":{"comment":"Returns the list of messages by their IDs.","arguments":{"id":"Message ID list"},"available":"both"},"messages.getDialogs":{"comment":"Returns the current user dialog list.","arguments":{"flags":"Flags, see TL conditional fields","excludePinned":"Exclude pinned dialogs","folderId":"Peer folder ID, for more info click here","offsetDate":"Offsets for pagination, for more info click here","offsetId":"Offsets for pagination, for more info click here","offsetPeer":"Offset peer for pagination","limit":"Number of list elements to be returned","hash":"Hash for pagination, for more info click here"},"throws":[{"code":400,"name":"FOLDER_ID_INVALID","comment":"Invalid folder ID."},{"code":400,"name":"OFFSET_PEER_ID_INVALID","comment":"The provided offset peer is invalid."}],"available":"user"},"messages.getHistory":{"comment":"Gets back the conversation history with one interlocutor / within a chat","arguments":{"peer":"Target peer","offsetId":"Only return messages starting from the specified message ID","offsetDate":"Only return messages sent before the specified date","addOffset":"Number of list elements to be skipped, negative values are also accepted.","limit":"Number of results to return","maxId":"If a positive value was transferred, the method will return only messages with IDs less than max_id","minId":"If a positive value was transferred, the method will return only messages with IDs more than min_id","hash":"Result hash"},"throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","comment":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.search":{"comment":"Gets back found messages","arguments":{"flags":"Flags, see TL conditional fields","peer":"User or chat, histories with which are searched, or {@link inputPeerEmpty} constructor for global search","q":"Text search request","fromId":"Only return messages sent by the specified user ID","topMsgId":"Thread ID","filter":"Filter to return only specified message types","minDate":"If a positive value was transferred, only messages with a sending date bigger than the transferred one will be returned","maxDate":"If a positive value was transferred, only messages with a sending date smaller than the transferred one will be returned","offsetId":"Only return messages starting from the specified message ID","addOffset":"Additional offset","limit":"Number of results to return","maxId":"Maximum message ID to return","minId":"Minimum message ID to return","hash":"Hash"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"FROM_PEER_INVALID","comment":"The specified from_id is invalid."},{"code":400,"name":"INPUT_FILTER_INVALID","comment":"The specified filter is invalid."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"PEER_ID_NOT_SUPPORTED","comment":"The provided peer ID is not supported."},{"code":400,"name":"SEARCH_QUERY_EMPTY","comment":"The search query is empty."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"user"},"messages.readHistory":{"comment":"Marks message history as read.","arguments":{"peer":"Target user or group","maxId":"If a positive value is passed, only messages with identifiers less or equal than the given one will be read"},"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.deleteHistory":{"comment":"Deletes communication history.","arguments":{"flags":"Flags, see TL conditional fields","justClear":"Just clear history for the current user, without actually removing messages for every chat user","revoke":"Whether to delete the message history for all chat participants","peer":"User or chat, communication history of which will be deleted","maxId":"Maximum ID of message to delete"},"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.deleteMessages":{"comment":"Deletes messages by their identifiers.","arguments":{"flags":"Flags, see TL conditional fields","revoke":"Whether to delete messages for all participants of the chat","id":"Message ID list"},"throws":[{"code":403,"name":"MESSAGE_DELETE_FORBIDDEN","comment":"You can't delete one of the messages you tried to delete, most likely because it is a service message."}],"available":"both"},"messages.receivedMessages":{"comment":"Confirms receipt of messages by a client, cancels PUSH-notification sending.","arguments":{"maxId":"Maximum message ID available in a client."},"available":"user"},"messages.setTyping":{"comment":"Sends a current user typing event (see SendMessageAction for all event types) to a conversation partner or group.","arguments":{"flags":"Flags, see TL conditional fields","peer":"Target user or group","topMsgId":"Thread ID","action":"Type of action
Parameter added in Layer 17."},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","comment":"You're banned from sending messages in supergroups/channels."},{"code":400,"name":"USER_IS_BLOCKED","comment":"You were blocked by this user."},{"code":400,"name":"USER_IS_BOT","comment":"Bots can't send messages to other bots."}],"available":"both"},"messages.sendMessage":{"comment":"Sends a message to a chat","arguments":{"flags":"Flags, see TL conditional fields","noWebpage":"Set this flag to disable generation of the webpage preview","silent":"Send this message silently (no notifications for the receivers)","background":"Send this message as background message","clearDraft":"Clear the draft field","peer":"The destination where the message will be sent","replyToMsgId":"The message ID to which this message will reply to","message":"The message","randomId":"Unique client message ID required to prevent message resending","replyMarkup":"Reply markup for sending bot buttons","entities":"Message entities for sending styled text","scheduleDate":"Scheduled message date for scheduled messages"},"throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","comment":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"BOT_DOMAIN_INVALID","comment":"Bot domain invalid."},{"code":400,"name":"BOT_INVALID","comment":"This is not a valid bot."},{"code":400,"name":"BUTTON_DATA_INVALID","comment":"The data of one or more of the buttons you provided is invalid."},{"code":400,"name":"BUTTON_TYPE_INVALID","comment":"The type of one or more of the buttons you provided is invalid."},{"code":400,"name":"BUTTON_URL_INVALID","comment":"Button URL invalid."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"CHAT_RESTRICTED","comment":"You can't send messages in this chat, you were restricted."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"ENCRYPTION_DECLINED","comment":"The secret chat was declined."},{"code":400,"name":"ENTITIES_TOO_LONG","comment":"You provided too many styled message entities."},{"code":400,"name":"ENTITY_MENTION_USER_INVALID","comment":"You mentioned an invalid user."},{"code":400,"name":"FROM_MESSAGE_BOT_DISABLED","comment":"Bots can't use fromMessage min constructors."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MESSAGE_EMPTY","comment":"The provided message is empty."},{"code":400,"name":"MESSAGE_TOO_LONG","comment":"The provided message is too long."},{"code":400,"name":"MSG_ID_INVALID","comment":"Provided reply_to_msg_id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"PINNED_DIALOGS_TOO_MUCH","comment":"Too many pinned dialogs."},{"code":400,"name":"POLL_OPTION_INVALID","comment":"Invalid poll option provided."},{"code":400,"name":"REPLY_MARKUP_INVALID","comment":"The provided reply markup is invalid."},{"code":400,"name":"REPLY_MARKUP_TOO_LONG","comment":"The specified reply_markup is too long."},{"code":400,"name":"SCHEDULE_BOT_NOT_ALLOWED","comment":"Bots cannot schedule messages."},{"code":400,"name":"SCHEDULE_DATE_TOO_LATE","comment":"You can't schedule a message this far in the future."},{"code":400,"name":"SCHEDULE_STATUS_PRIVATE","comment":"Can't schedule until user is online, if the user's last seen timestamp is hidden by their privacy settings."},{"code":400,"name":"SCHEDULE_TOO_MUCH","comment":"There are too many scheduled messages."},{"code":420,"name":"SLOWMODE_WAIT_X","comment":"Slowmode is enabled in this chat: you must wait for the specified number of seconds before sending another message to the chat."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","comment":"You're banned from sending messages in supergroups/channels."},{"code":400,"name":"USER_IS_BLOCKED","comment":"You were blocked by this user."},{"code":400,"name":"USER_IS_BOT","comment":"Bots can't send messages to other bots."},{"code":400,"name":"YOU_BLOCKED_USER","comment":"You blocked this user."}],"available":"both"},"messages.sendMedia":{"comment":"Send a media","arguments":{"flags":"Flags, see TL conditional fields","silent":"Send message silently (no notification should be triggered)","background":"Send message in background","clearDraft":"Clear the draft","peer":"Destination","replyToMsgId":"Message ID to which this message should reply to","media":"Attached media","message":"Caption","randomId":"Random ID to avoid resending the same message","replyMarkup":"Reply markup for bot keyboards","entities":"Message entities for styled text","scheduleDate":"Scheduled message date for scheduled messages"},"throws":[{"code":400,"name":"BOT_PAYMENTS_DISABLED","comment":"Please enable bot payments in botfather before calling this method."},{"code":400,"name":"BOT_POLLS_DISABLED","comment":" "},{"code":400,"name":"BROADCAST_PUBLIC_VOTERS_FORBIDDEN","comment":"You can't forward polls with public voters."},{"code":400,"name":"BUTTON_DATA_INVALID","comment":"The data of one or more of the buttons you provided is invalid."},{"code":400,"name":"BUTTON_TYPE_INVALID","comment":"The type of one or more of the buttons you provided is invalid."},{"code":400,"name":"BUTTON_URL_INVALID","comment":"Button URL invalid."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_RESTRICTED","comment":"You can't send messages in this chat, you were restricted."},{"code":403,"name":"CHAT_SEND_GIFS_FORBIDDEN","comment":"You can't send gifs in this chat."},{"code":403,"name":"CHAT_SEND_MEDIA_FORBIDDEN","comment":"You can't send media in this chat."},{"code":403,"name":"CHAT_SEND_POLL_FORBIDDEN","comment":"You can't send polls in this chat."},{"code":403,"name":"CHAT_SEND_STICKERS_FORBIDDEN","comment":"You can't send stickers in this chat."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"CURRENCY_TOTAL_AMOUNT_INVALID","comment":"The total amount of all prices is invalid."},{"code":400,"name":"EMOTICON_INVALID","comment":"The specified emoji is invalid."},{"code":400,"name":"EXTERNAL_URL_INVALID","comment":"External URL invalid."},{"code":400,"name":"FILE_PARTS_INVALID","comment":"The number of file parts is invalid."},{"code":400,"name":"FILE_PART_LENGTH_INVALID","comment":"The length of a file part is invalid."},{"code":400,"name":"FILE_REFERENCE_EMPTY","comment":"An empty file reference was specified."},{"code":400,"name":"FILE_REFERENCE_EXPIRED","comment":"File reference expired, it must be refetched as described in https://core.telegram.org/api/file_reference."},{"code":400,"name":"GAME_BOT_INVALID","comment":"Bots can't send another bot's game."},{"code":400,"name":"IMAGE_PROCESS_FAILED","comment":"Failure while processing image."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MD5_CHECKSUM_INVALID","comment":"The MD5 checksums do not match."},{"code":400,"name":"MEDIA_CAPTION_TOO_LONG","comment":"The caption is too long."},{"code":400,"name":"MEDIA_EMPTY","comment":"The provided media object is invalid."},{"code":400,"name":"MEDIA_INVALID","comment":"Media invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PAYMENT_PROVIDER_INVALID","comment":"The specified payment provider is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"PHOTO_EXT_INVALID","comment":"The extension of the photo is invalid."},{"code":400,"name":"PHOTO_INVALID_DIMENSIONS","comment":"The photo dimensions are invalid."},{"code":400,"name":"PHOTO_SAVE_FILE_INVALID","comment":"Internal issues, try again later."},{"code":400,"name":"POLL_ANSWERS_INVALID","comment":"Invalid poll answers were provided."},{"code":400,"name":"POLL_ANSWER_INVALID","comment":"One of the poll answers is not acceptable."},{"code":400,"name":"POLL_OPTION_DUPLICATE","comment":"Duplicate poll options provided."},{"code":400,"name":"POLL_OPTION_INVALID","comment":"Invalid poll option provided."},{"code":400,"name":"POLL_QUESTION_INVALID","comment":"One of the poll questions is not acceptable."},{"code":400,"name":"QUIZ_CORRECT_ANSWERS_EMPTY","comment":"No correct quiz answer was specified."},{"code":400,"name":"QUIZ_CORRECT_ANSWERS_TOO_MUCH","comment":"You specified too many correct answers in a quiz, quizes can only have one right answer!"},{"code":400,"name":"QUIZ_CORRECT_ANSWER_INVALID","comment":"An invalid value was provided to the correct_answers field."},{"code":400,"name":"QUIZ_MULTIPLE_INVALID","comment":"Quizes can't have the multiple_choice flag set!"},{"code":400,"name":"REPLY_MARKUP_BUY_EMPTY","comment":"Reply markup for buy button empty."},{"code":400,"name":"REPLY_MARKUP_INVALID","comment":"The provided reply markup is invalid."},{"code":400,"name":"SCHEDULE_BOT_NOT_ALLOWED","comment":"Bots cannot schedule messages."},{"code":400,"name":"SCHEDULE_DATE_TOO_LATE","comment":"You can't schedule a message this far in the future."},{"code":400,"name":"SCHEDULE_TOO_MUCH","comment":"There are too many scheduled messages."},{"code":420,"name":"SLOWMODE_WAIT_X","comment":"Slowmode is enabled in this chat: you must wait for the specified number of seconds before sending another message to the chat."},{"code":400,"name":"TTL_MEDIA_INVALID","comment":"Invalid media Time To Live was provided."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","comment":"You're banned from sending messages in supergroups/channels."},{"code":400,"name":"USER_IS_BLOCKED","comment":"You were blocked by this user."},{"code":400,"name":"USER_IS_BOT","comment":"Bots can't send messages to other bots."},{"code":400,"name":"VIDEO_CONTENT_TYPE_INVALID","comment":"The video's content type is invalid."},{"code":400,"name":"WEBPAGE_CURL_FAILED","comment":"Failure while fetching the webpage with cURL."},{"code":400,"name":"WEBPAGE_MEDIA_EMPTY","comment":"Webpage media empty."},{"code":400,"name":"YOU_BLOCKED_USER","comment":"You blocked this user."}],"available":"both"},"messages.forwardMessages":{"comment":"Forwards messages by their IDs.","arguments":{"flags":"Flags, see TL conditional fields","silent":"Whether to send messages silently (no notification will be triggered on the destination clients)","background":"Whether to send the message in background","withMyScore":"When forwarding games, whether to include your score in the game","dropAuthor":"Whether to forward messages without quoting the original author","dropMediaCaptions":"Whether to strip captions from media","fromPeer":"Source of messages","id":"IDs of messages","randomId":"Random ID to prevent resending of messages","toPeer":"Destination peer","scheduleDate":"Scheduled message date for scheduled messages"},"throws":[{"code":400,"name":"BROADCAST_PUBLIC_VOTERS_FORBIDDEN","comment":"You can't forward polls with public voters."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"CHAT_RESTRICTED","comment":"You can't send messages in this chat, you were restricted."},{"code":403,"name":"CHAT_SEND_GAME_FORBIDDEN","comment":"You can't send a game to this chat."},{"code":403,"name":"CHAT_SEND_GIFS_FORBIDDEN","comment":"You can't send gifs in this chat."},{"code":403,"name":"CHAT_SEND_MEDIA_FORBIDDEN","comment":"You can't send media in this chat."},{"code":403,"name":"CHAT_SEND_POLL_FORBIDDEN","comment":"You can't send polls in this chat."},{"code":403,"name":"CHAT_SEND_STICKERS_FORBIDDEN","comment":"You can't send stickers in this chat."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"GROUPED_MEDIA_INVALID","comment":"Invalid grouped media."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MEDIA_EMPTY","comment":"The provided media object is invalid."},{"code":400,"name":"MESSAGE_IDS_EMPTY","comment":"No message ids were provided."},{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":420,"name":"P0NY_FLOODWAIT","comment":" "},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"RANDOM_ID_INVALID","comment":"A provided random ID is invalid."},{"code":400,"name":"SCHEDULE_DATE_TOO_LATE","comment":"You can't schedule a message this far in the future."},{"code":400,"name":"SCHEDULE_TOO_MUCH","comment":"There are too many scheduled messages."},{"code":400,"name":"SLOWMODE_MULTI_MSGS_DISABLED","comment":"Slowmode is enabled, you cannot forward multiple messages to this group."},{"code":420,"name":"SLOWMODE_WAIT_X","comment":"Slowmode is enabled in this chat: you must wait for the specified number of seconds before sending another message to the chat."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","comment":"You're banned from sending messages in supergroups/channels."},{"code":400,"name":"USER_IS_BLOCKED","comment":"You were blocked by this user."},{"code":400,"name":"USER_IS_BOT","comment":"Bots can't send messages to other bots."},{"code":400,"name":"YOU_BLOCKED_USER","comment":"You blocked this user."}],"available":"both"},"messages.reportSpam":{"comment":"Report a new incoming chat for spam, if the {@link peerSettings} of the chat allow us to do that","arguments":{"peer":"Peer to report"},"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.getPeerSettings":{"comment":"Get peer settings","arguments":{"peer":"The peer"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.report":{"comment":"Report a message in a chat for violation of telegram's Terms of Service","arguments":{"peer":"Peer","id":"IDs of messages to report","reason":"Why are these messages being reported","message":"Comment for report moderation"},"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.getChats":{"comment":"Returns chat basic info on their IDs.","arguments":{"id":"List of chat IDs"},"throws":[{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"both"},"messages.getFullChat":{"comment":"Returns full chat info according to its ID.","arguments":{"chatId":"Chat ID"},"throws":[{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"both"},"messages.editChatTitle":{"comment":"Chanages chat name and sends a service message on it.","arguments":{"chatId":"Chat ID","title":"New chat name, different from the old one"},"throws":[{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":400,"name":"CHAT_TITLE_EMPTY","comment":"No chat title provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"both"},"messages.editChatPhoto":{"comment":"Changes chat photo and sends a service message on it","arguments":{"chatId":"Chat ID","photo":"Photo to be set"},"throws":[{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"PHOTO_CROP_SIZE_SMALL","comment":"Photo is too small."},{"code":400,"name":"PHOTO_EXT_INVALID","comment":"The extension of the photo is invalid."},{"code":400,"name":"PHOTO_INVALID","comment":"Photo invalid."}],"available":"both"},"messages.addChatUser":{"comment":"Adds a user to a chat and sends a service message on it.","arguments":{"chatId":"Chat ID","userId":"User ID to be added","fwdLimit":"Number of last messages to be forwarded"},"throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"USERS_TOO_MUCH","comment":"The maximum number of users has been exceeded (to create a chat, for example)."},{"code":400,"name":"USER_ALREADY_PARTICIPANT","comment":"The user is already in the group."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."},{"code":400,"name":"USER_IS_BLOCKED","comment":"You were blocked by this user."},{"code":403,"name":"USER_NOT_MUTUAL_CONTACT","comment":"The provided user is not a mutual contact."},{"code":403,"name":"USER_PRIVACY_RESTRICTED","comment":"The user's privacy settings do not allow you to do this."},{"code":400,"name":"YOU_BLOCKED_USER","comment":"You blocked this user."}],"available":"user"},"messages.deleteChatUser":{"comment":"Deletes a user from a chat and sends a service message on it.","arguments":{"flags":"Flags, see TL conditional fields","revokeHistory":"Remove the entire chat history of the specified user in this chat.","chatId":"Chat ID","userId":"User ID to be deleted"},"throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."},{"code":400,"name":"USER_NOT_PARTICIPANT","comment":"You're not a member of this supergroup/channel."}],"available":"both"},"messages.createChat":{"comment":"Creates a new chat.","arguments":{"users":"List of user IDs to be invited","title":"Chat name"},"throws":[{"code":400,"name":"CHAT_INVALID","comment":"Invalid chat."},{"code":400,"name":"CHAT_TITLE_EMPTY","comment":"No chat title provided."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"USERS_TOO_FEW","comment":"Not enough users (to create a chat, for example)."},{"code":403,"name":"USER_RESTRICTED","comment":"You're spamreported, you can't create channels or chats."}],"available":"user"},"messages.getDhConfig":{"comment":"Returns configuration parameters for Diffie-Hellman key generation. Can also return a random sequence of bytes of required length.","arguments":{"version":"Value of the version parameter from {@link messages.dhConfig}, avialable at the client","randomLength":"Length of the required random sequence"},"throws":[{"code":400,"name":"RANDOM_LENGTH_INVALID","comment":"Random length invalid."}],"available":"user"},"messages.requestEncryption":{"comment":"Sends a request to start a secret chat to the user.","arguments":{"userId":"User ID","randomId":"Unique client request ID required to prevent resending. This also doubles as the chat ID.","gA":"A = g ^ a mod p, see Wikipedia"},"throws":[{"code":400,"name":"DH_G_A_INVALID","comment":"g_a invalid."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"user"},"messages.acceptEncryption":{"comment":"Confirms creation of a secret chat","arguments":{"peer":"Secret chat ID","gB":"B = g ^ b mod p, see Wikipedia","keyFingerprint":"64-bit fingerprint of the received key"},"throws":[{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"ENCRYPTION_ALREADY_ACCEPTED","comment":"Secret chat already accepted."},{"code":400,"name":"ENCRYPTION_ALREADY_DECLINED","comment":"The secret chat was already declined."}],"available":"user"},"messages.discardEncryption":{"comment":"Cancels a request for creation and/or delete info on secret chat.","arguments":{"flags":"Flags, see TL conditional fields","deleteHistory":"Whether to delete the entire chat history for the other user as well","chatId":"Secret chat ID"},"throws":[{"code":400,"name":"CHAT_ID_EMPTY","comment":"The provided chat ID is empty."},{"code":400,"name":"ENCRYPTION_ALREADY_DECLINED","comment":"The secret chat was already declined."},{"code":400,"name":"ENCRYPTION_ID_INVALID","comment":"The provided secret chat ID is invalid."}],"available":"user"},"messages.setEncryptedTyping":{"comment":"Send typing event by the current user to a secret chat.","arguments":{"peer":"Secret chat ID","typing":"Typing.
Possible values:
{@link boolTrue}, if the user started typing and more than 5 seconds have passed since the last request
{@link boolFalse}, if the user stopped typing"},"throws":[{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."}],"available":"user"},"messages.readEncryptedHistory":{"comment":"Marks message history within a secret chat as read.","arguments":{"peer":"Secret chat ID","maxDate":"Maximum date value for received messages in history"},"throws":[{"code":400,"name":"MSG_WAIT_FAILED","comment":"A waiting call returned an error."}],"available":"user"},"messages.sendEncrypted":{"comment":"Sends a text message to a secret chat.","arguments":{"flags":"Flags, see TL conditional fields","silent":"Send encrypted message without a notification","peer":"Secret chat ID","randomId":"Unique client message ID, necessary to avoid message resending","data":"TL-serialization of DecryptedMessage type, encrypted with a key that was created during chat initialization"},"throws":[{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"DATA_INVALID","comment":"Encrypted data invalid."},{"code":400,"name":"ENCRYPTION_DECLINED","comment":"The secret chat was declined."},{"code":400,"name":"MSG_WAIT_FAILED","comment":"A waiting call returned an error."},{"code":403,"name":"USER_IS_BLOCKED","comment":"You were blocked by this user."}],"available":"user"},"messages.sendEncryptedFile":{"comment":"Sends a message with a file attachment to a secret chat","arguments":{"flags":"Flags, see TL conditional fields","silent":"Whether to send the file without triggering a notification","peer":"Secret chat ID","randomId":"Unique client message ID necessary to prevent message resending","data":"TL-serialization of DecryptedMessage type, encrypted with a key generated during chat initialization","file":"File attachment for the secret chat"},"throws":[{"code":400,"name":"DATA_TOO_LONG","comment":"Data too long."},{"code":400,"name":"ENCRYPTION_DECLINED","comment":"The secret chat was declined."},{"code":400,"name":"FILE_EMTPY","comment":"An empty file was provided."},{"code":400,"name":"MD5_CHECKSUM_INVALID","comment":"The MD5 checksums do not match."},{"code":400,"name":"MSG_WAIT_FAILED","comment":"A waiting call returned an error."}],"available":"user"},"messages.sendEncryptedService":{"comment":"Sends a service message to a secret chat.","arguments":{"peer":"Secret chat ID","randomId":"Unique client message ID required to prevent message resending","data":"TL-serialization of DecryptedMessage type, encrypted with a key generated during chat initialization"},"throws":[{"code":400,"name":"DATA_INVALID","comment":"Encrypted data invalid."},{"code":400,"name":"ENCRYPTION_DECLINED","comment":"The secret chat was declined."},{"code":400,"name":"ENCRYPTION_ID_INVALID","comment":"The provided secret chat ID is invalid."},{"code":400,"name":"MSG_WAIT_FAILED","comment":"A waiting call returned an error."},{"code":403,"name":"USER_DELETED","comment":"You can't send this secret message because the other participant deleted their account."},{"code":403,"name":"USER_IS_BLOCKED","comment":"You were blocked by this user."}],"available":"user"},"messages.receivedQueue":{"comment":"Confirms receipt of messages in a secret chat by client, cancels push notifications.","arguments":{"maxQts":"Maximum qts value available at the client"},"throws":[{"code":400,"name":"MAX_QTS_INVALID","comment":"The specified max_qts is invalid."},{"code":400,"name":"MSG_WAIT_FAILED","comment":"A waiting call returned an error."}],"available":"user"},"messages.reportEncryptedSpam":{"comment":"Report a secret chat for spam","arguments":{"peer":"The secret chat to report"},"throws":[{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."}],"available":"user"},"messages.readMessageContents":{"comment":"Notifies the sender about the recipient having listened a voice message or watched a video.","arguments":{"id":"Message ID list"},"available":"user"},"messages.getStickers":{"comment":"Get stickers by emoji","arguments":{"emoticon":"The emoji","hash":"Hash for pagination, for more info click here"},"throws":[{"code":400,"name":"EMOTICON_EMPTY","comment":"The emoji is empty."}],"available":"user"},"messages.getAllStickers":{"comment":"Get all installed stickers","arguments":{"hash":"Hash for pagination, for more info click here"},"available":"user"},"messages.getWebPagePreview":{"comment":"Get preview of webpage","arguments":{"flags":"Flags, see TL conditional fields","message":"Message from which to extract the preview","entities":"Message entities for styled text"},"throws":[{"code":400,"name":"MESSAGE_EMPTY","comment":"The provided message is empty."}],"available":"user"},"messages.exportChatInvite":{"comment":"Export an invite link for a chat","arguments":{"flags":"Flags, see TL conditional fields","legacyRevokePermanent":"Legacy flag, reproducing legacy behavior of this method: if set, revokes all previous links before creating a new one. Kept for bot API BC, should not be used by modern clients.","peer":"Chat","expireDate":"Expiration date","usageLimit":"Maximum number of users that can join using this link"},"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"EXPIRE_DATE_INVALID","comment":"The specified expiration date is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"USAGE_LIMIT_INVALID","comment":"The specified usage limit is invalid."}],"available":"both"},"messages.checkChatInvite":{"comment":"Check the validity of a chat invite link and get basic info about it","arguments":{"hash":"Invite hash in t.me/joinchat/hash"},"throws":[{"code":400,"name":"INVITE_HASH_EMPTY","comment":"The invite hash is empty."},{"code":400,"name":"INVITE_HASH_EXPIRED","comment":"The invite link has expired."},{"code":400,"name":"INVITE_HASH_INVALID","comment":"The invite hash is invalid."}],"available":"user"},"messages.importChatInvite":{"comment":"Import a chat invite and join a private chat/supergroup/channel","arguments":{"hash":"hash from t.me/joinchat/hash"},"throws":[{"code":400,"name":"CHANNELS_TOO_MUCH","comment":"You have joined too many channels/supergroups."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_INVALID","comment":"Invalid chat."},{"code":400,"name":"INVITE_HASH_EMPTY","comment":"The invite hash is empty."},{"code":400,"name":"INVITE_HASH_EXPIRED","comment":"The invite link has expired."},{"code":400,"name":"INVITE_HASH_INVALID","comment":"The invite hash is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"USERS_TOO_MUCH","comment":"The maximum number of users has been exceeded (to create a chat, for example)."},{"code":400,"name":"USER_ALREADY_PARTICIPANT","comment":"The user is already in the group."},{"code":400,"name":"USER_CHANNELS_TOO_MUCH","comment":"One of the users you tried to add is already in too many channels/supergroups."}],"available":"user"},"messages.getStickerSet":{"comment":"Get info about a stickerset","arguments":{"stickerset":"Stickerset"},"throws":[{"code":400,"name":"EMOTICON_STICKERPACK_MISSING","comment":" "},{"code":400,"name":"STICKERSET_INVALID","comment":"The provided sticker set is invalid."}],"available":"both"},"messages.installStickerSet":{"comment":"Install a stickerset","arguments":{"stickerset":"Stickerset to install","archived":"Whether to archive stickerset"},"throws":[{"code":400,"name":"STICKERSET_INVALID","comment":"The provided sticker set is invalid."}],"available":"user"},"messages.uninstallStickerSet":{"comment":"Uninstall a stickerset","arguments":{"stickerset":"The stickerset to uninstall"},"throws":[{"code":400,"name":"STICKERSET_INVALID","comment":"The provided sticker set is invalid."}],"available":"user"},"messages.startBot":{"comment":"Start a conversation with a bot using a deep linking parameter","arguments":{"bot":"The bot","peer":"The chat where to start the bot, can be the bot's private chat or a group","randomId":"Random ID to avoid resending the same message","startParam":"Deep linking parameter"},"throws":[{"code":400,"name":"BOT_INVALID","comment":"This is not a valid bot."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"START_PARAM_EMPTY","comment":"The start parameter is empty."},{"code":400,"name":"START_PARAM_INVALID","comment":"Start parameter invalid."},{"code":400,"name":"START_PARAM_TOO_LONG","comment":"Start parameter is too long."}],"available":"user"},"messages.getMessagesViews":{"comment":"Get and increase the view counter of a message sent or forwarded from a channel","arguments":{"peer":"Peer where the message was found","id":"ID of message","increment":"Whether to mark the message as viewed and increment the view counter"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.editChatAdmin":{"comment":"Make a user admin in a legacy group.","arguments":{"chatId":"The ID of the group","userId":"The user to make admin","isAdmin":"Whether to make them admin"},"throws":[{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."},{"code":400,"name":"USER_NOT_PARTICIPANT","comment":"You're not a member of this supergroup/channel."}],"available":"user"},"messages.migrateChat":{"comment":"Turn a legacy group into a supergroup","arguments":{"chatId":"Legacy group to migrate"},"throws":[{"code":400,"name":"CHANNELS_TOO_MUCH","comment":"You have joined too many channels/supergroups."},{"code":403,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.searchGlobal":{"comment":"Search for messages and peers globally","arguments":{"flags":"Flags, see TL conditional fields","folderId":"Peer folder ID, for more info click here","q":"Query","filter":"Global search filter","minDate":"If a positive value was specified, the method will return only messages with date bigger than min_date","maxDate":"If a positive value was transferred, the method will return only messages with date smaller than max_date","offsetRate":"Initially 0, then set to the {@link messages.messagesSlice}","offsetPeer":"Offsets for pagination, for more info click here","offsetId":"Offsets for pagination, for more info click here","limit":"Offsets for pagination, for more info click here"},"throws":[{"code":400,"name":"FOLDER_ID_INVALID","comment":"Invalid folder ID."},{"code":400,"name":"SEARCH_QUERY_EMPTY","comment":"The search query is empty."}],"available":"user"},"messages.reorderStickerSets":{"comment":"Reorder installed stickersets","arguments":{"flags":"Flags, see TL conditional fields","masks":"Reorder mask stickersets","order":"New stickerset order by stickerset IDs"},"available":"user"},"messages.getDocumentByHash":{"comment":"Get a document by its SHA256 hash, mainly used for gifs","arguments":{"sha256":"SHA256 of file","size":"Size of the file in bytes","mimeType":"Mime type"},"throws":[{"code":400,"name":"SHA256_HASH_INVALID","comment":"The provided SHA256 hash is invalid."}],"available":"both"},"messages.getSavedGifs":{"comment":"Get saved GIFs","arguments":{"hash":"Hash for pagination, for more info click here"},"available":"user"},"messages.saveGif":{"comment":"Add GIF to saved gifs list","arguments":{"id":"GIF to save","unsave":"Whether to remove GIF from saved gifs list"},"throws":[{"code":400,"name":"GIF_ID_INVALID","comment":"The provided GIF ID is invalid."}],"available":"user"},"messages.getInlineBotResults":{"comment":"Query an inline bot","arguments":{"flags":"Flags, see TL conditional fields","bot":"The bot to query","peer":"The currently opened chat","geoPoint":"The geolocation, if requested","query":"The query","offset":"The offset within the results, will be passed directly as-is to the bot."},"throws":[{"code":400,"name":"BOT_INLINE_DISABLED","comment":"This bot can't be used in inline mode."},{"code":400,"name":"BOT_INVALID","comment":"This is not a valid bot."},{"code":400,"name":"BOT_RESPONSE_TIMEOUT","comment":"A timeout occurred while fetching data from the bot."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":-503,"name":"Timeout","comment":"Timeout while fetching data."}],"available":"user"},"messages.setInlineBotResults":{"comment":"Answer an inline query, for bots only","arguments":{"flags":"Flags, see TL conditional fields","gallery":"Set this flag if the results are composed of media files","private":"Set this flag if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query","queryId":"Unique identifier for the answered query","results":"Vector of results for the inline query","cacheTime":"The maximum amount of time in seconds that the result of the inline query may be cached on the server. Defaults to 300.","nextOffset":"Pass the offset that a client should send in the next query with the same text to receive more results. Pass an empty string if there are no more results or if you don‘t support pagination. Offset length can’t exceed 64 bytes.","switchPm":"If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with a certain parameter."},"throws":[{"code":400,"name":"ARTICLE_TITLE_EMPTY","comment":"The title of the article is empty."},{"code":400,"name":"AUDIO_CONTENT_URL_EMPTY","comment":"The remote URL specified in the content field is empty."},{"code":400,"name":"AUDIO_TITLE_EMPTY","comment":"An empty audio title was provided."},{"code":400,"name":"BUTTON_DATA_INVALID","comment":"The data of one or more of the buttons you provided is invalid."},{"code":400,"name":"BUTTON_TYPE_INVALID","comment":"The type of one or more of the buttons you provided is invalid."},{"code":400,"name":"BUTTON_URL_INVALID","comment":"Button URL invalid."},{"code":400,"name":"DOCUMENT_INVALID","comment":"The specified document is invalid."},{"code":400,"name":"FILE_CONTENT_TYPE_INVALID","comment":"File content-type is invalid."},{"code":400,"name":"FILE_TITLE_EMPTY","comment":"An empty file title was specified."},{"code":400,"name":"GIF_CONTENT_TYPE_INVALID","comment":"GIF content-type invalid."},{"code":400,"name":"MESSAGE_EMPTY","comment":"The provided message is empty."},{"code":400,"name":"MESSAGE_TOO_LONG","comment":"The provided message is too long."},{"code":400,"name":"NEXT_OFFSET_INVALID","comment":"The specified offset is longer than 64 bytes."},{"code":400,"name":"PHOTO_CONTENT_TYPE_INVALID","comment":"Photo mime-type invalid."},{"code":400,"name":"PHOTO_CONTENT_URL_EMPTY","comment":"Photo URL invalid."},{"code":400,"name":"PHOTO_INVALID","comment":"Photo invalid."},{"code":400,"name":"PHOTO_THUMB_URL_EMPTY","comment":"Photo thumbnail URL is empty."},{"code":400,"name":"QUERY_ID_INVALID","comment":"The query ID is invalid."},{"code":400,"name":"REPLY_MARKUP_INVALID","comment":"The provided reply markup is invalid."},{"code":400,"name":"RESULTS_TOO_MUCH","comment":"Too many results were provided."},{"code":400,"name":"RESULT_ID_DUPLICATE","comment":"You provided a duplicate result ID."},{"code":400,"name":"RESULT_TYPE_INVALID","comment":"Result type invalid."},{"code":400,"name":"SEND_MESSAGE_MEDIA_INVALID","comment":"Invalid media provided."},{"code":400,"name":"SEND_MESSAGE_TYPE_INVALID","comment":"The message type is invalid."},{"code":400,"name":"START_PARAM_INVALID","comment":"Start parameter invalid."},{"code":400,"name":"STICKER_DOCUMENT_INVALID","comment":"The specified sticker document is invalid."},{"code":403,"name":"USER_BOT_INVALID","comment":"This method can only be called by a bot."},{"code":400,"name":"VIDEO_TITLE_EMPTY","comment":"The specified video title is empty."},{"code":400,"name":"WEBDOCUMENT_INVALID","comment":"Invalid webdocument URL provided."},{"code":400,"name":"WEBDOCUMENT_MIME_INVALID","comment":"Invalid webdocument mime type provided."},{"code":400,"name":"WEBDOCUMENT_SIZE_TOO_BIG","comment":"Webdocument is too big!"},{"code":400,"name":"WEBDOCUMENT_URL_INVALID","comment":"The specified webdocument URL is invalid."}],"available":"bot"},"messages.sendInlineBotResult":{"comment":"Send a result obtained using {@link messages.getInlineBotResults}.","arguments":{"flags":"Flags, see TL conditional fields","silent":"Whether to send the message silently (no notification will be triggered on the other client)","background":"Whether to send the message in background","clearDraft":"Whether to clear the draft","hideVia":"Whether to hide the via @botname in the resulting message (only for bot usernames encountered in the {@link config})","peer":"Destination","replyToMsgId":"ID of the message this message should reply to","randomId":"Random ID to avoid resending the same query","queryId":"Query ID from {@link messages.getInlineBotResults}","id":"Result ID from {@link messages.getInlineBotResults}","scheduleDate":"Scheduled message date for scheduled messages"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_RESTRICTED","comment":"You can't send messages in this chat, you were restricted."},{"code":403,"name":"CHAT_SEND_GAME_FORBIDDEN","comment":"You can't send a game to this chat."},{"code":403,"name":"CHAT_SEND_GIFS_FORBIDDEN","comment":"You can't send gifs in this chat."},{"code":403,"name":"CHAT_SEND_INLINE_FORBIDDEN","comment":"You can't send inline messages in this group."},{"code":403,"name":"CHAT_SEND_MEDIA_FORBIDDEN","comment":"You can't send media in this chat."},{"code":403,"name":"CHAT_SEND_STICKERS_FORBIDDEN","comment":"You can't send stickers in this chat."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"INLINE_RESULT_EXPIRED","comment":"The inline query expired."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MEDIA_EMPTY","comment":"The provided media object is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"QUERY_ID_EMPTY","comment":"The query ID is empty."},{"code":400,"name":"RESULT_ID_EMPTY","comment":"Result ID empty."},{"code":400,"name":"SCHEDULE_DATE_TOO_LATE","comment":"You can't schedule a message this far in the future."},{"code":400,"name":"SCHEDULE_TOO_MUCH","comment":"There are too many scheduled messages."},{"code":420,"name":"SLOWMODE_WAIT_X","comment":"Slowmode is enabled in this chat: you must wait for the specified number of seconds before sending another message to the chat."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","comment":"You're banned from sending messages in supergroups/channels."},{"code":400,"name":"WEBPAGE_CURL_FAILED","comment":"Failure while fetching the webpage with cURL."},{"code":400,"name":"WEBPAGE_MEDIA_EMPTY","comment":"Webpage media empty."},{"code":400,"name":"YOU_BLOCKED_USER","comment":"You blocked this user."}],"available":"user"},"messages.getMessageEditData":{"comment":"Find out if a media message's caption can be edited","arguments":{"peer":"Peer where the media was sent","id":"ID of message"},"throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":403,"name":"MESSAGE_AUTHOR_REQUIRED","comment":"Message author required."},{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.editMessage":{"comment":"Edit message","arguments":{"flags":"Flags, see TL conditional fields","noWebpage":"Disable webpage preview","peer":"Where was the message sent","id":"ID of the message to edit","message":"New message","media":"New attached media","replyMarkup":"Reply markup for inline keyboards","entities":"Message entities for styled text","scheduleDate":"Scheduled message date for scheduled messages"},"throws":[{"code":400,"name":"BUTTON_DATA_INVALID","comment":"The data of one or more of the buttons you provided is invalid."},{"code":400,"name":"BUTTON_TYPE_INVALID","comment":"The type of one or more of the buttons you provided is invalid."},{"code":400,"name":"BUTTON_URL_INVALID","comment":"Button URL invalid."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"ENTITIES_TOO_LONG","comment":"You provided too many styled message entities."},{"code":403,"name":"INLINE_BOT_REQUIRED","comment":"Only the inline bot can edit message."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MEDIA_CAPTION_TOO_LONG","comment":"The caption is too long."},{"code":400,"name":"MEDIA_GROUPED_INVALID","comment":"You tried to send media of different types in an album."},{"code":400,"name":"MEDIA_NEW_INVALID","comment":"The new media is invalid."},{"code":400,"name":"MEDIA_PREV_INVALID","comment":"Previous media invalid."},{"code":403,"name":"MESSAGE_AUTHOR_REQUIRED","comment":"Message author required."},{"code":400,"name":"MESSAGE_EDIT_TIME_EXPIRED","comment":"You can't edit this message anymore, too much time has passed since its creation."},{"code":400,"name":"MESSAGE_EMPTY","comment":"The provided message is empty."},{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"MESSAGE_NOT_MODIFIED","comment":"The message text has not changed."},{"code":400,"name":"MESSAGE_TOO_LONG","comment":"The provided message is too long."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"REPLY_MARKUP_INVALID","comment":"The provided reply markup is invalid."},{"code":400,"name":"SCHEDULE_DATE_INVALID","comment":"Invalid schedule date provided."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","comment":"You're banned from sending messages in supergroups/channels."}],"available":"both"},"messages.editInlineBotMessage":{"comment":"Edit an inline bot message","arguments":{"flags":"Flags, see TL conditional fields","noWebpage":"Disable webpage preview","id":"Sent inline message ID","message":"Message","media":"Media","replyMarkup":"Reply markup for inline keyboards","entities":"Message entities for styled text"},"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"MESSAGE_NOT_MODIFIED","comment":"The message text has not changed."}],"available":"both"},"messages.getBotCallbackAnswer":{"comment":"Press an inline callback button and get a callback answer from the bot","arguments":{"flags":"Flags, see TL conditional fields","game":"Whether this is a \"play game\" button","peer":"Where was the inline keyboard sent","msgId":"ID of the Message with the inline keyboard","data":"Callback data","password":"For buttons {@link keyboardButtonCallback}, the SRP payload generated using SRP."},"throws":[{"code":400,"name":"BOT_RESPONSE_TIMEOUT","comment":"A timeout occurred while fetching data from the bot."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"DATA_INVALID","comment":"Encrypted data invalid."},{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":-503,"name":"Timeout","comment":"Timeout while fetching data."}],"available":"user"},"messages.setBotCallbackAnswer":{"comment":"Set the callback answer to a user button press (bots only)","arguments":{"flags":"Flags, see TL conditional fields","alert":"Whether to show the message as a popup instead of a toast notification","queryId":"Query ID","message":"Popup to show","url":"URL to open","cacheTime":"Cache validity"},"throws":[{"code":400,"name":"MESSAGE_TOO_LONG","comment":"The provided message is too long."},{"code":400,"name":"QUERY_ID_INVALID","comment":"The query ID is invalid."},{"code":400,"name":"URL_INVALID","comment":"Invalid URL provided."}],"available":"both"},"messages.getPeerDialogs":{"comment":"Get dialog info of specified peers","arguments":{"peers":"Peers"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.saveDraft":{"comment":"Save a message draft associated to a chat.","arguments":{"flags":"Flags, see TL conditional fields","noWebpage":"Disable generation of the webpage preview","replyToMsgId":"Message ID the message should reply to","peer":"Destination of the message that should be sent","message":"The draft","entities":"Message entities for styled text"},"throws":[{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.getAllDrafts":{"comment":"Save get all message drafts.","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"messages.getFeaturedStickers":{"comment":"Get featured stickers","arguments":{"hash":"Hash for pagination, for more info click here"},"available":"user"},"messages.readFeaturedStickers":{"comment":"Mark new featured stickers as read","arguments":{"id":"IDs of stickersets to mark as read"},"available":"user"},"messages.getRecentStickers":{"comment":"Get recent stickers","arguments":{"flags":"Flags, see TL conditional fields","attached":"Get stickers recently attached to photo or video files","hash":"Hash for pagination, for more info click here"},"available":"user"},"messages.saveRecentSticker":{"comment":"Add/remove sticker from recent stickers list","arguments":{"flags":"Flags, see TL conditional fields","attached":"Whether to add/remove stickers recently attached to photo or video files","id":"Sticker","unsave":"Whether to save or unsave the sticker"},"throws":[{"code":400,"name":"STICKER_ID_INVALID","comment":"The provided sticker ID is invalid."}],"available":"user"},"messages.clearRecentStickers":{"comment":"Clear recent stickers","arguments":{"flags":"Flags, see TL conditional fields","attached":"Set this flag to clear the list of stickers recently attached to photo or video files"},"available":"user"},"messages.getArchivedStickers":{"comment":"Get all archived stickers","arguments":{"flags":"Flags, see TL conditional fields","masks":"Get mask stickers","offsetId":"Offsets for pagination, for more info click here","limit":"Maximum number of results to return, see pagination"},"available":"user"},"messages.getMaskStickers":{"comment":"Get installed mask stickers","arguments":{"hash":"Hash for pagination, for more info click here"},"available":"user"},"messages.getAttachedStickers":{"comment":"Get stickers attached to a photo or video","arguments":{"media":"Stickered media"},"available":"user"},"messages.setGameScore":{"comment":"Use this method to set the score of the specified user in a game sent as a normal message (bots only).","arguments":{"flags":"Flags, see TL conditional fields","editMessage":"Set this flag if the game message should be automatically edited to include the current scoreboard","force":"Set this flag if the high score is allowed to decrease. This can be useful when fixing mistakes or banning cheaters","peer":"Unique identifier of target chat","id":"Identifier of the sent message","userId":"User identifier","score":"New score"},"throws":[{"code":400,"name":"BOT_SCORE_NOT_MODIFIED","comment":"The score wasn't modified."},{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"USER_BOT_REQUIRED","comment":"This method can only be called by a bot."}],"available":"bot"},"messages.setInlineGameScore":{"comment":"Use this method to set the score of the specified user in a game sent as an inline message (bots only).","arguments":{"flags":"Flags, see TL conditional fields","editMessage":"Set this flag if the game message should be automatically edited to include the current scoreboard","force":"Set this flag if the high score is allowed to decrease. This can be useful when fixing mistakes or banning cheaters","id":"ID of the inline message","userId":"User identifier","score":"New score"},"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"USER_BOT_REQUIRED","comment":"This method can only be called by a bot."}],"available":"bot"},"messages.getGameHighScores":{"comment":"Get highscores of a game","arguments":{"peer":"Where was the game sent","id":"ID of message with game media attachment","userId":"Get high scores made by a certain user"},"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"USER_BOT_REQUIRED","comment":"This method can only be called by a bot."}],"available":"bot"},"messages.getInlineGameHighScores":{"comment":"Get highscores of a game sent using an inline bot","arguments":{"id":"ID of inline message","userId":"Get high scores of a certain user"},"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"USER_BOT_REQUIRED","comment":"This method can only be called by a bot."}],"available":"bot"},"messages.getCommonChats":{"comment":"Get chats in common with a user","arguments":{"userId":"User ID","maxId":"Maximum ID of chat to return (see pagination)","limit":"Maximum number of results to return, see pagination"},"throws":[{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"user"},"messages.getAllChats":{"comment":"Get all chats, channels and supergroups","arguments":{"exceptIds":"Except these chats/channels/supergroups"},"available":"user"},"messages.getWebPage":{"comment":"Get instant view page","arguments":{"url":"URL of IV page to fetch","hash":"Hash for pagination, for more info click here"},"throws":[{"code":400,"name":"WC_CONVERT_URL_INVALID","comment":"WC convert URL invalid."}],"available":"user"},"messages.toggleDialogPin":{"comment":"Pin/unpin a dialog","arguments":{"flags":"Flags, see TL conditional fields","pinned":"Whether to pin or unpin the dialog","peer":"The dialog to pin"},"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"PINNED_DIALOGS_TOO_MUCH","comment":"Too many pinned dialogs."}],"available":"user"},"messages.reorderPinnedDialogs":{"comment":"Reorder pinned dialogs","arguments":{"flags":"Flags, see TL conditional fields","force":"If set, dialogs pinned server-side but not present in the order field will be unpinned.","folderId":"Peer folder ID, for more info click here","order":"New dialog order"},"throws":[{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.getPinnedDialogs":{"comment":"Get pinned dialogs","arguments":{"folderId":"Peer folder ID, for more info click here"},"throws":[{"code":400,"name":"FOLDER_ID_INVALID","comment":"Invalid folder ID."}],"available":"user"},"messages.setBotShippingResults":{"comment":"If you sent an invoice requesting a shipping address and the parameter is_flexible was specified, the bot will receive an {@link updateBotShippingQuery} update. Use this method to reply to shipping queries.","arguments":{"flags":"Flags, see TL conditional fields","queryId":"Unique identifier for the query to be answered","error":"Error message in human readable form that explains why it is impossible to complete the order (e.g. \"Sorry, delivery to your desired address is unavailable'). Telegram will display this message to the user.","shippingOptions":"A vector of available shipping options."},"throws":[{"code":400,"name":"QUERY_ID_INVALID","comment":"The query ID is invalid."}],"available":"both"},"messages.setBotPrecheckoutResults":{"comment":"Once the user has confirmed their payment and shipping details, the bot receives an {@link updateBotPrecheckoutQuery} update.\nUse this method to respond to such pre-checkout queries.\nNote: Telegram must receive an answer within 10 seconds after the pre-checkout query was sent.","arguments":{"flags":"Flags, see TL conditional fields","success":"Set this flag if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order, otherwise do not set it, and set the error field, instead","queryId":"Unique identifier for the query to be answered","error":"Required if the success isn't set. Error message in human readable form that explains the reason for failure to proceed with the checkout (e.g. \"Sorry, somebody just bought the last of our amazing black T-shirts while you were busy filling out your payment details. Please choose a different color or garment!\"). Telegram will display this message to the user."},"throws":[{"code":400,"name":"ERROR_TEXT_EMPTY","comment":"The provided error message is empty."}],"available":"both"},"messages.uploadMedia":{"comment":"Upload a file and associate it to a chat (without actually sending it to the chat)","arguments":{"peer":"The chat, can be an {@link inputPeerEmpty} for bots","media":"File uploaded in chunks as described in files »"},"throws":[{"code":400,"name":"BOT_MISSING","comment":"This method can only be run by a bot."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_RESTRICTED","comment":"You can't send messages in this chat, you were restricted."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"FILE_PARTS_INVALID","comment":"The number of file parts is invalid."},{"code":400,"name":"IMAGE_PROCESS_FAILED","comment":"Failure while processing image."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MEDIA_INVALID","comment":"Media invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"PHOTO_EXT_INVALID","comment":"The extension of the photo is invalid."},{"code":400,"name":"PHOTO_INVALID_DIMENSIONS","comment":"The photo dimensions are invalid."},{"code":400,"name":"PHOTO_SAVE_FILE_INVALID","comment":"Internal issues, try again later."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","comment":"You're banned from sending messages in supergroups/channels."},{"code":400,"name":"WEBPAGE_CURL_FAILED","comment":"Failure while fetching the webpage with cURL."}],"available":"both"},"messages.sendScreenshotNotification":{"comment":"Notify the other user in a private chat that a screenshot of the chat was taken","arguments":{"peer":"Other user","replyToMsgId":"ID of message that was screenshotted, can be 0","randomId":"Random ID to avoid message resending"},"throws":[{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"YOU_BLOCKED_USER","comment":"You blocked this user."}],"available":"user"},"messages.getFavedStickers":{"comment":"Get faved stickers","arguments":{"hash":"Hash for pagination, for more info click here"},"available":"user"},"messages.faveSticker":{"comment":"Mark a sticker as favorite","arguments":{"id":"Sticker to mark as favorite","unfave":"Unfavorite"},"throws":[{"code":400,"name":"STICKER_ID_INVALID","comment":"The provided sticker ID is invalid."}],"available":"user"},"messages.getUnreadMentions":{"comment":"Get unread messages where we were mentioned","arguments":{"peer":"Peer where to look for mentions","offsetId":"Offsets for pagination, for more info click here","addOffset":"Offsets for pagination, for more info click here","limit":"Maximum number of results to return, see pagination","maxId":"Maximum message ID to return, see pagination","minId":"Minimum message ID to return, see pagination"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.readMentions":{"comment":"Mark mentions as read","arguments":{"peer":"Dialog"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.getRecentLocations":{"comment":"Get live location history of a certain user","arguments":{"peer":"User","limit":"Maximum number of results to return, see pagination","hash":"Hash for pagination, for more info click here"},"available":"user"},"messages.sendMultiMedia":{"comment":"Send an album or grouped media","arguments":{"flags":"Flags, see TL conditional fields","silent":"Whether to send the album silently (no notification triggered)","background":"Send in background?","clearDraft":"Whether to clear drafts","peer":"The destination chat","replyToMsgId":"The message to reply to","multiMedia":"The medias to send","scheduleDate":"Scheduled message date for scheduled messages"},"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"MEDIA_CAPTION_TOO_LONG","comment":"The caption is too long."},{"code":400,"name":"MEDIA_EMPTY","comment":"The provided media object is invalid."},{"code":400,"name":"MEDIA_INVALID","comment":"Media invalid."},{"code":400,"name":"MULTI_MEDIA_TOO_LONG","comment":"Too many media files for album."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"RANDOM_ID_EMPTY","comment":"Random ID empty."},{"code":400,"name":"SCHEDULE_DATE_TOO_LATE","comment":"You can't schedule a message this far in the future."},{"code":400,"name":"SCHEDULE_TOO_MUCH","comment":"There are too many scheduled messages."},{"code":420,"name":"SLOWMODE_WAIT_X","comment":"Slowmode is enabled in this chat: wait X seconds before sending another message to this chat."}],"available":"both"},"messages.uploadEncryptedFile":{"comment":"Upload encrypted file and associate it to a secret chat","arguments":{"peer":"The secret chat to associate the file to","file":"The file"},"available":"user"},"messages.searchStickerSets":{"comment":"Search for stickersets","arguments":{"flags":"Flags, see TL conditional fields","excludeFeatured":"Exclude featured stickersets from results","q":"Query string","hash":"Hash for pagination, for more info click here"},"available":"user"},"messages.getSplitRanges":{"comment":"Get message ranges for saving the user's chat history","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"messages.markDialogUnread":{"comment":"Manually mark dialog as unread","arguments":{"flags":"Flags, see TL conditional fields","unread":"Mark as unread/read","peer":"Dialog"},"available":"user"},"messages.getDialogUnreadMarks":{"comment":"Get dialogs manually marked as unread","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"messages.clearAllDrafts":{"comment":"Clear all drafts.","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"messages.updatePinnedMessage":{"comment":"Pin a message","arguments":{"flags":"Flags, see TL conditional fields","silent":"Pin the message silently, without triggering a notification","unpin":"Whether the message should unpinned or pinned","pmOneside":"Whether the message should only be pinned on the local side of a one-to-one chat","peer":"The peer where to pin the message","id":"The message to pin or unpin"},"throws":[{"code":400,"name":"BOT_ONESIDE_NOT_AVAIL","comment":"Bots can't pin messages in PM just for themselves."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"PIN_RESTRICTED","comment":"You can't pin messages."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","comment":"You're banned from sending messages in supergroups/channels."}],"available":"both"},"messages.sendVote":{"comment":"Vote in a {@link poll}","arguments":{"peer":"The chat where the poll was sent","msgId":"The message ID of the poll","options":"The options that were chosen"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"MESSAGE_POLL_CLOSED","comment":"Poll closed."},{"code":400,"name":"OPTIONS_TOO_MUCH","comment":"Too many options provided."},{"code":400,"name":"OPTION_INVALID","comment":"Invalid option selected."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"REVOTE_NOT_ALLOWED","comment":"You cannot change your vote."}],"available":"user"},"messages.getPollResults":{"comment":"Get poll results","arguments":{"peer":"Peer where the poll was found","msgId":"Message ID of poll message"},"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."}],"available":"user"},"messages.getOnlines":{"comment":"Get count of online users in a chat","arguments":{"peer":"The chat"},"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.editChatAbout":{"comment":"Edit the description of a group/supergroup/channel.","arguments":{"peer":"The group/supergroup/channel.","about":"The new description"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ABOUT_NOT_MODIFIED","comment":"About text has not changed."},{"code":400,"name":"CHAT_ABOUT_TOO_LONG","comment":"Chat about too long."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"both"},"messages.editChatDefaultBannedRights":{"comment":"Edit the default banned rights of a channel/supergroup/group.","arguments":{"peer":"The peer","bannedRights":"The new global rights"},"throws":[{"code":400,"name":"BANNED_RIGHTS_INVALID","comment":"You provided some invalid flags in the banned rights."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"UNTIL_DATE_INVALID","comment":"Invalid until date provided."}],"available":"both"},"messages.getEmojiKeywords":{"comment":"Get localized emoji keywords","arguments":{"langCode":"Language code"},"available":"user"},"messages.getEmojiKeywordsDifference":{"comment":"Get changed emoji keywords","arguments":{"langCode":"Language code","fromVersion":"Previous emoji keyword localization version"},"available":"user"},"messages.getEmojiKeywordsLanguages":{"comment":"Get info about an emoji keyword localization","arguments":{"langCodes":"Language codes"},"available":"user"},"messages.getEmojiURL":{"comment":"Returns an HTTP URL which can be used to automatically log in into translation platform and suggest new emoji replacements. The URL will be valid for 30 seconds after generation","arguments":{"langCode":"Language code for which the emoji replacements will be suggested"},"available":"user"},"messages.getSearchCounters":{"comment":"Get the number of results that would be found by a {@link messages.search} call with the same parameters","arguments":{"peer":"Peer where to search","filters":"Search filters"},"throws":[{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.requestUrlAuth":{"comment":"Get more info about a Seamless Telegram Login authorization request, for more info click here »","arguments":{"flags":"Flags, see TL conditional fields","peer":"Peer where the message is located","msgId":"The message","buttonId":"The ID of the button with the authorization request","url":"URL used for link URL authorization, click here for more info »"},"available":"user"},"messages.acceptUrlAuth":{"comment":"Use this to accept a Seamless Telegram Login authorization request, for more info click here »","arguments":{"flags":"Flags, see TL conditional fields","writeAllowed":"Set this flag to allow the bot to send messages to you (if requested)","peer":"The location of the message","msgId":"Message ID of the message with the login button","buttonId":"ID of the login button","url":"URL used for link URL authorization, click here for more info »"},"available":"user"},"messages.hidePeerSettingsBar":{"comment":"Should be called after the user hides the report spam/add as contact bar of a new chat, effectively prevents the user from executing the actions specified in the {@link peerSettings}.","arguments":{"peer":"Peer"},"available":"user"},"messages.getScheduledHistory":{"comment":"Get scheduled messages","arguments":{"peer":"Peer","hash":"Hash for pagination, for more info click here"},"throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.getScheduledMessages":{"comment":"Get scheduled messages","arguments":{"peer":"Peer","id":"IDs of scheduled messages"},"throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.sendScheduledMessages":{"comment":"Send scheduled messages right away","arguments":{"peer":"Peer","id":"Scheduled message IDs"},"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.deleteScheduledMessages":{"comment":"Delete scheduled messages","arguments":{"peer":"Peer","id":"Scheduled message IDs"},"available":"user"},"messages.getPollVotes":{"comment":"Get poll results for non-anonymous polls","arguments":{"flags":"Flags, see TL conditional fields","peer":"Chat where the poll was sent","id":"Message ID","option":"Get only results for the specified poll option","offset":"Offset for results, taken from the next_offset field of {@link messages.votesList}, initially an empty string.
Note: if no more results are available, the method call will return an empty next_offset; thus, avoid providing the next_offset returned in {@link messages.votesList} if it is empty, to avoid an infinite loop.","limit":"Number of results to return"},"throws":[{"code":403,"name":"BROADCAST_FORBIDDEN","comment":"Participants of polls in channels should stay anonymous."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":403,"name":"POLL_VOTE_REQUIRED","comment":"Cast a vote in the poll before calling this method."}],"available":"user"},"messages.toggleStickerSets":{"comment":"Apply changes to multiple stickersets","arguments":{"flags":"Flags, see TL conditional fields","uninstall":"Uninstall the specified stickersets","archive":"Archive the specified stickersets","unarchive":"Unarchive the specified stickersets","stickersets":"Stickersets to act upon"},"available":"user"},"messages.getDialogFilters":{"comment":"Get folders","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"messages.getSuggestedDialogFilters":{"comment":"Get suggested folders","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"messages.updateDialogFilter":{"comment":"Update folder","arguments":{"flags":"Flags, see TL conditional fields","id":"Folder ID","filter":"Folder info"},"throws":[{"code":400,"name":"FILTER_ID_INVALID","comment":"The specified filter ID is invalid."},{"code":400,"name":"FILTER_INCLUDE_EMPTY","comment":"The include_peers vector of the filter is empty."},{"code":400,"name":"FILTER_TITLE_EMPTY","comment":"The title field of the filter is empty."}],"available":"user"},"messages.updateDialogFiltersOrder":{"comment":"Reorder folders","arguments":{"order":"New folder order"},"available":"user"},"messages.getOldFeaturedStickers":{"comment":"Method for fetching previously featured stickers","arguments":{"offset":"Offset","limit":"Maximum number of results to return, see pagination","hash":"Hash for pagination, for more info click here"},"available":"user"},"messages.getReplies":{"comment":"Get messages in a reply thread","arguments":{"peer":"Peer","msgId":"Message ID","offsetId":"Offsets for pagination, for more info click here","offsetDate":"Offsets for pagination, for more info click here","addOffset":"Offsets for pagination, for more info click here","limit":"Maximum number of results to return, see pagination","maxId":"If a positive value was transferred, the method will return only messages with ID smaller than max_id","minId":"If a positive value was transferred, the method will return only messages with ID bigger than min_id","hash":"Hash for pagination, for more info click here"},"throws":[{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.getDiscussionMessage":{"comment":"Get discussion message from the associated discussion group of a channel to show it on top of the comment section, without actually joining the group","arguments":{"peer":"Channel ID","msgId":"Message ID"},"throws":[{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.readDiscussion":{"comment":"Mark a thread as read","arguments":{"peer":"Group ID","msgId":"ID of message that started the thread","readMaxId":"ID up to which thread messages were read"},"throws":[{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.unpinAllMessages":{"comment":"Unpin all pinned messages","arguments":{"peer":"Chat where to unpin"},"available":"both"},"messages.deleteChat":{"comment":"Delete a chat","arguments":{"chatId":"Chat ID"},"throws":[{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"user"},"messages.deletePhoneCallHistory":{"comment":"Delete the entire phone call history.","arguments":{"flags":"Flags, see TL conditional fields","revoke":"Whether to remove phone call history for participants as well"},"available":"user"},"messages.checkHistoryImport":{"comment":"Obtains information about a chat export file, generated by a foreign chat app, click here for more info about imported chats ».","arguments":{"importHead":"Beginning of the message file; up to 100 lines."},"available":"user"},"messages.initHistoryImport":{"comment":"Import chat history from a foreign chat app into a specific Telegram chat, click here for more info about imported chats ».","arguments":{"peer":"The Telegram chat where the history should be imported.","file":"File with messages to import.","mediaCount":"Number of media files associated with the chat that will be uploaded using {@link messages.uploadImportedMedia}."},"throws":[{"code":400,"name":"IMPORT_FILE_INVALID","comment":"The specified chat export file is invalid."},{"code":400,"name":"IMPORT_FORMAT_UNRECOGNIZED","comment":"The specified chat export file was exported from an unsupported chat app."},{"code":406,"name":"PREVIOUS_CHAT_IMPORT_ACTIVE_WAIT_5MIN","comment":"Import for this chat is already in progress, wait 5 minutes before starting a new one."}],"available":"user"},"messages.uploadImportedMedia":{"comment":"Upload a media file associated with an imported chat, click here for more info ».","arguments":{"peer":"The Telegram chat where the media will be imported","importId":"Identifier of a history import session, returned by {@link messages.initHistoryImport}","fileName":"File name","media":"Media metadata"},"available":"user"},"messages.startHistoryImport":{"comment":"Complete the history import process, importing all messages into the chat.\nTo be called only after initializing the import with {@link messages.initHistoryImport} and uploading all files using {@link messages.uploadImportedMedia}.","arguments":{"peer":"The Telegram chat where the messages should be imported, click here for more info »","importId":"Identifier of a history import session, returned by {@link messages.initHistoryImport}."},"throws":[{"code":400,"name":"IMPORT_ID_INVALID","comment":"The specified import ID is invalid."}],"available":"user"},"messages.getExportedChatInvites":{"comment":"Get info about the chat invites of a specific chat","arguments":{"flags":"Flags, see TL conditional fields","revoked":"Whether to fetch revoked chat invites","peer":"Chat","adminId":"Whether to only fetch chat invites from this admin","offsetDate":"Offsets for pagination, for more info click here","offsetLink":"Offsets for pagination, for more info click here","limit":"Maximum number of results to return, see pagination"},"available":"user"},"messages.getExportedChatInvite":{"comment":"Get info about a chat invite","arguments":{"peer":"Chat","link":"Invite link"},"available":"user"},"messages.editExportedChatInvite":{"comment":"Edit an exported chat invite","arguments":{"flags":"Flags, see TL conditional fields","revoked":"Whether to revoke the chat invite","peer":"Chat","link":"Invite link","expireDate":"New expiration date","usageLimit":"Maximum number of users that can join using this link"},"throws":[{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"both"},"messages.deleteRevokedExportedChatInvites":{"comment":"Delete all revoked chat invites","arguments":{"peer":"Chat","adminId":"ID of the admin that originally generated the revoked chat invites"},"available":"user"},"messages.deleteExportedChatInvite":{"comment":"Delete a chat invite","arguments":{"peer":"Peer","link":"Invite link"},"available":"user"},"messages.getAdminsWithInvites":{"comment":"Get info about chat invites generated by admins.","arguments":{"peer":"Chat"},"available":"user"},"messages.getChatInviteImporters":{"comment":"Get info about the users that joined the chat using a specific chat invite","arguments":{"peer":"Chat","link":"Invite link","offsetDate":"Offsets for pagination, for more info click here","offsetUser":"User ID for pagination","limit":"Maximum number of results to return, see pagination"},"available":"user"},"messages.setHistoryTTL":{"comment":"Set maximum Time-To-Live of all messages in the specified chat","arguments":{"peer":"The dialog","period":"Automatically delete all messages sent in the chat after this many seconds"},"throws":[{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":400,"name":"TTL_PERIOD_INVALID","comment":"The specified TTL period is invalid."}],"available":"user"},"messages.checkHistoryImportPeer":{"comment":"If the check succeeds, and no RPC errors are returned, a messages.CheckedHistoryImportPeer constructor will be returned, with a confirmation text to be shown to the user, before actually initializing the import.\n\nCheck whether chat history exported from another chat app can be imported into a specific Telegram chat, click here for more info ».","arguments":{"peer":"The chat where we want to import history »."},"throws":[{"code":400,"name":"USER_NOT_MUTUAL_CONTACT","comment":"The provided user is not a mutual contact."}],"available":"user"},"messages.setChatTheme":{"comment":"Change the chat theme of a certain chat","arguments":{"peer":"Private chat where to change theme","emoticon":"Emoji, identifying a specific chat theme; a list of chat themes can be fetched using {@link account.getChatThemes}"},"throws":[{"code":400,"name":"EMOJI_INVALID","comment":"The specified theme emoji is valid."},{"code":400,"name":"EMOJI_NOT_MODIFIED","comment":"The theme wasn't changed."}],"available":"user"},"messages.getMessageReadParticipants":{"comment":"Get which users read a specific message: only available for groups and supergroups with less than chat_read_mark_size_threshold members, read receipts will be stored for chat_read_mark_expire_period seconds after the message was sent, see client configuration for more info ».","arguments":{"peer":"Dialog","msgId":"Message ID"},"throws":[{"code":400,"name":"CHAT_TOO_BIG","comment":"This method is not available for groups with more than chat_read_mark_size_threshold members, see client configuration »."}],"available":"user"},"updates.getState":{"comment":"Returns a current state of updates.","available":"both","arguments":{"gigagroup":"Is this a broadcast group?"}},"updates.getDifference":{"comment":"Get new updates.","arguments":{"flags":"Flags, see TL conditional fields","pts":"PTS, see updates.","ptsTotalLimit":"For fast updating: if provided and pts + pts_total_limit < remote pts, {@link updates.differenceTooLong} will be returned.
Simply tells the server to not return the difference if it is bigger than pts_total_limit
If the remote pts is too big (> ~4000000), this field will default to 1000000","date":"date, see updates.","qts":"QTS, see updates."},"throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","comment":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"CDN_METHOD_INVALID","comment":"You can't call this method in a CDN DC."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"DATE_EMPTY","comment":"Date empty."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PERSISTENT_TIMESTAMP_EMPTY","comment":"Persistent timestamp empty."},{"code":400,"name":"PERSISTENT_TIMESTAMP_INVALID","comment":"Persistent timestamp invalid."}],"available":"both"},"updates.getChannelDifference":{"comment":"Returns the difference between the current state of updates of a certain channel and transmitted.","arguments":{"flags":"Flags, see TL conditional fields","force":"Set to true to skip some possibly unneeded updates and reduce server-side load","channel":"The channel","filter":"Messsage filter","pts":"Persistent timestamp (see updates)","limit":"How many updates to fetch, max 100000
Ordinary (non-bot) users are supposed to pass 10-100"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":403,"name":"CHANNEL_PUBLIC_GROUP_NA","comment":"channel/supergroup not available."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"FROM_MESSAGE_BOT_DISABLED","comment":"Bots can't use fromMessage min constructors."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PERSISTENT_TIMESTAMP_EMPTY","comment":"Persistent timestamp empty."},{"code":400,"name":"PERSISTENT_TIMESTAMP_INVALID","comment":"Persistent timestamp invalid."},{"code":400,"name":"PINNED_DIALOGS_TOO_MUCH","comment":"Too many pinned dialogs."},{"code":400,"name":"RANGES_INVALID","comment":"Invalid range provided."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","comment":"You're banned from sending messages in supergroups/channels."}],"available":"both"},"photos.updateProfilePhoto":{"comment":"Installs a previously uploaded photo as a profile photo.","arguments":{"id":"Input photo"},"throws":[{"code":400,"name":"ALBUM_PHOTOS_TOO_MANY","comment":"Too many."},{"code":400,"name":"FILE_PARTS_INVALID","comment":"The number of file parts is invalid."},{"code":400,"name":"IMAGE_PROCESS_FAILED","comment":"Failure while processing image."},{"code":400,"name":"LOCATION_INVALID","comment":"The provided location is invalid."},{"code":400,"name":"PHOTO_CROP_SIZE_SMALL","comment":"Photo is too small."},{"code":400,"name":"PHOTO_EXT_INVALID","comment":"The extension of the photo is invalid."},{"code":400,"name":"PHOTO_ID_INVALID","comment":"Photo ID invalid."}],"available":"user"},"photos.uploadProfilePhoto":{"comment":"Updates current user profile photo.","arguments":{"flags":"Flags, see TL conditional fields","file":"File saved in parts by means of {@link upload.saveFilePart} method","video":"Animated profile picture video","videoStartTs":"Floating point UNIX timestamp in seconds, indicating the frame of the video that should be used as static preview."},"throws":[{"code":400,"name":"ALBUM_PHOTOS_TOO_MANY","comment":"Too many ."},{"code":400,"name":"FILE_PARTS_INVALID","comment":"The number of file parts is invalid."},{"code":400,"name":"IMAGE_PROCESS_FAILED","comment":"Failure while processing image."},{"code":400,"name":"PHOTO_CROP_FILE_MISSING","comment":"Photo crop file missing."},{"code":400,"name":"PHOTO_CROP_SIZE_SMALL","comment":"Photo is too small."},{"code":400,"name":"PHOTO_EXT_INVALID","comment":"The extension of the photo is invalid."},{"code":400,"name":"PHOTO_FILE_MISSING","comment":"Profile photo file missing."},{"code":400,"name":"VIDEO_FILE_INVALID","comment":"The specified video file is invalid."}],"available":"user"},"photos.deletePhotos":{"comment":"Deletes profile photos.","arguments":{"id":"Input photos to delete"},"available":"user"},"photos.getUserPhotos":{"comment":"Returns the list of user photos.","arguments":{"userId":"User ID","offset":"Number of list elements to be skipped","maxId":"If a positive value was transferred, the method will return only photos with IDs less than the set one","limit":"Number of list elements to be returned"},"throws":[{"code":400,"name":"MAX_ID_INVALID","comment":"The provided max ID is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"both"},"upload.saveFilePart":{"comment":"Saves a part of file for futher sending to one of the methods.","arguments":{"fileId":"Random file identifier created by the client","filePart":"Numerical order of a part","bytes":"Binary data, contend of a part"},"throws":[{"code":400,"name":"FILE_PART_EMPTY","comment":"The provided file part is empty."},{"code":400,"name":"FILE_PART_INVALID","comment":"The file part number is invalid."}],"available":"both"},"upload.getFile":{"comment":"Returns content of a whole file or its part.","arguments":{"flags":"Flags, see TL conditional fields","precise":"Disable some checks on limit and offset values, useful for example to stream videos by keyframes","cdnSupported":"Whether the current client supports CDN downloads","location":"File location","offset":"Number of bytes to be skipped","limit":"Number of bytes to be returned"},"throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","comment":"The temporary auth key must be binded to the permanent auth key to use these methods."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":406,"name":"FILEREF_UPGRADE_NEEDED","comment":"The client has to be updated in order to support file references."},{"code":400,"name":"FILE_ID_INVALID","comment":"The provided file id is invalid."},{"code":400,"name":"FILE_REFERENCE_*","comment":"The file reference expired, it must be refreshed."},{"code":400,"name":"FILE_REFERENCE_EXPIRED","comment":"File reference expired, it must be refetched as described in https://core.telegram.org/api/file_reference."},{"code":400,"name":"LIMIT_INVALID","comment":"The provided limit is invalid."},{"code":400,"name":"LOCATION_INVALID","comment":"The provided location is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"OFFSET_INVALID","comment":"The provided offset is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."}],"available":"both"},"upload.saveBigFilePart":{"comment":"Saves a part of a large file (over 10Mb in size) to be later passed to one of the methods.","arguments":{"fileId":"Random file id, created by the client","filePart":"Part sequence number","fileTotalParts":"Total number of parts","bytes":"Binary data, part contents"},"throws":[{"code":400,"name":"FILE_PARTS_INVALID","comment":"The number of file parts is invalid."},{"code":400,"name":"FILE_PART_EMPTY","comment":"The provided file part is empty."},{"code":400,"name":"FILE_PART_INVALID","comment":"The file part number is invalid."},{"code":400,"name":"FILE_PART_SIZE_CHANGED","comment":"Provided file part size has changed."},{"code":400,"name":"FILE_PART_SIZE_INVALID","comment":"The provided file part size is invalid."},{"code":400,"name":"FILE_PART_TOO_BIG","comment":"The uploaded file part is too big."}],"available":"both"},"upload.getWebFile":{"comment":"Returns content of an HTTP file or a part, by proxying the request through telegram.","arguments":{"location":"The file to download","offset":"Number of bytes to be skipped","limit":"Number of bytes to be returned"},"throws":[{"code":400,"name":"LOCATION_INVALID","comment":"The provided location is invalid."}],"available":"user"},"upload.getCdnFile":{"comment":"Download a CDN file.","arguments":{"fileToken":"File token","offset":"Offset of chunk to download","limit":"Length of chunk to download"},"available":"user"},"upload.reuploadCdnFile":{"comment":"Request a reupload of a certain file to a CDN DC.","arguments":{"fileToken":"File token","requestToken":"Request token"},"throws":[{"code":400,"name":"RSA_DECRYPT_FAILED","comment":"Internal RSA decryption failed."}],"available":"both"},"upload.getCdnFileHashes":{"comment":"Get SHA256 hashes for verifying downloaded CDN files","arguments":{"fileToken":"File","offset":"Offset from which to start getting hashes"},"throws":[{"code":400,"name":"CDN_METHOD_INVALID","comment":"You can't call this method in a CDN DC."},{"code":400,"name":"RSA_DECRYPT_FAILED","comment":"Internal RSA decryption failed."}],"available":"both"},"upload.getFileHashes":{"comment":"Get SHA256 hashes for verifying downloaded files","arguments":{"location":"File","offset":"Offset from which to get file hashes"},"throws":[{"code":400,"name":"LOCATION_INVALID","comment":"The provided location is invalid."}],"available":"both"},"help.getConfig":{"comment":"Returns current configuration, including data center configuration.","throws":[{"code":400,"name":"CONNECTION_API_ID_INVALID","comment":"The provided API id is invalid."},{"code":400,"name":"CONNECTION_APP_VERSION_EMPTY","comment":"App version is empty."},{"code":400,"name":"CONNECTION_LAYER_INVALID","comment":"Layer invalid."},{"code":400,"name":"CONNECTION_NOT_INITED","comment":"Connection not initialized."},{"code":400,"name":"DATA_INVALID","comment":"Encrypted data invalid."},{"code":400,"name":"INPUT_LAYER_INVALID","comment":"The provided layer is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"USERNAME_INVALID","comment":"The provided username is not valid."},{"code":403,"name":"USER_PRIVACY_RESTRICTED","comment":"The user's privacy settings do not allow you to do this."}],"available":"both","arguments":{"gigagroup":"Is this a broadcast group?"}},"help.getNearestDc":{"comment":"Returns info on data centre nearest to the user.","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"help.getAppUpdate":{"comment":"Returns information on update availability for the current application.","arguments":{"source":"Source"},"available":"user"},"help.getInviteText":{"comment":"Returns localized text of a text message with an invitation.","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"help.getSupport":{"comment":"Returns the support user for the 'ask a question' feature.","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"help.getAppChangelog":{"comment":"Get changelog of current app.\nTypically, an {@link updates} constructor will be returned, containing one or more {@link updateServiceNotification} updates with app-specific changelogs.","arguments":{"prevAppVersion":"Previous app version"},"available":"user"},"help.setBotUpdatesStatus":{"comment":"Informs the server about the number of pending bot updates if they haven't been processed for a long time; for bots only","arguments":{"pendingUpdatesCount":"Number of pending updates","message":"Error message, if present"},"available":"bot"},"help.getCdnConfig":{"comment":"Get configuration for CDN file downloads.","throws":[{"code":401,"name":"AUTH_KEY_PERM_EMPTY","comment":"The temporary auth key must be binded to the permanent auth key to use these methods."}],"available":"both","arguments":{"gigagroup":"Is this a broadcast group?"}},"help.getRecentMeUrls":{"comment":"Get recently used t.me links","arguments":{"referer":"Referer"},"available":"user"},"help.getTermsOfServiceUpdate":{"comment":"Look for updates of telegram's terms of service","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"help.acceptTermsOfService":{"comment":"Accept the new terms of service","arguments":{"id":"ID of terms of service"},"available":"user"},"help.getDeepLinkInfo":{"comment":"Get info about a t.me link","arguments":{"path":"Path in t.me/path"},"available":"user"},"help.getAppConfig":{"comment":"Get app-specific configuration, see client configuration for more info on the result.","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"help.saveAppLog":{"comment":"Saves logs of application on the server.","arguments":{"events":"List of input events"},"available":"user"},"help.getPassportConfig":{"comment":"Get passport configuration","arguments":{"hash":"Hash for pagination, for more info click here"},"available":"user"},"help.getSupportName":{"comment":"Get localized name of the telegram support user","throws":[{"code":403,"name":"USER_INVALID","comment":"Invalid user provided."}],"available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"help.getUserInfo":{"comment":"Internal use","arguments":{"userId":"User ID"},"throws":[{"code":403,"name":"USER_INVALID","comment":"Invalid user provided."}],"available":"user"},"help.editUserInfo":{"comment":"Internal use","arguments":{"userId":"User","message":"Message","entities":"Message entities for styled text"},"throws":[{"code":400,"name":"USER_INVALID","comment":"Invalid user provided."}],"available":"user"},"help.getPromoData":{"comment":"Get MTProxy/Public Service Announcement information","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"help.hidePromoData":{"comment":"Hide MTProxy/Public Service Announcement information","arguments":{"peer":"Peer to hide"},"available":"user"},"help.dismissSuggestion":{"comment":"Dismiss a suggestion, see here for more info ».","arguments":{"peer":"In the case of pending suggestions in {@link channelFull}, the channel ID.","suggestion":"Suggestion, see here for more info »."},"available":"user"},"help.getCountriesList":{"comment":"Get name, ISO code, localized name and phone codes/patterns of all available countries","arguments":{"langCode":"Language code of the current user","hash":"Hash for pagination, for more info click here"},"available":"user"},"channels.readHistory":{"comment":"Mark channel/supergroup history as read","arguments":{"channel":"Channel/supergroup","maxId":"ID of message up to which messages should be marked as read"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"user"},"channels.deleteMessages":{"comment":"Delete messages in a channel/supergroup","arguments":{"channel":"Channel/supergroup","id":"IDs of messages to delete"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":403,"name":"MESSAGE_DELETE_FORBIDDEN","comment":"You can't delete one of the messages you tried to delete, most likely because it is a service message."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"both"},"channels.deleteUserHistory":{"comment":"Delete all messages sent by a certain user in a supergroup","arguments":{"channel":"Supergroup","userId":"User whose messages should be deleted"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"user"},"channels.reportSpam":{"comment":"Reports some messages from a user in a supergroup as spam; requires administrator rights in the supergroup","arguments":{"channel":"Supergroup","userId":"ID of the user that sent the spam messages","id":"IDs of spam messages"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"user"},"channels.getMessages":{"comment":"Get channel/supergroup messages","arguments":{"channel":"Channel/supergroup","id":"IDs of messages to get"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MESSAGE_IDS_EMPTY","comment":"No message ids were provided."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"both"},"channels.getParticipants":{"comment":"Get the participants of a supergroup/channel","arguments":{"channel":"Channel","filter":"Which participant types to fetch","offset":"Offset","limit":"Limit","hash":"Hash"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."}],"available":"both"},"channels.getParticipant":{"comment":"Get info about a channel/supergroup participant","arguments":{"channel":"Channel/supergroup","participant":"Participant to get info about"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PARTICIPANT_ID_INVALID","comment":"The specified participant ID is invalid."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."},{"code":400,"name":"USER_NOT_PARTICIPANT","comment":"You're not a member of this supergroup/channel."}],"available":"both"},"channels.getChannels":{"comment":"Get info about channels/supergroups","arguments":{"id":"IDs of channels/supergroups to get info about"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"both"},"channels.getFullChannel":{"comment":"Get full info about a channel","arguments":{"channel":"The channel to get info about"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":403,"name":"CHANNEL_PUBLIC_GROUP_NA","comment":"channel/supergroup not available."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"both"},"channels.createChannel":{"comment":"Create a supergroup/channel.","arguments":{"flags":"Flags, see TL conditional fields","broadcast":"Whether to create a channel","megagroup":"Whether to create a supergroup","forImport":"Whether the supergroup is being created to import messages from a foreign chat service using {@link messages.initHistoryImport}","title":"Channel title","about":"Channel description","geoPoint":"Geogroup location","address":"Geogroup address"},"throws":[{"code":400,"name":"CHANNELS_ADMIN_LOCATED_TOO_MUCH","comment":"The user has reached the limit of public geogroups."},{"code":400,"name":"CHANNELS_TOO_MUCH","comment":"You have joined too many channels/supergroups."},{"code":400,"name":"CHAT_ABOUT_TOO_LONG","comment":"Chat about too long."},{"code":400,"name":"CHAT_TITLE_EMPTY","comment":"No chat title provided."},{"code":403,"name":"USER_RESTRICTED","comment":"You're spamreported, you can't create channels or chats."}],"available":"user"},"channels.editAdmin":{"comment":"Modify the admin rights of a user in a supergroup/channel.","arguments":{"channel":"The supergroup/channel.","userId":"The ID of the user whose admin rights should be modified","adminRights":"The admin rights","rank":"Indicates the role (rank) of the admin in the group: just an arbitrary string"},"throws":[{"code":400,"name":"ADMINS_TOO_MUCH","comment":"There are too many admins."},{"code":400,"name":"ADMIN_RANK_EMOJI_NOT_ALLOWED","comment":"An admin rank cannot contain emojis."},{"code":400,"name":"ADMIN_RANK_INVALID","comment":"The specified admin rank is invalid."},{"code":400,"name":"BOTS_TOO_MUCH","comment":"There are too many bots in this chat/channel."},{"code":400,"name":"BOT_CHANNELS_NA","comment":"Bots can't edit admin privileges."},{"code":400,"name":"BOT_GROUPS_BLOCKED","comment":"This bot can't be added to groups."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":403,"name":"CHAT_ADMIN_INVITE_REQUIRED","comment":"You do not have the rights to do this."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":406,"name":"FRESH_CHANGE_ADMINS_FORBIDDEN","comment":"You were just elected admin, you can't add or modify other admins yet."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":403,"name":"RIGHT_FORBIDDEN","comment":"Your admin rights do not allow you to do this."},{"code":400,"name":"USERS_TOO_MUCH","comment":"The maximum number of users has been exceeded (to create a chat, for example)."},{"code":400,"name":"USER_BLOCKED","comment":"User blocked."},{"code":403,"name":"USER_CHANNELS_TOO_MUCH","comment":"One of the users you tried to add is already in too many channels/supergroups."},{"code":400,"name":"USER_CREATOR","comment":"You can't leave this channel, because you're its creator."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."},{"code":400,"name":"USER_NOT_MUTUAL_CONTACT","comment":"The provided user is not a mutual contact."},{"code":403,"name":"USER_PRIVACY_RESTRICTED","comment":"The user's privacy settings do not allow you to do this."},{"code":403,"name":"USER_RESTRICTED","comment":"You're spamreported, you can't create channels or chats."}],"available":"both"},"channels.editTitle":{"comment":"Edit the name of a channel/supergroup","arguments":{"channel":"Channel/supergroup","title":"New name"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":400,"name":"CHAT_TITLE_EMPTY","comment":"No chat title provided."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."}],"available":"both"},"channels.editPhoto":{"comment":"Change the photo of a channel/supergroup","arguments":{"channel":"Channel/supergroup whose photo should be edited","photo":"New photo"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"FILE_REFERENCE_INVALID","comment":"The specified file reference is invalid."},{"code":400,"name":"PHOTO_CROP_SIZE_SMALL","comment":"Photo is too small."},{"code":400,"name":"PHOTO_EXT_INVALID","comment":"The extension of the photo is invalid."},{"code":400,"name":"PHOTO_INVALID","comment":"Photo invalid."}],"available":"both"},"channels.checkUsername":{"comment":"Check if a username is free and can be assigned to a channel/supergroup","arguments":{"channel":"The channel/supergroup that will assigned the specified username","username":"The username to check"},"throws":[{"code":400,"name":"CHANNELS_ADMIN_PUBLIC_TOO_MUCH","comment":"You're admin of too many public channels, make some channels private to change the username of this channel."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"USERNAME_INVALID","comment":"The provided username is not valid."}],"available":"user"},"channels.updateUsername":{"comment":"Change the username of a supergroup/channel","arguments":{"channel":"Channel","username":"New username"},"throws":[{"code":400,"name":"CHANNELS_ADMIN_PUBLIC_TOO_MUCH","comment":"You're admin of too many public channels, make some channels private to change the username of this channel."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"USERNAME_INVALID","comment":"The provided username is not valid."},{"code":400,"name":"USERNAME_NOT_MODIFIED","comment":"The username was not modified."},{"code":400,"name":"USERNAME_OCCUPIED","comment":"The provided username is already occupied."}],"available":"user"},"channels.joinChannel":{"comment":"Join a channel/supergroup","arguments":{"channel":"Channel/supergroup to join"},"throws":[{"code":400,"name":"CHANNELS_TOO_MUCH","comment":"You have joined too many channels/supergroups."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_INVALID","comment":"Invalid chat."},{"code":400,"name":"INVITE_HASH_EMPTY","comment":"The invite hash is empty."},{"code":400,"name":"INVITE_HASH_EXPIRED","comment":"The invite link has expired."},{"code":400,"name":"INVITE_HASH_INVALID","comment":"The invite hash is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"USERS_TOO_MUCH","comment":"The maximum number of users has been exceeded (to create a chat, for example)."},{"code":400,"name":"USER_ALREADY_PARTICIPANT","comment":"The user is already in the group."},{"code":400,"name":"USER_CHANNELS_TOO_MUCH","comment":"One of the users you tried to add is already in too many channels/supergroups."}],"available":"user"},"channels.leaveChannel":{"comment":"Leave a channel/supergroup","arguments":{"channel":"Channel/supergroup to leave"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":403,"name":"CHANNEL_PUBLIC_GROUP_NA","comment":"channel/supergroup not available."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"USER_CREATOR","comment":"You can't leave this channel, because you're its creator."},{"code":400,"name":"USER_NOT_PARTICIPANT","comment":"You're not a member of this supergroup/channel."}],"available":"both"},"channels.inviteToChannel":{"comment":"Invite users to a channel/supergroup","arguments":{"channel":"Channel/supergroup","users":"Users to invite"},"throws":[{"code":400,"name":"BOTS_TOO_MUCH","comment":"There are too many bots in this chat/channel."},{"code":400,"name":"BOT_GROUPS_BLOCKED","comment":"This bot can't be added to groups."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_INVALID","comment":"Invalid chat."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"USERS_TOO_MUCH","comment":"The maximum number of users has been exceeded (to create a chat, for example)."},{"code":400,"name":"USER_BANNED_IN_CHANNEL","comment":"You're banned from sending messages in supergroups/channels."},{"code":400,"name":"USER_BLOCKED","comment":"User blocked."},{"code":400,"name":"USER_BOT","comment":"Bots can only be admins in channels."},{"code":403,"name":"USER_CHANNELS_TOO_MUCH","comment":"One of the users you tried to add is already in too many channels/supergroups."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."},{"code":400,"name":"USER_KICKED","comment":"This user was kicked from this supergroup/channel."},{"code":400,"name":"USER_NOT_MUTUAL_CONTACT","comment":"The provided user is not a mutual contact."},{"code":403,"name":"USER_PRIVACY_RESTRICTED","comment":"The user's privacy settings do not allow you to do this."}],"available":"user"},"channels.deleteChannel":{"comment":"Delete a channel/supergroup","arguments":{"channel":"Channel/supergroup to delete"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHANNEL_TOO_LARGE","comment":"Channel is too large to be deleted; this error is issued when trying to delete channels with more than 1000 members (subject to change)."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."}],"available":"user"},"channels.exportMessageLink":{"comment":"Get link and embed info of a message in a channel/supergroup","arguments":{"flags":"Flags, see TL conditional fields","grouped":"Whether to include other grouped media (for albums)","thread":"Whether to also include a thread ID, if available, inside of the link","channel":"Channel","id":"Message ID"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"user"},"channels.toggleSignatures":{"comment":"Enable/disable message signatures in channels","arguments":{"channel":"Channel","enabled":"Value"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."}],"available":"user"},"channels.getAdminedPublicChannels":{"comment":"Get channels/supergroups/geogroups we're admin in. Usually called when the user exceeds the {@link config} for owned public channels/supergroups/geogroups, and the user is given the choice to remove one of their channels/supergroups/geogroups.","arguments":{"flags":"Flags, see TL conditional fields","byLocation":"Get geogroups","checkLimit":"If set and the user has reached the limit of owned public channels/supergroups/geogroups, instead of returning the channel list one of the specified errors will be returned.
Useful to check if a new public channel can indeed be created, even before asking the user to enter a channel username to use in {@link channels.checkUsername}/{@link channels.updateUsername}."},"throws":[{"code":400,"name":"CHANNELS_ADMIN_LOCATED_TOO_MUCH","comment":"Returned if both the check_limit and the by_location flags are set and the user has reached the limit of public geogroups."},{"code":400,"name":"CHANNELS_ADMIN_PUBLIC_TOO_MUCH","comment":"Returned if the check_limit flag is set and the user has reached the limit of public channels/supergroups."}],"available":"user"},"channels.editBanned":{"comment":"Ban/unban/kick a user in a supergroup/channel.","arguments":{"channel":"The supergroup/channel.","participant":"Participant to ban","bannedRights":"The banned rights"},"throws":[{"code":400,"name":"CHANNEL_ADD_INVALID","comment":"Internal error."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"INPUT_USER_DEACTIVATED","comment":"The specified user was deleted."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."},{"code":400,"name":"PARTICIPANT_ID_INVALID","comment":"The specified participant ID is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"PINNED_DIALOGS_TOO_MUCH","comment":"Too many pinned dialogs."},{"code":400,"name":"USER_ADMIN_INVALID","comment":"You're not an admin."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"both"},"channels.getAdminLog":{"comment":"Get the admin log of a channel/supergroup","arguments":{"flags":"Flags, see TL conditional fields","channel":"Channel","q":"Search query, can be empty","eventsFilter":"Event filter","admins":"Only show events from these admins","maxId":"Maximum ID of message to return (see pagination)","minId":"Minimum ID of message to return (see pagination)","limit":"Maximum number of results to return, see pagination"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"user"},"channels.setStickers":{"comment":"Associate a stickerset to the supergroup","arguments":{"channel":"Supergroup","stickerset":"The stickerset to associate"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"PARTICIPANTS_TOO_FEW","comment":"Not enough participants."},{"code":406,"name":"STICKERSET_OWNER_ANONYMOUS","comment":"Provided stickerset can't be installed as group stickerset to prevent admin deanonymisation."}],"available":"both"},"channels.readMessageContents":{"comment":"Mark channel/supergroup message contents as read","arguments":{"channel":"Channel/supergroup","id":"IDs of messages whose contents should be marked as read"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"MSG_ID_INVALID","comment":"Invalid message ID provided."}],"available":"user"},"channels.deleteHistory":{"comment":"Delete the history of a supergroup","arguments":{"channel":"Supergroup whose history must be deleted","maxId":"ID of message up to which the history must be deleted"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."}],"available":"user"},"channels.togglePreHistoryHidden":{"comment":"Hide/unhide message history for new channel/supergroup users","arguments":{"channel":"Channel/supergroup","enabled":"Hide/unhide"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_ID_INVALID","comment":"The provided chat id is invalid."},{"code":400,"name":"CHAT_LINK_EXISTS","comment":"The chat is public, you can't hide the history to new users."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."}],"available":"user"},"channels.getLeftChannels":{"comment":"Get a list of channels/supergroups we left","arguments":{"offset":"Offset for pagination"},"throws":[{"code":403,"name":"TAKEOUT_REQUIRED","comment":"A takeout session has to be initialized, first."}],"available":"user"},"channels.getGroupsForDiscussion":{"comment":"Returned legacy group chats must be first upgraded to supergroups before they can be set as a discussion group.\nTo set a returned supergroup as a discussion group, access to its old messages must be enabled using {@link channels.togglePreHistoryHidden}, first.\n\nGet all groups that can be used as discussion groups.","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"channels.setDiscussionGroup":{"comment":"Associate a group to a channel as discussion group for that channel","arguments":{"broadcast":"Channel","group":"Discussion group to associate to the channel"},"throws":[{"code":400,"name":"BROADCAST_ID_INVALID","comment":"Broadcast ID invalid."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"LINK_NOT_MODIFIED","comment":"Discussion link not modified."},{"code":400,"name":"MEGAGROUP_ID_INVALID","comment":"Invalid supergroup ID."},{"code":400,"name":"MEGAGROUP_PREHISTORY_HIDDEN","comment":"Group with hidden history for new members can't be set as discussion groups."}],"available":"user"},"channels.editCreator":{"comment":"Transfer channel ownership","arguments":{"channel":"Channel","userId":"New channel owner","password":"2FA password of account"},"throws":[{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":403,"name":"CHAT_WRITE_FORBIDDEN","comment":"You can't write in this chat."},{"code":400,"name":"PASSWORD_HASH_INVALID","comment":"The provided password hash is invalid."},{"code":400,"name":"PASSWORD_MISSING","comment":"You must enable 2FA in order to transfer ownership of a channel."},{"code":400,"name":"PASSWORD_TOO_FRESH_X","comment":"The password was modified less than 24 hours ago, try again in X seconds."},{"code":400,"name":"SESSION_TOO_FRESH_X","comment":"This session was created less than 24 hours ago, try again in X seconds."},{"code":400,"name":"SRP_ID_INVALID","comment":"Invalid SRP ID provided."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"user"},"channels.editLocation":{"comment":"Edit location of geo group","arguments":{"channel":"Geogroup","geoPoint":"New geolocation","address":"Address string"},"throws":[{"code":400,"name":"MEGAGROUP_REQUIRED","comment":"You can only use this method on a supergroup."}],"available":"user"},"channels.toggleSlowMode":{"comment":"Toggle supergroup slow mode: if enabled, users will only be able to send one message every seconds seconds","arguments":{"channel":"The supergroup","seconds":"Users will only be able to send one message every seconds seconds, 0 to disable the limitation"},"throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"CHAT_NOT_MODIFIED","comment":"The pinned message wasn't modified."},{"code":400,"name":"SECONDS_INVALID","comment":"Invalid duration provided."}],"available":"user"},"channels.getInactiveChannels":{"comment":"Get inactive channels and supergroups","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"channels.convertToGigagroup":{"comment":"Convert a supergroup to a gigagroup, when requested by channel suggestions.","arguments":{"channel":"The supergroup to convert"},"throws":[{"code":400,"name":"PARTICIPANTS_TOO_FEW","comment":"Not enough participants."}],"available":"user"},"channels.viewSponsoredMessage":{"comment":"Mark a specific sponsored message as read","arguments":{"channel":"Peer","randomId":"Message ID"},"throws":[{"code":400,"name":"UNKNOWN_ERROR","comment":"Internal error."}],"available":"user"},"channels.getSponsoredMessages":{"comment":"Get a list of sponsored messages","arguments":{"channel":"Peer"},"available":"user"},"bots.sendCustomRequest":{"comment":"Sends a custom request; for bots only","arguments":{"customMethod":"The method name","params":"JSON-serialized method parameters"},"throws":[{"code":400,"name":"METHOD_INVALID","comment":"The specified method is invalid."},{"code":400,"name":"USER_BOT_INVALID","comment":"This method can only be called by a bot."}],"available":"bot"},"bots.answerWebhookJSONQuery":{"comment":"Answers a custom query; for bots only","arguments":{"queryId":"Identifier of a custom query","data":"JSON-serialized answer to the query"},"throws":[{"code":400,"name":"QUERY_ID_INVALID","comment":"The query ID is invalid."},{"code":400,"name":"USER_BOT_INVALID","comment":"This method can only be called by a bot."}],"available":"bot"},"bots.setBotCommands":{"comment":"Set bot command list","arguments":{"scope":"Command scope","langCode":"Language code","commands":"Bot commands"},"throws":[{"code":400,"name":"BOT_COMMAND_DESCRIPTION_INVALID","comment":"The specified command description is invalid."},{"code":400,"name":"BOT_COMMAND_INVALID","comment":"The specified command is invalid."},{"code":400,"name":"LANG_CODE_INVALID","comment":"The specified language code is invalid."}],"available":"both"},"bots.resetBotCommands":{"comment":"Clear bot commands for the specified bot scope and language code","arguments":{"scope":"Command scope","langCode":"Language code"},"available":"both"},"bots.getBotCommands":{"comment":"Obtain a list of bot commands for the specified bot scope and language code","arguments":{"scope":"Command scope","langCode":"Language code"},"available":"both"},"payments.getPaymentForm":{"comment":"Get a payment form","arguments":{"flags":"Flags, see TL conditional fields","peer":"The peer where the payment form was sent","msgId":"Message ID of payment form","themeParams":"A JSON object with the following keys, containing color theme information (integers, RGB24) to pass to the payment provider, to apply in eventual verification pages:
bg_color - Background color
text_color - Text color
hint_color - Hint text color
link_color - Link color
button_color - Button color
button_text_color - Button text color"},"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."}],"available":"user"},"payments.getPaymentReceipt":{"comment":"Get payment receipt","arguments":{"peer":"The peer where the payment receipt was sent","msgId":"Message ID of receipt"},"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."}],"available":"user"},"payments.validateRequestedInfo":{"comment":"Submit requested order information for validation","arguments":{"flags":"Flags, see TL conditional fields","save":"Save order information to re-use it for future orders","peer":"Peer where the payment form was sent","msgId":"Message ID of payment form","info":"Requested order information"},"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."}],"available":"user"},"payments.sendPaymentForm":{"comment":"Send compiled payment form","arguments":{"flags":"Flags, see TL conditional fields","formId":"Form ID","peer":"The peer where the payment form was sent","msgId":"Message ID of form","requestedInfoId":"ID of saved and validated {@link payments.validatedRequestedInfo}","shippingOptionId":"Chosen shipping option ID","credentials":"Payment credentials","tipAmount":"Tip, in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies)."},"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."}],"available":"user"},"payments.getSavedInfo":{"comment":"Get saved payment information","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"payments.clearSavedInfo":{"comment":"Clear saved payment information","arguments":{"flags":"Flags, see TL conditional fields","credentials":"Remove saved payment credentials","info":"Clear the last order settings saved by the user"},"available":"user"},"payments.getBankCardData":{"comment":"Get info about a credit card","arguments":{"number":"Credit card number"},"throws":[{"code":400,"name":"BANK_CARD_NUMBER_INVALID","comment":"The specified card number is invalid."}],"available":"user"},"stickers.createStickerSet":{"comment":"Create a stickerset, bots only.","arguments":{"flags":"Flags, see TL conditional fields","masks":"Whether this is a mask stickerset","animated":"Whether this is an animated stickerset","userId":"Stickerset owner","title":"Stickerset name, 1-64 chars","shortName":"Sticker set name. Can contain only English letters, digits and underscores. Must end with \"by\" ( is case insensitive); 1-64 characters","thumb":"Thumbnail","stickers":"Stickers","software":"Used when importing stickers using the sticker import SDKs, specifies the name of the software that created the stickers"},"throws":[{"code":400,"name":"BOT_MISSING","comment":"This method can only be run by a bot."},{"code":400,"name":"PACK_SHORT_NAME_INVALID","comment":"Short pack name invalid."},{"code":400,"name":"PACK_SHORT_NAME_OCCUPIED","comment":"A stickerpack with this name already exists."},{"code":400,"name":"PACK_TITLE_INVALID","comment":"The stickerpack title is invalid."},{"code":400,"name":"PEER_ID_INVALID","comment":"The provided peer id is invalid."},{"code":400,"name":"SHORTNAME_OCCUPY_FAILED","comment":"An internal error occurred."},{"code":400,"name":"STICKERS_EMPTY","comment":"No sticker provided."},{"code":400,"name":"STICKER_EMOJI_INVALID","comment":"Sticker emoji invalid."},{"code":400,"name":"STICKER_FILE_INVALID","comment":"Sticker file invalid."},{"code":400,"name":"STICKER_PNG_DIMENSIONS","comment":"Sticker png dimensions invalid."},{"code":400,"name":"STICKER_PNG_NOPNG","comment":"One of the specified stickers is not a valid PNG file."},{"code":400,"name":"STICKER_TGS_NODOC","comment":"Incorrect document type for sticker."},{"code":400,"name":"STICKER_TGS_NOTGS","comment":"Invalid TGS sticker provided."},{"code":400,"name":"STICKER_THUMB_PNG_NOPNG","comment":"Incorrect stickerset thumb file provided, PNG / WEBP expected."},{"code":400,"name":"STICKER_THUMB_TGS_NOTGS","comment":"Incorrect stickerset TGS thumb file provided."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."}],"available":"both"},"stickers.removeStickerFromSet":{"comment":"Remove a sticker from the set where it belongs, bots only. The sticker set must have been created by the bot.","arguments":{"sticker":"The sticker to remove"},"throws":[{"code":400,"name":"BOT_MISSING","comment":"This method can only be run by a bot."},{"code":400,"name":"STICKER_INVALID","comment":"The provided sticker is invalid."}],"available":"both"},"stickers.changeStickerPosition":{"comment":"Changes the absolute position of a sticker in the set to which it belongs; for bots only. The sticker set must have been created by the bot","arguments":{"sticker":"The sticker","position":"The new position of the sticker, zero-based"},"throws":[{"code":400,"name":"BOT_MISSING","comment":"This method can only be run by a bot."},{"code":400,"name":"STICKER_INVALID","comment":"The provided sticker is invalid."}],"available":"both"},"stickers.addStickerToSet":{"comment":"Add a sticker to a stickerset, bots only. The sticker set must have been created by the bot.","arguments":{"stickerset":"The stickerset","sticker":"The sticker"},"throws":[{"code":400,"name":"BOT_MISSING","comment":"This method can only be run by a bot."},{"code":400,"name":"STICKERSET_INVALID","comment":"The provided sticker set is invalid."},{"code":400,"name":"STICKER_PNG_NOPNG","comment":"One of the specified stickers is not a valid PNG file."},{"code":400,"name":"STICKER_TGS_NOTGS","comment":"Invalid TGS sticker provided."}],"available":"both"},"stickers.setStickerSetThumb":{"comment":"Set stickerset thumbnail","arguments":{"stickerset":"Stickerset","thumb":"Thumbnail"},"throws":[{"code":400,"name":"STICKERSET_INVALID","comment":"The provided sticker set is invalid."},{"code":400,"name":"STICKER_THUMB_PNG_NOPNG","comment":"Incorrect stickerset thumb file provided, PNG / WEBP expected."},{"code":400,"name":"STICKER_THUMB_TGS_NOTGS","comment":"Incorrect stickerset TGS thumb file provided."}],"available":"both"},"stickers.checkShortName":{"comment":"Check whether the given short name is available","arguments":{"shortName":"Short name"},"throws":[{"code":400,"name":"SHORT_NAME_INVALID","comment":"The specified short name is invalid."},{"code":400,"name":"SHORT_NAME_OCCUPIED","comment":"The specified short name is already in use."}],"available":"user"},"stickers.suggestShortName":{"comment":"Suggests a short name for a given stickerpack name","arguments":{"title":"Sticker pack name"},"throws":[{"code":400,"name":"TITLE_INVALID","comment":"The specified stickerpack title is invalid."}],"available":"user"},"phone.getCallConfig":{"comment":"Get phone call configuration to be passed to libtgvoip's shared config","available":"user","arguments":{"gigagroup":"Is this a broadcast group?"}},"phone.requestCall":{"comment":"Start a telegram phone call","arguments":{"flags":"Flags, see TL conditional fields","video":"Whether to start a video call","userId":"Destination of the phone call","randomId":"Random ID to avoid resending the same object","gAHash":"Parameter for E2E encryption key exchange »","protocol":"Phone call settings"},"throws":[{"code":400,"name":"CALL_PROTOCOL_FLAGS_INVALID","comment":"Call protocol flags invalid."},{"code":400,"name":"PARTICIPANT_VERSION_OUTDATED","comment":"The other participant does not use an up to date telegram client with support for calls."},{"code":400,"name":"USER_ID_INVALID","comment":"The provided user ID is invalid."},{"code":403,"name":"USER_IS_BLOCKED","comment":"You were blocked by this user."},{"code":403,"name":"USER_PRIVACY_RESTRICTED","comment":"The user's privacy settings do not allow you to do this."}],"available":"user"},"phone.acceptCall":{"comment":"Accept incoming call","arguments":{"peer":"The call to accept","gB":"Parameter for E2E encryption key exchange »","protocol":"Phone call settings"},"throws":[{"code":400,"name":"CALL_ALREADY_ACCEPTED","comment":"The call was already accepted."},{"code":400,"name":"CALL_ALREADY_DECLINED","comment":"The call was already declined."},{"code":400,"name":"CALL_PEER_INVALID","comment":"The provided call peer object is invalid."},{"code":400,"name":"CALL_PROTOCOL_FLAGS_INVALID","comment":"Call protocol flags invalid."}],"available":"user"},"phone.confirmCall":{"comment":"Complete phone call E2E encryption key exchange »","arguments":{"peer":"The phone call","gA":"Parameter for E2E encryption key exchange »","keyFingerprint":"Key fingerprint","protocol":"Phone call settings"},"throws":[{"code":400,"name":"CALL_ALREADY_DECLINED","comment":"The call was already declined."},{"code":400,"name":"CALL_PEER_INVALID","comment":"The provided call peer object is invalid."}],"available":"user"},"phone.receivedCall":{"comment":"Optional: notify the server that the user is currently busy in a call: this will automatically refuse all incoming phone calls until the current phone call is ended.","arguments":{"peer":"The phone call we're currently in"},"throws":[{"code":400,"name":"CALL_ALREADY_DECLINED","comment":"The call was already declined."},{"code":400,"name":"CALL_PEER_INVALID","comment":"The provided call peer object is invalid."}],"available":"user"},"phone.discardCall":{"comment":"Refuse or end running call","arguments":{"flags":"Flags, see TL conditional fields","video":"Whether this is a video call","peer":"The phone call","duration":"Call duration","reason":"Why was the call discarded","connectionId":"Preferred libtgvoip relay ID"},"throws":[{"code":400,"name":"CALL_ALREADY_ACCEPTED","comment":"The call was already accepted."},{"code":400,"name":"CALL_PEER_INVALID","comment":"The provided call peer object is invalid."}],"available":"user"},"phone.setCallRating":{"comment":"Rate a call","arguments":{"flags":"Flags, see TL conditional fields","userInitiative":"Whether the user decided on their own initiative to rate the call","peer":"The call to rate","rating":"Rating in 1-5 stars","comment":"An additional comment"},"throws":[{"code":400,"name":"CALL_PEER_INVALID","comment":"The provided call peer object is invalid."}],"available":"user"},"phone.saveCallDebug":{"comment":"Send phone call debug data to server","arguments":{"peer":"Phone call","debug":"Debug statistics obtained from libtgvoip"},"throws":[{"code":400,"name":"CALL_PEER_INVALID","comment":"The provided call peer object is invalid."},{"code":400,"name":"DATA_JSON_INVALID","comment":"The provided JSON data is invalid."}],"available":"user"},"phone.sendSignalingData":{"comment":"Send VoIP signaling data","arguments":{"peer":"Phone call","data":"Signaling payload"},"available":"user"},"phone.createGroupCall":{"comment":"Create a group call or livestream","arguments":{"flags":"Flags, see TL conditional fields","peer":"Associate the group call or livestream to the provided group/supergroup/channel","randomId":"Unique client message ID required to prevent creation of duplicate group calls","title":"Call title","scheduleDate":"For scheduled group call or livestreams, the absolute date when the group call will start"},"throws":[{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"SCHEDULE_DATE_INVALID","comment":"Invalid schedule date provided."}],"available":"user"},"phone.joinGroupCall":{"comment":"Join a group call","arguments":{"flags":"Flags, see TL conditional fields","muted":"If set, the user will be muted by default upon joining.","videoStopped":"If set, the user's video will be disabled by default upon joining.","call":"The group call","joinAs":"Join the group call, presenting yourself as the specified user/channel","inviteHash":"The invitation hash from the invite link: https://t.me/username?voicechat=hash","params":"WebRTC parameters"},"throws":[{"code":400,"name":"GROUPCALL_SSRC_DUPLICATE_MUCH","comment":"The app needs to retry joining the group call with a new SSRC value."}],"available":"user"},"phone.leaveGroupCall":{"comment":"Leave a group call","arguments":{"call":"The group call","source":"Your source ID"},"available":"user"},"phone.inviteToGroupCall":{"comment":"Invite a set of users to a group call.","arguments":{"call":"The group call","users":"The users to invite."},"throws":[{"code":403,"name":"GROUPCALL_FORBIDDEN","comment":"The group call has already ended."}],"available":"user"},"phone.discardGroupCall":{"comment":"Terminate a group call","arguments":{"call":"The group call to terminate"},"available":"user"},"phone.toggleGroupCallSettings":{"comment":"Change group call settings","arguments":{"flags":"Flags, see TL conditional fields","resetInviteHash":"Invalidate existing invite links","call":"Group call","joinMuted":"Whether all users will bthat join this group calle muted by default upon joining the group call"},"throws":[{"code":400,"name":"GROUPCALL_NOT_MODIFIED","comment":"Group call settings weren't modified."}],"available":"user"},"phone.getGroupCall":{"comment":"Get info about a group call","arguments":{"call":"The group call","limit":"Maximum number of results to return, see pagination"},"available":"user"},"phone.getGroupParticipants":{"comment":"Get group call participants","arguments":{"call":"Group call","ids":"If specified, will fetch group participant info about the specified peers","sources":"If specified, will fetch group participant info about the specified WebRTC source IDs","offset":"Offset for results, taken from the next_offset field of {@link phone.groupParticipants}, initially an empty string.
Note: if no more results are available, the method call will return an empty next_offset; thus, avoid providing the next_offset returned in {@link phone.groupParticipants} if it is empty, to avoid an infinite loop.","limit":"Maximum number of results to return, see pagination"},"available":"user"},"phone.checkGroupCall":{"comment":"Check whether the group call Server Forwarding Unit is currently receiving the streams with the specified WebRTC source IDs","arguments":{"call":"Group call","sources":"Source IDs"},"available":"user"},"phone.toggleGroupCallRecord":{"comment":"Start or stop recording a group call: the recorded audio and video streams will be automatically sent to Saved messages (the chat with ourselves).","arguments":{"flags":"Flags, see TL conditional fields","start":"Whether to start or stop recording","video":"Whether to also record video streams","call":"The group call or livestream","title":"Recording title","videoPortrait":"If video stream recording is enabled, whether to record in portrait or landscape mode"},"available":"user"},"phone.editGroupCallParticipant":{"comment":"Note: flags.N?Bool parameters can have three possible values:\n\nEdit information about a given group call participant","arguments":{"flags":"Flags, see TL conditional fields","call":"The group call","participant":"The group call participant (can also be the user itself)","muted":"Whether to mute or unmute the specified participant","volume":"New volume","raiseHand":"Raise or lower hand","videoStopped":"Start or stop the video stream","videoPaused":"Pause or resume the video stream","presentationPaused":"Pause or resume the screen sharing stream"},"throws":[{"code":400,"name":"USER_VOLUME_INVALID","comment":"The specified user volume is invalid."}],"available":"user"},"phone.editGroupCallTitle":{"comment":"Edit the title of a group call or livestream","arguments":{"call":"Group call","title":"New title"},"available":"user"},"phone.getGroupCallJoinAs":{"comment":"Get a list of peers that can be used to join a group call, presenting yourself as a specific user/channel.","arguments":{"peer":"The dialog whose group call or livestream we're trying to join"},"available":"user"},"phone.exportGroupCallInvite":{"comment":"Get an invite link for a group call or livestream","arguments":{"flags":"Flags, see TL conditional fields","canSelfUnmute":"For livestreams, if set, users that join using this link will be able to speak without explicitly requesting permission by (for example by raising their hand).","call":"The group call"},"available":"user"},"phone.toggleGroupCallStartSubscription":{"comment":"Subscribe or unsubscribe to a scheduled group call","arguments":{"call":"Scheduled group call","subscribed":"Enable or disable subscription"},"available":"user"},"phone.startScheduledGroupCall":{"comment":"Start a scheduled group call.","arguments":{"call":"The scheduled group call"},"available":"user"},"phone.saveDefaultGroupCallJoinAs":{"comment":"Set the default peer that will be used to join a group call in a specific dialog.","arguments":{"peer":"The dialog","joinAs":"The default peer that will be used to join group calls in this dialog, presenting yourself as a specific user/channel."},"available":"user"},"phone.joinGroupCallPresentation":{"comment":"Start screen sharing in a call","arguments":{"call":"The group call","params":"WebRTC parameters"},"throws":[{"code":403,"name":"PARTICIPANT_JOIN_MISSING","comment":"Trying to enable a presentation, when the user hasn't joined the Video Chat with {@link phone.joinGroupCall}."}],"available":"user"},"phone.leaveGroupCallPresentation":{"comment":"Stop screen sharing in a group call","arguments":{"call":"The group call"},"available":"user"},"langpack.getLangPack":{"comment":"Get localization pack strings","arguments":{"langPack":"Language pack name","langCode":"Language code"},"throws":[{"code":400,"name":"LANG_PACK_INVALID","comment":"The provided language pack is invalid."}],"available":"user"},"langpack.getStrings":{"comment":"Get strings from a language pack","arguments":{"langPack":"Language pack name","langCode":"Language code","keys":"Strings to get"},"throws":[{"code":400,"name":"LANG_PACK_INVALID","comment":"The provided language pack is invalid."}],"available":"user"},"langpack.getDifference":{"comment":"Get new strings in languagepack","arguments":{"langPack":"Language pack","langCode":"Language code","fromVersion":"Previous localization pack version"},"throws":[{"code":400,"name":"LANG_PACK_INVALID","comment":"The provided language pack is invalid."}],"available":"user"},"langpack.getLanguages":{"comment":"Get information about all languages in a localization pack","arguments":{"langPack":"Language pack"},"throws":[{"code":400,"name":"LANG_PACK_INVALID","comment":"The provided language pack is invalid."}],"available":"user"},"langpack.getLanguage":{"comment":"Get information about a language in a localization pack","arguments":{"langPack":"Language pack name","langCode":"Language code"},"available":"user"},"folders.editPeerFolders":{"comment":"Edit peers in peer folder","arguments":{"folderPeers":"New peer list"},"throws":[{"code":400,"name":"FOLDER_ID_INVALID","comment":"Invalid folder ID."}],"available":"user"},"folders.deleteFolder":{"comment":"Delete a peer folder","arguments":{"folderId":"Peer folder ID, for more info click here"},"throws":[{"code":400,"name":"FOLDER_ID_EMPTY","comment":"An empty folder ID was specified."}],"available":"user"},"stats.getBroadcastStats":{"comment":"Get channel statistics","arguments":{"flags":"Flags, see TL conditional fields","dark":"Whether to enable dark theme for graph colors","channel":"The channel"},"throws":[{"code":400,"name":"BROADCAST_REQUIRED","comment":"This method can only be called on a channel, please use stats.getMegagroupStats for supergroups."},{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHANNEL_PRIVATE","comment":"You haven't joined this channel/supergroup."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."}],"available":"user"},"stats.loadAsyncGraph":{"comment":"Load channel statistics graph asynchronously","arguments":{"flags":"Flags, see TL conditional fields","token":"Graph token from {@link statsGraphAsync} constructor","x":"Zoom value, if required"},"throws":[{"code":400,"name":"GRAPH_EXPIRED_RELOAD","comment":"This graph has expired, please obtain a new graph token."},{"code":400,"name":"GRAPH_INVALID_RELOAD","comment":"Invalid graph token provided, please reload the stats and provide the updated token."},{"code":400,"name":"GRAPH_OUTDATED_RELOAD","comment":"The graph is outdated, please get a new async token using stats.getBroadcastStats."}],"available":"user"},"stats.getMegagroupStats":{"comment":"Get supergroup statistics","arguments":{"flags":"Flags, see TL conditional fields","dark":"Whether to enable dark theme for graph colors","channel":"Supergroup ID"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"MEGAGROUP_REQUIRED","comment":"You can only use this method on a supergroup."}],"available":"user"},"stats.getMessagePublicForwards":{"comment":"Obtains a list of messages, indicating to which other public channels was a channel message forwarded.\nWill return a list of {@link message} with peer_id equal to the public channel to which this message was forwarded.","arguments":{"channel":"Source channel","msgId":"Source message ID","offsetRate":"Initially 0, then set to the next_rate parameter of {@link messages.messagesSlice}","offsetPeer":"Offsets for pagination, for more info click here","offsetId":"Offsets for pagination, for more info click here","limit":"Maximum number of results to return, see pagination"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."},{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."}],"available":"user"},"stats.getMessageStats":{"comment":"Get message statistics","arguments":{"flags":"Flags, see TL conditional fields","dark":"Whether to enable dark theme for graph colors","channel":"Channel ID","msgId":"Message ID"},"throws":[{"code":400,"name":"CHANNEL_INVALID","comment":"The provided channel is invalid."},{"code":400,"name":"CHAT_ADMIN_REQUIRED","comment":"You must be an admin in this chat to do this."}],"available":"user"},"messages.sendReaction":{"comment":"Send reaction to message","arguments":{"flags":"Flags, see TL conditional fields","peer":"Peer","msgId":"Message ID to react to","reaction":"Reaction (a UTF8 emoji)"},"throws":[{"code":400,"name":"MESSAGE_ID_INVALID","comment":"The provided message id is invalid."},{"code":400,"name":"REACTION_EMPTY","comment":"Empty reaction provided."}],"available":"both"},"messages.getMessagesReactions":{"comment":"Get message reactions","arguments":{"peer":"Peer","id":"Message IDs"},"available":"both"},"messages.getMessageReactionsList":{"comment":"Get full message reaction list","arguments":{"flags":"Flags, see TL conditional fields","peer":"Peer","id":"Message ID","reaction":"Get only reactions of this type (UTF8 emoji)","offset":"Offset (typically taken from the next_offset field of the returned MessageReactionsList)","limit":"Maximum number of results to return, see pagination"},"available":"both"}},"unions":{"Error":"An object containing a query error.","InputFileLocation":"Defines the location of a file for download.","InputPeer":"Peer","InputUser":"Defines a user for subsequent interaction.","InputContact":"Object defines a contact from the user's phonebook.","InputFile":"Defines a file uploaded by the client.","InputMedia":"Defines media content of a message.","InputChatPhoto":"Defines a new group profile photo.","InputGeoPoint":"Defines a GeoPoint.","InputPhoto":"Defines a photo for further interaction.","Peer":"Chat partner or group.","storage.FileType":"Object describes the file type.","User":"Object defines a user.","UserProfilePhoto":"Object contains info on the user's profile photo.","UserStatus":"User online status","Chat":"Object defines a group.","ChatFull":"Object containing detailed group info","ChatParticipant":"Details of a group member.","ChatParticipants":"Object contains info on group members.","ChatPhoto":"Object defines a group profile photo.","Message":"Object describing a message.","MessageMedia":"Media","MessageAction":"Object describing actions connected to a service message.","Dialog":"Chat info.","Photo":"Object describes a photo.","PhotoSize":"Location of a certain size of a picture","GeoPoint":"Object defines a GeoPoint.","auth.SentCode":"Contains info on a confirmation code message sent via SMS, phone call or Telegram.","auth.Authorization":"Oject contains info on user authorization.","auth.ExportedAuthorization":"Exported authorization","InputNotifyPeer":"Object defines the set of users and/or groups that generate notifications.","InputPeerNotifySettings":"Notifications settings.","PeerNotifySettings":"Notification settings.","PeerSettings":"Peer settings","WallPaper":"Object contains info on a wallpaper.","ReportReason":"Report reason","UserFull":"Object contains extended user info.","Contact":"A contact of the current user.","ImportedContact":"Object contains info on a successfully imported contact.","ContactStatus":"Contact status: online / offline.","contacts.Contacts":"Info on the current user's contact list.","contacts.ImportedContacts":"Object contains info on succesfully imported contacts.","contacts.Blocked":"Info on users from the current user's black list.","messages.Dialogs":"Object contains a list of chats with messages and auxiliary data.","messages.Messages":"Object contains infor on list of messages with auxiliary data.","messages.Chats":"Object contains list of chats with auxiliary data.","messages.ChatFull":"Object contains extended info on chat with auxiliary data.","messages.AffectedHistory":"Object contains info on affected part of communication history with the user or in a chat.","MessagesFilter":"Object describes message filter.","Update":"Object contains info on events occured.","updates.State":"Object contains info on state for further updates.","updates.Difference":"Occurred changes.","Updates":"Object which is perceived by the client without a call on its part when an event occurs.","photos.Photos":"Object contains list of photos with auxiliary data.","photos.Photo":"Photo with auxiliary data.","upload.File":"Contains info on file.","DcOption":"Information for connection to data centre.","Config":"Object contains info on API configuring parameters.","NearestDc":"Object contains info on nearest data centre.","help.AppUpdate":"Contains info on app update availability.","help.InviteText":"Object contains info on the text of a message with an invitation.","EncryptedChat":"Object contains info on an encrypted chat.","InputEncryptedChat":"Object sets an encrypted chat ID.","EncryptedFile":"Seta an encrypted file.","InputEncryptedFile":"Object sets encrypted file for attachment","EncryptedMessage":"Object contains encrypted message.","messages.DhConfig":"Contains info on cofiguring parameters for key generation by Diffie-Hellman protocol.","messages.SentEncryptedMessage":"Contains info on message sent to an encrypted chat.","InputDocument":"Defines a document for subsequent interaction.","Document":"A document.","help.Support":"Info about the support user, relevant to the current user.","NotifyPeer":"Object defines the set of users and/or groups that generate notifications.","SendMessageAction":"User actions. Use this to provide users with detailed info about their chat partners' actions: typing or sending attachments of all kinds.","contacts.Found":"Object contains info on users found by name substring and auxiliary data.","InputPrivacyKey":"Privacy key","PrivacyKey":"Privacy key","InputPrivacyRule":"Privacy rule","PrivacyRule":"Privacy rule","account.PrivacyRules":"Privacy rules","AccountDaysTTL":"Time-to-live of current account","DocumentAttribute":"Various possible attributes of a document (used to define if it's a sticker, a GIF, a video, a mask sticker, an image, an audio, and so on)","messages.Stickers":"Stickers","StickerPack":"Stickerpack","messages.AllStickers":"All stickers","messages.AffectedMessages":"Messages affected by changes","WebPage":"Instant View webpage preview","Authorization":"Represents a logged-in session","account.Authorizations":"Logged-in sessions","account.Password":"Configuration for two-factor authorization","account.PasswordSettings":"Private info associated to the password info (recovery email, telegram passport info & so on)","account.PasswordInputSettings":"Constructor for setting up a new 2FA SRP password","auth.PasswordRecovery":"Recovery info of a 2FA password, only for accounts with a recovery email configured.","ReceivedNotifyMessage":"Confirmation of message receipt","ExportedChatInvite":"Exported chat invite","ChatInvite":"Chat invite","InputStickerSet":"Represents a stickerset","StickerSet":"Represents a stickerset (stickerpack)","messages.StickerSet":"Stickerset","BotCommand":"Describes a bot command that can be used in a chat","BotInfo":"Info about bots (available bot commands, etc)","KeyboardButton":"Bot or inline keyboard buttons","KeyboardButtonRow":"Bot or inline keyboard rows","ReplyMarkup":"Reply markup for bot and inline keyboards","MessageEntity":"Message entities, representing styled text in a message","InputChannel":"Represents a channel","contacts.ResolvedPeer":"Peer returned after resolving a @username","MessageRange":"Indicates a range of chat messages","updates.ChannelDifference":"Contains the difference (new messages) between our local channel state and the remote state","ChannelMessagesFilter":"Filter for fetching only certain types of channel messages","ChannelParticipant":"Channel participant","ChannelParticipantsFilter":"Filter for fetching channel participants","channels.ChannelParticipants":"Channel/supergroup participants","channels.ChannelParticipant":"Channel participant","help.TermsOfService":"Contains info about the latest telegram Terms Of Service.","messages.SavedGifs":"Saved GIFs","InputBotInlineMessage":"Represents a sent inline message from the perspective of a bot","InputBotInlineResult":"Inline bot result","BotInlineMessage":"Inline message","BotInlineResult":"Results of an inline query","messages.BotResults":"Result of a query to an inline bot","ExportedMessageLink":"HTTP link and embed info of channel message","MessageFwdHeader":"Info about a forwarded message","auth.CodeType":"Type of verification code that will be sent next if you call the resendCode method","auth.SentCodeType":"Type of the verification code that was sent","messages.BotCallbackAnswer":"Callback answer of bot","messages.MessageEditData":"Message edit data for media","InputBotInlineMessageID":"Represents a sent inline message from the perspective of a bot","InlineBotSwitchPM":"The bot requested the user to message them in private","messages.PeerDialogs":"List of dialogs","TopPeer":"Top peer","TopPeerCategory":"Top peer category","TopPeerCategoryPeers":"Top peers by top peer category","contacts.TopPeers":"Top peers","DraftMessage":"Represents a message draft.","messages.FeaturedStickers":"Featured stickers","messages.RecentStickers":"Recent stickers","messages.ArchivedStickers":"Archived stickers","messages.StickerSetInstallResult":"Result of stickerset installation process","StickerSetCovered":"Stickerset, with a specific sticker as preview","MaskCoords":"Mask coordinates (if this is a mask sticker, attached to a photo)","InputStickeredMedia":"Represents a media with attached stickers","Game":"Indicates an already sent game","InputGame":"A game to send","HighScore":"Game high score","messages.HighScores":"High scores (in games)","RichText":"Rich text","PageBlock":"Represents an instant view page element","PhoneCallDiscardReason":"Why was the phone call discarded?","DataJSON":"Represent a JSON-encoded object","LabeledPrice":"Labeled pricetag","Invoice":"Invoice","PaymentCharge":"Charged payment","PostAddress":"Shipping address","PaymentRequestedInfo":"Requested payment info","PaymentSavedCredentials":"Saved payment credentials","WebDocument":"Remote document","InputWebDocument":"Specifies a document that will have to be downloaded from the URL by the telegram servers","InputWebFileLocation":"Location of remote file","upload.WebFile":"Remote file","payments.PaymentForm":"Payment form","payments.ValidatedRequestedInfo":"Validated requested info","payments.PaymentResult":"Payment result","payments.PaymentReceipt":"Payment receipt","payments.SavedInfo":"Saved payment info","InputPaymentCredentials":"Payment credentials","account.TmpPassword":"Temporary password","ShippingOption":"Shipping options","InputStickerSetItem":"Sticker","InputPhoneCall":"Phone call","PhoneCall":"Phone call","PhoneConnection":"Phone call connection","PhoneCallProtocol":"Phone call protocol","phone.PhoneCall":"Phone call","upload.CdnFile":"Represents the download status of a CDN file","CdnPublicKey":"Public key to use only during handshakes to CDN DCs.","CdnConfig":"Configuration for CDN file downloads.","LangPackString":"Language pack string","LangPackDifference":"Language pack changes","LangPackLanguage":"Language pack language","ChannelAdminLogEventAction":"Channel admin log event","ChannelAdminLogEvent":"An event in a channel admin log","channels.AdminLogResults":"Admin log events","ChannelAdminLogEventsFilter":"Filter for fetching events in the channel admin log","PopularContact":"Popular contact","messages.FavedStickers":"Favorited stickers","RecentMeUrl":"Recent t.me urls","help.RecentMeUrls":"Recent t.me URLs","InputSingleMedia":"A single media in an album or grouped media sent with {@link messages.sendMultiMedia}.","WebAuthorization":"Web authorization","account.WebAuthorizations":"Web authorizations","InputMessage":"A message","InputDialogPeer":"Peer, or all peers in a certain folder","DialogPeer":"Peer, or all peers in a folder","messages.FoundStickerSets":"Found stickersets","FileHash":"Hash of an uploaded file, to be checked for validity after download","InputClientProxy":"Info about an MTProxy used to connect.","help.TermsOfServiceUpdate":"Update of Telegram's terms of service","InputSecureFile":"Secure passport file, for more info see the passport docs »","SecureFile":"Secure passport file, for more info see the passport docs »","SecureData":"Secure passport data, for more info see the passport docs »","SecurePlainData":"Plaintext verified passport data.","SecureValueType":"Secure value type","SecureValue":"Secure tgpassport value","InputSecureValue":"Secure value, for more info see the passport docs »","SecureValueHash":"Secure value hash","SecureValueError":"Secure value error","SecureCredentialsEncrypted":"Encrypted secure credentials","account.AuthorizationForm":"Authorization form","account.SentEmailCode":"The email code that was sent","help.DeepLinkInfo":"Contains information about a tg:// deep link","SavedContact":"Saved contact","account.Takeout":"Takeout info","PasswordKdfAlgo":"Key derivation function to use when generating the password hash for SRP two-factor authorization","SecurePasswordKdfAlgo":"KDF algorithm to use for computing telegram passport hash","SecureSecretSettings":"Telegram passport settings","InputCheckPasswordSRP":"Constructors for checking the validity of a 2FA SRP password","SecureRequiredType":"Required secure file type","help.PassportConfig":"Telegram passport configuration","InputAppEvent":"Object contains info about an event that occured in the application.","JSONObjectValue":"JSON key: value pair","JSONValue":"JSON value","PageTableCell":"Represents a table in an instant view table","PageTableRow":"Table row","PageCaption":"Page caption","PageListItem":"Item in block list","PageListOrderedItem":"Represents an instant view ordered list","PageRelatedArticle":"Related articles","Page":"Instant view page","help.SupportName":"Get localized name for support user","help.UserInfo":"User info","PollAnswer":"Indicates a possible answer to a poll.","Poll":"Indicates a poll message","PollAnswerVoters":"How users voted on a certain poll answer","PollResults":"Results of poll","ChatOnlines":"Number of online users in a chat","StatsURL":"URL with chat statistics","ChatAdminRights":"Represents the rights of an admin in a channel/supergroup.","ChatBannedRights":"Represents the rights of a normal user in a supergroup/channel/chat.","InputWallPaper":"Wallpaper","account.WallPapers":"Wallpapers","CodeSettings":"Settings for the code type to send","WallPaperSettings":"Wallpaper settings","AutoDownloadSettings":"Media autodownload settings","account.AutoDownloadSettings":"Media autodownload settings","EmojiKeyword":"Emoji keyword","EmojiKeywordsDifference":"New emoji keywords","EmojiURL":"Emoji URL","EmojiLanguage":"Emoji language","Folder":"A folder","InputFolderPeer":"Peer in a folder","FolderPeer":"Peer associated to folder","messages.SearchCounter":"Number of results that would be returned by a search","UrlAuthResult":"URL authorization result","ChannelLocation":"Geographical location of supergroup (geogroups)","PeerLocated":"Geolocated peer","RestrictionReason":"Restriction reason","InputTheme":"Cloud theme","Theme":"Cloud theme","account.Themes":"Installed themes","auth.LoginToken":"Login token (for QR code login)","account.ContentSettings":"Sensitive content settings","messages.InactiveChats":"Inactive chat list","BaseTheme":"Basic theme settings","InputThemeSettings":"Theme settings","ThemeSettings":"Theme settings","WebPageAttribute":"Webpage attributes","MessageUserVote":"How a user voted in a poll","messages.VotesList":"How users voted in a poll","BankCardOpenUrl":"Credit card info URL provided by the bank","payments.BankCardData":"Credit card info, provided by the card's bank(s)","DialogFilter":"Dialog filter (folders)","DialogFilterSuggested":"Suggested dialog filters (folders)","StatsDateRangeDays":"Channel statistics date range","StatsAbsValueAndPrev":"Channel statistics value pair","StatsPercentValue":"Channel statistics percentage","StatsGraph":"Channel statistics graph","MessageInteractionCounters":"Message interaction counters","stats.BroadcastStats":"Channel statistics","help.PromoData":"Info about pinned MTProxy or Public Service Announcement peers.","VideoSize":"Represents an animated video thumbnail","StatsGroupTopPoster":"Most active user in a supergroup","StatsGroupTopAdmin":"Most active admin in a supergroup","StatsGroupTopInviter":"Most active inviter in a supergroup","stats.MegagroupStats":"Supergroup statistics","GlobalPrivacySettings":"Global privacy settings","help.CountryCode":"Country code and phone number pattern of a specific country","help.Country":"Name, ISO code, localized name and phone codes/patterns of a specific country","help.CountriesList":"Name, ISO code, localized name and phone codes/patterns of all available countries","MessageViews":"View, forward counter + info about replies of a specific message","messages.MessageViews":"View, forward counter + info about replies","messages.DiscussionMessage":"Info about a message thread","MessageReplyHeader":"Reply information","MessageReplies":"Info about post comments (for channels) or message replies (for groups)","PeerBlocked":"Info about a blocked user","stats.MessageStats":"Message statistics","GroupCall":"A group call","InputGroupCall":"Indicates a group call","GroupCallParticipant":"Info about a group call participant","phone.GroupCall":"Contains info about a group call, and partial info about its participants.","phone.GroupParticipants":"Info about the participants of a group call or livestream","InlineQueryPeerType":"Type of the chat from which the inline query was sent.","messages.HistoryImport":"Identifier of a history import session, click here for more info ».","messages.HistoryImportParsed":"Contains information about a chat export file, generated by a foreign chat app.","messages.AffectedFoundMessages":"Messages found and affected by changes","ChatInviteImporter":"When and which user joined the chat using a chat invite","messages.ExportedChatInvites":"Info about chat invites exported by a certain admin.","messages.ExportedChatInvite":"Contains info about a chat invite, and eventually a pointer to the newest chat invite.","messages.ChatInviteImporters":"List of users that imported a chat invitation link.","ChatAdminWithInvites":"Info about chat invites generated by admins.","messages.ChatAdminsWithInvites":"Info about chat invites generated by admins.","messages.CheckedHistoryImportPeer":"Contains a confirmation text to be shown to the user, upon importing chat history, click here for more info ».","phone.JoinAsPeers":"A list of peers that can be used to join a group call, presenting yourself as a specific user/channel.","phone.ExportedGroupCallInvite":"An exported group call invitation.","GroupCallParticipantVideoSourceGroup":"Describes a group of video synchronization source identifiers","GroupCallParticipantVideo":"Info about a video stream","stickers.SuggestedShortName":"A suggested short name for the specified stickerpack","BotCommandScope":"Represents a scope where the bot commands, specified using {@link bots.setBotCommands} will be valid.","account.ResetPasswordResult":"Result of an {@link account.resetPassword} request.","SponsoredMessage":"A sponsored message","messages.SponsoredMessages":"A set of sponsored messages associated to a channel","ChatTheme":"A chat theme","account.ChatThemes":"Available chat themes","MessageReactions":"Message reactions","ReactionCount":"Number of users that reacted with a certain emoji","MessageReactionsList":"List of message reactions","MessageUserReaction":"Message reaction","Bool":"Boolean type."}} \ No newline at end of file diff --git a/packages/tl/scripts/generate-keys.js b/packages/tl/scripts/.rsa-keys.txt similarity index 59% rename from packages/tl/scripts/generate-keys.js rename to packages/tl/scripts/.rsa-keys.txt index 09cae707..c31070f1 100644 --- a/packages/tl/scripts/generate-keys.js +++ b/packages/tl/scripts/.rsa-keys.txt @@ -1,119 +1,96 @@ -// generates fingerprints for public keys. -// since they are rarely changed, not included in `generate-code` npm script -// keys can still be added at runtime, this is just an optimization -const { parsePublicKey } = require('../../core/dist/utils/crypto/keys') -const { - NodeCryptoProvider, -} = require('../../core/dist/utils/crypto/node-crypto') -const { createWriter } = require('./common') +# from https://github.com/DrKLO/Telegram/blob/3b971647e2c5eff7390b4d0ab953314932d41b77/TMessagesProj/jni/tgnet/Handshake.cpp#L349 +### NEW ### +-----BEGIN RSA PUBLIC KEY----- +MIIBCgKCAQEAyMEdY1aR+sCR3ZSJrtztKTKqigvO/vBfqACJLZtS7QMgCGXJ6XIR +yy7mx66W0/sOFa7/1mAZtEoIokDP3ShoqF4fVNb6XeqgQfaUHd8wJpDWHcR2OFwv +plUUI1PLTktZ9uW2WE23b+ixNwJjJGwBDJPQEQFBE+vfmH0JP503wr5INS1poWg/ +j25sIWeYPHYeOrFp/eXaqhISP6G+q2IeTaWTXpwZj4LzXq5YOpk4bYEQ6mvRq7D1 +aHWfYmlEGepfaYR8Q0YqvvhYtMte3ITnuSJs171+GDqpdKcSwHnd6FudwGO4pcCO +j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB +-----END RSA PUBLIC KEY----- -// https://github.com/DrKLO/Telegram/blob/a724d96e9c008b609fe188d122aa2922e40de5fc/TMessagesProj/jni/tgnet/Handshake.cpp#L356-L436 -const newPublicKeys = [ - `-----BEGIN RSA PUBLIC KEY----- +-----BEGIN RSA PUBLIC KEY----- +MIIBCgKCAQEA6LszBcC1LGzyr992NzE0ieY+BSaOW622Aa9Bd4ZHLl+TuFQ4lo4g +5nKaMBwK/BIb9xUfg0Q29/2mgIR6Zr9krM7HjuIcCzFvDtr+L0GQjae9H0pRB2OO +62cECs5HKhT5DZ98K33vmWiLowc621dQuwKWSQKjWf50XYFw42h21P2KXUGyp2y/ ++aEyZ+uVgLLQbRA1dEjSDZ2iGRy12Mk5gpYc397aYp438fsJoHIgJ2lgMv5h7WY9 +t6N/byY9Nw9p21Og3AoXSL2q/2IJ1WRUhebgAdGVMlV1fkuOQoEzR7EdpqtQD9Cs +5+bfo3Nhmcyvk5ftB0WkJ9z6bNZ7yxrP8wIDAQAB +-----END RSA PUBLIC KEY----- + +# from https://github.com/DrKLO/Telegram/blob/a724d96e9c008b609fe188d122aa2922e40de5fc/TMessagesProj/jni/tgnet/Handshake.cpp#L356-L436 +### OLD ### + +-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAruw2yP/BCcsJliRoW5eBVBVle9dtjJw+OYED160Wybum9SXtBBLX riwt4rROd9csv0t0OHCaTmRqBcQ0J8fxhN6/cpR1GWgOZRUAiQxoMnlt0R93LCX/ j1dnVa/gVbCjdSxpbrfY2g2L4frzjJvdl84Kd9ORYjDEAyFnEA7dD556OptgLQQ2 e2iVNq8NZLYTzLp5YpOdO1doK+ttrltggTCy5SrKeLoCPPbOgGsdxJxyz5KKcZnS Lj16yE5HvJQn0CNpRdENvRUXe6tBP78O39oJ8BTHp9oIjd6XWXAsp2CvK45Ol8wF XGF710w9lwCGNbmNxNYhtIkdqfsEcwR5JwIDAQAB ------END RSA PUBLIC KEY-----`, - `-----BEGIN RSA PUBLIC KEY----- +-----END RSA PUBLIC KEY----- + +-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAvfLHfYH2r9R70w8prHblWt/nDkh+XkgpflqQVcnAfSuTtO05lNPs pQmL8Y2XjVT4t8cT6xAkdgfmmvnvRPOOKPi0OfJXoRVylFzAQG/j83u5K3kRLbae 7fLccVhKZhY46lvsueI1hQdLgNV9n1cQ3TDS2pQOCtovG4eDl9wacrXOJTG2990V jgnIKNA0UMoP+KF03qzryqIt3oTvZq03DyWdGK+AZjgBLaDKSnC6qD2cFY81UryR WOab8zKkWAnhw2kFpcqhI0jdV5QaSCExvnsjVaX0Y1N0870931/5Jb9ICe4nweZ9 kSDF/gip3kWLG0o8XQpChDfyvsqB9OLV/wIDAQAB ------END RSA PUBLIC KEY-----`, - `-----BEGIN RSA PUBLIC KEY----- +-----END RSA PUBLIC KEY----- + +-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAs/ditzm+mPND6xkhzwFIz6J/968CtkcSE/7Z2qAJiXbmZ3UDJPGr zqTDHkO30R8VeRM/Kz2f4nR05GIFiITl4bEjvpy7xqRDspJcCFIOcyXm8abVDhF+ th6knSU0yLtNKuQVP6voMrnt9MV1X92LGZQLgdHZbPQz0Z5qIpaKhdyA8DEvWWvS Uwwc+yi1/gGaybwlzZwqXYoPOhwMebzKUk0xW14htcJrRrq+PXXQbRzTMynseCoP Ioke0dtCodbA3qQxQovE16q9zz4Otv2k4j63cz53J+mhkVWAeWxVGI0lltJmWtEY K6er8VqqWot3nqmWMXogrgRLggv/NbbooQIDAQAB ------END RSA PUBLIC KEY-----`, - `-----BEGIN RSA PUBLIC KEY----- +-----END RSA PUBLIC KEY----- + +-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAvmpxVY7ld/8DAjz6F6q05shjg8/4p6047bn6/m8yPy1RBsvIyvuD uGnP/RzPEhzXQ9UJ5Ynmh2XJZgHoE9xbnfxL5BXHplJhMtADXKM9bWB11PU1Eioc 3+AXBB8QiNFBn2XI5UkO5hPhbb9mJpjA9Uhw8EdfqJP8QetVsI/xrCEbwEXe0xvi fRLJbY08/Gp66KpQvy7g8w7VB8wlgePexW3pT13Ap6vuC+mQuJPyiHvSxjEKHgqe Pji9NP3tJUFQjcECqcm0yV7/2d0t/pbCm+ZH1sadZspQCEPPrtbkQBlvHb4OLiIW PGHKSMeRFvp3IWcmdJqXahxLCUS1Eh6MAQIDAQAB ------END RSA PUBLIC KEY-----`, -] +-----END RSA PUBLIC KEY----- -const oldPublicKeys = [ - `-----BEGIN RSA PUBLIC KEY----- +-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6 lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS an9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+ 8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB ------END RSA PUBLIC KEY-----`, - `-----BEGIN RSA PUBLIC KEY----- +-----END RSA PUBLIC KEY----- + +-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAxq7aeLAqJR20tkQQMfRn+ocfrtMlJsQ2Uksfs7Xcoo77jAid0bRt ksiVmT2HEIJUlRxfABoPBV8wY9zRTUMaMA654pUX41mhyVN+XoerGxFvrs9dF1Ru vCHbI02dM2ppPvyytvvMoefRoL5BTcpAihFgm5xCaakgsJ/tH5oVl74CdhQw8J5L xI/K++KJBUyZ26Uba1632cOiq05JBUW0Z2vWIOk4BLysk7+U9z+SxynKiZR3/xdi XvFKk01R3BHV+GUKM2RYazpS/P8v7eyKhAbKxOdRcFpHLlVwfjyM1VlDQrEZxsMp NTLYXb6Sce1Uov0YtNx5wEowlREH1WOTlwIDAQAB ------END RSA PUBLIC KEY-----`, - `-----BEGIN RSA PUBLIC KEY----- +-----END RSA PUBLIC KEY----- + +-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAsQZnSWVZNfClk29RcDTJQ76n8zZaiTGuUsi8sUhW8AS4PSbPKDm+ DyJgdHDWdIF3HBzl7DHeFrILuqTs0vfS7Pa2NW8nUBwiaYQmPtwEa4n7bTmBVGsB 1700/tz8wQWOLUlL2nMv+BPlDhxq4kmJCyJfgrIrHlX8sGPcPA4Y6Rwo0MSqYn3s g1Pu5gOKlaT9HKmE6wn5Sut6IiBjWozrRQ6n5h2RXNtO7O2qCDqjgB2vBxhV7B+z hRbLbCmW0tYMDsvPpX5M8fsO05svN+lKtCAuz1leFns8piZpptpSCFn7bWxiA9/f x5x17D7pfah3Sy2pA+NDXyzSlGcKdaUmwQIDAQAB ------END RSA PUBLIC KEY-----`, - `-----BEGIN RSA PUBLIC KEY----- +-----END RSA PUBLIC KEY----- + +-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAwqjFW0pi4reKGbkc9pK83Eunwj/k0G8ZTioMMPbZmW99GivMibwa xDM9RDWabEMyUtGoQC2ZcDeLWRK3W8jMP6dnEKAlvLkDLfC4fXYHzFO5KHEqF06i qAqBdmI1iBGdQv/OQCBcbXIWCGDY2AsiqLhlGQfPOI7/vvKc188rTriocgUtoTUc /n/sIUzkgwTqRyvWYynWARWzQg0I9olLBBC2q5RQJJlnYXZwyTL3y9tdb7zOHkks WV9IMQmZmyZh/N7sMbGWQpt4NMchGpPGeJ2e5gHBjDnlIf2p1yZOYeUYrdbwcS0t UiggS4UeE8TzIuXFQxw7fzEIlmhIaq3FnwIDAQAB ------END RSA PUBLIC KEY-----`, -] +-----END RSA PUBLIC KEY----- -const index = {} -const crypto = new NodeCryptoProvider() - -const addPublicKey = async (pem, old) => { - const parsed = await parsePublicKey(crypto, pem, old) - - index[parsed.fingerprint] = parsed -} - -async function main() { - for (let key of newPublicKeys) { - await addPublicKey(key, false) - } - for (let key of oldPublicKeys) { - await addPublicKey(key, true) - } - - const file = createWriter('../binary/rsa-keys.js') - file.write(`// This file was auto-generated. Do not edit. -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); - -exports.default = {`) - file.tab() - - Object.entries(index).forEach(([key, value]) => { - file.write(`'${key}': { - modulus: '${value.modulus}', - exponent: '${value.exponent}', - fingerprint: '${value.fingerprint}', - old: ${value.old}, -},`) - }) - - file.untab() - file.write(`}`) -} - -main().catch(console.error) +-----END OLD KEYS----- diff --git a/packages/tl/scripts/append.tl b/packages/tl/scripts/append.tl deleted file mode 100644 index 4018362c..00000000 --- a/packages/tl/scripts/append.tl +++ /dev/null @@ -1,5 +0,0 @@ -// this file is sourced *after* actual schema. -// should be used for types that *are* in the schema, -// but for one reason or another incorrect or somehow invalid in schema. -// (i.e. should be used very rarely) -// in case of conflict, type from this schema is preferred diff --git a/packages/tl/scripts/common.js b/packages/tl/scripts/common.js deleted file mode 100644 index eeb846a3..00000000 --- a/packages/tl/scripts/common.js +++ /dev/null @@ -1,60 +0,0 @@ -const fs = require('fs') -const path = require('path') - -const createWriter = (file, dir = __dirname) => { - let indent = '' - let output = fs.createWriteStream(path.join(dir, file)) - - const funcs = { - get indent() { - return indent - }, - tab: () => (indent += ' '), - untab: () => (indent = indent.substr(0, indent.length - 4)), - wrap: (s, pref = '', exceptFirst = false) => - s - .replace(/(?![^\n]{1,90}$)([^\n]{1,90})\s/g, '$1\n') - .replace(/@see\n(.+?)}/g, '@see $1}\n') - .split('\n') - .map((it, i) => (!exceptFirst || i !== 0 ? pref + it : it)) - .join('\n'), - write: (text = '', end = '\n') => - output.write( - text - .split('\n') - .map((i) => indent + i) - .join('\n') + end - ), - comment: (text) => - funcs.write('/**\n' + funcs.wrap(text, '* ') + '\n' + '*/'), - } - - return funcs -} - -const camelToPascal = (s) => s[0].toUpperCase() + s.substr(1) -const snakeToCamel = (s) => { - return s.replace(/(? { - return $1.substr(1).toUpperCase() - }) -} -const camelToSnake = (s) => { - return s.replace( - /(?<=[a-zA-Z0-9])([A-Z0-9]+(?=[A-Z]|$)|[A-Z0-9])/g, - ($1) => { - return '_' + $1.toLowerCase() - } - ) -} -const snakeToPascal = (s) => camelToPascal(snakeToCamel(s)) - -const signedInt32ToUnsigned = (val) => val >>> 0 - -module.exports = { - createWriter, - camelToPascal, - snakeToCamel, - camelToSnake, - snakeToPascal, - signedInt32ToUnsigned -} diff --git a/packages/tl/scripts/constants.ts b/packages/tl/scripts/constants.ts new file mode 100644 index 00000000..9aa23158 --- /dev/null +++ b/packages/tl/scripts/constants.ts @@ -0,0 +1,18 @@ +import { join } from 'path' + +export const DOC_CACHE_FILE = join(__dirname, '.documentation.cache.json') +export const DESCRIPTIONS_YAML_FILE = join(__dirname, '../data/descriptions.yaml') +export const API_SCHEMA_JSON_FILE = join(__dirname, '../api-schema.json') +export const MTP_SCHEMA_JSON_FILE = join(__dirname, '../mtp-schema.json') + +export const CORE_DOMAIN = 'https://corefork.telegram.org' + +export const TDESKTOP_SCHEMA = + 'https://raw.githubusercontent.com/telegramdesktop/tdesktop/dev/Telegram/Resources/tl/api.tl' +export const TDLIB_SCHEMA = + 'https://raw.githubusercontent.com/tdlib/td/master/td/generate/scheme/telegram_api.tl' + +export const ESM_PRELUDE = `// This file is auto-generated. Do not edit. +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +` diff --git a/packages/tl/scripts/documentation.ts b/packages/tl/scripts/documentation.ts new file mode 100644 index 00000000..0ad97274 --- /dev/null +++ b/packages/tl/scripts/documentation.ts @@ -0,0 +1,343 @@ +import { TlEntry, TlFullSchema } from '@mtcute/tl-utils/src/types' +import cheerio from 'cheerio' +import { splitNameToNamespace } from '@mtcute/tl-utils/src/utils' +import { snakeToCamel } from '@mtcute/tl-utils/src/codegen/utils' +import { + API_SCHEMA_JSON_FILE, + CORE_DOMAIN, + DESCRIPTIONS_YAML_FILE, + DOC_CACHE_FILE, +} from './constants' +import { fetchRetry } from './utils' +import { readFile, writeFile } from 'fs/promises' +// @ts-ignore +import jsYaml from 'js-yaml' +import { applyDescriptionsYamlFile } from './process-descriptions-yaml' +import { packTlSchema, unpackTlSchema } from './schema' + +type Cheerio = typeof cheerio['root'] extends () => infer T ? T : never +type CheerioInit = typeof cheerio['load'] extends (...a: any[]) => infer T + ? T + : never + +export interface CachedDocumentationEntry { + comment?: string + arguments?: Record + throws?: TlEntry['throws'] + available?: TlEntry['available'] +} + +export interface CachedDocumentation { + updated: string + classes: Record + methods: Record + unions: Record +} + +function normalizeLinks(url: string, el: Cheerio): void { + el.find('a').each((i, _it) => { + const it = cheerio(_it) + + if (it.attr('href')![0] === '#') return + + it.attr('href', new URL(it.attr('href')!, url).href) + let href = it.attr('href')! + + let m + if ( + (m = href.match(/\/(constructor|method|union)\/([^#?]+)(?:\?|#|$)/)) + ) { + let [, type, name] = m + if (type === 'method') { + const [ns, n] = splitNameToNamespace(name) + const q = snakeToCamel(n) + name = ns ? ns + '.' + q : q + } + + it.replaceWith(`{@link ${name}}`) + } + }) +} + +function extractDescription($: CheerioInit) { + return $('.page_scheme') + .prevAll('p') + .get() + .reverse() + .map((el) => $(el).html()!.trim()) + .join('\n\n') + .trim() +} + +// from https://github.com/sindresorhus/cli-spinners/blob/main/spinners.json +const PROGRESS_CHARS = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'] + +export async function fetchDocumentation( + schema: TlFullSchema, + layer: number, + silent = !process.stdout.isTTY +): Promise { + const headers = { + cookie: `stel_dev_layer=${layer}`, + 'User-Agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' + + 'Chrome/87.0.4280.88 Safari/537.36', + } + + const ret: CachedDocumentation = { + updated: `${new Date().toLocaleString('ru-RU')} (layer ${layer})`, + classes: {}, + methods: {}, + unions: {}, + } + + let prevSize = 0 + let logPos = 0 + + function log(str: string) { + if (silent) return + while (str.length < prevSize) str += ' ' + + process.stdout.write('\r' + PROGRESS_CHARS[logPos] + ' ' + str) + + prevSize = str.length + logPos = (logPos + 1) % PROGRESS_CHARS.length + } + + for (const entry of schema.entries) { + log(`📥 ${entry.kind} ${entry.name}`) + + const url = `${CORE_DOMAIN}/${ + entry.kind === 'class' ? 'constructor' : 'method' + }/${entry.name}` + + const html = await fetchRetry(url, { + headers, + }) + const $ = cheerio.load(html) + const content = $('#dev_page_content') + + if (content.text().trim() === 'The page has not been saved') continue + + normalizeLinks(url, content) + + const retClass: CachedDocumentationEntry = {} + + const description = extractDescription($) + + if (description) { + retClass.comment = description + } + + const parametersTable = $('#parameters').parent().next('table') + parametersTable.find('tr').each((idx, _el) => { + const el = $(_el) + const cols = el.find('td') + if (!cols.length) return // + + const name = snakeToCamel(cols.first().text().trim()) + const description = cols.last().html()!.trim() + + if (description) { + if (!retClass.arguments) retClass.arguments = {} + retClass.arguments[name] = description + } + }) + + if (entry.kind === 'method') { + const errorsTable = $('#possible-errors').parent().next('table') + + let userBotRequired = false + + errorsTable.find('tr').each((idx, _el) => { + const el = $(_el) + let cols = el.find('td') + if (!cols.length) return // + + let code = parseInt($(cols[0]).text()) + let name = $(cols[1]).text() + let comment = $(cols[2]).text() + + if (name === 'USER_BOT_REQUIRED') userBotRequired = true + + if (!retClass.throws) retClass.throws = [] + retClass.throws.push({ code, name, comment }) + }) + + const botsCanUse = !!$('#bots-can-use-this-method').length + const onlyBotsCanUse = + botsCanUse && + (!!description.match(/[,;]( for)? bots only$/) || + userBotRequired) + + retClass.available = onlyBotsCanUse + ? 'bot' + : botsCanUse + ? 'both' + : 'user' + } + + ret[entry.kind === 'class' ? 'classes' : 'methods'][ + entry.name + ] = retClass + } + + for (const name in schema.unions) { + if (!schema.unions.hasOwnProperty(name)) continue + + log(`📥 union ${name}`) + + const url = `${CORE_DOMAIN}/type/${name}` + + const html = await fetchRetry(url, { + headers, + }) + const $ = cheerio.load(html) + const content = $('#dev_page_content') + + if (content.text().trim() === 'The page has not been saved') continue + + normalizeLinks(url, content) + + const description = extractDescription($) + if (description) ret.unions[name] = description + } + + log('✨ Patching descriptions') + + const descriptionsYaml = jsYaml.load( + await readFile(DESCRIPTIONS_YAML_FILE, 'utf8') + ) + applyDescriptionsYamlFile(ret, descriptionsYaml) + + log('🔄 Writing to file') + + await writeFile(DOC_CACHE_FILE, JSON.stringify(ret)) + + if (!silent) process.stdout.write('\n') + + return ret +} + +export function applyDocumentation( + schema: TlFullSchema, + docs: CachedDocumentation +) { + for (let i = 0; i < 2; i++) { + const kind = i === 0 ? 'classes' : 'methods' + + const objIndex = schema[kind] + const docIndex = docs[kind] + + for (let name in docIndex) { + if (!docIndex.hasOwnProperty(name)) continue + if (!(name in objIndex)) continue + + const obj = objIndex[name] + const doc = docIndex[name] + + if (doc.comment) obj.comment = doc.comment + if (doc.throws) obj.throws = doc.throws + if (doc.available) obj.available = doc.available + if (doc.arguments) { + obj.arguments.forEach((arg) => { + if (arg.name in doc.arguments!) { + arg.comment = doc.arguments![arg.name] + } + }) + } + } + } + + for (let name in schema.unions) { + if (!schema.unions.hasOwnProperty(name)) continue + if (!(name in docs.unions)) continue + + schema.unions[name].comment = docs.unions[name] + } +} + +export async function getCachedDocumentation(): Promise { + try { + const file = await readFile(DOC_CACHE_FILE, 'utf8') + return JSON.parse(file) + } catch (e) { + if (e.code === 'ENOENT') { + return null + } + throw e + } +} + +async function main() { + let cached = await getCachedDocumentation() + + if (cached) { + console.log('Cached documentation: %d', cached.updated) + } + + const rl = require('readline').createInterface({ + input: process.stdin, + output: process.stdout, + }) + const input = (q: string): Promise => + new Promise((res) => rl.question(q, res)) + + while (true) { + console.log('Choose action:') + console.log('0. Exit') + console.log('1. Update documentation') + console.log('2. Apply descriptions.yaml') + console.log('3. Apply documentation to schema') + + const act = parseInt(await input('[0-3] > ')) + if (isNaN(act) || act < 0 || act > 3) { + console.log('Invalid action') + continue + } + + if (act === 0) return + + if (act === 1) { + const [schema, layer] = unpackTlSchema( + JSON.parse(await readFile(API_SCHEMA_JSON_FILE, 'utf8')) + ) + cached = await fetchDocumentation(schema, layer) + } + + if (act === 2) { + if (!cached) { + console.log('No schema available, fetch it first') + continue + } + + const descriptionsYaml = jsYaml.load( + await readFile(DESCRIPTIONS_YAML_FILE, 'utf8') + ) + applyDescriptionsYamlFile(cached, descriptionsYaml) + + await writeFile(DOC_CACHE_FILE, JSON.stringify(cached)) + } + + if (act === 3) { + if (!cached) { + console.log('No schema available, fetch it first') + continue + } + + const [schema, layer] = unpackTlSchema( + JSON.parse(await readFile(API_SCHEMA_JSON_FILE, 'utf8')) + ) + + applyDocumentation(schema, cached) + await writeFile( + API_SCHEMA_JSON_FILE, + JSON.stringify(packTlSchema(schema, layer)) + ) + } + } +} + +if (require.main === module) { + main().catch(console.error) +} diff --git a/packages/tl/scripts/fetch-api.ts b/packages/tl/scripts/fetch-api.ts new file mode 100644 index 00000000..94b6bb76 --- /dev/null +++ b/packages/tl/scripts/fetch-api.ts @@ -0,0 +1,312 @@ +// Downloads latest .tl schemas from various sources, +// fetches documentation from https://corefork.telegram.org/schema +// and builds a single .json file from all of that +// +// Conflicts merging is interactive, so we can't put this in CI + +import { parseTlToEntries } from '@mtcute/tl-utils/src/parse' +import { parseFullTlSchema } from '@mtcute/tl-utils/src/schema' +import { mergeTlEntries, mergeTlSchemas } from '@mtcute/tl-utils/src/merge' +import { TlEntry, TlFullSchema } from '@mtcute/tl-utils/src/types' +import { readFile, writeFile } from 'fs/promises' +import { join } from 'path' +import cheerio from 'cheerio' +import fetch from 'node-fetch' +import readline from 'readline' +import { writeTlEntryToString } from '@mtcute/tl-utils/src/stringify' +import { + CORE_DOMAIN, + API_SCHEMA_JSON_FILE, + TDESKTOP_SCHEMA, + TDLIB_SCHEMA, +} from './constants' +import { fetchRetry } from './utils' +import { + applyDocumentation, + fetchDocumentation, + getCachedDocumentation, +} from './documentation' +import { packTlSchema } from './schema' + +const README_MD_FILE = join(__dirname, '../README.md') +const PACKAGE_JSON_FILE = join(__dirname, '../package.json') + +function tlToFullSchema(tl: string): TlFullSchema { + return parseFullTlSchema(parseTlToEntries(tl)) +} + +interface Schema { + name: string + layer: number + content: TlFullSchema +} + +async function fetchTdlibSchema(): Promise { + const schema = await fetchRetry(TDLIB_SCHEMA) + const versionHtml = await fetch( + 'https://raw.githubusercontent.com/tdlib/td/master/td/telegram/Version.h' + ).then((i) => i.text()) + + const layer = versionHtml.match(/^constexpr int32 MTPROTO_LAYER = (\d+)/m) + if (!layer) throw new Error('Layer number not available') + + return { + name: 'TDLib', + layer: parseInt(layer[1]), + content: tlToFullSchema(schema), + } +} + +async function fetchTdesktopSchema(): Promise { + const schema = await fetchRetry(TDESKTOP_SCHEMA) + const layer = schema.match(/^\/\/ LAYER (\d+)/m) + if (!layer) throw new Error('Layer number not available') + + return { + name: 'TDesktop', + layer: parseInt(layer[1]), + content: tlToFullSchema(schema), + } +} + +async function fetchCoreSchema(): Promise { + const html = await fetchRetry(`${CORE_DOMAIN}/schema`) + const $ = cheerio.load(html) + // cheerio doesn't always unescape them + const schema = $('.page_scheme code') + .text() + .replace(/</g, '<') + .replace(/>/g, '>') + + const layer = $('.dev_layer_select .dropdown-toggle') + .text() + .trim() + .match(/^Layer (\d+)$/i) + if (!layer) throw new Error('Layer number not available') + + return { + name: 'Core', + layer: parseInt(layer[1]), + content: tlToFullSchema(schema), + } +} + +function input(rl: readline.Interface, q: string): Promise { + return new Promise((resolve) => rl.question(q, resolve)) +} + +interface ConflictOption { + schema: Schema + entry?: TlEntry +} + +async function updateReadme(currentLayer: number) { + const oldReadme = await readFile(README_MD_FILE, 'utf8') + const today = new Date().toLocaleDateString('ru') + await writeFile( + README_MD_FILE, + oldReadme.replace( + /^Generated from TL layer \*\*\d+\*\* \(last updated on \d+\.\d+\.\d+\)\.$/m, + `Generated from TL layer **${currentLayer}** (last updated on ${today}).` + ) + ) +} + +async function updatePackageVersion( + rl: readline.Interface, + currentLayer: number +) { + const packageJson = JSON.parse(await readFile(PACKAGE_JSON_FILE, 'utf8')) + const version: string = packageJson.version + let [major, minor, patch] = version.split('.').map((i) => parseInt(i)) + + patch = 0 + + if (major === currentLayer) { + console.log('Current version: %s. Bump minor version?', version) + const res = await input(rl, '[Y/n] > ') + + if (res.trim().toLowerCase() === 'n') { + return + } + } else { + major = currentLayer + minor = 0 + } + + console.log('Updating package version...') + packageJson.version = `${major}.${minor}.${patch}` + await writeFile(PACKAGE_JSON_FILE, JSON.stringify(packageJson, null, 4)) +} + +async function overrideInt53(schema: TlFullSchema): Promise { + console.log('Applying int53 overrides...') + + const config = JSON.parse( + await readFile(join(__dirname, '../data/int53-overrides.json'), 'utf8') + ) + + schema.entries.forEach((entry) => { + const overrides: string[] | undefined = config[entry.kind][entry.name] + if (!overrides) return + + overrides.forEach((argName) => { + const arg = entry.arguments.find((it) => it.name === argName) + if (!arg) { + console.log(`[warn] Cannot override ${entry.name}#${argName}: argument does not exist`) + return + } + + if (arg.type === 'long') { + arg.type = 'int53' + } else if (arg.type.toLowerCase() === 'vector') { + arg.type = 'vector' + } else { + console.log(`[warn] Cannot override ${entry.name}#${argName}: argument is not long (${arg.type})`) + } + }) + }) +} + +async function main() { + console.log('Loading schemas...') + + const schemas: Schema[] = [ + await fetchTdlibSchema(), + await fetchTdesktopSchema(), + await fetchCoreSchema(), + { + name: 'Custom', + layer: 0, // handled manually + content: tlToFullSchema( + await readFile(join(__dirname, '../data/custom.tl'), 'utf8') + ), + }, + ] + + console.log('Available schemas:') + schemas.forEach((schema) => + console.log( + ' - %s (layer %d): %d entries', + schema.name, + schema.layer, + schema.content.entries.length + ) + ) + + const resultLayer = Math.max(...schemas.map((it) => it.layer)) + console.log(`Final schema will be on layer ${resultLayer}. Merging...`) + + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }) + + const resultSchema = await mergeTlSchemas( + schemas.map((it) => it.content), + async (_options) => { + const options: ConflictOption[] = _options.map((it, idx) => ({ + schema: schemas[idx], + entry: it, + })) + + let chooseOptions: ConflictOption[] = [] + + const customEntry = options[options.length - 1] + if (customEntry.entry) { + // if there is custom entry in conflict, we must present it, otherwise something may go wrong + chooseOptions = options + } else { + // first of all, prefer entries from the latest layer + const fromLastSchema = options.filter( + (opt) => opt.schema.layer === resultLayer + ) + + // if there is only one schema on the latest layer, we can simply return it + if (fromLastSchema.length === 1) return fromLastSchema[0].entry + + // there are multiple choices on the latest layer + // if they are all the same, it's just conflict between layers, + // and we can merge the ones from the latest layer + const mergedEntry = mergeTlEntries( + fromLastSchema + .filter((opt) => opt.entry) + .map((opt) => opt.entry!) + ) + if (typeof mergedEntry === 'string') { + // merge failed, so there is in fact some conflict + chooseOptions = fromLastSchema + } else return mergedEntry + } + + let nonEmptyOptions = chooseOptions.filter((opt) => opt.entry) + + console.log( + 'Conflict detected at %s %s:', + nonEmptyOptions[0].entry!.kind, + nonEmptyOptions[0].entry!.name + ) + console.log('0. Remove') + nonEmptyOptions.forEach((opt, idx) => { + console.log( + `${idx + 1}. ${opt.schema.name}: ${writeTlEntryToString( + opt.entry! + )}` + ) + }) + + while (true) { + const res = parseInt( + await input(rl, `[0-${nonEmptyOptions.length}] > `) + ) + if (isNaN(res) || res < 0 || res > nonEmptyOptions.length) + continue + + if (res === 0) return undefined + return nonEmptyOptions[res - 1].entry + } + } + ) + + console.log( + 'Done! Final schema contains %d entries', + resultSchema.entries.length + ) + + let docs = await getCachedDocumentation() + + if (docs) { + console.log('Cached documentation from %s, use it?', docs.updated) + const res = await input(rl, '[Y/n] > ') + + if (res.trim().toLowerCase() === 'n') { + docs = null + } + } + + if (docs === null) { + console.log('Downloading documentation...') + docs = await fetchDocumentation(resultSchema, resultLayer) + } + + applyDocumentation(resultSchema, docs) + + await overrideInt53(resultSchema) + + console.log('Writing result to file...') + await writeFile( + API_SCHEMA_JSON_FILE, + JSON.stringify(packTlSchema(resultSchema, resultLayer)) + ) + + console.log('Updating README.md...') + await updateReadme(resultLayer) + + await updatePackageVersion(rl, resultLayer) + + rl.close() + + console.log('Done!') +} + +main().catch(console.error) diff --git a/packages/tl/scripts/fetch-errors.ts b/packages/tl/scripts/fetch-errors.ts new file mode 100644 index 00000000..ed0aa677 --- /dev/null +++ b/packages/tl/scripts/fetch-errors.ts @@ -0,0 +1,308 @@ +import { join } from 'path' +import fetch from 'node-fetch' +import csvParser from 'csv-parser' +import { camelToPascal, snakeToCamel } from '@mtcute/tl-utils/src/codegen/utils' +import { writeFile } from 'fs/promises' + +const OUT_TS_FILE = join(__dirname, '../errors.d.ts') +const OUT_JS_FILE = join(__dirname, '../errors.js') + +const TELETHON_ERRORS_CSV = + 'https://raw.githubusercontent.com/LonamiWebs/Telethon/master/telethon_generator/data/errors.csv' + +interface ErrorInfo { + base?: true + virtual?: true + codes: string + name: string + description: string +} + +const baseErrors: ErrorInfo[] = [ + { + codes: '400', + name: 'BAD_REQUEST', + description: + 'The query contains errors. In the event that a request was created using a form ' + + 'and contains user generated data, the user should be notified that the data must ' + + 'be corrected before the query is repeated', + }, + { + codes: '401', + name: 'UNAUTHORIZED', + description: + 'There was an unauthorized attempt to use functionality available only to authorized users.', + }, + { + codes: '403', + name: 'FORBIDDEN', + description: + 'Privacy violation. For example, an attempt to write a message ' + + 'to someone who has blacklisted the current user.', + }, + { + codes: '404', + name: 'NOT_FOUND', + description: + 'An attempt to invoke a non-existent object, such as a method.', + }, + { + codes: '420', + name: 'FLOOD', + description: + 'The maximum allowed number of attempts to invoke the given method' + + 'with the given input parameters has been exceeded. For example, in an' + + 'attempt to request a large number of text messages (SMS) for the same' + + 'phone number.', + }, + { + codes: '303', + name: 'SEE_OTHER', + description: + 'The request must be repeated, but directed to a different data center', + }, + { + codes: '406', + name: 'NOT_ACCEPTABLE', + description: + 'Similar to 400 BAD_REQUEST, but the app should not display any error messages to user ' + + 'in UI as a result of this response. The error message will be delivered via ' + + 'updateServiceNotification instead.', + }, + { + codes: '500', + name: 'INTERNAL', + description: + 'An internal server error occurred while a request was being processed; ' + + 'for example, there was a disruption while accessing a database or file storage.', + }, +] +baseErrors.forEach((it) => (it.base = true)) + +const virtualErrors: ErrorInfo[] = [ + { + name: 'RPC_TIMEOUT', + codes: '408', + description: 'The set RPC timeout has exceeded', + }, + { + name: 'MESSAGE_NOT_FOUND', + codes: '404', + description: 'Message was not found', + }, +] +virtualErrors.forEach((it) => (it.virtual = true)) + +const inheritanceTable: Record = { + 400: 'BadRequestError', + 401: 'UnauthorizedError', + 403: 'ForbiddenError', + 404: 'NotFoundError', + 420: 'FloodError', + 303: 'SeeOtherError', + 406: 'NotAcceptableError', + 500: 'InternalError', +} + +const RPC_ERROR_CLASS_JS = ` +class RpcError extends Error { + constructor(code, text, description) { + super(description); + this.code = code; + this.text = text; + } +} +exports.RpcError = RpcError; +`.trimStart() + +const RPC_ERROR_CLASS_TS = ` +export declare class RpcError extends Error { + code: number; + text: string; + constructor (code: number, text: string, description?: string); +} +`.trimStart() + +const BASE_ERROR_TEMPLATE_JS = ` +class {className} extends RpcError { + constructor(name, description) { + super({code}, name, description); + } +} +exports.{className} = {className} +` +const BASE_ERROR_TEMPLATE_TS = ` +export declare class {className} extends RpcError { + constructor (name: string, description: string); +} +` + +const TL_BUILDER_TEMPLATE_JS = ` +exports.createRpcErrorFromTl = function (obj) { + if (obj.errorMessage in _staticNameErrors) return new _staticNameErrors[obj.errorMessage](); + + let match; + {parametrized} + + if (obj.errorCode in _baseCodeErrors) return new _baseCodeErrors[obj.errorCode](obj.errorMessage); + + return new RpcError(obj.errorCode, obj.errorMessage); +} +`.trim() + +const DESCRIPTION_PARAM_RE = /_X_|_X$|^X_/ + +function jsComment(s: string): string { + return ( + '/**' + + s + .replace(/(?![^\n]{1,60}$)([^\n]{1,60})\s/g, '$1\n') + .replace(/\n|^/g, '\n * ') + + '\n */' + ) +} + +async function fetchTelethonErrors(): Promise { + const stream = await fetch(TELETHON_ERRORS_CSV).then((i) => i.body!) + + return new Promise((resolve, reject) => { + const ret: ErrorInfo[] = [] + + stream + .pipe(csvParser()) + .on('data', (it) => ret.push(it)) + .on('end', () => resolve(ret)) + .on('error', reject) + }) +} + +interface ExtendedErrorInfo extends ErrorInfo { + className: string + code: string + inherits: string + argument: string | null +} + +function getExtendedErrorInfo(err: ErrorInfo): ExtendedErrorInfo { + const [, argument] = err.description.match(/{([a-z_]+)}/i) ?? [, null] + + const ret = err as ExtendedErrorInfo + + let className = err.name + if (className[0].match(/[0-9]/)) { + className = '_' + className + } + className = className.replace(DESCRIPTION_PARAM_RE, (i) => + i === '_X_' ? '_' : '' + ) + + ret.className = + camelToPascal(snakeToCamel(className.toLowerCase())) + 'Error' + ret.code = err.codes.split(' ')[0] + ret.inherits = err.base + ? 'RpcError' + : inheritanceTable[ret.code] ?? 'RpcError' + ret.argument = argument ? snakeToCamel(argument) : null + + return ret +} + +async function main() { + console.log('Fetching errors from Telethon...') + + const errors = [ + ...baseErrors, + ...(await fetchTelethonErrors()), + ...virtualErrors, + ].map(getExtendedErrorInfo) + + console.log('Generating code...') + + let js = RPC_ERROR_CLASS_JS + let ts = RPC_ERROR_CLASS_TS + + for (const err of errors) { + // generate error class in js + if (err.base) { + js += BASE_ERROR_TEMPLATE_JS.replace( + /{className}/g, + err.className + ).replace(/{code}/g, err.code) + } else { + js += `class ${err.className} extends ${err.inherits} {\n` + js += ` constructor(${err.argument ?? ''}) {\n` + + const description = JSON.stringify(err.description).replace( + /{([a-z_]+)}/gi, + (_, $1) => `" + ${snakeToCamel($1)} + "` + ) + + if (err.inherits === 'RpcError') { + js += ` super(${err.code}, '${err.name}', ${description});\n` + } else { + js += ` super('${err.name}', ${description});\n` + } + + if (err.argument) { + js += ` this.${err.argument} = ${err.argument};\n` + } + + js += ' }\n}\n' + js += `exports.${err.className} = ${err.className};\n` + } + + // generate error class typings + if (err.description) { + ts += jsComment(err.description) + '\n' + } + if (err.base) { + ts += BASE_ERROR_TEMPLATE_TS.replace(/{className}/g, err.className) + } else { + ts += `export declare class ${err.className} extends ${err.inherits} {\n` + + if (err.argument) { + ts += ` ${err.argument}: number;\n` + } + + ts += ` constructor (${ + err.argument ? err.argument + ': number' : '' + });\n` + ts += '}\n' + } + } + + ts += + 'export declare function createRpcErrorFromTl (obj: object): RpcError;\n' + + js += 'const _staticNameErrors = {\n' + for (const err of errors) { + if (err.virtual || err.argument) continue + js += ` '${err.name}': ${err.className},\n` + } + js += ` 'Timeout': TimeoutError,\n` // because telegram c: + js += '};\n' + + js += 'const _baseCodeErrors = {\n' + for (const [code, error] of Object.entries(inheritanceTable)) { + js += ` ${code}: ${error},\n` + } + js += '};\n' + + let builderInner = '' + for (const err of errors) { + if (err.virtual || !err.argument) continue + + const regex = err.name.replace(DESCRIPTION_PARAM_RE, (s) => + s.replace('X', '(\\d+)') + ) + builderInner += + `if ((match=obj.errorMessage.match(/${regex}/))!=null)` + + `return new ${err.className}(parseInt(match[1]));\n` + } + js += TL_BUILDER_TEMPLATE_JS.replace('{parametrized}', builderInner) + + await writeFile(OUT_JS_FILE, js) + await writeFile(OUT_TS_FILE, ts) +} + +main().catch(console.error) diff --git a/packages/tl/scripts/fetch-mtp.ts b/packages/tl/scripts/fetch-mtp.ts new file mode 100644 index 00000000..8941858a --- /dev/null +++ b/packages/tl/scripts/fetch-mtp.ts @@ -0,0 +1,58 @@ +// Downloads latest MTProto .tl schema + +import cheerio from 'cheerio' +import { CORE_DOMAIN, MTP_SCHEMA_JSON_FILE } from './constants' +import { fetchRetry } from './utils' +import { parseTlToEntries } from '@mtcute/tl-utils/src/parse' +import { writeFile } from 'fs/promises' + +async function fetchMtprotoSchema(): Promise { + const html = await fetchRetry(`${CORE_DOMAIN}/schema/mtproto`) + const $ = cheerio.load(html) + // cheerio doesn't always unescape them + return $('#dev_page_content pre code') + .text() + .replace(/</g, '<') + .replace(/>/g, '>') +} + +async function main() { + console.log('Downloading MTProto schema...') + const schema = await fetchMtprotoSchema() + + console.log('Parsing...') + let entries = parseTlToEntries(schema, { + prefix: 'mt_', + applyPrefixToArguments: true, + }) + + // remove manually parsed types + entries = entries.filter( + (it) => + [ + 'mt_msg_container', + 'mt_message', + 'mt_msg_copy', + 'mt_gzip_packed', + ].indexOf(it.name) === -1 + ) + + const rpcResult = entries.find((it) => it.name === 'mt_rpc_result')! + rpcResult.arguments.forEach((arg) => { + if (arg.name === 'result') { + arg.type = 'any' + } + }) + + // mtproto is handled internally, for simplicity we make them all classes + entries.forEach((entry) => { + entry.kind = 'class' + }) + + console.log('Writing to file...') + await writeFile(MTP_SCHEMA_JSON_FILE, JSON.stringify(entries)) + + console.log('Done!') +} + +main().catch(console.error) diff --git a/packages/tl/scripts/gen-code.ts b/packages/tl/scripts/gen-code.ts new file mode 100644 index 00000000..8da25a1b --- /dev/null +++ b/packages/tl/scripts/gen-code.ts @@ -0,0 +1,84 @@ +import { unpackTlSchema } from './schema' +import { API_SCHEMA_JSON_FILE, MTP_SCHEMA_JSON_FILE, ESM_PRELUDE } from './constants' +import { readFile, writeFile } from 'fs/promises' +import { parseFullTlSchema } from '@mtcute/tl-utils/src/schema' +import { TlFullSchema } from '@mtcute/tl-utils/src/types' +import { join } from 'path' +import { generateTypescriptDefinitionsForTlSchema } from '@mtcute/tl-utils/src/codegen/types' +import { generateReaderCodeForTlEntries } from '@mtcute/tl-utils/src/codegen/reader' +import { generateWriterCodeForTlEntries } from '@mtcute/tl-utils/src/codegen/writer' + +const OUT_TYPINGS_FILE = join(__dirname, '../index.d.ts') +const OUT_TYPINGS_JS_FILE = join(__dirname, '../index.js') +const OUT_READERS_FILE = join(__dirname, '../binary/reader.js') +const OUT_WRITERS_FILE = join(__dirname, '../binary/writer.js') + +async function generateTypings( + apiSchema: TlFullSchema, + apiLayer: number, + mtpSchema: TlFullSchema +) { + console.log('Generating typings...') + const [apiTs, apiJs] = generateTypescriptDefinitionsForTlSchema( + apiSchema, + apiLayer + ) + const [mtpTs, mtpJs] = generateTypescriptDefinitionsForTlSchema( + mtpSchema, + 0, + 'mtp' + ) + + await writeFile( + OUT_TYPINGS_FILE, + apiTs + '\n\n' + mtpTs.replace("import _Long from 'long';", '') + ) + await writeFile(OUT_TYPINGS_JS_FILE, ESM_PRELUDE + apiJs + '\n\n' + mtpJs) +} + +async function generateReaders( + apiSchema: TlFullSchema, + mtpSchema: TlFullSchema +) { + console.log('Generating readers...') + + let code = generateReaderCodeForTlEntries(apiSchema.entries, 'r', false) + + const mtpCode = generateReaderCodeForTlEntries(mtpSchema.entries, '') + code = code.substring(0, code.length - 1) + mtpCode.substring(7) + code += '\nexports.default = r;' + + await writeFile(OUT_READERS_FILE, ESM_PRELUDE + code) +} + +async function generateWriters( + apiSchema: TlFullSchema, + mtpSchema: TlFullSchema +) { + console.log('Generating writers...') + + let code = generateWriterCodeForTlEntries(apiSchema.entries, 'r') + + const mtpCode = generateWriterCodeForTlEntries(mtpSchema.entries, '', false) + code = code.substring(0, code.length - 1) + mtpCode.substring(7) + code += '\nexports.default = r;' + + await writeFile(OUT_WRITERS_FILE, ESM_PRELUDE + code) +} + +async function main() { + const [apiSchema, apiLayer] = unpackTlSchema( + JSON.parse(await readFile(API_SCHEMA_JSON_FILE, 'utf8')) + ) + const mtpSchema = parseFullTlSchema( + JSON.parse(await readFile(MTP_SCHEMA_JSON_FILE, 'utf8')) + ) + + await generateTypings(apiSchema, apiLayer, mtpSchema) + await generateReaders(apiSchema, mtpSchema) + await generateWriters(apiSchema, mtpSchema) + + console.log('Done!') +} + +main().catch(console.error) diff --git a/packages/tl/scripts/gen-rsa-keys.ts b/packages/tl/scripts/gen-rsa-keys.ts new file mode 100644 index 00000000..04edd7fd --- /dev/null +++ b/packages/tl/scripts/gen-rsa-keys.ts @@ -0,0 +1,64 @@ +import { TlPublicKey } from '../binary/rsa-keys' +import { join } from 'path' +import readline from 'readline' +import { createReadStream } from 'fs' +import { NodeCryptoProvider } from '@mtcute/core/src/utils/crypto' +import { parsePublicKey } from '@mtcute/core/src/utils/crypto/keys' +import { writeFile } from 'fs/promises' +import { ESM_PRELUDE } from './constants' + +const IN_TXT_FILE = join(__dirname, '.rsa-keys.txt') +const OUT_JS_FILE = join(__dirname, '../binary/rsa-keys.js') + +interface InputKey { + kind: 'old' | 'new' + pem: string +} + +async function* parseInputFile(): AsyncIterableIterator { + const rl = readline.createInterface({ + input: createReadStream(IN_TXT_FILE), + crlfDelay: Infinity, + }) + + let currentKind: InputKey['kind'] = 'old' + + let current = '' + + for await (const line of rl) { + if (line === '### OLD ###') currentKind = 'old' + if (line === '### NEW ###') currentKind = 'new' + + if (line[0] === '#') continue + + current += line + '\n' + + if (line === '-----END RSA PUBLIC KEY-----') { + yield { + kind: currentKind, + pem: current.trim(), + } + current = '' + } + } +} + +async function main() { + const crypto = new NodeCryptoProvider() + const obj: Record = {} + + for await (const key of parseInputFile()) { + const parsed = await parsePublicKey(crypto, key.pem, key.kind === 'old') + obj[parsed.fingerprint] = parsed + } + + await writeFile( + OUT_JS_FILE, + ESM_PRELUDE + + "exports.default=JSON.parse('" + + JSON.stringify(obj) + + "');" + ) +} + +main().catch(console.error) diff --git a/packages/tl/scripts/generate-binary-reader.js b/packages/tl/scripts/generate-binary-reader.js deleted file mode 100644 index 30582aa2..00000000 --- a/packages/tl/scripts/generate-binary-reader.js +++ /dev/null @@ -1,149 +0,0 @@ -// disclaimer: code sucks because tl itself sucks :shrug: -const { createWriter } = require('./common') -const schema = require('../raw-schema.json') - -const output = createWriter('../binary/reader.js') -const { tab, untab, write } = output - -write(`// This file is auto-generated. Do not edit. -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); - -exports.default = {`) -tab() - -function getFunctionCallByTypeName(ns, type, arg) { - let funcName, - args = [] - if (type === 'Object') return 'object()' - if (type === 'number') funcName = 'int32' - else if (type === 'Long') funcName = 'long' - else if (type === 'RawLong') funcName = 'rawLong' - else if (type === 'Int128') funcName = 'int128' - else if (type === 'Int256') funcName = 'int256' - else if (type === 'Double') funcName = 'double' - else if (type === 'string') funcName = 'string' - else if (type === 'Buffer') funcName = 'bytes' - else if (type.match(/^(boolean|true|false)$/)) funcName = 'boolean' - else if (type.endsWith('[]')) { - let typ = type.substr(0, type.length - 2) - - let reader // = `(r, val) => r.${getFunctionCallByTypeName(ns, typ, 'val')}` - if (typ === '%Message') { - reader = 'exports.default[1538843921]' // because tl sucks :shrug: - } else if (typ[0] === '%') - throw new Error( - 'Only bare mt_message is supported. Please update codegen for ' + - typ - ) - else if (typ === 'number') reader = 'this.int32' - else if (typ === 'Buffer') reader = 'this.bytes' - else if (typ === 'Long') reader = 'this.long' - else if (typ === 'RawLong') reader = 'this.rawLong' - else if (typ === 'Int128') reader = 'this.int128' - else if (typ === 'Int256') reader = 'this.int256' - else if (typ === 'Double') reader = 'this.double' - else if (typ === 'string') reader = 'this.string' - else reader = 'this.object' - - if (arg && arg.name === 'serverPublicKeyFingerprints') - reader = 'this.ulong' - - funcName = `vector` - args = [reader] - - if (typ === '%Message') args.push(true) - } else funcName = `object` - - return `${funcName}(${args.join(', ')})` -} - -const parsedManually = [ - 0x1cb5c415, - 0x3072cfa1, - 0xbc799737, - 0x997275b5, - 0x3fedd339, - 0x56730bcc, -] - -function writeNamespace(nsType) { - return ([namespace, content]) => { - let baseTypePrefix = nsType === 'mtproto' ? 'mtproto.' : '' - let nsPrefix = namespace === '$root' ? '' : namespace + '.' - let prefix = (nsType === 'mtproto' ? 'mt_' : '') + nsPrefix - - const addObject = (isMethod) => { - return (cls) => { - if (cls.id === 0 || parsedManually.includes(cls.id)) return - - write(`${cls.id}: function () {`) - tab() - - let is_mt_message = - nsType === 'mtproto' && cls.name === 'message' - - if (cls.arguments?.length) { - write(`var ret = {};`) - write(`ret._ = '${prefix}${cls.name}';`) - - cls.arguments.forEach((arg) => { - if (arg.type === '$FlagsBitField') { - write('var ' + arg.name + ' = this.uint32();') - } else if (arg.optional) { - let bitIndex = +arg.predicate.split('.')[1] - let condition = `${arg.predicate.split('.')[0]} & ${ - 2 ** bitIndex - }` - - if (arg.type === 'true') { - write(`ret.${arg.name} = !!(${condition});`) - } else { - write( - `if (${condition}) ret.${ - arg.name - } = this.${getFunctionCallByTypeName( - baseTypePrefix, - arg.type - )}` - ) - } - } else { - if (is_mt_message && arg.name === 'body') { - write('ret.body = this.raw(ret.bytes)') - } else { - write( - `ret.${ - arg.name - } = this.${getFunctionCallByTypeName( - baseTypePrefix, - arg.type, - arg - )};` - ) - } - } - }) - - write('return ret;') - } else { - write(`return { _: '${prefix}${cls.name}' };`) - } - - untab() - write('},') - } - } - - content.classes.forEach(addObject(false)) - // we don't really need to read methods :shrug: - // content.methods.forEach(addObject(true)) - } -} - -Object.entries(schema.mtproto).forEach(writeNamespace('mtproto')) -Object.entries(schema.api).forEach(writeNamespace()) - -// for readerMap -untab() -write('};') diff --git a/packages/tl/scripts/generate-binary-writer.js b/packages/tl/scripts/generate-binary-writer.js deleted file mode 100644 index 7cde3c32..00000000 --- a/packages/tl/scripts/generate-binary-writer.js +++ /dev/null @@ -1,156 +0,0 @@ -// disclaimer: code sucks because tl itself sucks :shrug: -const { createWriter } = require('./common') - -const schema = require('../raw-schema.json') - -const output = createWriter('../binary/writer.js') -const { tab, untab, write } = output - -write(`// This file is auto-generated. Do not edit. -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); - -function _has (value) { return +!!(Array.isArray(value) ? value.length : value !== undefined); } -function _assert_has (obj, prop) { if (obj[prop] === void 0) throw new Error('Object ' + obj._ + ' is missing required property ' + prop) } - -exports.default = {`) -tab() - -const parsedManually = [ - 0x1cb5c415, - 0x3072cfa1, - 0xbc799737, - 0x997275b5, - 0x3fedd339, - 0x56730bcc, -] - -function getFunctionCallByTypeName(ns, type, value = 'obj') { - let funcName, - args = [value] - if (type === 'number') funcName = 'int32' - else if (type === 'Long') funcName = 'long' - else if (type === 'RawLong') funcName = 'rawLong' - else if (type === 'Int128') funcName = 'int128' - else if (type === 'Int256') funcName = 'int256' - else if (type === 'Double') funcName = 'double' - else if (type === 'string') funcName = 'string' - else if (type === 'Buffer') funcName = 'bytes' - else if (type.match(/^(boolean|true|false)$/)) funcName = 'boolean' - else if (type.endsWith('[]')) { - let typ = type.substr(0, type.length - 2) - let isBare = false - - let writer // = `(r, val) => r.${getFunctionCallByTypeName(ns, typ, 'val')}` - if (typ[0] === '%') { - typ = typ.substr(1) - isBare = true - if (typ !== 'Message') - throw new Error('Only bare mt_message is supported!') - } - if (typ === 'number') writer = 'this.int32' - else if (typ === 'Buffer') writer = 'this.bytes' - else if (typ === 'Long') writer = 'this.long' - else if (typ === 'RawLong') writer = 'this.rawLong' - else if (typ === 'Int128') writer = 'this.int128' - else if (typ === 'Int256') writer = 'this.int256' - else if (typ === 'Double') writer = 'this.double' - else if (typ === 'string') writer = 'this.string' - else writer = 'this.object' - - funcName = `vector` - args = [writer, value] - if (isBare) args.push(true) - } else funcName = `object` - - return `${funcName}(${args.join(', ')})` -} - -function writeNamespace(nsType) { - return ([namespace, content]) => { - let baseTypePrefix = nsType === 'mtproto' ? 'mtproto.' : '' - let nsPrefix = namespace === '$root' ? '' : namespace + '.' - let prefix = (nsType === 'mtproto' ? 'mt_' : '') + nsPrefix - - const addObject = (isMethod) => { - return (cls) => { - if (cls.id === 0 || parsedManually.includes(cls.id)) return - - let is_mt_message = - nsType === 'mtproto' && cls.name === 'message' - if (cls.arguments && cls.arguments.length) { - write( - `'${prefix}${cls.name}': function (obj${ - is_mt_message ? ', bare' : '' - }) {` - ) - } else { - write(`'${prefix}${cls.name}': function () {`) - } - tab() - if (is_mt_message) { - write('if (!bare)') - } - write(`${is_mt_message ? ' ' : ''}this.uint32(${cls.id})`) - - if (cls.arguments) { - cls.arguments.forEach((arg) => { - if (arg.type === '$FlagsBitField') { - let content = cls.arguments - .filter( - (i) => - i.optional && - i.predicate.split('.')[0] === arg.name - ) - .map( - (arg) => - `(${ - arg.type === 'true' - ? `+!!obj.${arg.name}` - : `_has(obj.${arg.name})` - } << ${arg.predicate.split('.')[1]})` - ) - .join(' | ') - write(`this.uint32(${content})`) - } else { - if (arg.optional && arg.type === 'true') return // zero-size, included in flags - if (arg.optional) { - write(`if (_has(obj.${arg.name}))`) - tab() - } else { - write(`_assert_has(obj, '${arg.name}')`) - } - if (is_mt_message && arg.name === 'body') { - write('this.raw(obj.body)') - } else { - write( - `this.${getFunctionCallByTypeName( - baseTypePrefix, - arg.type, - `obj.${arg.name}` - )}` - ) - } - if (arg.optional) { - untab() - } - } - }) - } - - untab() - write('},') - } - } - - content.classes.forEach(addObject(false)) - content.methods.forEach(addObject(true)) - } -} - -Object.entries(schema.mtproto).forEach(writeNamespace('mtproto')) -Object.entries(schema.api).forEach(writeNamespace()) - -// for writerMap -untab() -write('}') diff --git a/packages/tl/scripts/generate-errors.js b/packages/tl/scripts/generate-errors.js deleted file mode 100644 index cf660cec..00000000 --- a/packages/tl/scripts/generate-errors.js +++ /dev/null @@ -1,242 +0,0 @@ -const { createWriter, snakeToCamel, camelToPascal } = require('./common') -const fetch = require('node-fetch') -const csvParser = require('csv-parser') -const fs = require('fs') -const path = require('path') - -const ts = createWriter('../errors.d.ts') -const js = createWriter('../errors.js') - -const baseErrors = [ - { - codes: '400', - name: 'BAD_REQUEST', - description: - 'The query contains errors. In the event that a request was created using a form ' + - 'and contains user generated data, the user should be notified that the data must ' + - 'be corrected before the query is repeated', - }, - { - codes: '401', - name: 'UNAUTHORIZED', - description: - 'There was an unauthorized attempt to use functionality available only to authorized users.', - }, - { - codes: '403', - name: 'FORBIDDEN', - description: - 'Privacy violation. For example, an attempt to write a message ' + - 'to someone who has blacklisted the current user.', - }, - { - codes: '404', - name: 'NOT_FOUND', - description: - 'An attempt to invoke a non-existent object, such as a method.', - }, - { - codes: '420', - name: 'FLOOD', - description: - 'The maximum allowed number of attempts to invoke the given method' + - 'with the given input parameters has been exceeded. For example, in an' + - 'attempt to request a large number of text messages (SMS) for the same' + - 'phone number.', - }, - { - codes: '303', - name: 'SEE_OTHER', - description: - 'The request must be repeated, but directed to a different data center', - }, - { - codes: '406', - name: 'NOT_ACCEPTABLE', - description: - 'Similar to 400 BAD_REQUEST, but the app should not display any error messages to user ' + - 'in UI as a result of this response. The error message will be delivered via ' + - 'updateServiceNotification instead.', - }, - { - codes: '500', - name: 'INTERNAL', - description: - 'An internal server error occurred while a request was being processed; ' + - 'for example, there was a disruption while accessing a database or file storage.', - } -] - -const inheritanceTable = { - 400: 'BadRequestError', - 401: 'UnauthorizedError', - 403: 'ForbiddenError', - 404: 'NotFoundError', - 420: 'FloodError', - 303: 'SeeOtherError', - 406: 'NotAcceptableError', - 500: 'InternalError', -} - -const customErrors = [ - { - virtual: true, - name: 'RPC_TIMEOUT', - codes: '408', - description: 'Timeout of {ms} ms exceeded', - }, - { - virtual: true, - name: 'MESSAGE_NOT_FOUND', - codes: '404', - description: 'Message was not found' - } -] - -async function main() { - // relying on slightly more exhaustive than official docs errors.csv from telethon - const csvText = await fetch( - 'https://raw.githubusercontent.com/LonamiWebs/Telethon/master/telethon_generator/data/errors.csv' - ).then((i) => i.body) - - const csv = await new Promise((resolve, reject) => { - const ret = [] - csvText - .pipe(csvParser()) - .on('data', (it) => ret.push(it)) - .on('end', () => resolve(ret)) - .on('error', reject) - }) - - csv.push(...customErrors) - - js.write(`// This file was auto-generated. Do not edit! -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -class RpcError extends Error { - constructor(code, text, description) { - super(description); - this.code = code; - this.text = text; - } -} -exports.RpcError = RpcError;`) - ts.write(`// This file was auto-generated. Do not edit! -export declare class RpcError extends Error { - code: number; - text: string; - constructor (code: number, text: string, description?: string); -}`) - baseErrors.forEach((it) => (it.base = true)) - const allErrors = [...baseErrors, ...csv] - - fs.writeFileSync( - path.join(__dirname, '../raw-errors.json'), - JSON.stringify(allErrors) - ) - - allErrors.forEach((err) => { - let hasArgument = - err.description && err.description.match(/{([a-z_]+)}/i) - let argName = hasArgument ? snakeToCamel(hasArgument[1]) : '' - if (err.name[0].match(/[0-9]/)) err.clsname = '_' + err.name - else err.clsname = err.name - err.clsname = err.clsname.replace(/_X_|_X$|^X_/, (i) => - i === '_X_' ? '_' : '' - ) - const className = - camelToPascal(snakeToCamel(err.clsname.toLowerCase())) + 'Error' - err.fclsname = className - - const descrCode = JSON.stringify(err.description).replace( - /{([a-z_]+)}/gi, - (_, $1) => `" + ${snakeToCamel($1)} + "` - ) - err.code = err.codes.split(' ')[0] - err.inherits = err.base - ? 'RpcError' - : inheritanceTable[err.code] || 'RpcError' - if (err.base) { - js.write(`class ${className} extends RpcError { - constructor(name, description) { - super(${err.code}, name, description); - } -} -exports.${className} = ${className}`) - } else { - js.write(`class ${className} extends ${err.inherits} { - constructor(${argName}) { - super(${err.inherits === 'RpcError' ? `${err.code}, ` : ''}'${ - err.name - }', ${descrCode});${ - hasArgument - ? ` - this.${argName} = ${argName};` - : '' - } - } -} -exports.${className} = ${className}`) - } - ts.comment(err.description) - if (err.base) { - ts.write(`export declare class ${className} extends RpcError { - constructor (name: string, description: string); -}`) - } else { - ts.write(`export declare class ${className} extends ${ - err.inherits - } {${ - hasArgument - ? ` - ${argName}: number;` - : '' - } - constructor (${hasArgument ? argName + ': number' : ''}); -}`) - } - }) - - ts.write( - 'export declare function createRpcErrorFromTl (obj: any): RpcError;' - ) - - js.write('const _staticNameErrors = {') - js.tab() - csv.filter( - (i) => !i.virtual && !i.name.match(/_X_|_X$|^X_/) - ).forEach((err) => js.write(`'${err.name}': ${err.fclsname},`)) - js.write(`'Timeout': TimeoutError,`) - js.untab() - js.write('};') - - js.write('const _baseCodeErrors = {') - js.tab() - Object.entries(inheritanceTable).forEach(([code, error]) => { - js.write(`${code}: ${error},`) - }) - js.untab() - js.write('};') - - js.write(`exports.createRpcErrorFromTl = function (obj) { - if (obj.errorMessage in _staticNameErrors) return new _staticNameErrors[obj.errorMessage](); - - let match; -${allErrors - .filter((i) => !i.virtual && !!i.name.match(/_X_|_X$|^X_/)) - .map( - (i) => - ` if ((match = obj.errorMessage.match(/${i.name.replace( - /_X_|_X$|^X_/g, - (i) => i.replace('X', '(\\d+)') - )}/)) != null) return new ${i.fclsname}(parseInt(match[1]));` - ) - .join('\n')} - - if (obj.errorCode in _baseCodeErrors) return new _baseCodeErrors[obj.errorCode](obj.errorMessage); - - return new RpcError(obj.errorCode, obj.errorMessage); -}`) -} - -main().catch(console.error) diff --git a/packages/tl/scripts/generate-schema.js b/packages/tl/scripts/generate-schema.js deleted file mode 100644 index 4f906b11..00000000 --- a/packages/tl/scripts/generate-schema.js +++ /dev/null @@ -1,660 +0,0 @@ -// Downloads latest .tl schemas from TDesktop repo, -// fetches documentation from https://core.telegram.org/schema -// and builds a single .json file from all of that, -// while also changing default types (they suck) to ts-like -// disclaimer: code sucks because tl itself sucks :shrug: - -const fetch = require('node-fetch') -const fs = require('fs') -const path = require('path') -const cheerio = require('cheerio') -const { applyDescriptionsFile } = require('./process-descriptions-yaml') -const yaml = require('js-yaml') -const { snakeToCamel, signedInt32ToUnsigned } = require('./common') -const { asyncPool } = require('eager-async-pool') -const { mergeSchemas, stringifyType } = require('./merge-schemas') -const CRC32 = require('crc-32') - -const SingleRegex = /^(.+?)(?:#([0-f]{1,8}))?(?: \?)?(?: {(.+?:.+?)})? ((?:.+? )*)= (.+);$/ - -const transformIgnoreNamespace = (fn, s) => { - if (s.includes('.')) { - let [namespace, name] = s.split('.') - return namespace + '.' + fn(name) - } - return fn(s) -} -const normalizeGenerics = (s) => { - if (!s.includes(' ')) return s - - let [base, ...args] = s.split(' ') - let ret = base - let depth = 0 - args.forEach((arg) => { - depth += 1 - ret += '<' + arg - }) - while (depth--) ret += '>' - return ret -} - -// all result types must be different to allow differentiating wire format -// (we could make int128 = Buffer, int256 = Buffer, but we would have no way to determine which is which after building json) -const _types = { - int: 'number', - long: 'Long', - int128: 'Int128', - int256: 'Int256', - double: 'Double', - string: 'string', - bytes: 'Buffer', - boolFalse: 'false', - boolTrue: 'true', - bool: 'boolean', - Bool: 'boolean', - true: 'true', - null: 'null', - Type: 'any', - // this will be removed by generate-reader/writer script and replaced with flags calculation - '#': '$FlagsBitField', -} - -// override Long to RawLong type for some mtproto types -const overrideLongToRawLong = Object.entries({ - mt_bind_auth_key_inner: [ - 'nonce', - 'temp_auth_key_id', - 'perm_auth_key_id', - 'temp_session_id', - ], - mt_bad_server_salt: ['new_server_salt'], - mt_future_salt: ['salt'], - mt_destroy_session_ok: ['session_id'], - mt_destroy_session_none: ['session_id'], - mt_destroy_session: ['session_id'], - mt_new_session_created: ['server_salt'], -}).flatMap(([obj, args]) => args.map((it) => `${obj}#${it}`)) - -function getJSType(typ, argName) { - if (typ[0] === '!') typ = typ.substr(1) - if (typ === 'long' && overrideLongToRawLong.includes(argName)) - return 'RawLong' - if (typ in _types) return _types[typ] - let m = typ.match(/^[Vv]ector[< ](.+?)[> ]$/) - if (m) { - return getJSType(m[1], argName) + '[]' - } - return normalizeGenerics(typ) -} - -async function convertTlToJson(tlText, tlType, silent = false) { - if (tlType === 'api') { - tlText = - fs.readFileSync(path.join(__dirname, '_prepend.tl'), 'utf8') - + '\n---$start-main---\n' - + tlText - + '\n---$start-append---\n' - + fs.readFileSync(path.join(__dirname, 'append.tl'), 'utf8') - } - - let lines = tlText.split('\n') - let pos = 0 - let line = lines[0].trim() - - const padSize = (lines.length + '').length - const pad = (i) => { - const len = (i + '').length - if (len < padSize) { - let pre = '' - for (let i = 0; i < padSize - len; i++) { - pre += ' ' - } - return pre + i - } else return i - } - - const state = { - comment: '', - annotations: null, - type: 'class', - extends: null, - blankLines: 0, - stop: false, - part: 'prepend', - source: {} - } - - const unions = {} - - let nextLine = () => { - state.stop = pos === lines.length - 1 - if (state.stop) return - - line = lines[++pos].trim() - if (line === '') { - state.blankLines++ - } else { - state.blankLines = 0 - } - if (line && line.startsWith('---functions---')) { - state.type = 'method' - return nextLine() - } - if (line && line.startsWith('---types---')) { - state.type = 'class' - return nextLine() - } - if (line && line.startsWith('---$start-')) { - state.part = line.split('$start-')[1].split('-')[0] - state.type = 'class' - return nextLine() - } - if (!silent) - process.stdout.write( - `[${pad(pos)}/${lines.length}] Processing ${tlType}.tl..\r` - ) - } - - const ret = {} - - function getNamespace(name) { - if (!ret[name]) { - ret[name] = { - classes: [], - methods: [], - unions: [], - } - } - return ret[name] - } - - if (!silent) - process.stdout.write( - `[${pad(pos)}/${lines.length}] Processing ${tlType}.tl..\r` - ) - - while (!state.stop) { - if (line === '' || line.startsWith('//')) { - // skip empty lines and comments - nextLine() - continue - } - - const match = SingleRegex.exec(line) - if (!match) { - console.warn(`Regex failed on:\n${line}`) - } else { - let [, fullName, typeId, generics, args, type] = match - if (fullName in _types || fullName === 'vector') { - // vector is parsed manually - nextLine() - continue - } - - if (!typeId) { - typeId = - signedInt32ToUnsigned( - CRC32.str( - // normalize - line - .replace( - /[{};]|[a-zA-Z0-9_]+:flags\.[0-9]+\?true/g, - '' - ) - .replace(/[<>]/g, ' ') - .replace(/ +/g, ' ') - .trim() - ) - ).toString(16) - } - - args = args.trim() - args = - args && !args.match(/\[ [a-z]+ ]/i) - ? args.split(' ').map((j) => j.split(':')) - : [] - - let [namespace, name] = fullName.split('.') - if (!name) { - name = namespace - namespace = '$root' - } - - if (state.type === 'class') { - if (!unions[type]) unions[type] = [] - unions[type].push( - namespace === '$root' ? name : namespace + '.' + name - ) - } - - let r = { - name: state.type === 'class' ? name : snakeToCamel(name), - id: parseInt(typeId, 16), - [state.type === 'class' ? 'type' : 'returns']: getJSType(type), - arguments: [], - } - if (generics) { - r.generics = generics.split(',').map((it) => { - let [name, superClass] = it.split(':') - return { name, super: getJSType(superClass) } - }) - } - if (args.length) { - r.arguments = args.map(([name, typ]) => { - let [predicate, type] = typ.split('?') - if (!type) { - return { - name: snakeToCamel(name), - type: getJSType( - typ, - tlType === 'mtproto' - ? `mt_${fullName}#${name}` - : '' - ), - } - } - return { - name: snakeToCamel(name), - type: getJSType( - type, - tlType === 'mtproto' - ? `mt_${fullName}#${name}` - : '' - ), - optional: true, - predicate, - } - }) - } - - // check for overrides/conflicts - if (fullName in state.source) { - // this object was already met - const source = state.source[fullName] - const collection = getNamespace(namespace)[ - state.type === 'class' ? 'classes' : 'methods' - ] - const oldIdx = collection.findIndex( - (it) => it.name === r.name - ) - - if (source === state.part) { - console.log( - 'warn: %s was met >1 times in %s part, was overridden', - fullName, - source - ) - collection.splice(oldIdx, 1) - } else { - const former = collection[oldIdx] - - const latter = r - - if (former.id === latter.id) { - console.log( - 'warn: %s was met in %s, then in %s, was overridden', - fullName, - source, - state.part - ) - collection.splice(oldIdx, 1) - } else { - const rl = require('readline').createInterface({ - input: process.stdin, - output: process.stdout, - }) - - const input = (q) => - new Promise((res) => rl.question(q, res)) - - console.log( - `!! Conflict on %s !! First met in %s, then in %s.`, - fullName, - source, - state.part - ) - console.log( - 'Option A (%s): %s', - source, - stringifyType(former, namespace) - ) - console.log( - 'Option B (%s): %s', - state.part, - stringifyType(latter, namespace) - ) - - let keep - while (true) { - keep = await input('Which to keep? [A/B] > ') - keep = keep.toUpperCase() - - if (keep !== 'A' && keep !== 'B') { - console.log('Invalid input! Please type A or B') - continue - } - - break - } - - rl.close() - - if (keep === 'A') { - nextLine() - continue - } else { - collection.splice(oldIdx, 1) - } - } - } - } - - state.source[fullName] = state.part - getNamespace(namespace)[state.type === 'class' ? 'classes' : 'methods'].push(r) - } - nextLine() - } - - Object.entries(unions).forEach(([type, subtypes]) => { - let [namespace, name] = type.split('.') - if (!name) { - name = namespace - namespace = '$root' - } - - getNamespace(namespace).unions.push({ - type: name, - subtypes, - }) - }) - - if (!silent) - console.log(`[${lines.length}/${lines.length}] Processed ${tlType}.tl`) - - return ret -} - -async function addDocumentation(obj) { - console.log('[i] Parsing documentation entries') - // structure: { type: 'class' | 'method' | 'type', name: string, target: object } - let tasks = [] - - Object.entries(obj).forEach(([namespace, content]) => { - if (namespace === '$root') namespace = '' - else namespace += '.' - content.classes.forEach((cls) => - tasks.push({ - type: 'class', - name: namespace + cls.name, - target: cls, - }) - ) - content.methods.forEach((cls) => - tasks.push({ - type: 'method', - name: namespace + cls.name, - target: cls, - }) - ) - content.unions.forEach((cls) => - tasks.push({ - type: 'union', - name: namespace + cls.type, - target: cls, - }) - ) - }) - - async function parseDocumentation(task) { - const { type, name, target } = task - let path = { - class: 'constructor', - method: 'method', - union: 'type', - }[type] - - const url = `https://core.telegram.org/${path}/${name}` - - function normalizeLinks(el) { - el.find('a').each((i, it) => { - it = $(it) - it.attr('href', new URL(it.attr('href'), url).href) - let href = it.attr('href') - let m - if ( - (m = href.match( - /\/(constructor|method|union)\/([^#?]+)(?:\?|#|$)/ - )) - ) { - let [, type, name] = m - if (type === 'method') - name = transformIgnoreNamespace(snakeToCamel, name) - it.replaceWith(`{@link ${name}}`) - } - }) - } - - let html = await fetch(url, { - headers: { - 'User-Agent': - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' + - 'Chrome/87.0.4280.88 Safari/537.36', - }, - }).then((i) => i.text()) - let $ = cheerio.load(html) - normalizeLinks($('#dev_page_content')) - - if ($('#dev_page_content').text().includes('has not been saved')) return - - target.description = $('#dev_page_content') - .find('p') - .first() - .html() - .trim() - let parametersTable = $("h3:contains('Parameters')").next() - parametersTable.find('tr').each((idx, el) => { - el = $(el) - let cols = el.find('td') - if (!cols.length) return // - let name = snakeToCamel(cols.first().text().trim()) - let description = cols.last().html().trim() - - target.arguments.forEach((arg) => { - if (arg.name === name) arg.description = description - }) - }) - - if (type === 'method') { - let errorsTable = $("h3:contains('Possible errors')").next() - errorsTable.find('tr').each((idx, el) => { - el = $(el) - let cols = el.find('td') - if (!cols.length) return // - let code = parseInt($(cols[0]).text()) - let name = $(cols[1]).text() - let description = $(cols[2]).text() - - if (!target.throws) target.throws = [] - target.throws.push({ code, name, description }) - }) - - let botsCanUse = !!$("h3:contains('Bots can use this method')") - .length - let onlyBotsCanUse = - botsCanUse && - (!!target.description.match(/[,;]( for)? bots only$/) || - (target.throws && - target.throws.some( - (it) => it.code === 'USER_BOT_REQUIRED' - ))) - - target.available = onlyBotsCanUse - ? 'bot' - : botsCanUse - ? 'both' - : 'user' - } - } - - let count = 0 - for await (let { idx, error } of asyncPool(parseDocumentation, tasks, { - limit: 5, - })) { - if (error) { - if (error instanceof fetch.FetchError) { - console.error( - 'Network error %s while downloading docs for %s %s, retrying', - error.message, - tasks[idx].type, - tasks[idx].name - ) - tasks.push(tasks[idx]) - } else { - console.error( - 'Error while downloading docs for %o: %s', - tasks[idx], - error - ) - } - } - if (++count % 50 === 0) - process.stdout.write(`Downloading documentation: ${count} so far\r`) - } -} - -// converts telegram's json to tl -function convertJsonToTl(json) { - // their json schema uses signed integers for ids, we use unsigned, so we need to convert them - - const lines = [] - const objectToLine = (cls) => { - let line = `${cls.predicate || cls.method}#${signedInt32ToUnsigned( - parseInt(cls.id) - ).toString(16)}${cls.params - .map((p) => ` ${p.name}:${p.type}`) - .join('')} = ${cls.type};` - lines.push(line) - } - - // i honestly have no idea why http_wait is a function in schema. - // it can't be a function. - // there's literally no way. - // and it is a type in tgdesktop schema. - // (see https://t.me/teispam/998, in russian) - // durov why - let httpWait = json.methods.find((it) => it.method === 'http_wait') - json.methods = json.methods.filter((it) => it.method !== 'http_wait') - json.constructors.push(httpWait) - - json.constructors.filter(Boolean).forEach(objectToLine) - lines.push('---functions---') - json.methods.filter(Boolean).forEach(objectToLine) - return lines.join('\n') -} - -async function main() { - const descriptionsYaml = yaml.load( - await fs.promises.readFile(path.join(__dirname, '../descriptions.yaml')) - ) - - console.log('[i] Fetching mtproto.tl') - // using this instead of one in tgdesktop repo because tgdesktop one uses strings instead of bytes in many places - // idk why, i don't wanna know why, maybe some memes with strings in c++ or smth... - // seems like in api.tl there's no such thing (hopefully?) - // - // and also tl-schema inside the docs is outdated, unlike json (wtf???) - // so we basically convert their json to tl, just to convert it back to json immediately after that - // thank you durov - let mtprotoTl = await fetch('https://core.telegram.org/schema/mtproto-json') - .then((i) => i.json()) - .then((json) => convertJsonToTl(json)) - let ret = {} - - ret.mtproto = await convertTlToJson(mtprotoTl, 'mtproto') - - console.log('[i] Fetching api.tl from tdesktop') - const apiTlDesktop = await fetch( - 'https://raw.githubusercontent.com/telegramdesktop/tdesktop/dev/Telegram/Resources/tl/api.tl' - ).then((i) => i.text()) - const apiDesktopLayer = parseInt( - apiTlDesktop.match(/^\/\/ LAYER (\d+)/m)[1] - ) - - console.log('[i] Fetching telegram_api.tl from TDLib') - const apiTlTdlib = await fetch( - 'https://raw.githubusercontent.com/tdlib/td/master/td/generate/scheme/telegram_api.tl' - ).then((i) => i.text()) - const apiTdlibLayer = await fetch( - 'https://raw.githubusercontent.com/tdlib/td/master/td/telegram/Version.h' - ) - .then((r) => r.text()) - .then((res) => - parseInt(res.match(/^constexpr int32 MTPROTO_LAYER = (\d+)/m)[1]) - ) - - console.log( - '[i] tdesktop has layer %d, tdlib has %d', - apiDesktopLayer, - apiTdlibLayer - ) - - if (Math.abs(apiDesktopLayer - apiTdlibLayer) > 2) { - console.log('[i] Too different layers, using newer one') - - const newer = - apiDesktopLayer > apiTdlibLayer ? apiTlDesktop : apiTlTdlib - const newerLayer = - apiDesktopLayer > apiTdlibLayer ? apiDesktopLayer : apiTdlibLayer - - ret.apiLayer = newerLayer + '' - ret.api = await convertTlToJson(newer, 'api') - } else { - console.log('[i] Merging schemas...') - - const first = await convertTlToJson(apiTlTdlib, 'api') - const second = await convertTlToJson(apiTlDesktop, 'api') - - const onConflict = - apiDesktopLayer === apiTdlibLayer - ? null // manual conflict resolving - : apiTdlibLayer > apiDesktopLayer // use ctor from newer schema - ? 'A' - : 'B' - - await mergeSchemas(first, second, onConflict) - - ret.apiLayer = apiTdlibLayer + '' - ret.api = first - } - - await addDocumentation(ret.api) - - await applyDescriptionsFile(ret, descriptionsYaml) - - await fs.promises.writeFile( - path.join(__dirname, '../raw-schema.json'), - JSON.stringify(ret, 4) - ) - - // update version in README.md - let readmeMd = await fs.promises.readFile( - path.join(__dirname, '../README.md'), - 'utf-8' - ) - readmeMd = readmeMd.replace( - /^Generated from TL layer \*\*\d+\*\* \(last updated on \d+\.\d+\.\d+\)\.$/m, - `Generated from TL layer **${ - ret.apiLayer - }** (last updated on ${new Date().toLocaleDateString('ru')}).` - ) - await fs.promises.writeFile(path.join(__dirname, '../README.md'), readmeMd) -} - -module.exports = { - convertTlToJson, - convertJsonToTl, -} - -if (require.main === module) { - main().catch(console.error) -} diff --git a/packages/tl/scripts/generate-types.js b/packages/tl/scripts/generate-types.js deleted file mode 100644 index 53ee20e1..00000000 --- a/packages/tl/scripts/generate-types.js +++ /dev/null @@ -1,309 +0,0 @@ -// disclaimer: code sucks because tl itself sucks :shrug: -// this file generates TS and JS files simultaneously to prevent re-compilation of TS file -// and to optimize indexing -const { createWriter, camelToPascal } = require('./common') -const schema = require('../raw-schema.json') - -let ts = createWriter('../index.d.ts') -let js = createWriter('../index.js') - -// language=TypeScript -ts.write(`// This file is auto-generated. Do not edit. -import { BigInteger } from 'big-integer'; - -/** - * Classes and methods generated from \`.tl\` files - * - * Quick guide: - * - types prefixed with Type are unions and aliases (e.g. \`tl.TypeError = tl.RawError\`) - * - types prefixed with Raw are TL objects, be it class or method (e.g \`tl.RawError\`) - * - types ending with Request are RPC methods (e.g. \`tl.auth.RawSignInRequest\`) - * - you can use \`{ _: 'error', ... }\` to create needed types - * - to check if something is of some type, check \`_\` property of an object - * - to check if something is of some union, use \`isAny*()\` functions - * @hidden - */ -export declare namespace tl { - /** - * Currently used TL layer. - */ - const CURRENT_LAYER = ${schema.apiLayer}; - - type Long = BigInteger; - type RawLong = Buffer; - type Int128 = Buffer; - type Int256 = Buffer; - type Double = number; - - /** - * Find type in a union by its name - * - * @example - * \`\`\`typescript - * const object: tl.RawUser - * // is the same as - * const object: tl.FindByName - * // and this (not recommended because performance will suffer) - * const object: tl.FindByName - * - * // this example is pretty primitive, but this may be very useful - * // in more complex use-cases with generics and stuff - * \`\`\` - */ - type FindByName = Extract - - /** - * By default, all TL types are immutable. - * - * In some cases, though, you might want to mutate it, - * so you can use this type. - * - * @example - * \`\`\`typescript - * const obj: tl.Mutable = user - * obj.username = 'something' - * commitChangesToUser(user) - * \`\`\` - */ - type Mutable = { - -readonly [P in keyof T]: T[P] - } -`) -ts.tab() - -// language=JavaScript -js.write(`// This file is auto-generated. Do not edit. -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.tl = {}; -(function(tl) { - tl.CURRENT_LAYER = ${schema.apiLayer}; -`) -js.tab() - -const makePascalCaseNotNamespace = (type) => { - let split = type.split('.') - let name = split.pop() - let ns = split - - if (!ns.length) { - if (name[0].match(/[A-Z]/)) - // this is union/alias - return 'Type' + name - - return 'Raw' + camelToPascal(name) - } - if (name[0].match(/[A-Z]/)) return ns.join('.') + '.Type' + name - return ns.join('.') + '.Raw' + camelToPascal(name) -} - -const writeSingleSchemaEntry = (type) => { - // to prevent (possible?) collisions between mtproto.tl and api.tl - const prefix_ = type === 'mtproto' ? 'mt_' : '' - let baseTypePrefix = type === 'mtproto' ? 'tl.mtproto.' : 'tl.' - const fullTypeName = (type) => { - if (type === 'X') return 'any' - if (type[0] === '%') type = type.substr(1) - if (prefix_ === 'mt_' && type === 'Object') return 'tl.TlObject' - if ( - type === 'number' || - type === 'any' || - type === 'Long' || - type === 'RawLong' || - type === 'Int128' || - type === 'Int256' || - type === 'Double' || - type === 'string' || - type === 'Buffer' || - type.match(/^(boolean|true|false)$/) - ) - return type - if (type.endsWith('[]')) { - let wrap = type.substr(0, type.length - 2) - return fullTypeName(wrap) + '[]' - } - - return baseTypePrefix + makePascalCaseNotNamespace(type) - } - - return ([namespace, content]) => { - let prefix = prefix_ + (namespace === '$root' ? '' : namespace + '.') - let jsPrefix = - baseTypePrefix + (namespace === '$root' ? '' : namespace + '.') - - if (namespace !== '$root') { - ts.write(`\nnamespace ${namespace} {`) - ts.tab() - - if (content.unions.length) { - js.write(`${baseTypePrefix}${namespace} = {};`) - js.write(`(function () {`) - js.tab() - } - } - - const writeObject = (cls) => { - let comment = '' - if (cls.description) { - comment = cls.description - } - if (cls.returns) { - if (comment) comment += '\n\n' - comment += `RPC call returns {@link ${fullTypeName( - cls.returns - )}}` - } - if (comment) ts.comment(comment) - - const capName = - 'Raw' + camelToPascal(cls.name) + (cls.returns ? 'Request' : '') - - ts.write(`interface ${capName} {`) - ts.tab() - ts.write(`readonly _: '${prefix}${cls.name}',`) - - let is_mt_message = - type === 'mtproto' && cls.name === 'message' - - if (cls.arguments && cls.arguments.length) { - cls.arguments.forEach((arg) => { - if (arg.type === '$FlagsBitField') return - if (arg.type === 'X') arg.type = 'any' - if (arg.type === 'true' || arg.type === 'false') - arg.type = 'boolean' - if (arg.description) ts.comment(arg.description) - - if (is_mt_message && arg.name === 'body') { - ts.write('readonly body: Buffer;') - } else { - ts.write( - `readonly ${arg.name}${ - arg.optional ? '?' : '' - }: ${fullTypeName(arg.type)};` - ) - } - }) - } - - ts.untab() - ts.write('}') - } - - ts.write('// classes') - content.classes.forEach(writeObject) - - ts.write('// methods') - content.methods.forEach(writeObject) - - if (namespace !== '$root') { - ts.write('interface RpcCallReturn {') - } else { - let otherNamespaces = Object.keys(schema[type]).filter( - (i) => i !== namespace - ) - if (type === 'api') otherNamespaces.unshift('mtproto') - if (otherNamespaces.length) - ts.write( - `interface RpcCallReturn extends ${otherNamespaces - .map((i) => `${i}.RpcCallReturn`) - .join(', ')} {` - ) - else ts.write('interface RpcCallReturn {') - } - ts.tab() - content.methods.forEach((cls) => { - ts.write(`'${prefix}${cls.name}': ${fullTypeName(cls.returns)}`) - }) - ts.untab() - ts.write('}') - - ts.write('// unions and aliases') - content.unions.forEach((union) => { - if (union.description) ts.comment(union.description) - ts.write( - `type Type${union.type} = ${union.subtypes - .map(makePascalCaseNotNamespace) - .join(' | ')}` - ) - ts.write( - `function isAny${union.type} (obj: object): obj is Type${union.type};` - ) - js.write(`${jsPrefix}isAny${camelToPascal( - union.type - )} = function (obj) { - switch (obj._) { - ${union.subtypes - .map((typ) => `case '${prefix_}${typ}':`) - .join('')}return true; - } - return false; -};`) - }) - - if (namespace !== '$root') { - ts.untab() - ts.write('}') - - if (content.unions.length) { - js.untab() - js.write(`})();`) - } - } - } -} - -// mtproto -ts.write('namespace mtproto {') -ts.tab() -js.write('tl.mtproto = {};\n(function () {') -js.tab() - -Object.entries(schema.mtproto).forEach(writeSingleSchemaEntry('mtproto')) - -// } for namespace mtproto -ts.untab() -ts.write('}\n') -js.untab() -js.write('})();') - -// api -Object.entries(schema.api).forEach(writeSingleSchemaEntry('api')) - -const writeEnumMembers = (sch, prefix = '', methods = true, objects = true) => - Object.entries(sch) - .map(([ns, content]) => { - let items = [] - if (methods) items = content.methods - if (objects) items = [...items, ...content.classes] - return items - .filter((it) => !!it.name) - .map( - (it) => - `\n${ts.indent}| tl.${prefix}${ - ns === '$root' ? '' : ns + '.' - }Raw${camelToPascal(it.name)}${ - it.returns ? 'Request' : '' - }` - ) - .join('') - }) - .join('') - -// enum unions -ts.write( - '// all available RPC methods\n' + - 'type RpcMethod = ' + - writeEnumMembers(schema.mtproto, 'mtproto.', true, false) + - writeEnumMembers(schema.api, '', true, false) -) -ts.write( - '// all available TL objects\n' + - 'type TlObject = ' + - writeEnumMembers(schema.mtproto, 'mtproto.') + - writeEnumMembers(schema.api) -) -// } for namespace tl -ts.untab() -ts.write('}') -js.untab() -js.write('})(exports.tl);') diff --git a/packages/tl/scripts/merge-schemas.js b/packages/tl/scripts/merge-schemas.js deleted file mode 100644 index ce70e565..00000000 --- a/packages/tl/scripts/merge-schemas.js +++ /dev/null @@ -1,459 +0,0 @@ -// used by generate-schema, but since logic is quite large, moved it to a separate file -const CRC32 = require('crc-32') -const { signedInt32ToUnsigned } = require('./common') - -// converting map from custom type back to tl -const _types = { - number: 'int', - Long: 'long', - Int128: 'int128', - Int256: 'int256', - Double: 'double', - string: 'string', - Buffer: 'bytes', - false: 'boolFalse', - true: 'boolTrue', - boolean: 'bool', - boolean: 'Bool', - true: 'true', - null: 'null', - any: 'Type', - $FlagsBitField: '#', -} - -function convertType(typ) { - if (typ in _types) return _types[typ] - let m = typ.match(/^(.+?)\[\]$/) - if (m) { - return 'Vector ' + convertType(m[1]) - } - return typ -} - -function stringifyType(cls, ns = cls._ns, includeId = true) { - let str = '' - - if (ns !== '$root') { - str += ns + '.' - } - - str += cls.name - - if (includeId && cls.id) { - str += '#' + cls.id.toString(16) - } - - str += ' ' - - if (cls.generics) { - for (const g of cls.generics) { - str += g.name + ':' + convertType(g.super) + ' ' - } - } - - for (const arg of cls.arguments) { - if (arg.optional && arg.type === 'true') continue - - str += arg.name + ':' - - if (arg.optional) { - str += arg.predicate + '?' - } - - if (arg.type === 'X') { - str += '!' - } - - str += convertType(arg.type) + ' ' - } - - str += '= ' + convertType(cls.type || cls.returns) - - return str -} - -function computeConstructorId(ns, cls) { - return signedInt32ToUnsigned(CRC32.str(stringifyType(cls, ns, false))) -} - -function createTlSchemaIndex(schema) { - let ret = {} - Object.entries(schema).forEach(([ns, it]) => { - it.classes.forEach((obj) => { - obj.uid = 'c_' + ns + '.' + obj.name - obj._ns = ns - obj._type = 'classes' - ret[obj.uid] = obj - }) - it.methods.forEach((obj) => { - obj.uid = 'm_' + ns + '.' + obj.name - obj._ns = ns - obj._type = 'methods' - ret[obj.uid] = obj - }) - it.unions.forEach((obj) => { - obj.uid = 'u_' + ns + '.' + obj.type - obj._ns = ns - obj._type = 'unions' - ret[obj.uid] = obj - }) - }) - return ret -} - -// merge schema `b` into `a` (does not copy) -async function mergeSchemas(a, b, conflict = null) { - const rl = conflict === null ? require('readline').createInterface({ - input: process.stdin, - output: process.stdout, - }) : null - - const input = (q) => new Promise((res) => rl.question(q, res)) - - const index = createTlSchemaIndex(a) - const indexB = createTlSchemaIndex(b) - - for (const [uid, objB] of Object.entries(indexB)) { - if (!(uid in index)) { - // just add - index[uid] = objB - - if (!a[objB._ns]) - a[objB._ns] = { - classes: [], - methods: [], - unions: [], - } - - if (!a[objB._ns][objB._type]) a[objB._ns][objB._type] = [] - - a[objB._ns][objB._type].push(objB) - continue - } - - const objA = index[uid] - - if (objB._type === 'unions') { - // merge subclasses - objA.subtypes = [...new Set([...objA.subtypes, ...objB.subtypes])] - continue - } - - // check for conflict - if (objA.id !== objB.id) { - let keep = conflict - if (conflict === null) { - console.log('! CONFLICT !') - console.log('Schema A (tdlib): %s', stringifyType(objA)) - console.log('Schema B (tdesktop): %s', stringifyType(objB)) - - while (true) { - keep = await input('Which to keep? [A/B] > ') - keep = keep.toUpperCase() - - if (keep !== 'A' && keep !== 'B') { - console.log('Invalid input! Please type A or B') - continue - } - - break - } - } - - if (keep === 'B') { - index[objB.uid] = objB - - const idx = a[objB._ns][objB._type].findIndex((it) => it.uid === objB.uid) - a[objB._ns][objB._type][idx] = objB - } - - continue - } - - // now ctor id is the same, meaning that only `true` flags may differ. - // merge them. - - const argsIndex = {} - - objA.arguments.forEach((arg) => { - argsIndex[arg.name] = arg - }) - - objB.arguments.forEach((arg) => { - if (!(arg.name in argsIndex)) { - objA.arguments.push(arg) - } - }) - } - - // clean up - Object.values(index).forEach((obj) => { - delete obj.uid - delete obj._ns - delete obj._type - }) - - if (rl) rl.close() -} - -module.exports = { mergeSchemas, stringifyType } - -if (require.main === module) { - const { expect } = require('chai') - const schema = require('../raw-schema.json') - - console.log('testing ctor id computation') - Object.entries(schema.api, (ns, items) => { - for (const obj of items.methods) { - if (obj.id !== computeConstructorId(ns, obj)) { - console.log('invalid ctor id: %s', obj.name) - } - } - for (const obj of items.classes) { - if (obj.id !== computeConstructorId(ns, obj)) { - console.log('invalid ctor id: %s', obj.name) - } - } - }) - - - async function test() { - function makeNamespace (obj = {}) { - return { - classes: [], - methods: [], - unions: [], - ...obj - } - } - - async function testMergeSchemas (name, a, b, expected) { - await mergeSchemas(a, b) - - expect(a).eql(expected, name) - } - - console.log('testing merging') - - await testMergeSchemas('new type', { - test: makeNamespace() - }, { - test: makeNamespace({ - methods: [ - { - name: 'useError', - returns: 'Error', - arguments: [] - } - ] - }) - }, { - test: makeNamespace({ - methods: [ - { - name: 'useError', - returns: 'Error', - arguments: [] - } - ] - }) - }) - - await testMergeSchemas('different union', { - help: makeNamespace({ - unions: [ - { - type: 'ConfigSimple', - subtypes: [ - 'testA', - 'testB' - ] - } - ] - }) - }, { - help: makeNamespace({ - unions: [ - { - type: 'ConfigSimple', - subtypes: [ - 'testB', - 'testC' - ] - } - ] - }) - }, { - help: makeNamespace({ - unions: [ - { - type: 'ConfigSimple', - subtypes: [ - 'testA', - 'testB', - 'testC' - ] - } - ] - }) - }) - - await testMergeSchemas('different class', { - help: makeNamespace({ - classes: [ - { - name: 'configSimple', - type: 'ConfigSimple', - arguments: [ - { - name: 'flags', - type: '$FlagsBitField' - }, - { - name: 'date', - type: 'number' - }, - { - name: 'includeThis', - optional: true, - predicate: 'flags.0', - type: 'true' - } - ] - } - ] - }) - }, { - help: makeNamespace({ - classes: [ - { - name: 'configSimple', - type: 'ConfigSimple', - arguments: [ - { - name: 'flags', - type: '$FlagsBitField' - }, - { - name: 'date', - type: 'number' - }, - { - name: 'includeThat', - optional: true, - predicate: 'flags.0', - type: 'true' - } - ] - } - ] - }) - }, { - help: makeNamespace({ - classes: [ - { - name: 'configSimple', - type: 'ConfigSimple', - arguments: [ - { - name: 'flags', - type: '$FlagsBitField' - }, - { - name: 'date', - type: 'number' - }, - { - name: 'includeThis', - optional: true, - predicate: 'flags.0', - type: 'true' - }, - { - name: 'includeThat', - optional: true, - predicate: 'flags.0', - type: 'true' - } - ] - } - ] - }) - }) - - function addId(ns, obj) { - obj.id = computeConstructorId(ns, obj) - return obj - } - - console.log('vv choose B vv') - await testMergeSchemas('conflicting class', { - help: makeNamespace({ - classes: [ - addId('help', { - name: 'configSimple', - type: 'ConfigSimple', - arguments: [ - { - name: 'flags', - type: '$FlagsBitField' - }, - { - name: 'date', - type: 'number' - } - ] - }) - ] - }) - }, { - help: makeNamespace({ - classes: [ - addId('help', { - name: 'configSimple', - type: 'ConfigSimple', - arguments: [ - { - name: 'flags', - type: '$FlagsBitField' - }, - { - name: 'date', - type: 'number' - }, - { - name: 'expires', - type: 'number' - } - ] - }) - ] - }) - }, { - help: makeNamespace({ - classes: [ - addId('help', { - name: 'configSimple', - type: 'ConfigSimple', - arguments: [ - { - name: 'flags', - type: '$FlagsBitField' - }, - { - name: 'date', - type: 'number' - }, - { - name: 'expires', - type: 'number' - } - ] - }) - ] - }) - }) - } - - test().catch(console.error) -} diff --git a/packages/tl/scripts/process-descriptions-yaml.js b/packages/tl/scripts/process-descriptions-yaml.js deleted file mode 100644 index 447ea8eb..00000000 --- a/packages/tl/scripts/process-descriptions-yaml.js +++ /dev/null @@ -1,164 +0,0 @@ -// since this logic is quite big, i decided to -// implement it in a separate file -// -// this basically takes descriptions.yaml and applies -// it to the resulting JSON. - -async function applyDescriptionsFile(input, yaml) { - const { objects: byObjects, arguments: byArguments, regex: byRegex } = yaml - - // util function to handle text and objects with text for overrides - function applyOverwrite(where, what) { - let text = what - let overwrite = false - if (typeof what === 'object') { - if (!what.text) throw new Error('Invalid overwrite object') - text = what.text - overwrite = what.overwrite - } - - if (!where.description || overwrite) { - where.description = text - } - } - - // first create an index of all classes, methods and unions - const index = {} - - function indexNs(ns, prefix = '') { - Object.entries(ns).forEach(([name, content]) => { - const pref = prefix + (name === '$root' ? '' : `${name}.`) - - content.classes.forEach( - (obj) => (index['o_' + pref + obj.name] = obj) - ) - content.methods.forEach( - (obj) => (index['o_' + pref + obj.name] = obj) - ) - content.unions.forEach( - (obj) => (index['u_' + pref + obj.type] = obj) - ) - }) - } - - indexNs(input.mtproto, 'mt_') - indexNs(input.api) - - // process byObject - Object.entries(byObjects).forEach(([name, content]) => { - if (!(name in index)) { - return - } - - const obj = index[name] - - if (content.desc) applyOverwrite(obj, content.desc) - if (content.arguments) - Object.entries(content.arguments).forEach(([arg, repl]) => { - const argObj = (obj.arguments || []).find( - (it) => it.name === arg - ) - if (!argObj) return - - applyOverwrite(argObj, repl) - }) - }) - - // process byArguments - // first create index based on `name` - const byArgumentsIndex = {} - byArguments.forEach((rule) => { - let name = rule.name - if (!Array.isArray(name)) name = [name] - - name.forEach((n) => { - if (!(n in byArgumentsIndex)) byArgumentsIndex[n] = [] - byArgumentsIndex[n].push(rule) - }) - }) - - // now find all objects with these arguments and patch - Object.entries(index).forEach(([key, obj]) => { - if (!obj.arguments) return // unions - - args: for (const arg of obj.arguments) { - if (!(arg.name in byArgumentsIndex)) continue - - const rules = byArgumentsIndex[arg.name] - for (const rule of rules) { - if (rule.filters) { - for (const filter of rule.filters) { - if (filter.objType && filter.objType[0] !== key[0]) - break args - if ( - filter.type && - !filter.type.split(' | ').includes(arg.type) - ) - break args - - // noinspection EqualityComparisonWithCoercionJS - if (filter.optional && arg.optional != filter.optional) - break args - } - } - - applyOverwrite(arg, rule.desc) - } - } - }) - - // process byRegex - function replaceRegex(obj, regex) { - if (!obj.description) return - if (!regex._cached) { - let flags = regex.flags || '' - if (flags.indexOf('g') === -1) flags += 'g' - - regex._cached = new RegExp(regex.regex, flags) - } - obj.description = obj.description.replace(regex._cached, regex.repl) - } - - Object.values(index).forEach((obj) => { - for (const regex of byRegex) { - replaceRegex(obj, regex) - - if (obj.arguments) - obj.arguments.forEach((arg) => replaceRegex(arg, regex)) - } - }) - - return input -} - -module.exports = { - applyDescriptionsFile, -} - -if (require.main === module) { - const fs = require('fs') - const path = require('path') - const yaml = require('js-yaml') - - applyDescriptionsFile( - JSON.parse( - fs.readFileSync(path.join(__dirname, '../raw-schema.json'), 'utf-8') - ), - yaml.load( - fs.readFileSync( - path.join(__dirname, '../descriptions.yaml'), - 'utf-8' - ) - ) - ) - .then((res) => - fs.writeFileSync( - path.join(__dirname, '../raw-schema.json'), - JSON.stringify(res) - ) - ) - .catch((err) => { - console.error(err) - process.exit(1) - }) -} diff --git a/packages/tl/scripts/process-descriptions-yaml.ts b/packages/tl/scripts/process-descriptions-yaml.ts new file mode 100644 index 00000000..ec6bddb8 --- /dev/null +++ b/packages/tl/scripts/process-descriptions-yaml.ts @@ -0,0 +1,166 @@ +import { + CachedDocumentation, + CachedDocumentationEntry, +} from './documentation' + +type MaybeOverwrite = + | string + | { + text: string + overwrite: boolean + } + +interface RegexRule { + _cached?: RegExp + + regex: string + flags?: string + repl: string +} + +interface DescriptionsYaml { + objects: Record< + string, + { + desc?: MaybeOverwrite + arguments?: Record + } + > + + arguments: Record + + regex: RegexRule[] +} + +function unwrapMaybe(what: MaybeOverwrite, has: boolean): string | undefined { + let text: string + let overwrite = false + if (typeof what === 'object') { + if (!what.text) throw new Error('Invalid overwrite object') + text = what.text + overwrite = what.overwrite + } else { + text = what + } + + if (!has || overwrite) { + return text + } + + return undefined +} + +export function applyDescriptionsYamlFile( + input: CachedDocumentation, + yaml: DescriptionsYaml +) { + const { objects: byObjects, arguments: byArguments, regex: byRegex } = yaml + + // first create an index of all classes and methods + const objIndex: Record = {} + + function indexObject( + obj: Record, + prefix: string + ) { + for (const name in obj) { + if (!obj.hasOwnProperty(name)) continue + objIndex[prefix + name] = obj[name] + } + } + + indexObject(input.classes, 'c_') + indexObject(input.methods, 'm_') + + // process byObjects + for (const name in byObjects) { + if (!byObjects.hasOwnProperty(name)) continue + + const rules = byObjects[name] + const obj = objIndex[name] + + if (!obj) continue + + if (rules.desc) { + const desc = unwrapMaybe(rules.desc, !!obj.comment) + if (desc) obj.comment = desc + } + + if (rules.arguments) { + for (const arg in rules.arguments) { + if (!rules.arguments.hasOwnProperty(arg)) continue + + const repl = unwrapMaybe( + rules.arguments[arg], + !!obj.arguments && arg in obj.arguments + ) + + if (repl) { + if (!obj.arguments) obj.arguments = {} + obj.arguments[arg] = repl + } + } + } + } + + // process byArguments + for (const i in objIndex) { + if (!objIndex.hasOwnProperty(i)) continue + const obj = objIndex[i] + + for (const arg in byArguments) { + if (!byArguments.hasOwnProperty(arg)) continue + if (obj.arguments && !(arg in obj.arguments)) continue + + const repl = unwrapMaybe( + byArguments[arg], + !!obj.arguments && arg in obj.arguments + ) + + if (repl) { + if (!obj.arguments) obj.arguments = {} + obj.arguments[arg] = repl + } + } + } + + // process byRegex + function applyRegex(str: string | undefined, rule: RegexRule) { + if (!str) return + if (!rule._cached) { + let flags = rule.flags || '' + if (flags.indexOf('g') === -1) flags += 'g' + + rule._cached = new RegExp(rule.regex, flags) + } + + return str.replace(rule._cached, rule.repl) + } + + for (const i in objIndex) { + if (!objIndex.hasOwnProperty(i)) continue + const obj = objIndex[i] + + byRegex.forEach((rule) => { + obj.comment = applyRegex(obj.comment, rule) + + if (obj.arguments) { + for (let name in obj.arguments) { + if (!obj.arguments.hasOwnProperty(name)) continue + + obj.arguments[name] = applyRegex(obj.arguments[name], rule)! + } + } + }) + } + + for (const i in input.unions) { + if (!input.unions.hasOwnProperty(i)) continue + + byRegex.forEach((rule) => { + input.unions[i] = applyRegex(input.unions[i], rule)! + }) + } + + return input +} diff --git a/packages/tl/scripts/schema.ts b/packages/tl/scripts/schema.ts new file mode 100644 index 00000000..98fa71d3 --- /dev/null +++ b/packages/tl/scripts/schema.ts @@ -0,0 +1,54 @@ +import { TlEntry, TlFullSchema } from '@mtcute/tl-utils/src/types' +import { parseFullTlSchema } from '@mtcute/tl-utils/src/schema' + +interface TlPackedUnion { + // name + n: string + // comment + d?: string + // methods + m: string[] + // classes + c: string[] +} + +export interface TlPackedSchema { + // layer + l: number + // entries + e: TlEntry[] + // unions (only comments) + u: Record +} + +export function packTlSchema(schema: TlFullSchema, layer: number): TlPackedSchema { + const ret: TlPackedSchema = { + l: layer, + e: schema.entries, + u: {} + } + + for (const name in schema.unions) { + if (!schema.unions.hasOwnProperty(name)) continue + const union = schema.unions[name] + + if (union.comment) { + ret.u[name] = union.comment + } + } + + return ret +} + +export function unpackTlSchema(schema: TlPackedSchema): [TlFullSchema, number] { + const res = parseFullTlSchema(schema.e) + + for (const name in schema.u) { + if (!schema.u.hasOwnProperty(name)) continue + if (!res.unions[name]) continue + + res.unions[name].comment = schema.u[name] + } + + return [res, schema.l] +} diff --git a/packages/tl/scripts/utils.ts b/packages/tl/scripts/utils.ts new file mode 100644 index 00000000..5e34fbef --- /dev/null +++ b/packages/tl/scripts/utils.ts @@ -0,0 +1,17 @@ +import fetch, { RequestInit } from 'node-fetch' + +export async function fetchRetry( + url: string, + params?: RequestInit, + retries = 5 +): Promise { + while (true) { + try { + return await fetch(url, params).then((i) => i.text()) + } catch (e) { + if (!retries--) { + throw e + } + } + } +} diff --git a/yarn.lock b/yarn.lock index 8494686b..d920451d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1130,6 +1130,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== +"@types/long@4.0.1", "@types/long@^4.0.1": + version "4.0.1" + resolved "http://localhost:4873/@types%2flong/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" + integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== + "@types/minimatch@^3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" @@ -1578,7 +1583,7 @@ better-sqlite3@^7.4.0: prebuild-install "^6.0.1" tar "^6.1.0" -big-integer@1.6.48, big-integer@^1.6.48: +big-integer@1.6.48: version "1.6.48" resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e" integrity sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w== @@ -2117,9 +2122,9 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" -crc-32@^1.2.0: +crc-32@1.2.0: version "1.2.0" - resolved "http://localhost:4873/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208" integrity sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA== dependencies: exit-on-epipe "~1.0.1" @@ -2186,7 +2191,7 @@ dateformat@^3.0.0: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.3.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== @@ -3815,11 +3820,6 @@ kind-of@^6.0.2, kind-of@^6.0.3: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -leemon@6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/leemon/-/leemon-6.2.0.tgz#85ff020d9dc5959d0837ed816f01fd2c3849ceb2" - integrity sha512-a5ieuGSGEb5ezCL6UNds5//cVFaKpeexVK0VDCE8/eOF0r0/9Og94LQ33U2Px5dUcHVCDPWQY8gXLgDlDJnyyg== - lerna@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/lerna/-/lerna-4.0.0.tgz#b139d685d50ea0ca1be87713a7c2f44a5b678e9e" @@ -3991,6 +3991,11 @@ log-symbols@4.0.0: dependencies: chalk "^4.0.0" +long@4.0.0, long@^4.0.0: + version "4.0.0" + resolved "http://localhost:4873/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"