#API reference
A map of the plugin API, generated from the typings that ship with the SDK. Use it to find the name of a call, see its signature and the grant it needs. Every entry links back to the typings, where the full documentation of each method lives.
How to read an entry: the signature comes first, then what the call does. Grant names the permission it needs. Experimental means the call may change between releases.
#Top level
Functions and constants on the global inu object itself: accounts, events, interceptors, registration of actions and settings, and the error classes.
info(): { platform: 'android' | (string & {}); appVersion: string; appBuild: string; apiVersion: number; layer: number; language: string; header: Record<string, string[]> }Get information about the current environment ↗onUnload(callback: () => void | Promise<void>): DisposerRegister a handler that will run when the plugin is unloaded Handlers run in registration order and share one cleanup window. Resources remain available until settlement/timeout. Ordinary callbacks and timers stop first. JVM runnables created during cleanup can run on the UI thread; existing callbacks stay stopped. Limits: 2 seconds total for async cleanup. ↗openUrl(url: string): voidOpen a web URL, or an internal deeplink (t.me/telegram.org) Grant:openUrl. ↗onAppVisibilityChange(callback: (mode: 'foreground' | 'resumed' | 'paused' | 'background') => void): DisposerSubscribe to app visibility changes. Possible events: -'foreground'- the app was moved to the foreground -'background'- the app was backgrounded (e.g. user switched to another app) -'paused'- the main activity was paused by the system (e.g. permission dialogs/file picker/etc) -'resumed'- the main activity was resumed Grant:onAppVisibilityChange. ↗accounts(): Account[]Get the logged-in accounts, in the order the user sees them in the app ↗onAccountsChanged(callback: (accounts: Account[]) => void): DisposerRegister on updates for when the account list updates, including when the user reorders it ↗withCurrentAccount(callback: (account: Account) => (() => void) | Disposable | void): DisposerRunscallbackfor the current account, and again on each account switch. When a function/Disposable is returned, it's called whenever the account changes, or the returnedDisposeris disposed ↗account(id?: number): AccountThe account in slotid(Account.id), or the current account whenidis omitted. Throwsnot-foundwhen no account is logged in there. ↗registerSettings(page: ui.UIPage): DisposerRegister a UIPage as a plugin settings page ↗invokeRpc<T extends tl.TypeRpcMethod>(params: T): Promise<tl.RpcCallReturn[T['_']] | null>invoke a raw mtproto rpc method via the currently active account Grant:invokeRpc. ↗invokeRaw(method: Uint8Array): Promise<Uint8Array | null>invoke a raw mtproto rpc method via the currently active account, given its TL serialization, and return the TL serialization of the server's response ⚠️ this method is primarily intended for advanced users, primarily for cases where you want to call a method that is not yet supported by the current app's layer. beware that can cause the app to crash or otherwise misbehave, due to the server "bumping" the layer. in almost all cases, you should useinvokeRpcinstead. Grant:unsafe.invokeRaw. ↗interceptRpc<M extends tl.TypeRpcMethod['_']>(method: M, middleware: (context: RpcMiddlewareContext<M>, next: RpcNext<M, tl.RpcCallReturn[M]>) => MaybePromise<tl.RpcCallReturn[M] | null | undefined>, options?: Interce…Register an RPC interceptor Grant:interceptRpc. ↗interceptRpc<M extends tl.TypeRpcMethod['_']>(methods: M[], middleware: (context: RpcMiddlewareContext<M>, next: RpcNext<M, SharedRpcReturn<M>>) => MaybePromise<SharedRpcReturn<M> | null | undefined>, options?: Interc…↗onNewMessage(callback: (message: Message, account: Account) => void): DisposerRegister a new message handler Receives everyupdateNewMessageandupdateNewChannelMessagefor every account. Runs before the app applies the update, so chat state may not reflect it yet. Local and scheduled messages are not included. Grant:onUpdate(new_message). ↗onMessageEdited(callback: (message: Message, account: Account) => void): DisposerRegister a message edit handler Grant:onUpdate(edit_message). ↗onMessageDeleted(callback: (dialogId: number | null, messageIds: number[], account: Account) => void): DisposerRegister a message deletion handler Grant:onUpdate(delete_message). ↗onUpdate<U extends tl.TypeUpdate['_']>(types: U | U[], callback: (update: Extract<tl.TypeUpdate, { _: U }>, account: Account) => void): DisposerRegister a handler for raw TL updates Grant:onUpdate. ↗interceptUpdate<U extends tl.TypeUpdate['_']>(types: U | U[], middleware: (context: UpdateMiddlewareContext<U>) => MaybePromise<'deliver' | 'drop'>): DisposerRegister an app-wide update interceptor Grant:interceptUpdate. ↗registerGlobalAction(options: ActionOptions<ActionContext>): DisposerRegister a global action handler, shown in the drawer or the hamburger menu on the main page Limits: 150 ms to render plugin actions, 8 rows per menu per plugin. ↗registerChatAction(options: ActionOptions<ChatActionContext, ChatActionContext | null>): DisposerRegister a chat action, shown in the chat hamburger menu ↗registerMessageAction(options: MessageActionOptions): DisposerRegister a message action, shown in the message context menu ↗registerProfileAction(options: ActionOptions<ChatActionContext>): DisposerRegister a profile action, shown in the profile hamburger menu ↗registerTranslationProvider(provider: TranslationProvider): DisposerRegister a translation provider, which can be chosen by the user in Translator settings. When the provider is selected, every translation request will be forwarded to thetranslatemethod of the provider. Limits: 30 s per request. ↗interceptSendMessage(middleware: (context: SendMessageContext) => MaybePromise<'send' | 'drop'>): DisposerRegister an interceptor for the user-initiated message sends. This overload registers one in thecomposestage. Grant:interceptSendMessage. Experimental. ↗interceptSendMessage(filter: SendMessageFilter, middleware: (context: SendMessageContext) => MaybePromise<'send' | 'drop'>): DisposerRegister a hook before the message is sent, with a filter. This overload is recommended over the general one because it avoids unnecessary JS jumps for messages that are not matched. Additionally, this overload allows configuring the stage the hook runs on. Grant:interceptSendMessage. Experimental. ↗
#inu.ui
Pages, rows, dialogs, files and screens. See UI for how they fit together.
openPage(page: UIPage): voidopen a page defined by the plugin ↗openPage(screen: PageTarget): voidopen a stock commonly used page ↗openChatHistory(options: { title: InputText; subtitle?: InputText; icon?: UIIcon; account?: number; synthetic?: boolean; avatars?: boolean; load(cursor: string | null, direction: 'older' | 'newer'): MaybePromise<ChatH…Open a chat screen, with the data supplied by the pluggin. Supports infinite scrolling in both directions Every item in the dataset gets its own ID, so the same server message can appear more than once Experimental. ↗toast(text: string): voidshow a toast ↗bulletin(options: { text: InputText; subtitle?: InputText; icon: UIIcon | BulletinAvatars; duration?: 'short' | 'long' | number; position?: 'top' | 'bottom'; button?: string }): Promise<'clicked' | 'button' | 'dismiss…Shows a bulletin (snackbar). Resolves with'button'when its button is tapped,'clicked'when its body is tapped, or'dismissed'when it closes otherwise. You can ignore the promise if you do not need the result. ↗dialog(options: { title?: InputText; message?: InputText; body?: UIElement; positive?: string; negative?: string; neutral?: string }): Promise<'positive' | 'negative' | 'neutral' | 'dismissed'>show a dialog returns a promise that resolves to the result of the dialog once it's dismissed, with either the name of the button pressed, or'dismissed'if the dialog was dismissed (e.g. by tapping outside of it, or by pressing the back button). ↗chooser(options: { title?: string; items: (string | { text: string, subtitle?: string, danger?: boolean })[]; selected?: number; multiple?: false }): Promise<number | null>show a list of choices; tapping one only changes the selection, and the OK button submits it resolves to the selected index (or a list of indices withmultiple), ornullif the chooser was cancelled or dismissed. withoutmultiple, OK stays disabled until something is selected ↗chooser(options: { title?: string; items: (string | { text: string, subtitle?: string, danger?: boolean })[]; selected?: number[]; multiple: true }): Promise<number[] | null>↗selectPeers(options: { title?: string; account?: number; selected?: number[]; peerType?: ('group' | 'broadcast' | 'user')[]; limit?: number; allowEmpty?: boolean }): Promise<number[] | null>Select users, groups or channels. Resolves to marked peer IDs, or null on cancellation. ↗getCurrentScreen(): CurrentScreen | nullGet the currently visible screen Experimental. ↗onScreenChanged(callback: (change: ScreenChange) => void): DisposerSubscribe to navigation changes Experimental. ↗prompt(options: { title: string; hint?: string; value?: string; selectAll?: boolean }): Promise<string | null>Prompt a user for some text input ↗pickFile(options?: { accept?: string[]; multiple?: false }): Promise<File | null>open a file picker, returning the File the user chose (ornullif they chose nothing) ↗pickFile(options: { accept?: string[]; multiple: true }): Promise<File[]>open a file picker, returning the File-s the user chose (or[]if they chose nothing) ↗saveFile(content: Blob | Uint8Array | { path: string }, options?: { fileName?: string; type?: string }): Promise<boolean>save a file to the device storage, opening a "save as" dialog Grant:fs to name a file. ↗header(text: string): UIElementHeader element ↗check(options: { id?: string; text: string; subtitle?: string; icon?: UIIcon; checked: boolean; onChange: (checked: boolean, anchor: UIAnchor) => void; onSecondaryClick?: (anchor: UIAnchor) => void }): UIElementCheck (switch) element ↗button(options: { id?: string; text: InputText; subtitle?: InputText; icon?: UIIcon; value?: InputText; danger?: boolean; onClick: (anchor: UIAnchor) => void; onSecondaryClick?: (anchor: UIAnchor) => void }): UIElementButton element ↗select(options: { id?: string; text: InputText; icon?: UIIcon; items: (string | { text: string, subtitle?: string })[]; selected: number; dialog?: boolean; onChange: (index: number, anchor: UIAnchor) => void; onSecond…Select element. A button showing a selector on tap ↗slider(options: { id?: string; text?: string; min: number; max: number; step: number; value: number; default?: number; label?: (value: number) => string; onChange: (value: number, anchor: UIAnchor) => void }): UIElementSlider element Limits: 501 label steps. ↗separator(text?: InputText): UIElementA separator element, with optional text ↗settingsPage(options: { title: string; transient?: boolean; items: () => UIElement[]; bottomButton?: { text: string; onClick: (anchor: UIAnchor) => void }; onClose?: () => void }): UIPageDefine a declarative settings page ↗openPage(fragment: JavaObject): voidOpen a page from a manually createdFragment↗
#inu.icons
Icons for rows and menus.
animation(name: 'success' | 'error' | 'info' | 'loading'): UIIconUIIconfrom a built-in common animation ↗customEmoji(id: string, options?: LottieOptions): UIIconUIIconfrom a Telegram custom emoji (idis the custom emoji ID as a string) ↗sticker(options: StickerOptions): UIIconUIIconfrom a Telegram sticker.slugis the slug of a stickerset, and to address a sticker you can use one of: -index- zero-based index of a sticker in the set -emoji- emoji the sticker represents -id- the sticker's ididis the most stable way to address a sticker, but none of them are that stable because indices might shift, emojis might change, and sticker with the ID might be deleted from the set. for use in interfaces it is highly recommended to use a set you control, or use custom emojis. ↗avatar(dialogId: number, options?: { account?: number }): UIIconUIIconwith a peer's profile photo (or initials placeholder) ↗common(name: | 'settings' | 'info' | 'search' | 'edit' | 'delete' | 'copy' | 'share' | 'download' | 'link' | 'pin' | 'star' | 'mute' | 'unmute' | 'archive' | 'forward' | 'reply' | 'user' | 'group' | 'channel' | 'bot…UIIconfrom a built-in common icon ↗svg(source: string): UIIconUIIconfrom a custom SVG. Limits: 64 KiB of SVG source. ↗
#inu.utils
Peer helpers and the text formatting parsers (md, html, thtml).
const md: TextFormatmtcute's markdown: -**bold**-__italic__---underline---~~strike~~-\|\|spoiler\|\|-`code`-```pre-[text](url)-> quote. ↗const html: TextFormattelegram's html subset. Whitespace collapses as it does in real html;<br>breaks a line. ↗const thtml: TextFormathtml, but whitespace is kept as written and the template is dedented first, like Bot API ↗joinTextWithEntities(parts: InputText[], delim?: InputText): TextWithEntitiesJoins text parts and adjusts their entity offsets:ts const board = inu.utils.joinTextWithEntities(scores.map(entry => md`**${entry.name}**: ${entry.score}`), '\n')The delimiter is inserted only after nonempty output, so leading empty parts add no delimiter. ↗toBase64(bytes: Uint8Array): stringconvert an array of bytes to base64 ↗fromBase64(base64: string): Uint8Arrayconvert base64 to an array of bytes ↗toHex(bytes: Uint8Array): stringconvert an array of bytes to hex ↗fromHex(hex: string): Uint8Arrayconvert hex to an array of bytes ↗formatDate(unix: number, style?: 'date' | 'time' | 'dateTime' | 'relative'): stringapp-localized date text.unixmust be a safe integer Unix timestamp in seconds. ↗formatNumber(value: number, options?: { compact?: boolean }): stringapp-localized integer text.compactuses Telegram's million formatter. ↗formatFileSize(bytes: number): stringapp-localized file size text.bytesmust be a safe integer. ↗formatDuration(seconds: number): stringapp's clock-style duration text.secondsmust be a non-negative signed 32-bit integer. ↗
#inu.clipboard
The system clipboard.
write(text: string): voidWrite something into the clipboard Grant:clipboard.write. ↗read(): stringRead the clipboard value Grant:clipboard.read. ↗
#inu.notifications
Control over the notifications the app posts.
suppress(): DisposerSuppresses all app notifications for all accounts and dismisses existing notifications. Suppression lasts until the returnedDisposerruns or the plugin unloads. Multiple holds can coexist; notifications resume only when all holds are released. SeeAccount.suppressNotificationsto suppress one account's notifications. Grant:notifications.suppress. ↗
#inu.fs
The plugin's private folder. See Network, files and storage.
read(path: string): Uint8Array↗write(path: string, data: Blob | Uint8Array): void↗append(path: string, data: Blob | Uint8Array): void↗mkdir(path: string): void↗rm(path: string, options?: { recursive?: boolean }): void↗exists(path: string): boolean↗readdir(path: string): string[]Returns sorted file and directory names. Usestatto distinguish them. ↗stat(path: string): { isFile: boolean; isDirectory: boolean; size: number; mtime: number; ctime: number }↗copy(src: string, dest: string): void↗move(src: string, dest: string): void↗usage(): numberGet the storage amount in bytes used by the plugin currently ↗quota(): numberGet the storage limit in bytes ↗
#inu.canvas
The offscreen 2D canvas. See Canvas.
create(width: number, height: number): OffscreenCanvas↗decode(source: Blob | Uint8Array): Promise<ImageBitmap>↗load(file: { path: string }): Promise<ImageBitmap>Grant:fs. ↗decodeAnimation(source: Blob | Uint8Array | { path: string }, options?: { width?: number; height?: number; fps?: number }): Promise<AnimatedImage>Opens an animated source (tgs/webm/mp4) for reading. Grant:fs to name a file. Experimental. ↗createEncoder(options: { type?: 'video/mp4'; width: number; height: number; fps?: number; bitrate?: number }): Promise<VideoEncoder>create a video encoder Experimental. ↗loadFont(family: string, source: Blob | Uint8Array | { path: string }): Promise<void>↗listFonts(): Promise<FontEntry[]>Lists fonts available forCanvasRenderingContext2D.font. Device fonts appear only when enabled in the app's font settings. An unlisted name may still resolve to a device font, or may not resolve at all. ↗
#inu.android
Helpers for the Android side: the current screen, bundles, native views.
bundle(values: Record<string, boolean | number | bigint | string | Uint8Array | JavaObject>): JavaObjectCreate an AndroidBundlefrom a JS object. Supported value types: -number(putInt,putLongorputDouble) -bigint(putLong) -boolean-string-Uint8Array- Bundle-compatible Java objects such asParcelable,IBinder, andSerializablevalues. Grant:unsafe.jvm. ↗getPluginsDir(): stringGet a full path to the plugins installation directory Grant:unsafe.fs. ↗getCacheDir(): stringGet a full path to the app cache directory Grant:unsafe.fs. ↗getMediaDir(type: 'files' | 'images' | 'videos' | 'audios' | 'documents'): stringGet a full path to the media directory, of the specified type Grant:unsafe.fs. ↗getCurrentFragment(): JavaObject | nullGet a reference to the currently visibleFragmentGrant:unsafe.jvm. ↗getCurrentActivity(): JavaObject | nullGet a reference to the currently visibleActivityGrant:unsafe.jvm. ↗resourceIcon(name: string): UIIconGet aUIIconfromR.drawable.{name}Limits: 128 characters per name. ↗rawAnimation(name: string, options?: icons.LottieOptions): UIIconGet aUIIconfrom a Lottie JSON animation fromR.raw.{name}↗drawableIcon(drawable: JavaObject): UIIconGet aUIIconfrom a manual JavaDrawableGrant:unsafe.jvm. ↗nativeView(view: JavaObject): UIElementGet aUIElementfrom a manual JavaViewGrant:unsafe.jvm. ↗
#inu.jvm
Java reflection. See JVM reflection.
routine(body: JvmRoutineBody): JvmRoutineRunnableRuns Java operations on the calling thread, without ever touching JS. Can be used as adefineClassbody, aRunnable, or aninu.xposedhook filter. Function body is compiled by@inugram/cli. Supported JS subset: -const/letdeclarations -if-while/do/for/for ofwithbreak/continue-switch-try/catch/finally-throw-return- math operators: -+concatenates if either operand is text -/does integer division if both args are integers - other math operators work as expected -===/!==(coerced comparisons are NOT supported) -inu.jvm.callSuperandinu.jvm.getSuper, the onlyinumembers available - expressions on Java values. Unsupported syntax is a build error with a source location. Captured values, including arrays, are snapshots taken when the routine is created. Unload cancels running routines, but cannot interrupt a Java call already in progress. Limits: 1024 instructions, 256 slots, 256 captures, 512 live routines, 1 MB of captures, 250 ms per run. Experimental. ↗runnable(callback: () => void): JavaObjectWraps a JS function in ajava.lang.Runnable, preserving its closure. Runs synchronously on the calling thread; promise continuations run later on the plugin thread. A call from the plugin thread throughinu.jvmruns the callback nested. Entry from inside another of this plugin's callbacks on the same thread throws IllegalStateException. A stopped plugin, or another app thread holding the engine for over 2 s, skips the callback. Do not wait synchronously for another thread that may call into the same plugin. Limits: 2 s to acquire the engine. Experimental. ↗cls(name: string): JavaClassGet a Java class by its FQN ↗fromTl(value: TLObject): JavaObjectConverts a TL value to aJavaObjectfor calling methods or passing to app code. If the object is backed by Java, returns the existing object, otherwise creates a newTLObject↗toTl(value: JavaObject): TLObjectReturns a writable TL view of aJavaObject. Throws aTypeErrorif the handle is not aTLObject. ↗loadDex(path: string | Uint8Array): voidLoad a DEX file from a path or Uint8Array Note: avoid using this API when possible. If you need to load a DEX in your plugin, please talk to us about your use-case ↗defineClass(spec: JvmClassSpec): DefinedClassDefines a public JVM class, by default extending Object with a no-argsuper()constructor. Types accept primitive or fully qualified class names,[]arrays, and JVM descriptors. Omitted params/returns use an unambiguous inherited signature, or()void. Fields are public with Java defaults. Static methods receive the JavaClass asself. Bodies and initializers accept sync JS functions orinu.jvm.routineobjects. A JS body may return an array of scalars and handles, which Java receives as anObject[]. JS errors becomeIllegalStateException; recursive JS entry is rejected. After unload, void callbacks do nothing and other methods fail. Java calls cannot be interrupted. Note: Due to ART limitations, generated classes cannot be unloaded. To work around that, by default classes get a randomly generated name, available inDefinedClass.nameIf you pass a specific class name, a full app restart will be required for the changes to take effect Limits: 128 classes per engine, 256 fields and 256 methods per class (including constructors and covariant bridges), 64 interfaces, 64 parameters, 1 MB for definitions/captures, 64 nested calls sharing a 250 ms admission budget. Experimental. ↗defineClass(name: string, spec: JvmClassSpec): DefinedClassExperimental. ↗callSuper(cls: JavaClass, self: JavaObject, method: string, ...args: any[]): anyCallsmethodthe waysuper.method(...args)written insideclswould: resolved on the superclass ofcls, and running that implementation even ifselfoverrides it.selfmust be an instance ofcls. Overloads are picked likeJavaObject.call. Available insideinu.jvm.routineandinu.xposed.routinebodies as well. Throws aTypeErrorifclshas no superclass, or the picked method is abstract. Experimental. ↗getSuper(self: JavaObject): anysuperfor a routine used as ainu.jvm.defineClassbody, which cannot capture the class it is defining:inu.jvm.getSuper(this).method(...args)isinu.jvm.callSuperon the class the routine is bound to. Only available ininu.jvm.routinebodies, and only called through directly. A routine run outside a defineClass body throws there. Experimental. ↗
#inu.xposed
Method hooks. See Method hooks.
routine(body: (ctx: RoutineContext) => void): XposedRoutineRunnableCreates a hook that runs on the hooked thread without a JS callback. Uses the same compiled subset asinu.jvm.routine, with a context parameter. Set results throughsetReturnValue;returncannot take a value, andthisis unavailable. Both arrows and function expressions are supported. Experimental. ↗hookMethod(method: JavaMethod, hook: MethodHook): DisposerHook a Java method with an Xposed-style hook.beforehooks run in registration order,afterhooks are run in reverse order. Setting a result in a before hook skips later plugins and the original method. Each plugin receives its own context. Recursive calls during a callback skip that plugin's hooks. Calls made by the original method still run hooks normally. Disposing your hook leaves other plugins' hooks active. Experimental. ↗hookAllOverloads(cls: JavaClass, name: string, hook: MethodHook): DisposerHook all overloads of a class method by its name Experimental. ↗hookAllConstructors(cls: JavaClass, hook: MethodHook): DisposerHook all constructors of a class Experimental. ↗callOriginalMethod(method: JavaMethod | JavaConstructor, thisObject: JavaObject | null, args: any[]): anyCalls the original member, bypassing all plugins' hooks. Experimental. ↗allocateInstance(cls: JavaClass): JavaObjectCreate an instance of the class without running any constructor, via JNIAllocObject. ↗disableProfileSaver(): booleanDisable Android Profile Saver for the process. ↗
#Objects you get back
The main shapes you handle once you have an account or a message.
#Account
Defined in common.d.ts.
readonly id: numberopaque slot of the account ↗readonly userId: numberid of the user this account represents ↗isCurrent(): booleanwhether this is the currently active account ↗isPremium(): booleanwhether this account has Telegram Premium active Grant:account.read(self). ↗suppressNotifications(): DisposerSuppresses app notifications for this account until the returnedDisposerruns or the plugin unloads. Suppression remains active while any hold exists. App-wide suppression also applies to this account. Grant:notifications.suppress. ↗getMe(): tl.TypeUser | nullget the user this account represents Grant:account.read(self). ↗getUser(peer: InputPeerLike): tl.TypeUser | nullget a user (cached, this method never does a network call, and returnsnullon miss) Grant:account.read(peers). ↗getChat(peer: InputPeerLike): tl.TypeChat | nullget a chat (cached, this method never does a network call, and returnsnullon miss) Grant:account.read(peers). ↗getPeer(peer: InputPeerLike): tl.TypeUser | tl.TypeChat | nullget a user or a chat (cached, this method never does a network call, and returnsnullon miss) Grant:account.read(peers). ↗getDialog(peer: InputPeerLike): tl.TypeDialog | nullget a dialog with a specific peer (cached, this method never does a network call, and returnsnullon miss) Grant:account.read(dialogs). ↗isDialogMuted(peer: InputPeerLike, options?: { topicId?: number }): booleanWhether the app considers this dialog muted. Combines account defaults, dialog overrides, and topic overrides when a topic is specified. Unknown dialogs are not muted. Grant:account.read(dialogs). ↗previewMessage(message: Message | tl.TypeMessage, options?: { hideSpoilers?: boolean }): TextWithEntitiesThe app's message preview: message text, a media label such as "📷 Photo", or a service message in the app's language. Entities describe the preview, not the original message.hideSpoilersmasks spoilers and removes entities covering masked text. Media labels are unchanged. Grant:account.read(messages). ↗getTopicCached(peer: InputPeerLike, topicId: number): tl.TypeForumTopic | nullReturns a cached forum topic, ornullif missing or the peer is not a forum. Never uses the network; usegetTopicsto fetch topics. Grant:account.read(dialogs). ↗getUsers(peers: InputPeerLike[]): (tl.TypeUser | null)[]get one or more users (cached, this method never does a network call, and returnsnullon miss) Grant:account.read(peers). ↗getChats(peers: InputPeerLike[]): (tl.TypeChat | null)[]get one or more chats (cached, this method never does a network call, and returnsnullon miss) Grant:account.read(peers). ↗getMessagesCached(peer: InputPeerLike, messageId: number): Message | nullReads locally stored messages without using the network. Returnsnullfor missing messages. May block on a database read; usegetHistoryto read a range instead of looping. Grant:account.read(messages). ↗getMessagesCached(peer: InputPeerLike, messageIds: number[]): (Message | null)[]↗getMessages(peer: InputPeerLike, messageId: number): Promise<Message | null>get one or more messages, always fetching them from the server Grant:account.read(messages). ↗getMessages(peer: InputPeerLike, messageIds: number[]): Promise<(Message | null)[]>↗getMessageFile(message: Message | tl.TypeMessage): { path: string, exists: boolean } | nullget an app-owned file representing the attachment of a message Grant:account.read(messages). Experimental. ↗downloadMedia(message: Message | tl.TypeMessage, options?: { onProgress?: ProgressCallback }): Promise<File>download a message's attachment to a File Grant:account.read(messages). ↗downloadMediaToFile(message: Message | tl.TypeMessage, options?: { onProgress?: ProgressCallback }): Promise<{ path: string }>download a message's attachment to a file, and return the path Grant:account.read(messages). Experimental. ↗uploadFile(file: Blob | Uint8Array | { path: string }, options?: { fileName?: string; onProgress?: ProgressCallback }): Promise<tl.TypeInputFile>{ path }names a file the wayfsdoes, and needs its grant. Limits: 256 MB per staged copy, for all write operations. Grant:account.write(send). ↗createLocalMedia(file: Blob | Uint8Array | { path: string }, options?: { fileName?: string; asDocument?: boolean }): Promise<LocalMedia>Stages a file for an outgoing message'sOutgoingMessage.media, where the app processes and uploads it like a file the user picked. Expires 10 minutes after it was created if no send takes it.{ path }names a file the wayfsdoes, and needs its grant. Keep it available for upload and retries after the send. Limits: 256 MB per staged copy, for all write operations. Grant:account.write(send). Experimental. ↗getUserFull(peer: InputPeerLike): Promise<tl.TypeUserFull | null>Grant:account.read(peers). ↗getChatFull(peer: InputPeerLike): Promise<tl.TypeChatFull | null>Grant:account.read(peers). ↗getDialogs(options?: { folderId?: number; limit?: number; fields?: readonly string[]; cursor?: Cursor<'dialogs'> }): Promise<Paged<tl.TypeDialog, 'dialogs'>>fieldspreloads selected fields for faster reads, as ingetDialogsCached. Grant:account.read(dialogs). ↗iterDialogs(options?: { folderId?: number; limit?: number; batchSize?: number; fields?: readonly string[] }): AsyncIterableIterator<tl.TypeDialog>Grant:account.read(dialogs).batchSizedefaults to Telegram's page size of 100.fieldsapplies to every page.. ↗getDialogsCached(options?: { archive?: 'exclude' | 'only' | 'keep'; chatFolderId?: number; limit?: number; fields?: readonly string[] }): Promise<tl.TypeDialog[]>Returns all cached dialogs in chat-list order: pinned first, then by date. Does not use the network or paginate. Resolves on the next turn; unavailable in synchronous contexts such as aninu.xposedphase.archiveselects the main list ('exclude', the default), the archive ('only'), or both ('keep').chatFolderIdselects a folder fromgetChatFoldersCachedinstead. Specifying both options throws aTypeError; folders have their own archive rules.fieldspreloads selected fields for faster reads. Other fields remain readable. Grant:account.read(dialogs). ↗getChatFoldersCached(): Promise<ChatFolder[]>Returns cached chat folders in tab order. Asynchronous, likegetDialogsCached. Grant:account.read(dialogs). ↗getHistory(peer: InputPeerLike, options?: { limit?: number; offsetId?: number; minId?: number; maxId?: number; topicId?: number; force?: boolean }): Promise<Message[]>Get chat history in a specific dialog, preferring data from the local cache. Grant:account.read(history). ↗iterHistory(peer: InputPeerLike, options?: { limit?: number; offsetId?: number; minId?: number; maxId?: number; batchSize?: number; topicId?: number; force?: boolean }): AsyncIterableIterator<Message>Grant:account.read(history). ↗getTopics(peer: InputPeerLike, options?: { limit?: number; cursor?: Cursor<'topics'> }): Promise<Paged<tl.TypeForumTopic, 'topics'>>Get topics in a specific forum Grant:account.read(dialogs). ↗iterTopics(peer: InputPeerLike, options?: { limit?: number; batchSize?: number }): AsyncIterableIterator<tl.TypeForumTopic>Grant:account.read(dialogs). ↗resolvePeer(peer: InputPeerLike): Promise<tl.TypeInputPeer>Resolve anInputPeerfromInputPeerLikeGrant:account.read(peers). ↗resolvePeerCached(peer: InputPeerLike): tl.TypeInputPeer | nullResolve anInputPeerfromInputPeerLike, without hitting the network, returningnullon miss Grant:account.read(peers). ↗resolveUser(peer: InputPeerLike): Promise<tl.TypeInputUser>Resolve anInputUserfromInputPeerLikeGrant:account.read(peers). ↗resolveChannel(peer: InputPeerLike): Promise<tl.TypeInputChannel>Resolve anInputChannelfromInputPeerLikeGrant:account.read(peers). ↗resolvePeerMany(peers: InputPeerLike[]): Promise<(tl.TypeInputPeer | null)[]>Limits: 8 resolutions in flight. Grant:account.read(peers). ↗sendMessage(peer: InputPeerLike, text: InputText, options?: { replyToMessageId?: number; topicId?: number; silent?: boolean; scheduleDate?: number; noWebpage?: boolean; sendAs?: InputPeerLike; clearDraft?: boolean; op…send a text message Grant:account.write(send). ↗sendMedia(peer: InputPeerLike, file: Blob | Uint8Array | tl.TypeInputFile | tl.TypeInputMedia | { path: string }, options?: { caption?: InputText; replyToMessageId?: number; topicId?: number; silent?: boolean; schedul…send a message containing a media Grant:account.write(send). Experimental. ↗sendMultiMedia(peer: InputPeerLike, items: { file: Blob | Uint8Array | tl.TypeInputFile | tl.TypeInputMedia | { path: string }; caption?: InputText; fileName?: string; asDocument?: boolean }[], options?: { replyToMess…send an album Grant:account.write(send). Experimental. ↗editMessage(peer: InputPeerLike, messageId: number, text: InputText, options?: { noWebpage?: boolean }): Promise<Message>edit a message Grant:account.write(edit). ↗deleteMessages(peer: InputPeerLike, messageIds: number[], options?: { revoke?: boolean }): Promise<void>delete one or more messages Grant:account.write(delete). ↗forwardMessages(fromPeer: InputPeerLike, messageIds: number[], toPeer: InputPeerLike, options?: { silent?: boolean; scheduleDate?: number; topicId?: number; dropAuthor?: boolean; dropCaption?: boolean }): Promise<Mess…forward one or more messages Grant:account.write(forward). ↗setReaction(peer: InputPeerLike, messageId: number, reactions: (string | { customEmojiId: string })[], options?: { big?: boolean }): Promise<void>send or retract a reaction on a message Grant:account.write(react). ↗readHistory(peer: InputPeerLike, options?: { maxId?: number; topicId?: number }): Promise<void>mark messages as read Grant:account.write(read). ↗sendTyping(peer: InputPeerLike, action?: | 'typing' | 'cancel' | 'recordVideo' | 'uploadVideo' | 'recordVoice' | 'uploadVoice' | 'uploadPhoto' | 'uploadDocument' | 'chooseSticker' | 'chooseContact', options?: { topicI…send a typing status Grant:account.write(typing). ↗getDraft(peer: InputPeerLike, options?: { topicId?: number }): TextWithEntities | nullGrant:account.read(draft). ↗setDraft(peer: InputPeerLike, draft: InputText | null, options?: { topicId?: number; replyToMessageId?: number }): Promise<void>Grant:account.write(draft). ↗invokeRpc<T extends tl.TypeRpcMethod>(params: T): Promise<tl.RpcCallReturn[T['_']] | null>invoke a raw mtproto rpc method Grant:invokeRpc. ↗invokeRaw(method: Uint8Array): Promise<Uint8Array | null>invoke a raw mtproto rpc method, given its TL serialization, and return the TL serialization of the server's response ⚠️ this method is primarily intended for advanced users, primarily for cases where you want to call a method that is not yet supported by the current app's layer. beware that can cause the app to crash or otherwise misbehave, due to the server "bumping" the layer. in almost all cases, you should useinvokeRpcinstead. Grant:unsafe.invokeRaw. ↗initTakeoutSession(options?: { contacts?: boolean; messageUsers?: boolean; messageChats?: boolean; messageMegagroups?: boolean; messageChannels?: boolean; fileMaxSize?: number }): Promise<TakeoutSession>Opens a takeout session with higher rate limits for history reads. CallTakeoutSession.finishwhen done; Telegram keeps the session until then. Grant:takeout. ↗
#Message
Defined in common.d.ts.
readonly raw: tl.TypeMessageRaw TL object of the message ↗isService(): booleanWhether the message is a service message ↗id(): numberMessage ID ↗groupedId(): string | nullIf the message belongs to an album, ID of the album ↗dialogId(): number | nullID of the dialog where the message was sent ↗topicId(): number | nullID of the topic where the message belongs ↗senderId(): number | nullID of the message sender ↗date(): numberDate of the message, as a unix date in seconds ↗editDate(): number | nullDate when the message was edited, as a unix date in seconds ↗out(): booleanWhether this message is outgoing ↗text(): stringPlain text of the message ↗textWithEntities(): TextWithEntitiesFormatted text of the message ↗media(): tl.TypeMessageMedia | nullMedia inside the message, if any ↗document(): tl.TypeDocument | nullDocument inside the message, if any ↗mediaType(): MediaType | nullType of the media in the message, if any ↗duration(): number | nullFor playable media types, duration of the media ↗replyToMessageId(): number | nullIf the message is a reply to another message, ID of that message ↗forwardedFrom(): tl.TypeMessageFwdHeader | nullIf the message is a forward, info about that ↗viaBotId(): number | nullIf the message was sent via an inline bot, ID of that bot ↗isPinned(): booleanWhether the message is pinned ↗views(): number | nullView count of the message, if available ↗forwards(): number | nullForwards count of the message, if available ↗reactions(): tl.TypeMessageReactions | nullReactions on the message, if any ↗toJSON(): tl.TypeMessage↗
#TakeoutSession
Defined in common.d.ts.
readonly id: stringthe session's int64 id, as a string ↗invokeRpc<T extends tl.TypeRpcMethod>(params: T): Promise<tl.RpcCallReturn[T['_']] | null>invoke a raw mtproto rpc method, wrapped ininvokeWithTakeoutGrant:takeout, plusinvokeRpcfor the method being called. ↗finish(success?: boolean): Promise<boolean>closes the session server-side;successdefaults totrue. Grant:takeout. ↗
#ChatFolder
Defined in common.d.ts.
id: number0is the "All chats" tab, which every account has and which cannot be edited. ↗title: TextWithEntities↗emoticon: string | nullThe folder's emoji, ornullif unset. ↗colorIndex: number | nullTelegram's folder colour,0-7into its own palette - not an ARGB value.nullwhen the folder has no colour. ↗unreadCount: number↗dialogCount: numberThe number of dialogsAccount.getDialogsCachedcurrently returns for this folder. ↗isDefault: boolean↗isChatlist: booleanA shared folder, added from an invite link. ↗pinned: number[]The folder's pinned dialogs, in the order they are pinned. ↗
#RpcError
Defined in common.d.ts.
#PluginError
Defined in common.d.ts.
code: | 'not-granted' | 'forbidden' | 'quota-exceeded' | 'handle-expired' | 'unknown-constructor' | 'not-found' | 'unsupported' | 'timed-out' | 'aborted' | 'network' | 'internal' | (string & {})Error code: -not-granted: You are trying to use an API you haven't specified a@grantfor -forbidden: You are trying to access a protected resource -quota-exceeded: You are trying to use more resources than the quota allows (fs, timers, native memory, actions, etc.) -handle-expired: You are trying to read data from an expired/disposed handle. -unknown-constructor: You are trying to use an unknown TL constructor -not-found: You are trying to access something that doesn't exist -unsupported: You are trying to use an unsupported API -timed-out: Your operation has timed out -aborted: You operation was aborted -network: Some network-related issue happened -internal: An internal plugin engine error happened, please report this ↗grant?: stringIfcode == 'not-granted', the grant you're missing ↗usage?: numberIfcode ==quota-exceeded'`, the current quota usage ↗quota?: numberIfcode ==quota-exceeded'`, the current quota limit ↗