diff --git a/data/core.telegram.org/animated_stickers.html b/data/core.telegram.org/animated_stickers.html new file mode 100644 index 0000000000..dd12a33112 --- /dev/null +++ b/data/core.telegram.org/animated_stickers.html @@ -0,0 +1,138 @@ + + +
+ +Telegram apps support animated stickers as of version 5.9. All artists are welcome to create and upload new packs of animated stickers.
+To create animated stickers for the Telegram platform, you will need the following:
+Once your stickers are ready, send the /newanimated
command to the @stickers bot – then send it the .TGS files.
Your set will need an icon. Icons for animated sticker sets must be 100x100 pixels, with a looped animation not exceeding 3 seconds.
+If the user has successfully provided the login code, but they forgot the 2FA password, the account should be reset: this can be done using account.deleteAccount.
+If the account's 2FA password was modified more than 7 days ago and was active in the last 7 days, account deletion will be delayed for 7 days, and a service message will be sent to the user, containing a link in one of the following formats:
+https://telegram.me/confirmphone?phone=XXX&hash=YYYY
tg://confirmphone?phone=XXX&hash=YYYY
When clicked, account.sendConfirmPhoneCode must be called with the specified hash
, using the account with the specified phone
number.
+This will send a phone number verification code to the phone number associated with the account.
+The phone code settings are the same as for the login code, and auth.cancelCode with auth.resendCode can be used as well, to resend or cancel the phone code as for the login code.
Once the SMS code is received, the account.confirmPhone method will have to be called with the SMS code and the phone hash received from the account.sendConfirmPhoneCode method.
+This will cancel deletion of the account and will log out the user that tried to reset it. +Otherwise, if the number isn't confirmed in 7 days, the account will be deleted and the user will be free to recreate it.
+How to register a user's phone to start using the API.
Graphical telegram clients should transform emojis into their respective animated version.
+inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet;
+
+messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet;
+
+---functions---
+
+messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet;
+On startup, clients should fetch the animated emoji stickerset by calling the messages.getStickerSet method, providing inputStickerSetAnimatedEmoji to the stickerset
field.
+The returned stickerset will contain a set of animated stickers, one for each of the supported emojis.
Clients should substitute messages containing only one instance of one of the allowed emojis with the respective animated sticker.
+Animated emojis should loop only once when first sent or received, or when clicked.
+For special dice emojis like , , or , clients are supposed to behave differently both when sending and receiving such emojis: click here for more info ».
+Certained animated emojis should play sound when clicked, as specified by server-side configuration.
+The returned JSON object will contain the following map, with a list of file IDs to download:
+ "emojies_sounds": {
+ "\ud83c\udf83": {
+ "id": "4956223179606458539",
+ "access_hash": "-2107001400913062971",
+ "file_reference_base64": "AF-4ApC7ukC0UWEPZN0TeSJURe7T"
+ },
+ "\u26b0": {
+ "id": "4956223179606458540",
+ "access_hash": "-1498869544183595185",
+ "file_reference_base64": "AF-4ApCLKMGt96WCvLm58kbqZHd3"
+ },
+ "\ud83e\udddf\u200d\u2642": {
+ "id": "4960929110848176331",
+ "access_hash": "3986395821757915468",
+ "file_reference_base64": "AF-4ApAedNln3IMEHH-SUQuH8L9g"
+ },
+ }
+The file reference field should be base64-decoded before downloading the file
Authorization is associated with a client’s encryption key identifier: auth_key_id. No additional parameters need to be passed into methods following authorization.
+To log in as a bot, follow these instructions ».
+Example implementations: telegram for android, tdlib.
+To show a nicely formatted and validated phone number field, the help.countriesList constructor can be obtained using the help.getCountriesList method.
+The help.countriesList config is then used as described here ».
Authorization requires that a text message containing an authorization code first be sent to the user’s phone.
+This may be done using the auth.sendCode method.
+The system will automatically choose how to send the authorization code; there are four possible ways the code can arrive:
The auth.sendCode method also has parameters for enabling/disabling use of flash calls, and allows passing an SMS token that will be included in the sent SMS. +For example, the latter is required in newer versions of android, to use the android SMS receiver APIs.
+The returned auth.SentCode object will contain multiple parameters:
+flags | +# | +Flags, see TL conditional fields | +
type | +auth.SentCodeType | +Phone code type | +
phone_code_hash | +string | +Phone code hash, to be stored and later re-used with auth.signIn | +
next_type | +flags.1?auth.CodeType | +Phone code type that will be sent next, if the phone code is not received within timeout seconds: to send it use auth.resendCode |
+
timeout | +flags.2?int | +Timeout for reception of the phone code | +
If the message takes too long (timeout
seconds) to arrive at the phone, the auth.resendCode method may be invoked to resend a code of type next_type
.
+If the same happens again, you can use auth.resendCode with the next_type
returned by the previous call to auth.resendCode.
+To cancel the verification code use auth.cancelCode.
When user enters verification code, the auth.signIn method must be used to validate it and possibly sign user in.
+If the code was entered correctly, but the method returns auth.authorizationSignUpRequired, it means that account with this phone number doesn't exist yet: user needs to provide basic information, accept terms of service and then the new user registration method (auth.signUp) must be invoked.
+When trying to sign in using auth.signIn, an error 400 SESSION_PASSWORD_NEEDED may be returned, if the user has two-factor authentication enabled. +In this case, instructions for SRP 2FA authentication must be followed.
+To set up two-factor authorization on an already authorized account, follow the SRP 2FA authentication docs.
+Each phone number is limited to only a certain amount of logins per day (e.g. 5, but this is subject to change) after which the API will return a FLOOD error until the next day. This might not be enough for testing the implementation of User Authorization flows in client applications.
+There are several reserved phone number prefixes for testing that your application handles redirects between DCs, sign up, sign in and 2FA flows correctly. These numbers are only available on Test DCs (their IP addresses for TCP transport are availble in API development tools panel after api_id was obtained, URI format for HTTPS/Websocket transport).
+If you wish to emulate an application of a user associated with DC number X, it is sufficient to specify the phone number as 99966XYYYY
, where YYYY are random numbers, when registering the user. A user like this would always get XXXXX as the login confirmation code (the DC number, repeated five times). Note that the value of X must be in the range of 1-3 because there are only 3 Test DCs. When the flood limit is reached for any particular test number, just choose another number (changing the YYYY random part).
Do not store any important or private information in the messages of such test accounts; anyone can make use of the simplified authorization mechanism – and we periodically wipe all information stored there.
+Proceed with User Authorization flows in Production DCs only after you make sure everything works correctly on Test DCs first to avoid reaching flood limits.
+++To help you with working on production DCs, logins with the same phone number with which the
+api_id
was registered have more generous flood limits.
As a result of authorization, the client key, auth_key_id, becomes associated with the user, and each subsequent API call with this key will be executed with that user’s identity. The authorization method itself returns the relevant user. It is best to immediately store the User ID locally in a binding with the key.
+Only a small portion of the API methods are available to unauthorized users:
+Other methods will result in an error: 401 UNAUTHORIZED.
Working with bots, using the MTProto API.
+See here for more info about bots.
+Please note that you can also use the simplified HTTP Bot API to use bots, see here for more info ».
+---functions---
+
+auth.importBotAuthorization#67a3ff2c flags:int api_id:int api_hash:string bot_auth_token:string = auth.Authorization;
+In order to login as a bot, instead of using the standard login code flow, simply provide the bot token generated by @botfather.
+You must still provide your API ID, as per user logins.
After successful authorization, you will be able to use most MTProto API methods, just as any normal user.
+Methods that can be called by bots will have a Bots can use this method notice.
Bots offer a set of commands that can be used by users in private, or in a chat.
+Users can interact with your bot via buttons or even inline buttons, straight from inline messages in any chat.
+Users can interact with your bot via inline queries, straight from the text input field in any chat.
+Bots can offer users HTML5 games to play solo or to compete against each other in groups and one-on-one chats; how to work with games in the MTProto API.
Users can interact with your bot via buttons or even inline buttons, straight from inline messages in any chat.
+This article describes the full button flow, using the MTProto API.
For a simplified description using the HTTP bot API, see here ».
+keyboardButton#a2fa4880 text:string = KeyboardButton;
+keyboardButtonUrl#258aff05 text:string url:string = KeyboardButton;
+keyboardButtonCallback#35bbdb6b flags:# requires_password:flags.0?true text:string data:bytes = KeyboardButton;
+keyboardButtonRequestPhone#b16a6c29 text:string = KeyboardButton;
+keyboardButtonRequestGeoLocation#fc796b3f text:string = KeyboardButton;
+keyboardButtonSwitchInline#568a748 flags:# same_peer:flags.0?true text:string query:string = KeyboardButton;
+keyboardButtonGame#50f41ccf text:string = KeyboardButton;
+keyboardButtonBuy#afd93fbb text:string = KeyboardButton;
+keyboardButtonUrlAuth#10b78d29 flags:# text:string fwd_text:flags.0?string url:string button_id:int = KeyboardButton;
+inputKeyboardButtonUrlAuth#d02e7fd4 flags:# request_write_access:flags.0?true text:string fwd_text:flags.1?string url:string bot:InputUser = KeyboardButton;
+keyboardButtonRequestPoll#bbc7515d flags:# quiz:flags.0?Bool text:string = KeyboardButton;
+
+keyboardButtonRow#77608b83 buttons:Vector<KeyboardButton> = KeyboardButtonRow;
+
+replyKeyboardHide#a03e5b85 flags:# selective:flags.2?true = ReplyMarkup;
+replyKeyboardForceReply#f4108aa0 flags:# single_use:flags.1?true selective:flags.2?true = ReplyMarkup;
+replyKeyboardMarkup#3502758c flags:# resize:flags.0?true single_use:flags.1?true selective:flags.2?true rows:Vector<KeyboardButtonRow> = ReplyMarkup;
+replyInlineMarkup#48a30254 rows:Vector<KeyboardButtonRow> = ReplyMarkup;
+
+message#58ae39c9 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> = Message;
+
+---functions---
+
+messages.sendMessage#520c3870 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
+Bots can attach a ReplyMarkup constructor to outgoing messages, to attach an inline keyboard or a custom reply keyboard:
+keyboardButton#a2fa4880 text:string = KeyboardButton;
+keyboardButtonUrl#258aff05 text:string url:string = KeyboardButton;
+keyboardButtonCallback#35bbdb6b flags:# requires_password:flags.0?true text:string data:bytes = KeyboardButton;
+keyboardButtonRequestPhone#b16a6c29 text:string = KeyboardButton;
+keyboardButtonRequestGeoLocation#fc796b3f text:string = KeyboardButton;
+keyboardButtonRequestPoll#bbc7515d flags:# quiz:flags.0?Bool text:string = KeyboardButton;
+keyboardButtonSwitchInline#568a748 flags:# same_peer:flags.0?true text:string query:string = KeyboardButton;
+keyboardButtonGame#50f41ccf text:string = KeyboardButton;
+keyboardButtonBuy#afd93fbb text:string = KeyboardButton;
+keyboardButtonUrlAuth#10b78d29 flags:# text:string fwd_text:flags.0?string url:string button_id:int = KeyboardButton;
+
+// Used by bots to send a keyboardButtonUrlAuth
+inputKeyboardButtonUrlAuth#d02e7fd4 flags:# request_write_access:flags.0?true text:string fwd_text:flags.1?string url:string bot:InputUser = KeyboardButton;
+
+keyboardButtonRow#77608b83 buttons:Vector<KeyboardButton> = KeyboardButtonRow;
+Both reply and inline keyboards are composed of a vector of rows, each row containing a vector of buttons, for each column.
+Each row can have a different number of columns, and user clients should properly handle clicking buttons of every type.
Buttons available only in reply keyboards:
+quiz
flag), replying to the message that attached the reply keyboardButtons available only in inline keyboards:
+keyboardButtonSwitchInline.same_peer
is set, insert the bot's username and keyboardButtonSwitchInline.query
in the current chat's input field, triggering an inline query.keyboardButtonSwitchInline.same_peer
is not set, prompt the user to select one of their chats, and then insert the bot's username and keyboardButtonSwitchInline.query
in the current chat's input field, triggering an inline query.keyboardButtonCallback buttons can be used to send the specified data
payload back to the bot, when they are clicked.
+Additionally, a bot can verify a user's identity by requiring they verify their 2FA password with SRP.
keyboardButtonGame#50f41ccf text:string = KeyboardButton;
+keyboardButtonCallback#35bbdb6b flags:# requires_password:flags.0?true text:string data:bytes = KeyboardButton;
+
+messages.botCallbackAnswer#36585ea4 flags:# alert:flags.1?true has_url:flags.3?true native_ui:flags.4?true message:flags.0?string url:flags.2?string cache_time:int = messages.BotCallbackAnswer;
+
+---functions---
+
+messages.getBotCallbackAnswer#9342ca07 flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes password:flags.2?InputCheckPasswordSRP = messages.BotCallbackAnswer;
+When the user clicks on a keyboardButtonCallback in a message sent by a bot, or generated by an inline query, messages.getBotCallbackAnswer should be called, passing the peer and ID of the message.
+The same should happen when clicking on keyboardButtonGame buttons, with the difference that the game
flag must be set instead of the data
parameter.
Make sure to properly handle bot timeouts in the form of BOT_RESPONSE_TIMEOUT
RPC errors, as the bot may be offline and unable to reply.
The returned messages.botCallbackAnswer constructor contains:
+message
if specified, a message that should be shown in a non-blocking toast notificationalert
indicates whether the message
should be shown as a dismissable prompt, instead of a simple toast notificationhas_url
Whether an URL is presenturl
if specified, the client should open the URL, without showing a confirmation prompt. t.me/bot?start=aaa
)native_ui
whether to open game URLs in a WebView or in native UI.cache_time
specifies for how long should this answer be cached, client-sideIf the requires_password
flag is set, the SRP 2FA payload must also be generated and attached to the query, to verify the identity of the user.
Note that the bot will NOT be able to access your password or the SRP payload.
+The SRP payload will be processed exclusively on the Telegram's servers, simply returning an RPC error without passing the query to the bot if the verification fails.
+This is just a way of verifying the identity of the user, mainly used by the official @botfather bot to allow securely transferring the ownership of a bot to another user.
updateBotCallbackQuery#e73547e1 flags:# query_id:long user_id:int peer:Peer msg_id:int chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update;
+
+updateInlineBotCallbackQuery#f9d27a5a flags:# query_id:long user_id:int msg_id:InputBotInlineMessageID chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update;
+
+---functions---
+
+messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool;
+After the user invokes messages.getBotCallbackAnswer, an updateBotCallbackQuery or updateInlineBotCallbackQuery is generated and sent to the bot, depending on whether the query originated from a normal message sent by the bot, or from a message sent from an inline query.
+Either way, bots must reply to the query as quickly as possible using messages.setBotCallbackAnswer:
+query_id
is the query_id
from messages.getBotCallbackAnswer, an updateBotCallbackQuery or updateInlineBotCallbackQuerymessage
, alert
, url
can contain messages and URLs to trigger different client behaviour, as specified above »cache_time
indicates the maximum amount of time in seconds that the result of the callback query may be cached by the client. If a game_short_name
is present in the update, the bot should return the URL of the game with the specified name.
+The messages.setBotCallbackAnswer method must be called anyway, even if no message
or url
is returned, to avoid timeouts on the client.
Bots offer a set of commands that can be used by users in private, or in a chat.
+For a simplified description using the HTTP bot API, see here ».
+botCommand#c27ac8c7 command:string description:string = BotCommand;
+
+botInfo#98e81d3a user_id:int description:string commands:Vector<BotCommand> = BotInfo;
+
+channelFull#f0e6672a flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int = ChatFull;
+userFull#edf17c12 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int = UserFull;
+
+user#938458c1 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
+The botInfo constructors contained in the userFull, chatFull, channelFull contain a list of commands, and for groups, the ID and a description of each bot.
+In graphical clients, when users begin a message with a /
, a list of commands supported by all bots present in the current chat should be shown; the same should be done for one-to-one chats with the bot itself.
If the command list of a bot changes, the bot_info_version
contained in the user constructor received in updates will change; this indicates that the client should refetch full bot information using users.getFullUser.
botCommand#c27ac8c7 command:string description:string = BotCommand;
+
+---functions---
+
+bots.setBotCommands#805d46f6 commands:Vector<BotCommand> = Bool;
+The command list can be changed by the owner of the bot through @botfather, but bots can also change their own command list by invoking bots.setBotCommands.
Bots can offer users HTML5 games to play solo or to compete against each other in groups and one-on-one chats.
+inputUserSelf#f7c1b13f = InputUser;
+
+inputGameID#32c3e77 id:long access_hash:long = InputGame;
+inputGameShortName#c331e80a bot_id:InputUser short_name:string = InputGame;
+
+inputMediaGame#d33f43f3 id:InputGame = InputMedia;
+
+game#bdf9653b flags:# id:long access_hash:long short_name:string title:string description:string photo:Photo document:flags.0?Document = Game;
+messageMediaGame#fdb19008 game:Game = MessageMedia;
+
+---functions---
+
+messages.sendMedia#3491eba9 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
+Bots can directly send a game using messages.sendMedia, providing:
+inputGameShortName.short_name
inputGameShortName.bot_id
The sent message will contain a messageMediaGame with a game, that can then be used by users to forward the game using sendMedia with inputGameID.
+Games are started clicking on the button, which triggers an callback query that returns the game URL, for more info see here ».
+The game should then be opened in a WebView or in native UI (specified by the native_ui
flag), exposing the appropriate HTML5 APIs in order to receive various JS game events directly from the code of the game, as described here ».
---functions---
+
+messages.setGameScore#8ef8ecc0 flags:# edit_message:flags.0?true force:flags.1?true peer:InputPeer id:int user_id:InputUser score:int = Updates;
+messages.setInlineGameScore#15ad9f64 flags:# edit_message:flags.0?true force:flags.1?true id:InputBotInlineMessageID user_id:InputUser score:int = Bool;
+Games are supposed to report back to the MTProto API every time the user looses a game with a new highscore.
+Since games run in the browser, they cannot directly report data to the API using the bot token, which must be kept secret.
+Instead, they should send highscores to an intermediate server, that will then report scores using messages.setGameScore or messages.setInlineGameScore, depending on the source of the game.
edit_message
flag should be set if the game message should be automatically edited to include the current scoreboardforce
flag should be set if the high score is allowed to decrease. This can be useful when fixing mistakes or banning cheaters.messageActionGameScore#92a72876 game_id:long score:int = MessageAction;
+
+messageService#286fa604 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction = Message;
+
+highScore#58fffcd0 pos:int user_id:int score:int = HighScore;
+
+messages.highScores#9a3bfd99 scores:Vector<HighScore> users:Vector<User> = messages.HighScores;
+
+---functions---
+
+messages.getGameHighScores#e822649d peer:InputPeer id:int user_id:InputUser = messages.HighScores;
+messages.getInlineGameHighScores#f635e1b id:InputBotInlineMessageID user_id:InputUser = messages.HighScores;
+Every time a highscore is reached, and the edit_message
flag is set when reporting the score, a messageService with a messageActionGameScore is generated, indicating that the highscore of a certain game has changed, thanks to a certain user_id
.
+Our own current position of the scoreboard is also reported as pos
.
When receiving such an update, graphical clients should refetch the scoreboard using messages.getGameHighScores or messages.getInlineGameHighScores.
Users can interact with your bot via inline queries, straight from the text input field in any chat.
+This article describes the full inline bot flow, using the MTProto API.
For a simplified description using the HTTP bot API, see here ».
+messages.botResults#947ca848 flags:# gallery:flags.0?true query_id:long next_offset:flags.1?string switch_pm:flags.2?InlineBotSwitchPM results:Vector<BotInlineResult> cache_time:int users:Vector<User> = messages.BotResults;
+
+---functions---
+
+messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
+When, in a graphical client, the user starts a message with an @
, clients should:
bot
- The bot peerpeer
- The chat where the user made the querygeo_point
- The user's current geolocation, if the bot requires location-based inline results (the bot_inline_geo
flag of the bot's user constructor will be set)query
- What the user typed after the bot's usernameoffset
- If the user scrolls past the first len(results)
results, and next_offset
field is set, the inline query should be repeated with this offset.inputBotInlineMessageMediaAuto#3380c786 flags:# message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
+inputBotInlineMessageText#3dcd7a87 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
+inputBotInlineMessageMediaGeo#96929a85 flags:# geo_point:InputGeoPoint heading:flags.0?int period:flags.1?int proximity_notification_radius:flags.3?int reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
+inputBotInlineMessageMediaVenue#417bbf11 flags:# geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
+inputBotInlineMessageMediaContact#a6edbffd flags:# phone_number:string first_name:string last_name:string vcard:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
+inputBotInlineMessageGame#4b425864 flags:# reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
+
+inputBotInlineResult#88bf9319 flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?InputWebDocument content:flags.5?InputWebDocument send_message:InputBotInlineMessage = InputBotInlineResult;
+inputBotInlineResultPhoto#a8d864a7 id:string type:string photo:InputPhoto send_message:InputBotInlineMessage = InputBotInlineResult;
+inputBotInlineResultDocument#fff8fdc4 flags:# id:string type:string title:flags.1?string description:flags.2?string document:InputDocument send_message:InputBotInlineMessage = InputBotInlineResult;
+inputBotInlineResultGame#4fa417f2 id:string short_name:string send_message:InputBotInlineMessage = InputBotInlineResult;
+
+updateBotInlineQuery#54826690 flags:# query_id:long user_id:int query:string geo:flags.0?GeoPoint offset:string = Update;
+
+---functions---
+
+messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool;
+Bots can answer to incoming updateBotInlineQuery updates using messages.setInlineBotResults.
+Just like its bot API counterpart, the method can be used to send a set of inline results to the user; see the constructor page for more info on the MTProto method parameters ».
In general, the method accepts a vector of InputBotInlineResult constructors, that when chosen, generates a message with optionally attached media, and even inline buttons.
+botInlineMessageMediaAuto#764cf810 flags:# message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
+botInlineMessageText#8c7f65e2 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
+botInlineMessageMediaGeo#51846fd flags:# geo:GeoPoint heading:flags.0?int period:flags.1?int proximity_notification_radius:flags.3?int reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
+botInlineMessageMediaVenue#8a86659c flags:# geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
+botInlineMessageMediaContact#18d1cdc2 flags:# phone_number:string first_name:string last_name:string vcard:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
+
+botInlineResult#11965f3a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?WebDocument content:flags.5?WebDocument send_message:BotInlineMessage = BotInlineResult;
+botInlineMediaResult#17db940b flags:# id:string type:string photo:flags.0?Photo document:flags.1?Document title:flags.2?string description:flags.3?string send_message:BotInlineMessage = BotInlineResult;
+
+messages.botResults#947ca848 flags:# gallery:flags.0?true query_id:long next_offset:flags.1?string switch_pm:flags.2?InlineBotSwitchPM results:Vector<BotInlineResult> cache_time:int users:Vector<User> = messages.BotResults;
+
+---functions---
+
+messages.sendInlineBotResult#220815b0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string schedule_date:flags.10?int = Updates;
+The user client should display the results
obtained during querying in a list, making sure to handle eventual bot timeouts in the form of a BOT_RESPONSE_TIMEOUT
RPC error, by simply not displaying anything.
If the user then chooses a specific BotInlineResult
, the messages.sendInlineBotResult method should be invoked, passing:
query_id
from messages.botResults
id
of the chosen resultpeer
where to send the chosen resultThe resulting message will have the via_bot_id
field set, to indicate that the result was generated by the bot that generated the inline result.
+Graphical clients should display the bot @username
in the header of the message, allowing the user to click on it, automatically starting an inline query by inserting @username
in the text bar.
inputBotInlineMessageID#890c3d89 dc_id:int id:long access_hash:long = InputBotInlineMessageID;
+
+updateBotInlineSend#e48f964 flags:# user_id:int query:string geo:flags.0?GeoPoint id:string msg_id:flags.1?InputBotInlineMessageID = Update;
+If feedback collection is enabled, the bot may receive an updateBotInlineSend when the user chooses and sends an inline result.
+Even if the probability setting is set to 100%, not all inline results may be reported due to caching (see the cache_time
parameter in Answering a callback query).
+Feedback collection can also create load issues for popular bots, so adjust the probability setting to a lower value in such cases.
Either way, feedback collection should only be used for statistical purposes rather than functional.
+The updateBotInlineSend will contain:
+id
- The ID of the chosen resultmsg_id
- The ID of the sent inline messageuser_id
- The ID of the user that chose the resultquery
- The query string that was used to obtain the resultgeo
- For bots requiring location-based inline results, the user's locationupdateInlineBotCallbackQuery#f9d27a5a flags:# query_id:long user_id:int msg_id:InputBotInlineMessageID chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update;
+
+inputBotInlineMessageID#890c3d89 dc_id:int id:long access_hash:long = InputBotInlineMessageID;
+
+---functions---
+
+messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Bool;
+Sent inline messages can be edited by the bot, for example in response to a button press callback query.
+Simply pass the inputBotInlineMessageID specified in the updateInlineBotCallbackQuery to messages.editInlineBotMessage along with the new message, making sure to send the query to the datacenter specified in inputBotInlineMessageID.dc_id
.
Channels are a tool for broadcasting your messages to large audiences. They can have an unlimited number of subscribers, they can be public with a permanent URL and each post in a channel has its own view counter. +Technically, they are represented by channel constructors.
+Supergroups are a powerful tool for building communities and can support up to 200,000 members each.
+Technically, supergroups are actually channels: they are represented by channel constructors, with the megagroup
flag set to true.
Channels and supergroup can be created using the channels.createChannel method, by setting the appropriate broadcast
or megagroup
flags.
+Supergroups can also be assigned a geo_point
to become geochats.
In previous versions of telegram, only normal groups (represented by chat constructors) could be created using messages.createChat: these groups have fewer features, and can only have 200 members at max.
+To upgrade a legacy group to a supergroup, messages.migrateChat can be used.
+The chats
field of the result will have two objects:
migrated_to
field, indicating the address of the new supergroupWhen getting full info about the migrated channel, the channelFull object will have migrated_from_chat_id
and migrated_from_max_id
fields indicating the original ID of the chat, and the message ID in the original chat at which the group was migrated.
All users of the chat will receive an updateNewMessage from the old chat with a messageService containing a messageActionChatMigrateTo constructor.
+All new messages have to be sent to the new supergroup.
+When working with migrated groups clients need to handle loading of the message history (as well as search results et cetera) from both the legacy group and the new supergroup. This is done by merging the two messages lists (requested with different Peer values) client side.
+Channels, legacy groups and supergroups allow setting granular permissions both for admins and specific users; channels, supergroups and legacy groups also allow setting global granular permissions for users.
+For more info on how to set and modify rights, see here ».
+Telegram allows pinning multiple messages on top in a chat, group, supergroup or channel.
+See here » for more info on pinning and unpinning messages.
+Groups can be associated to a channel as a discussion group, to allow users to discuss about posts.
+For more info on how to set a discussion group in channel, see here »
+Both supergroups and channels offer a so-called admin log, a log of recent relevant supergroup and channel actions, like the modification of group/channel settings or information on behalf of an admin, user kicks and bans, and more.
+See here » for more info.
The MTProto API has multiple configuration parameters that can be fetched with the appropriate methods.
+config#330b4067 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true pfs_enabled:flags.13?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int pinned_infolder_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int base_lang_pack_version:flags.2?int = Config;
+nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
+
+---functions---
+
+help.getConfig#c4f9186b = Config;
+help.getNearestDc#1fb33026 = NearestDc;
+The huge config constructor contains lots of useful information, from chat and message size limitations, to privacy settings, online status refresh interval and timeout, VoIP configuration, default inline bot usernames for GIF, image and venue lookup, and lots of other global and user-specific information, check out the constructor page for more information.
+jsonObjectValue#c0de1bd9 key:string value:JSONValue = JSONObjectValue;
+
+jsonNull#3f6d7b68 = JSONValue;
+jsonBool#c7345e6a value:Bool = JSONValue;
+jsonNumber#2be0dfa4 value:double = JSONValue;
+jsonString#b71e767a value:string = JSONValue;
+jsonArray#f7444763 value:Vector<JSONValue> = JSONValue;
+jsonObject#99c1d49d value:Vector<JSONObjectValue> = JSONValue;
+
+---functions---
+
+help.getAppConfig#98914110 = JSONValue;
+The help.getAppConfig method returns a JSON object containing rapidly evolving, client-specific configuration parameters.
+While help.getConfig returns MTProto-specific configuration with information about server-side limitations and other MTProto-related information, help.getAppConfig returns configuration parameters useful for graphical Telegram clients.
Typical fields included in the resulting JSON object are:
+emojies_animated_zoom
- Animated emojis and animated dice should be scaled by this factor before being shown to the user (float)keep_alive_service
- Whether app clients should start a keepalive service to keep the app running and fetch updates even when the app is closed (boolean)background_connection
- Whether app clients should start a background TCP connection for MTProto update fetching (boolean)emojies_send_dice
- A list of supported animated dice stickers (array of strings).emojies_send_dice_success
- For animated dice emojis other than the basic , indicates the winning dice value and the final frame of the animated sticker, at which to show the fireworks (object with emoji keys and object values, containing value
and frame_start
float values)emojies_sounds
- A map of soundbites to be played when the user clicks on the specified animated emoji; the file reference field should be base64-decoded before downloading the file (map of file IDs, with emoji string keys)gif_search_branding
- Specifies the name of the service providing GIF search through gif_search_username (string)gif_search_emojies
- Specifies a list of emojies that should be suggested as search term in a bar above the GIF search box (array of string emojis)qr_login_camera
- Whether the Settings->Devices menu should show an option to scan a QR login code (boolean)qr_login_code
- Whether the login screen should show a QR code login option, possibly as default login method (string, "disabled", "primary" or "secondary")dialog_filters_enabled
- Whether clients should show an option for managing dialog filters AKA folders (boolean)dialog_filters_tooltip
- Whether clients should actively show a tooltip, inviting the user to configure dialog filters AKA folders; typically this happens when the chat list is long enough to start getting cluttered. (boolean)Example value:
+{
+ "test": 1,
+ "emojies_animated_zoom": 0.625,
+ "keep_alive_service": true,
+ "background_connection": true,
+ "emojies_send_dice": [
+ "\ud83c\udfb2",
+ "\ud83c\udfaf",
+ "\ud83c\udfc0",
+ "\u26bd",
+ "\u26bd\ufe0f",
+ "\ud83c\udfb0"
+ ],
+ "emojies_send_dice_success": {
+ "\ud83c\udfaf": {
+ "value": 6,
+ "frame_start": 62
+ },
+ "\ud83c\udfc0": {
+ "value": 5,
+ "frame_start": 110
+ },
+ "\u26bd": {
+ "value": 5,
+ "frame_start": 110
+ },
+ "\u26bd\ufe0f": {
+ "value": 5,
+ "frame_start": 110
+ },
+ "\ud83c\udfb0": {
+ "value": 64,
+ "frame_start": 110
+ }
+ },
+ "emojies_sounds": {
+ "\ud83c\udf83": {
+ "id": "4956223179606458539",
+ "access_hash": "-2107001400913062971",
+ "file_reference_base64": "AF-4ApC7ukC0UWEPZN0TeSJURe7T"
+ },
+ "\u26b0": {
+ "id": "4956223179606458540",
+ "access_hash": "-1498869544183595185",
+ "file_reference_base64": "AF-4ApCLKMGt96WCvLm58kbqZHd3"
+ },
+ "\ud83e\udddf\u200d\u2642": {
+ "id": "4960929110848176331",
+ "access_hash": "3986395821757915468",
+ "file_reference_base64": "AF-4ApAedNln3IMEHH-SUQuH8L9g"
+ },
+ "\ud83e\udddf": {
+ "id": "4960929110848176332",
+ "access_hash": "-8929417974289765626",
+ "file_reference_base64": "AF-4ApArGURtGa2KVC-Yovh1kQoW"
+ },
+ "\ud83e\udddf\u200d\u2640": {
+ "id": "4960929110848176333",
+ "access_hash": "9161696144162881753",
+ "file_reference_base64": "AF-4ApD-eOqXvTBmcszAEkzQN615"
+ },
+ "\ud83c\udf51": {
+ "id": "4963180910661861548",
+ "access_hash": "-7431729439735063448",
+ "file_reference_base64": "AF-4ApBimvRxhcXg-iQ5Gw4Eelit"
+ },
+ "\u2764": {
+ "id": "4978826754966683841",
+ "access_hash": "3926211553285686901",
+ "file_reference_base64": "AF-4ApDBkyjgN2Tk9zJvXPhfJXPA"
+ }
+ },
+ "gif_search_branding": "tenor",
+ "gif_search_emojies": [
+ "\ud83d\udc4d",
+ "\ud83d\ude18",
+ "\ud83d\ude0d",
+ "\ud83d\ude21",
+ "\ud83e\udd73",
+ "\ud83d\ude02",
+ "\ud83d\ude2e",
+ "\ud83d\ude44",
+ "\ud83d\ude0e",
+ "\ud83d\udc4e"
+ ],
+ "qr_login_camera": true,
+ "qr_login_code": "secondary",
+ "dialog_filters_enabled": true,
+ "dialog_filters_tooltip": false
+}
+help.appUpdate#1da7158f flags:# can_not_skip:flags.0?true id:int version:string text:string entities:Vector<MessageEntity> document:flags.1?Document url:flags.2?string = help.AppUpdate;
+help.noAppUpdate#c45a6536 = help.AppUpdate;
+
+updates#74ae4240 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq:int = Updates;
+updateServiceNotification#ebe46819 flags:# popup:flags.0?true inbox_date:flags.1?int type:string message:string media:MessageMedia entities:Vector<MessageEntity> = Update;
+
+help.inviteText#18cb9f78 message:string = help.InviteText;
+
+---functions---
+
+help.getAppUpdate#522d5a7d source:string = help.AppUpdate;
+help.getAppChangelog#9010ef6f prev_app_version:string = Updates;
+
+help.getInviteText#4d392343 = help.InviteText;
+help.termsOfServiceUpdateEmpty#e3309f7f expires:int = help.TermsOfServiceUpdate;
+help.termsOfServiceUpdate#28ecf961 expires:int terms_of_service:help.TermsOfService = help.TermsOfServiceUpdate;
+
+help.termsOfService#780a0310 flags:# popup:flags.0?true id:DataJSON text:string entities:Vector<MessageEntity> min_age_confirm:flags.1?int = help.TermsOfService;
+
+auth.authorizationSignUpRequired#44747e9a flags:# terms_of_service:flags.0?help.TermsOfService = auth.Authorization;
+
+---functions---
+
+help.getTermsOfServiceUpdate#2ca51fd1 = help.TermsOfServiceUpdate;
+help.acceptTermsOfService#ee72f79a id:DataJSON = Bool;
+
+auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization;
+
+account.deleteAccount#418d4e0b reason:string = Bool;
+These methods can be used for managing consent to Telegram's Terms Of Service.
+Typically, before a user signs up by invoking auth.signUp, apps should show a pop-up (if the popup
flag of the help.termsOfService method is set), asking the user to accept Telegram's terms of service; in case of denial, the user is to be returned to the initial page of the login flow.
When signing up for the first time, the help.termsOfService is to be obtained from the auth.authorizationSignUpRequired constructor returned by the auth.signIn.
+After signing up, or when logging in as an existing user, apps are supposed to call help.getTermsOfServiceUpdate to check for any updates to the Terms of Service; this call should be repeated after expires
seconds have elapsed.
+If an update to the Terms Of Service is available, clients are supposed to show a consent popup; if accepted, clients should call help.acceptTermsOfService, providing the termsOfService id
JSON object; in case of denial, clients are to delete the account using account.deleteAccount, providing Decline ToS update
as deletion reason
.
Example implementation: android (signup), android (after login)
+user#938458c1 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
+
+help.support#17c6b5f6 phone_number:string user:User = help.Support;
+help.supportName#8c05f1c9 name:string = help.SupportName;
+
+---functions---
+
+help.getSupport#9cdf08cd = help.Support;
+help.getSupportName#d360e72c = help.SupportName;
+These methods can be used for fetching info about Telegram's support user, that users can use to get support and ask questions about the app.
+help.countryCode#4203c5ef flags:# country_code:string prefixes:flags.0?Vector<string> patterns:flags.1?Vector<string> = help.CountryCode;
+
+help.country#c3878e23 flags:# hidden:flags.0?true iso2:string default_name:string name:flags.1?string country_codes:Vector<help.CountryCode> = help.Country;
+
+help.countriesListNotModified#93cc1f32 = help.CountriesList;
+help.countriesList#87d0759e countries:Vector<help.Country> hash:int = help.CountriesList;
+
+---functions---
+help.getCountriesList#735787a8 lang_code:string hash:int = help.CountriesList;
+help.getCountriesList can be used to fetch a list of localized names for all available countries and phone code patterns for logging in.
+The phone code pattern should be used when showing the login screen, or when changing phone number: for example, a pattern value of XXX XXX XXX
with country_code
+39
indicates that the phone field for login should accept a spaced pattern like +39 123 456 789
.
+Also, the beginning of the national part of the phone number (123 456 789
) should with match one of the prefixes
, if any were returned.
The servers are divided into several data centers (hereinafter “DCs”) in different parts of the world. +A complete list of proxy access points for these DCs may be obtained using help.getConfig:
+dcOption#18b7a10d flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true id:int ip_address:string port:int secret:flags.10?bytes = DcOption;
+config#330b4067 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true pfs_enabled:flags.13?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int pinned_infolder_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int base_lang_pack_version:flags.2?int = Config;
+---functions---
+help.getConfig#c4f9186b = Config;
+In this context, this_dc is the number of the current DC, dc_options is a list of all DCs available at the moment, each of which has an id, ip, and port for establishing a connection. Please note that ip and port may change frequently, based on proxy server load and the user's current location.
+To optimize client communication with the API, each client must use the connection to the closest access point for its main queries (sending messages, getting contacts, etc.). Therefore, knowing how to select a DC is required before communicating with the API.
+The auth.sendCode method is the basic entry point when registering a new user or authorizing an existing user. 95% of all redirection cases to a different DC will occure when invoking this method.
+The client does not yet know which DC it will be associated with; therefore, it establishes an encrypted connection to a random address and sends its query to that address. +Having received a phone_number from a client, we can find out whether or not it is registered in the system. If it is, then, if necessary, instead of sending a text message, we request that it establish a connection with a different DC first (PHONE_MIGRATE_X error). +If we do not yet have a user with this number, we examine its IP-address. We can use it to identify the closest DC. Again, if necessary, we redirect the user to a different DC (NETWORK_MIGRATE_X error).
+There are reserved phone number prefixes to test the correctness of the application’s handling of redirects between DCs. Read more in User Authorization article.
+A file saved by a user with upload.saveFilePart will be available for direct download only from the DC where the query was executed. That is why each file has a dc_id parameter:
+document#1e87342b flags:# id:long access_hash:long file_reference:bytes date:int mime_type:string size:int thumbs:flags.0?Vector<PhotoSize> video_thumbs:flags.1?Vector<VideoSize> dc_id:int attributes:Vector<DocumentAttribute> = Document;
+photo#fb197a65 flags:# has_stickers:flags.0?true id:long access_hash:long file_reference:bytes date:int sizes:Vector<PhotoSize> video_sizes:flags.1?Vector<VideoSize> dc_id:int = Photo;
+
+encryptedFile#4a70994c id:long access_hash:long size:int dc_id:int key_fingerprint:int = EncryptedFile;
+
+userProfilePhoto#69d3ab26 flags:# has_video:flags.0?true photo_id:long photo_small:FileLocation photo_big:FileLocation dc_id:int = UserProfilePhoto;
+chatPhoto#d20b9f3c flags:# has_video:flags.0?true photo_small:FileLocation photo_big:FileLocation dc_id:int = ChatPhoto;
+To download the file, an encrypted connection to DC dc_id must be established and used to execute the upload.getFile query. +If an attempt is made to download the file over a wrong connection, the FILE_MIGRATE_X error will be returned.
+Please note that encryption keys are not copied between DCs; therefore, the process of establishing an encrypted connection is started from the very beginning for each new DC. An issued auth_key can be associated with the current authorized user by using an authorization transfer.
+During the process of working with the API, user information is accumulated in the DC with which the user is associated. This is the reason a user cannot be associated with a different DC by means of the client. However, in the future, during prolonged communication from an unusual location, we may decide that the user’s data must be moved to a different DC. After some time, the data will be copied and the association will be updated. Once this happens, when executing any query transmitted to the old DC, the API will return the USER_MIGRATE_X error. The client will then have to establish a connection with the new DC and repeat the query.
+The following methods can be used to eliminate the need for users to enter the code from a text message every time:
+auth.exportedAuthorization#df969c2d id:int bytes:bytes = auth.ExportedAuthorization;
+auth.authorization#cd050916 flags:# tmp_sessions:flags.0?int user:User = auth.Authorization;
+---functions---
+auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization;
+auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization;
+auth.exportAuthorization must be executed in the current DC (the DC with which a connection has already been established), passing in dc_id as the value for the new DC. The method should return the user identifier and a long string of random data. An import operation can be performed at the new DC by sending it what was received. Queries requiring authorization can then be successfully executed in the new DC.
Telegram supports sending animated dice emojis.
+This is implemented by using the dice constructors:
inputMediaDice#e66fbf7b emoticon:string = InputMedia;
+messageMediaDice#3f7ee58b value:int emoticon:string = MessageMedia;
+
+inputStickerSetDice#e67f520e emoticon:string = InputStickerSet;
+
+messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet;
+
+---functions---
+
+messages.sendMedia#3491eba9 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
+
+messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet;
+
+help.getAppConfig#98914110 = JSONValue;
+On startup, clients should fetch app configuration using help.getAppConfig.
+Then, for each dice emoji contained in the emojies_send_dice
field, clients should fetch the dice emoji stickerset by calling the messages.getStickerSet method, providing the properly populated inputStickerSetDice to the stickerset
field.
+The returned stickerset will contain a set of animated stickers, one for each of the dice outcomes, plus a first looping sticker that should be shown as preview to the user before actually sending the dice.
If a user attempts to send a single emoji from the ones specified in emojies_send_dice
, the dice should be sent using messages.sendMedia, providing the dice emoji to the emoticon
field.
Incoming dice stickers will be received as a messageMediaDice constructor, along with a randomly generated server-side value, ranging from 1 to the maximum allowed value for this type of dice.
+Clients should display the correct dice animated sticker for the specified value: since dice values start from 1, and the first animated sticker in dice stickerset is the preview, value
can be used to directly index the documents
sticker array from the animated stickerset.
The emojies_send_dice_success
configuration parameter contains more info about dice emojis other than the basic :
"emojies_send_dice_success": {
+ "\ud83c\udfaf": {
+ "value": 6,
+ "frame_start": 62
+ },
+ "\ud83c\udfc0": {
+ "value": 5,
+ "frame_start": 110
+ }
+ }
+For each of the dice emojis, a maximum "winning" value is specified, along with the frame number at which to show the fireworks .
+Please note that dice animated stickers should loop only once, right after being sent/received for the first time; clicking on the dice sticker should bring up a popup, inviting the user to send a new dice of the same type.
Groups can be associated to a channel as a discussion group, to allow users to discuss about posts.
+boolFalse#bc799737 = Bool;
+boolTrue#997275b5 = Bool;
+
+channelFull#f0e6672a flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int = ChatFull;
+
+messages.chats#64ff9fd5 chats:Vector<Chat> = messages.Chats;
+
+---functions---
+
+channels.setDiscussionGroup#40582bb2 broadcast:InputChannel group:InputChannel = Bool;
+channels.getGroupsForDiscussion#f5dad378 = messages.Chats;
+A discussion group can be associated to a channel using channels.setDiscussionGroup.
+The discussion group can be accessed in the client by clicking on the discuss button of the channel, or by accessing the comment section of a specific post; the discussion group ID is also present in the linked_chat_id
field of the channelFull constructor.
All messages sent to the channel will also be sent to the linked group (with sender peer from_id
equal to the peer of the linked channel); those messages will also be automatically pinned in the group.
To obtain a list of admined supergroups that a channel admin can possibly associate to a channel, use channels.getGroupsForDiscussion.
+Returned legacy group chats must be first upgraded to supergroups before they can be set as a discussion group.
+Before linking a supergroup to a channel, access to the supergroup's old messages must also be enabled using channels.togglePreHistoryHidden.
To set a returned supergroup as a discussion group use channels.setDiscussionGroup.
+Schema:
+boolFalse#bc799737 = Bool;
+boolTrue#997275b5 = Bool;
+
+messages.chats#64ff9fd5 chats:Vector<Chat> = messages.Chats;
+
+---functions---
+
+channels.setDiscussionGroup#40582bb2 broadcast:InputChannel group:InputChannel = Bool;
+channels.getGroupsForDiscussion#f5dad378 = messages.Chats;
+
+channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates;
Message drafts in Telegram allow syncing the text typed into message fields between devices.
+Drafts are represented by the DraftMessage constructors. +The parameters of the peer-specific draft should be used as defaults when composing a message to be sent to a certain peer (in the case of media, the same draft should still be used as base, the message will become the caption). +If the user exits the app before sending the message, the message should be saved as a draft:
+Drafts can be saved using the messages.saveDraft method.
+New drafts are automatically sent to all devices via updateDraftMessage updates.
+Dialog objects fetched via the API also contain the draft associated with the dialog.
+Drafts can be cleared by setting the clear_draft
flag when sending messages or media using messages.sendMessage, messages.sendMedia, messages.sendMultiMedia, messages.sendInlineBotResult and similar or manually by passing empty values to messages.saveDraft.
++This article on MTProto's End-to-End encryption is meant for advanced users. +If you want to learn more about Secret Chats from a less intimidating source, kindly see our general FAQ.
+
++Note that as of version 4.6, major Telegram clients are using MTProto 2.0. +MTProto v.1.0 is deprecated and is currently being phased out.
+
Secret Chats are one-on-one chats wherein messages are encrypted with a key held only by the chat’s participants. Note that the schema for these end-to-end encrypted Secret Chats is different from what is used for cloud chats:
+ +This article describes the end-to-end encryption layer in the MTProto protocol version 2.0. +The principal differences from version 1.0 (described here for reference) are as follows:
+See also: MTProto 2.0: Cloud Chats, server-client encryption
+Keys are generated using the Diffie-Hellman protocol.
+Let us consider the following scenario: User A would like to initiate end-to-end encrypted communication with User B.
+User A executes messages.getDhConfig to obtain the Diffie-Hellman parameters: a prime p, and a high order element g.
+Executing this method before each new key generation procedure is of vital importance. It makes sense to cache the values of the parameters together with the version in order to avoid having to receive all of the values every time. If the version stored on the client is still up-to-date, the server will return the constructor messages.dhConfigNotModified.
+Client is expected to check whether p is a safe 2048-bit prime (meaning that both p and (p-1)/2 are prime, and that 2^2047 < p < 2^2048), and that g generates a cyclic subgroup of prime order (p-1)/2, i.e. is a quadratic residue mod p. Since g is always equal to 2, 3, 4, 5, 6 or 7, this is easily done using quadratic reciprocity law, yielding a simple condition on p mod 4g -- namely, p mod 8 = 7 for g = 2; p mod 3 = 2 for g = 3; no extra condition for g = 4; p mod 5 = 1 or 4 for g = 5; p mod 24 = 19 or 23 for g = 6; and p mod 7 = 3, 5 or 6 for g = 7. After g and p have been checked by the client, it makes sense to cache the result, so as to avoid repeating lengthy computations in future. This cache might be shared with one used for Authorization Key generation.
+If the client has an inadequate random number generator, it makes sense to pass the random_length parameter (random_length> 0) so the server generates its own random sequence random of the appropriate length.
+Important: using the server’s random sequence in its raw form may be unsafe. It must be combined with a client sequence, for example, by generating a client random number of the same length (client_random) and using final_random := random XOR client_random
.
Client A computes a 2048-bit number a (using sufficient entropy or the server’s random; see above) and executes messages.requestEncryption after passing in g_a := pow(g, a) mod dh_prime
.
User B receives the update updateEncryption for all associated authorization keys (all authorized devices) with the chat constructor encryptedChatRequested. The user must be shown basic information about User A and must be prompted to accept or reject the request.
+Both clients are to check that g, g_a and g_b are greater than one and smaller than p-1. We recommend checking that g_a and g_b are between 2^{2048-64} and p - 2^{2048-64} as well.
+After User B confirms the creation of a secret chat with A in the client interface, Client B also receives up-to-date configuration parameters for the Diffie-Hellman method. Thereafter, it generates a random 2048-bit number, b, using rules similar to those for a.
+Having received g_a from the update with encryptedChatRequested, it can immediately generate the final shared key: key = (pow(g_a, b) mod dh_prime)
. If key length < 256 bytes, add several leading zero bytes as padding — so that the key is exactly 256 bytes long. Its fingerprint, key_fingerprint, is equal to the 64 last bits of SHA1 (key).
Note 1: in this particular case SHA1 is used here even for MTProto 2.0 secret chats.
+Note 2: this fingerprint is used as a sanity check for the key exchange procedure to detect bugs when developing client software — it is not connected to the key visualization used on the clients as means of external authentication in secret chats. Key visualizations on the clients are generated using the first 128 bits of SHA1(intial key) followed by the first 160 bits of SHA256(key used when secret chat was updated to layer 46).
+Client B executes messages.acceptEncryption after passing it g_b := pow(g, b) mod dh_prime
and key_fingerprint.
For all of Client B’s authorized devices, except the current one, updateEncryption updates are sent with the constructor encryptedChatDiscarded. Thereafter, the only device that will be able to access the secret chat is Device B, which made the call to messages.acceptEncryption.
+User A will be sent an updateEncryption update with the constructor encryptedChat, for the authorization key that initiated the chat.
+With g_b from the update, Client A can also compute the shared key key = (pow(g_b, a) mod dh_prime)
. If key length < 256 bytes, add several leading zero bytes as padding — so that the key is exactly 256 bytes long. If the fingerprint for the received key is identical to the one that was passed to encryptedChat, incoming messages can be sent and processed. Otherwise, messages.discardEncryption must be executed and the user notified.
In order to keep past communications safe, official Telegram clients will initiate re-keying once a key has been used to decrypt and encrypt more than 100 messages, or has been in use for more than one week, provided the key has been used to encrypt at least one message. Old keys are then securely discarded and cannot be reconstructed, even with access to the new keys currently in use.
+++The re-keying protocol is further described in this article: Perfect Forward Secrecy in Secret Chats.
+
Please note that your client must support Forward Secrecy in Secret Chats to be compatible with official Telegram clients.
+A TL object of type DecryptedMessage is created and contains the message in plain text. For backward compatibility, the object must be wrapped in the constructor decryptedMessageLayer with an indication of the supported layer (starting with 46).
+++The TL-Schema for the contents of end-to-end encrypted messages is available here »
+
The resulting construct is serialized as an array of bytes using generic TL rules. The resulting array is prepended by 4 bytes containing the array length not counting these 4 bytes.
+The byte array is padded with 12 to 1024 random padding bytes to make its length divisible by 16 bytes. (In the older MTProto 1.0 encryption, only 0 to 15 padding bytes were used.)
+Message key, msg_key, is computed as the 128 middle bits of the SHA256 of the data obtained in the previous step, prepended by 32 bytes from the shared key key. (For the older MTProto 1.0 encryption, msg_key was computed differently, as the 128 lower bits of SHA1 of the data obtained in the previous steps, excluding the padding bytes.)
+For MTProto 2.0, the AES key aes_key and initialization vector aes_iv are computed ( key is the shared key obtained during Key Generation ) as follows:
+For MTProto 2.0, x=0 for messages from the originator of the secret chat, x=8 for the messages in the opposite direction.
+For the obsolete MTProto 1.0, msg_key, aes_key, and aes_iv were computed differently (see this document for reference).
+Data is encrypted with a 256-bit key, aes_key, and a 256-bit initialization vector, aes-iv, using AES-256 encryption with infinite garble extension (IGE). Encryption key fingerprint key_fingerprint and the message key msg_key are added at the top of the resulting byte array.
+Encrypted data is embedded into a messages.sendEncrypted API call and passed to Telegram server for delivery to the other party of the Secret Chat.
+As soon as both parties in a secret chat are using at least Layer 73, they should only use MTProto 2.0 for all outgoing messages. Some of the first received messages may use MTProto 1.0, if a sufficiently high starting layer has not been negotiated during the creation of the secret chat. After the first message encrypted with MTProto 2.0 (or the first message with Layer 73 or higher) is received, all messages with higher sequence numbers must be encrypted with MTProto 2.0 as well.
+As long as the current layer is lower than 73, each party should try to decrypt received messages with MTProto 1.0, and if this is not successfull (msg_key does not match), try MTProto 2.0. Once the first MTProto 2.0-encrypted message arrives (or the layer is upgraded to 73), there is no need to try MTProto 1.0 decryption for any of the further messages (unless the client is still waiting for some gaps to be closed).
+The steps above are performed in reverse order. +When an encrypted message is received, you must check that msg_key is in fact equal to the 128 middle bits of the SHA256 hash of the decrypted message, prepended by 32 bytes taken from the shared key. +If the message layer is greater than the one supported by the client, the user must be notified that the client version is out of date and prompted to update.
+It is necessary to interpret all messages in their original order to protect against possible manipulations. Secret chats support a special mechanism for handling seq_no counters independently from the server.
+++Proper handling of these counters is further described in this article: Sequence numbers in Secret Chats.
+
Please note that your client must support sequence numbers in Secret Chats to be compatible with official Telegram clients.
+All files sent to secret chats are encrypted with one-time keys that are in no way related to the chat’s shared key. Before an encrypted file is sent, it is assumed that the encrypted file’s address will be attached to the outside of an encrypted message using the file parameter of the messages.sendEncryptedFile method and that the key for direct decryption will be sent in the body of the message (the key parameter in the constructors decryptedMessageMediaPhoto, decryptedMessageMediaVideo and decryptedMessageMediaFile.
+Prior to a file being sent to a secret chat, 2 random 256-bit numbers are computed which will serve as the AES key and initialization vector used to encrypt the file. AES-256 encryption with infinite garble extension (IGE) is used in like manner.
+The key fingerprint is computed as follows:
+The encrypted contents of a file are stored on the server in much the same way as those of a file in cloud chats: piece by piece using calls to upload.saveFilePart. +A subsequent call to messages.sendEncryptedFile will assign an identifier to the stored file and send the address together with the message. The recipient will receive an update with encryptedMessage, and the file parameter will contain file information.
+Incoming and outgoing encrypted files can be forwarded to other secret chats using the constructor inputEncryptedFile to avoid saving the same content on the server twice.
+Secret chats are associated with specific devices (or rather with authorization keys), not users. A conventional message box, which uses pts to describe the client’s status, is not suitable, because it is designed for long-term message storage and message access from different devices.
+An additional temporary message queue is introduced as a solution to this problem. When an update regarding a message from a secret chat is sent, a new value of qts is sent, which helps reconstruct the difference if there has been a long break in the connection or in case of loss of an update.
+As the number of events increases, the value of qts increases by 1 with each new event. The initial value may not (and will not) be equal to 0.
+The fact that events from the temporary queue have been received and stored by the client is acknowledged explicitly by a call to the messages.receivedQueue method or implicitly by a call to updates.getDifference (the value of qts passed, not the final state). All messages acknowledged as delivered by the client, as well as any messages older than 7 days, may (and will) be deleted from the server.
+Upon de-authorization, the event queue of the corresponding device will be forcibly cleared, and the value of qts will become irrelevant.
+Your client should always store the maximal layer that is known to be supported by the client on the other side of a secret chat. When the secret chat is first created, this value should be initialized to 46. This remote layer value must always be updated immediately after receiving any packet containing information of an upper layer, i.e.:
+decryptedMessageLayer
with layer>=46, ordecryptedMessageService#aa48327d
).In order to notify the remote client of your local layer, your client must send a message of the decryptedMessageActionNotifyLayer
type. This notification must be wrapped in a constructor of an appropriate layer.
There are two cases when your client must notify the remote client about its local layer:
+++Telegram supports Perfect Forward Secrecy (PFS) in Secret Chats as of Layer 20. See updating to new layers.
+
In order to keep past communications safe, official Telegram clients will initiate re-keying once a key has been used to decrypt and encrypt more than 100 messages, or has been in use for more than one week, provided the key has been used to encrypt at least one message. Old keys are then securely discarded and cannot be reconstructed, even with access to the new keys currently in use.
+Any client participating in a Secret Chat can initiate re-keying as soon as it perceives that the current key has been used for too long or for encrypting too many messages. Please note that you should never initiate a new instance of the re-keying protocol if an uncompleted instance exists, initiated by either party.
+Note: third-party developers are required to maintain the same level of security. All clients with secret chat support must be able to initiate re-keying and accept relevant service messages. See Security Guidelines.
+New keys are generated by exchanging special messages, using previously established keys for encryption. The re-keying protocol between parties A and B normally consists of four steps:
+A (re-keying initiator) generates a new value of a, subject to the same limitations as for the initial Diffie-Hellman key exchange, and sends the value of pow(g,a) to B, embedded in a decryptedMessageService:
+decryptedMessageActionRequestKey exchange_id:long g_a:string = DecryptedMessageAction;
+Note that the same Diffie--Hellman parameters (p,g) as for the initial Diffie--Hellman key exchange in this secret chat are used. They do not need to be re-transmitted explicitly.
+Upon receipt of the above service message, B checks its content, and generates a response with same exchange_id, for a newly generated value of b:
+decryptedMessageActionAcceptKey exchange_id:long g_b:string key_fingerprint:long = DecryptedMessageAction;
+At this stage, B can already compute the new key key = pow(g_a, b) mod p and its key_fingerprint (last 64 bits of its SHA-1). However, it continues using the previous key until the completion of the exchange.
+Once side B sends decryptedMessageActionAcceptKey, it cannot abort the key exchange; it must be ready to switch to the new key immediately after a decryptedMessageActionCommitKey
is received. Therefore, if side B wishes to delay the usage of new key, for example in order to fill some seq_no gaps first, it must delay the decryptedMessageActionAcceptKey
answer accordingly.
Once A receives a valid decryptedMessageActionAcceptKey
, it performs all necessary checks, and "commits" the new key by means of the following service message:
decryptedMessageActionCommitKey exchange_id:long key_fingerprint:long = DecryptedMessageAction;
+After that, A can (and must) encrypt all following messages with the new key.
+If side A wishes to delay installation of the new key, for example because there are some seq_no gaps that it wants to fill first, it must delay decryptedMessageActionCommitKey answer accordingly.
+When B receives either a decryptedMessageActionCommitKey
or a message encrypted by the new key, recognized by the value of key_fingerprint prepended to the encrypted message (it may happen that the decryptedMessageActionCommitKey
has been lost and will be re-requested later), it assumes that A has started using the new key for encryption, and does the same.
However, the previous key may be kept until there are no gaps in received messages up to the switch to the new key. Once all the gaps have been filled, the old key must be securely discarded.
+There is one exception to this rule — the SHA-1 of the original key (generated during the establishment of Secret Chat in question) is always stored, in order to show key visualizations on the clients.
+Any of the parties may abort any instance of an uncompleted re-keying protocol, unless decryptedMessageActionCommitKey
or decryptedMessageActionAcceptKey
has been already sent by the party in question. In order to abort re-keying, send
decryptedMessageActionAbortKey exchange_id:long = DecryptedMessageAction;
+This could be done, for example, if the party is already participating in a different instance of the re-keying protocol, or if the received values of g_a, g_b and other parameters do not pass security checks. In the latter case, it might be advisable to abort the Secret Chat altogether.
+Once B receives decryptedMessageActionCommitKey
, it can safely discard the previous key provided there are no gaps. However, A may only discard the previous key after a message encrypted with the new key has been received. If no ordinary messages are scheduled to be sent, a special no-op message should sent by B for this purpose:
decryptedMessageActionNoop = DecryptedMessageAction;
+It may happen that both parties concurrently initiate re-keying by sending decryptedMessageActionRequestKey
without knowing that the other party has already done so. If each side aborts re-keying because it is already participating in another instance of the protocol initiated by itself, the re-keying will never happen.
Because of this possibility, we suggest that only the instance with the smaller exchange_id is aborted, with the option to re-use its (a,g_a) for the re-keying protocol instance with the larger exchange_id (when compared as a long
, i.e. signed little-endian 64-bit integer).
In other words, if a decryptedMessageActionRequestKey
is received after A has sent its decryptedMessageActionRequestKey
, but has not yet received decryptedMessageActionAcceptKey
, the following is to be done:
decryptedMessageActionRequestKey
was larger than that in the decryptionActionRequestKey
just received, abort the newly-suggested re-keying protocol instance without sending explicit decryptedMessageActionAbortKey (the other side will do the same according to the next rule).decryptedMessageActionRequestKey
was smaller, respond to the newly-received decryptedMessageActionRequestKey
with a decryptedMessageActionAcceptKey
, and participate only in the re-keying protocol instance initiated by the other side. It is possible to re-use at this stage the value of g_a (now called g_b) that was generated for the original decryptedMessageActionRequestKey
, now abandoned, or totally new (b,g_b) can be generated. decryptedMessageActionAbortKey
. The other side will do the same.Since all re-keying instances are carried over the secure channel established when the secret chat is created, it is necessary for the user to confirm that no MITM attack had taken place during the initial exchange. The key visualization on the clients uses the first 128-bits of the SHA-1 of the original key created when the Secret Chat was first established, followed by the first 160 bits of the SHA-256 of the key in use when the secret chat was updated to layer 46 (coincides with the original key if chat was created using layer 46).
++Please note that the key_fingerprint parameter was introduced as a maintenance tool (with a misleading name) and is not related to key visualization on the clients.
+
It is necessary to interpret all messages in their original order to protect against reordering, reflection, replay, omission and other manipulations (decryptedMessageActionResend messages are the only exception to this rule, see avoiding concurrent gaps). Secret chats support a special mechanism for handling seq_no counters independently from the server. Note that any service messages in secret chats must also increment the seq_no.
+All Secret Chats messages in clients using Layer 17 or higher are wrapped in decryptedMessageLayer and have seq_no (sequence number) counters attached to them. The seq_no counters in their raw form are initialized with (out_seq_no, in_seq_no) := (0,0), and incremented strictly by 1 after any message (service or not) is sent/received and processed. They must be protected from mirroring before being sent to the remote client by transformation according to formula 2*raw_seq_no+x, where x is 0 or 1, determined by the following rule:
+in_seq_no | +out_seq_no | +|
secret chat initiated by sender | +0 | +1 | +
secret chat initiated by recipient | +1 | +0 | +
In this way the least significant bit of each seq_no field included in the message is different for incoming and outgoing messages. This is done to prevent a possible attacker from mirroring the messages. If any of the received in_seq_no or out_seq_no are not consistent in terms of parity (see table above), the client is required to immediately abort the secret chat.
+++E.g., the first message the local client sends to any secret chat will have out_seq_no of 0+x_out, the second one will have out_seq_no of 2*1+x_out, and so on, where x_out is 0 if the chat was initiated by the remote client, 1 otherwise; similarly for the received messages, but there x_in is used instead of x_out and is equal to 0 if the chat was initiated by the local client, 1 otherwise.
+
Raw sequence numbers will be used in the remaining part of this text, unless otherwise specified.
+Your client must ensure that all outgoing secret chat messages are queued on the server in the correct order. This is achieved by correctly placing them into the invokeAfterMsgs chain. Failure to do this may result in gaps on the remote client, which may in turn lead to aborted secret chats. The local client must maintain the correct sequence of in_seq_no for the remote client. To achieve this, assign in_seq_no and out_seq_no to each message at the exact moment when the message is created, and never change them in the future.
+Your client must check that it has received each message with the sequence number out_seq_no starting from 0 to some current point C. It should then expect the next message to have the sequence number out_seq_no=C+1. If the out_seq_no in the received message does not match this, the following needs to be done:
+In order to correctly handle incoming messages after a hole has been identified (when received out_seq_no>C+1), it is necessary to put received messages with the wrong seq_no into a "waiting queue" on the local client, and to re-request the missing messages using the special constructor decryptedMessageActionResend start_seq_no:int end_seq_no:int = DecryptedMessageAction;
. The sequence numbers used in this constructor must be ready for interpretation by the remote client and therefore cannot be in their raw form: you can easily get the necessary start_seq_no by adding 2 to the out_seq_no of the last message before the hole and the end_seq_no by subtracting 2 from the out_seq_no of the received message with the wrong sequence number.
Each hole normally requires only one request to resend messages — if the remote client keeps sending out of sync messages, they should be put into the queue without sending a new request. Having received the missing messages, the local client must first interpret these messages in the right order by their seq_no. Once this is done, the client can proceed to interpret messages from the queue (again, in the right seq_no order).
+Special cases:
+In order to avoid getting stuck with concurrent gaps on both sides, decryptedMessageActionResend must always be interpreted immediately upon receipt in all cases, even if its out_seq_no>=C+1. Note that each decryptedMessageActionResend must only be handled once, it must not be interpreted again when we interpret messages in the queue.
+in_seq_no of all received messages must be valid. To ensure this, perform the following checks:
+If in_seq_no
contradicts these criteria, the local client is required to immediately abort the secret chat. This could happen only in case of malicious or buggy behaviour on either server or remote client side.
In case the user on the local client has deleted a message before the server (or the remote client, if decryptedMessageActionResend is handled correctly) could acknowledge the message, for security reasons, you must:
+decryptedMessageActionDeleteMessages
with random_id equal to its own random_id;This must be done because your client doesn't know whether the remote client really received the message or not. In the case the message was already received, it will be deleted by the second message; otherwise it must arrive as a "self-delete" message to maintain the correct sequence of seq_no.
This article describes the end-to-end encryption used for Telegram voice and video calls.
+Before a call is ready, some preliminary actions have to be performed. The calling party needs to contact the party to be called and check whether it is ready to accept the call. Besides that, the parties have to negotiate the protocols to be used, learn the IP addresses of each other or of the Telegram relay servers to be used (so-called reflectors), and generate a one-time encryption key for this voice call with the aid of Diffie--Hellman key exchange. All of this is accomplished in parallel with the aid of several Telegram API methods and related notifications. This document covers details related to key generation, encryption and security.
+The Diffie-Hellman key exchange, as well as the whole protocol used to create a new voice call, is quite similar to the one used for Secret Chats. We recommend studying the linked article before proceeding.
+However, we have introduced some important changes to facilitate the key verification process. Below is the entire exchange between the two communicating parties, the Caller (A) and the Callee (B), through the Telegram servers (S).
+g_a_hash:bytes
, among others. For this call, this field is to be filled with g_a_hash, not g_a itself.g_a:bytes
and key_fingerprint:long
.key_fingerprint:long
received from the other side, as an implementation sanity check.At this point, the Diffie--Hellman key exchange is complete, and both parties have a 256-byte shared secret key key which is used to encrypt all further exchanges between A and B.
+It is of paramount importance to accept each update only once for each instance of the key generation protocol, discarding any duplicates or alternative versions of already received and processed messages (updates).
+++This document describes encryption in voice and video calls as implemented in Telegram apps with versions 7.0 and above. See this document for details on encryption used in voice calls in app versions released before August 14, 2020.
+
The Telegram Voice and Video Call Library uses an optimized version of MTProto 2.0 to send and receive packets, consisting of one or more end-to-end encrypted messages of various types (ice candidates list, video formats, remote video status, audio stream data, video stream data, message ack or empty).
+This document describes only the encryption process, leaving out encoding and network-dependent parts.
+The library starts working with:
+key
shared between the parties, as generated above.Both data transfer channels are unreliable (messages may get lost), but signaling is slower and more reliable.
+The body of a packet (decrypted_body
) consists of several messages and their respective seq
numbers concatenated together.
Each decrypted_body
is unique because no two seq
numbers of the first message can be the same. If only old messages need to be re-sent, an empty message with new unique seq
is added to the packet first.
The encryption key key
is used to compute a 128-bit msg_key
and then a 256-bit aes_key
and a 128-bit aes_iv
:
x
depends on whether the call is outgoing or incoming and on the connection type:
This allows apps to decide which packet types will be sent to which connections and work in these connections independently (with each having its own seq
counter).
The resulting aes_key
and aes_iv
are used to encrypt decrypted_body
:
The packet that gets sent consists of msg_key
and encrypted_body
:
When received, the packet gets decrypted using key
and msg_key
, after which msg_key
is checked against the relevant SHA256
substring. If the check fails, the packet must be discarded.
Each of the peers maintains its own 32-bit monotonically increasing counter for outgoing messages, seq
, starting with 1
. This seq
counter is prepended to each sent message and increased by 1
for each new message. No two seq
numbers of the first message in a packet can be the same. If only old messages need to be re-sent, an empty message with a new unique seq
is added to the packet first. When the seq
counter reaches 2^30
, the call must be aborted. Each peer stores seq
values of all the messages it has received (and processed) which are larger than max_received_seq - 64
, where max_received_seq
is the largest seq
number received so far.
If a packet is received, the first message of which has a seq
that is smaller or equal to max_received_seq - 64
or its seq
had already been received, the message is discarded. Otherwise, the seq
values of all incoming messages are memorized and max_received_seq
is adjusted. This guarantees that no two packets will be processed twice.
To verify the key, and ensure that no MITM attack is taking place, both parties concatenate the secret key key with the value g_a of the Caller ( A ), compute SHA256 and use it to generate a sequence of emoticons. More precisely, the SHA256 hash is split into four 64-bit integers; each of them is divided by the total number of emoticons used (currently 333), and the remainder is used to select specific emoticons. The specifics of the protocol guarantee that comparing four emoticons out of a set of 333 is sufficient to prevent eavesdropping (MiTM attack on DH) with a probability of 0.9999999999.
+This is because instead of the standard Diffie-Hellman key exchange which requires only two messages between the parties:
+we use a three-message modification thereof that works well when both parties are online (which also happens to be a requirement for voice calls):
+The idea here is that A commits to a specific value of a (and of g_a) without disclosing it to B. B has to choose its value of b and g_b without knowing the true value of g_a, so that it cannot try different values of b to force the final key (g_a)^b to have any specific properties (such as fixed lower 32 bits of SHA256(key)). At this point, B commits to a specific value of g_b without knowing g_a. Then A has to send its value g_a; it cannot change it even though it knows g_b now, because the other party B would accept only a value of g_a that has a hash specified in the very first message of the exchange.
+If some impostor is pretending to be either A or B and tries to perform a Man-in-the-Middle Attack on this Diffie--Hellman key exchange, the above still holds. Party A will generate a shared key with B -- or whoever pretends to be B -- without having a second chance to change its exponent a depending on the value g_b received from the other side; and the impostor will not have a chance to adapt his value of b depending on g_a, because it has to commit to a value of g_b before learning g_a. The same is valid for the key generation between the impostor and the party B.
+The use of hash commitment in the DH exchange constrains the attacker to only one guess to generate the correct visualization in their attack, which means that using just over 33 bits of entropy represented by four emoji in the visualization is enough to make a successful attack highly improbable.
++For a slightly more user-friendly explanation of the above see: How are calls authenticated?
+
++This document describes encryption in voice calls as implemented in Telegram apps with versions < 7.0. See this document for details on encryption used in voice and video calls in app versions released on August 14, 2020 and later.
+
Before a voice call is ready, some preliminary actions have to be performed. The calling party needs to contact the party to be called and check whether it is ready to accept the call. Besides that, the parties have to negotiate the protocols to be used, learn the IP addresses of each other or of the Telegram relay servers to be used (so-called reflectors), and generate a one-time encryption key for this voice call with the aid of Diffie--Hellman key exchange. All of this is accomplished in parallel with the aid of several Telegram API methods and related notifications. This document details the generation of the encryption key. Other negotiations will be eventually documented elsewhere.
+The Diffie-Hellman key exchange, as well as the whole protocol used to create a new voice call, is quite similar to the one used for Secret Chats. We recommend studying the linked article before proceeding.
+However, we have introduced some important changes to facilitate the key verification process. Below is the entire exchange between the two communicating parties, the Caller (A) and the Callee (B), through the Telegram servers (S).
+g_a_hash:bytes
, among others. For this call, this field is to be filled with g_a_hash, not g_a itself.g_a:bytes
and key_fingerprint:long
.key_fingerprint:long
received from the other side, as an implementation sanity check.At this point, the Diffie--Hellman key exchange is complete, and both parties have a 256-byte shared secret key key which is used to encrypt all further exchanges between A and B.
+It is of paramount importance to accept each update only once for each instance of the key generation protocol, discarding any duplicates or alternative versions of already received and processed messages (updates).
+Both parties A (the Caller) and B (the Callee) transform the voice information into a sequence of small chunks or packets, not more than 1 kilobyte each. This information is to be encrypted using the shared key key generated during the initial exchange, and sent to the other party, either directly (P2P) or through Telegram's relay servers (so-called reflectors). This document describes only the encryption process for each chunk, leaving out voice encoding and the network-dependent parts.
+The low-level data chunk raw_data:string
, obtained from voice encoder, is first encapsulated into one of the two constructors for the DecryptedDataBlock type, similar to DecryptedMessage used in secret chats:
decryptedDataBlock#dbf948c1 random_id:long random_bytes:string flags:# voice_call_id:flags.2?int128 in_seq_no:flags.4?int out_seq_no:flags.4?int recent_received_mask:flags.5?int proto:flags.3?int extra:flags.1?string raw_data:flags.0?string = DecryptedDataBlock;
+simpleDataBlock#cc0d0e76 random_id:long random_bytes:string raw_data:string = DecryptedDataBlock;
+Here out_seq_no
is the chunk's sequence number among all sent by this party (starting from one), in_seq_no
-- the highest known out_seq_no from the received packets. The parameter recent_received_mask
is a 32-bit mask, used to track delivery of the last 32 packets sent by the other party. The bit i is set if a packet with out_seq_no
equal to in_seq_no
-i has been received.
The higher 8 bits in flags
are reserved for use by the lower-level protocol (the one which generates and interprets raw_data
), and will never be used for future extensions of decryptedDataBlock
.
The parameters voice_call_id
and proto
are mandatory until the other side confirms reception of at least one packet by sending a packet with a non-zero in_seq_no
. After that, they become optional, and the simpleDataBlock
constructor can be used if the lower level protocol wants to.
The parameter voice_call_id
is computed from the key key
and equals the lower 128 bits of its SHA-256.
The random_bytes
string should contain at least 7 bytes of random data. The field random_id
also contains 8 random bytes, which can be used as a unique packet identifier if necessary.
Once the data is encapsulated in DecryptedDataBlock
, it is TL-serialized and encrypted with MTProto, using key
instead of auth_key
; the parameter x is to be set to 0 for messages from A to B, and to 8 for messages in the opposite direction. Encrypted data are prepended by the 128-bit msg_key
(usual for MTProto); before that, either the 128-bit voice_call_id
(if P2P is used) or the peer_tag
(if reflectors are used) is prepended. The resulting data packet is sent by UDP either directly to the other party (if P2P is possible) or to the Telegram relay servers (reflectors).
To verify the key, both parties concatenate the secret key key with the value g_a of the Caller ( A ), compute SHA256 and use it to generate a sequence of emoticons. More precisely, the SHA256 hash is split into four 64-bit integers; each of them is divided by the total number of emoticons used (currently 333), and the remainder is used to select specific emoticons. The specifics of the protocol guarantee that comparing four emoticons out of a set of 333 is sufficient to prevent eavesdropping (MiTM attack on DH) with a probability of 0.9999999999.
+This is because instead of the standard Diffie-Hellman key exchange which requires only two messages between the parties:
+we use a three-message modification thereof that works well when both parties are online (which also happens to be a requirement for voice calls):
+The idea here is that A commits to a specific value of a (and of g_a) without disclosing it to B. B has to choose its value of b and g_b without knowing the true value of g_a, so that it cannot try different values of b to force the final key (g_a)^b to have any specific properties (such as fixed lower 32 bits of SHA256(key)). At this point, B commits to a specific value of g_b without knowing g_a. Then A has to send its value g_a; it cannot change it even though it knows g_b now, because the other party B would accept only a value of g_a that has a hash specified in the very first message of the exchange.
+If some impostor is pretending to be either A or B and tries to perform a Man-in-the-Middle Attack on this Diffie--Hellman key exchange, the above still holds. Party A will generate a shared key with B -- or whoever pretends to be B -- without having a second chance to change its exponent a depending on the value g_b received from the other side; and the impostor will not have a chance to adapt his value of b depending on g_a, because it has to commit to a value of g_b before learning g_a. The same is valid for the key generation between the impostor and the party B.
+The use of hash commitment in the DH exchange constrains the attacker to only one guess to generate the correct visualization in their attack, which means that using just over 33 bits of entropy represented by four emoji in the visualization is enough to make a successful attack highly improbable.
++For a slightly more user-friendly explanation of the above see: How are calls authenticated?
+
++This document describes end-to-end encrypted Secret Chats in MTProto 1.0, its status is DEPRECATED. +For information on end-to-end encryption used in up-to-date Telegram clients, kindly see this document.
+
Secret Chats are one-on-one chats wherein messages are encrypted with a key held only by the chat’s participants. Please note that the schema for end-to-end encrypted Secret Chats is different from what is used for cloud chats:
+ +The Diffie-Hellman protocol is used for key generation. For more information, see Wikipedia.
+Let us consider the following scenario: User A would like to initiate encrypted communication with User B.
+User A executes messages.getDhConfig to obtain the Diffie-Hellman parameters: a prime p, and a high order element g.
+Executing this method before each new key generation procedure is of vital importance. It makes sense to cache the values of the parameters together with the version in order to avoid having to receive all of the values every time. If the version stored on the client is still up-to-date, the server will return the constructor messages.dhConfigNotModified.
+Client is expected to check whether p is a safe 2048-bit prime (meaning that both p and (p-1)/2 are prime, and that 2^2047 < p < 2^2048), and that g generates a cyclic subgroup of prime order (p-1)/2, i.e. is a quadratic residue mod p. Since g is always equal to 2, 3, 4, 5, 6 or 7, this is easily done using quadratic reciprocity law, yielding a simple condition on p mod 4g -- namely, p mod 8 = 7 for g = 2; p mod 3 = 2 for g = 3; no extra condition for g = 4; p mod 5 = 1 or 4 for g = 5; p mod 24 = 19 or 23 for g = 6; and p mod 7 = 3, 5 or 6 for g = 7. After g and p have been checked by the client, it makes sense to cache the result, so as to avoid repeating lengthy computations in future. This cache might be shared with one used for Authorization Key generation.
+If the client has an inadequate random number generator, it makes sense to pass the random_length parameter (random_length> 0) so the server generates its own random sequence random of the appropriate length.
+Important: using the server’s random sequence in its raw form may be unsafe. It must be combined with a client sequence, for example, by generating a client random number of the same length (client_random) and using final_random := random XOR client_random
.
Client A computes a 2048-bit number a (using sufficient entropy or the server’s random; see above) and executes messages.requestEncryption after passing in g_a := pow(g, a) mod dh_prime
.
User B receives the update updateEncryption for all associated authorization keys (all authorized devices) with the chat constructor encryptedChatRequested. The user must be shown basic information about User A and must be prompted to accept or reject the request.
+Both clients are to check that g, g_a and g_b are greater than one and smaller than p-1. We recommend checking that g_a and g_b are between 2^{2048-64} and p - 2^{2048-64} as well.
+After User B confirms the creation of a secret chat with A in the client interface, Client B also receives up-to-date configuration parameters for the Diffie-Hellman method. Thereafter, it generates a random 2048-bit number, b, using rules similar to those for a.
+Having received g_a from the update with encryptedChatRequested, it can immediately generate the final shared key: key = (pow(g_a, b) mod dh_prime)
. If key length < 256 bytes, add several leading zero bytes as padding — so that the key is exactly 256 bytes long. Its fingerprint, key_fingerprint, is equal to the 64 last bits of SHA1 (key).
Note: this fingerprint is used as a sanity check for the key exchange procedure to detect bugs while developing client software — it is not connected to the key visualization used on the clients as means of external authentication in secret chats. Key visualizations on the clients are generated using the first 128 bits of SHA1(intial key) followed by the first 160 bits of SHA256(key used when secret chat was updated to layer 46).
+Client B executes messages.acceptEncryption after passing it g_b := pow(g, b) mod dh_prime
and key_fingerprint.
For all of Client B’s authorized devices, except the current one, updateEncryption updates are sent with the constructor encryptedChatDiscarded. Thereafter, the only device that will be able to access the secret chat is Device B, which made the call to messages.acceptEncryption.
+User A will be sent an updateEncryption update with the constructor encryptedChat, for the authorization key that initiated the chat.
+With g_b from the update, Client A can also receive the shared key key = (pow(g_b, a) mod dh_prime)
. If key length < 256 bytes, add several leading zero bytes as padding — so that the key is exactly 256 bytes long. If the fingerprint for the received key is identical to the one that was passed to encryptedChat, incoming messages can be sent and processed. Otherwise, messages.discardEncryption must be executed and the user notified.
In order to keep past communications safe, official Telegram clients will initiate re-keying once a key has been used to decrypt and encrypt more than 100 messages, or has been in use for more than one week, provided the key has been used to encrypt at least one message. Old keys are then securely discarded and cannot be reconstructed, even with access to the new keys currently in use.
+++The re-keying protocol is further described in this article: Perfect Forward Secrecy in Secret Chats.
+
Please note that your client must support Forward Secrecy in Secret Chats to be compatible with official Telegram clients.
+A TL object of type DecryptedMessage is created and contains the message in plain text. For backward compatibility, the object must be wrapped in the constructor decryptedMessageLayer with an indication of the supported layer (starting with 8). +The TL-Schema for end-to-end encrypted messages contents is represented here ».
+The resulting construct is serialized as an array of bytes using generic TL rules. The resulting array is padded at the top with 4 bytes of the array length not counting these 4 bytes. +A message key, msg_key, is computed as the 128 low-order bits of the SHA1 of the data obtained in the previous step. +The byte array is padded with random data until its length is divisible by 16 bytes. +An AES key and an initialization vector are computed ( key is the shared key obtained during Key Generation; in MTProto 1.0, x = 0 ):
+Data is encrypted with a 256-bit key, aes_key, and a 256-bit initialization vector, aes-iv, using AES-256 encryption with infinite garble extension (IGE). Encryption key fingerprint key_fingerprint and the message key msg_key are added at the top of the resulting byte array.
+Encrypted data is embedded into a messages.sendEncrypted API call and passed to Telegram server for delivery to the other party of the Secret Chat.
+The steps above are performed in reverse order. +When an encrypted message is received, you must check that msg_key is in fact equal to the 128 low-order bits of the SHA1 hash of the decrypted message. +If the message layer is greater than the one supported by the client, the user must be notified that the client version is out of date and prompted to update.
+It is necessary to interpret all messages in their original order to protect against possible manipulations. Secret chats support a special mechanism for handling seq_no counters independently from the server.
+++Proper handling of these counters is further described in this article: Sequence numbers in Secret Chats.
+
Please note that your client must support sequence numbers in Secret Chats to be compatible with official Telegram clients.
+All files sent to secret chats are encrypted with one-time keys that are in no way related to the chat’s shared key. Before an encrypted file is sent, it is assumed that the encrypted file’s address will be attached to the outside of an encrypted message using the file parameter of the messages.sendEncryptedFile method and that the key for direct decryption will be sent in the body of the message (the key parameter in the constructors decryptedMessageMediaPhoto, decryptedMessageMediaVideo and decryptedMessageMediaFile.
+Prior to a file being sent to a secret chat, 2 random 256-bit numbers are computed which will serve as the AES key and initialization vector used to encrypt the file. AES-256 encryption with infinite garble extension (IGE) is used in like manner.
+The key fingerprint is computed as follows:
+The encrypted contents of a file are stored on the server in much the same way as those of a file in cloud chats: piece by piece using calls to upload.saveFilePart. +A subsequent call to messages.sendEncryptedFile will assign an identifier to the stored file and send the address together with the message. The recipient will receive an update with encryptedMessage, and the file parameter will contain file information.
+Incoming and outgoing encrypted files can be forwarded to other secret chats using the constructor inputEncryptedFile to avoid saving the same content on the server twice.
+Secret chats are associated with specific devices (or rather with authorization keys), not users. A conventional message box, which uses pts to describe the client’s status, is not suitable, because it is designed for long-term message storage and message access from different devices.
+An additional temporary message queue is introduced as a solution to this problem. When an update regarding a message from a secret chat is sent, a new value of qts is sent, which helps reconstruct the difference if there has been a long break in the connection or in case of loss of an update.
+As the number of events increases, the value of qts increases monotonically (not always by 1). The initial value may not (and will not) be equal to 0.
+The fact that events from the temporary queue have been received and stored by the client is acknowledged explicitly by a call to the messages.receivedQueue method or implicitly by a call to updates.getDifference (the value of qts passed, not the final state). All messages acknowledged as delivered by the client, as well as any messages older than 7 days, may (and will) be deleted from the server.
+Upon de-authorization, the event queue of the corresponding device will be forcibly cleared, and the value of qts will become irrelevant.
+Your client should always store the maximal layer that is known to be supported by the client on the other side of a secret chat. When the secret chat is first created, this value should be initialized to 8, the first layer where Secret Chats became available. This remote layer value must always be updated immediately after receiving any packet containing information of an upper layer, i.e.:
+decryptedMessageLayer
with layer>=17, ordecryptedMessageService#aa48327d
).In order to notify the remote client of your local layer, your client must send a message of the decryptedMessageActionNotifyLayer
type. This notification must be wrapped in a constructor of an appropriate layer. For instance, if the remote layer for the chat in question is deemed to be lower than 17, the notification must be wrapped as if it were the decryptedMessageService
constructor of the obsolete layer 8 (constructor decryptedMessageService#aa48327d
), despite the fact that the decryptedMessageActionNotifyLayer
constructor is actually not present in Layer 8.
There are three cases when your client must notify the remote client about its local layer:
++Note that all pending obsolete layer messages must be sent prior to the layer update notification (more on this in Handling Sequence numbers).
+
Telegram supports styled text using message entities.
+A client that wants to send styled messages would simply have to integrate a Markdown/HTML parser, and generate an array of message entities by iterating through the parsed tags.
+Special care must be taken to consider the UTF-8 length of strings when generating message entities, see example implementations: tdlib, MadelineProto.
+Nested entities are supported.
+For example the following HTML/Markdown aliases for message entities can be used:
<b>bold</b>
, <strong>bold</strong>
, **bold**
<i>italic</i>
, <em>italic</em>
*italic*
messageEntityCode
=> <code>code</code>
, `code`
<s>strike</s>
, <strike>strike</strike>
, <del>strike</del>
, ~~strike~~
<u>underline</u>
messageEntityPre
=> <pre language="c++">code</pre>
, +```c++ +code +``` ++
The following entities can also be used to mention users:
+There will be errors when working with the API, and they must be correctly handled on the client.
+An error is characterized by several parameters:
+Numerical value similar to HTTP status. Contains information on the type of error that occurred: for example, a data input error, privacy error, or server error. This is a required parameter.
+A string literal in the form of /[A-Z_0-9]+/
, which summarizes the problem. For example, AUTH_KEY_UNREGISTERED
. This is an optional parameter.
There should be a way to handle errors that are returned in rpc_error constructors.
+Below is a list of error codes and their meanings:
+The request must be repeated, but directed to a different data center.
+In all these cases, the error description’s string literal contains the number of the data center (instead of the X) to which the repeated query must be sent. +More information about redirects between data centers »
+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.
+There was an unauthorized attempt to use functionality available only to authorized users.
+Privacy violation. For example, an attempt to write a message to someone who has blacklisted the current user.
+An attempt to invoke a non-existent object, such as a method.
+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.
+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.
+An internal server error occurred while a request was being processed; for example, there was a disruption while accessing a database or file storage.
+If a client receives a 500 error, or you believe this error should not have occurred, please collect as much information as possible about the query and error and send it to the developers.
+If a server returns an error with a code other than the ones listed above, it may be considered the same as a 500 error and treated as an internal server error.
File references are strings of bytes, that can be encountered in the file_reference
fields of document and photo objects.
They must be cached by the client, along with the origin context where the document/photo object was found, in order to be refetched when the file reference expires.
+Example implementation of a reference database: MadelineProto, android, telegram desktop, tdlib.
+Assume you receive a message from your friend: that message contains a messageMediaPhoto with a photo.
+Your client has to cache not only the file_reference
field of the photo, but also the context in which the file reference was seen (in this case, a message coming from a specific user).
The context info is in this case, an origin context of type message, containing the message ID and the peer ID of the chat/channel/user where the message was seen.
+The context info has to be associated with the file reference: when downloading a file using upload.getFile, a FILE_REFERENCE_EXPIRED
error (or another error starting with FILE_REFERENCE_
) may be returned.
+If this happens, the context info must be used to refetch the object that contained the file reference: in this example, the peer info and the message ID have to be used with channels.getMessages or messages.getMessages to refetch the message, recache the file reference and use it in a new file download request.
More than one origin context can be associated to one file reference, for greater resilience (in the case of a message that was deleted in one chat but was also forwarded in another chat, the file reference can be refetched from the second chat, instead).
+Origin contexts for objects returned by method calls with certain parameters can be considered, too (for example, in the case of favorited sticker sets returned by messages.getFavedStickers).
When working with the API, it is sometimes necessary to send a relatively large file to the server. For example, when sending a message with a photo/video attachment or when setting the current user’s profile picture.
+There are a number of API methods to save files. The schema of the types and methods used is presented below:
+inputFile#f52ff27f id:long parts:int name:string md5_checksum:string = InputFile;
+inputFileBig#fa4f0bb5 id:long parts:int name:string = InputFile;
+
+
+inputEncryptedFileUploaded#64bd0306 id:long parts:int md5_checksum:string key_fingerprint:int = InputEncryptedFile;
+inputEncryptedFileBigUploaded#2dc173c8 id:long parts:int key_fingerprint:int = InputEncryptedFile;
+
+inputSecureFileUploaded#3334b0f0 id:long parts:int md5_checksum:string file_hash:bytes secret:bytes = InputSecureFile;
+inputSecureFile#5367e5be id:long access_hash:long = InputSecureFile;
+
+inputMediaUploadedPhoto#1e287d04 flags:# file:InputFile stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
+inputMediaUploadedDocument#5b38c6c1 flags:# nosound_video:flags.3?true force_file:flags.4?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector<DocumentAttribute> stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
+
+inputChatUploadedPhoto#c642724e flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double = InputChatPhoto;
+
+
+---functions---
+
+messages.sendMedia#3491eba9 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
+messages.uploadMedia#519bc2b1 peer:InputPeer media:InputMedia = MessageMedia;
+messages.sendEncryptedFile#5559481d flags:# silent:flags.0?true peer:InputEncryptedChat random_id:long data:bytes file:InputEncryptedFile = messages.SentEncryptedMessage;
+
+photos.uploadProfilePhoto#89f30f69 flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double = photos.Photo;
+
+upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool;
+upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool;
+Before transmitting the contents of the file itself, the file has to be assigned a unique 64-bit client identifier: file_id.
+The file’s binary content is then split into parts. All parts must have the same size ( part_size ) and the following conditions must be met:
+part_size % 1024 = 0
(divisible by 1KB)524288 % part_size = 0
(512KB must be evenly divisible by part_size)The last part does not have to satisfy these conditions, provided its size is less than part_size.
+Each part should have a sequence number, file_part, with a value ranging from 0 to 2,999.
+After the file has been partitioned you need to choose a method for saving it on the server. Use upload.saveBigFilePart in case the full size of the file is more than 10 MB and upload.saveFilePart for smaller files.
+Each call saves a portion of the data in a temporary location on the server to be used later. The storage life of each portion of data is between several minutes and several hours (depending on how busy the storage area is). After this time, the file part will become unavailable. To increase the time efficiency of a file save operation, we recommend using a call queue, so X pieces of the file are being saved at any given moment in time. Each successful operation to save a part invokes the method call to save the next part. The value of X can be tuned to achieve maximum performance.
+When using one of the methods mentioned above to save file parts, one of the following data input errors may be returned:
+1..3000
0 and 2,999
.While the parts are being uploaded, an MD5 hash of the file contents can also be computed to be used later as the md5_checksum parameter in the inputFile constructor (since it is checked only by the server, for encrypted secret chat files it must be generated from the encrypted file). +After the entire file is successfully saved, the final method may be called and passed the generated inputFile object. In case the upload.saveBigFilePart method is used, the inputFileBig constructor must be passed, in other cases use inputFile.
+The file save operation may return one of the following data input errors:
+inputMediaUploadedPhoto#1e287d04 flags:# file:InputFile stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
+inputMediaUploadedDocument#5b38c6c1 flags:# nosound_video:flags.3?true force_file:flags.4?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector<DocumentAttribute> stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
+
+inputSingleMedia#1cc6e91f flags:# media:InputMedia random_id:long message:string entities:flags.0?Vector<MessageEntity> = InputSingleMedia;
+
+---functions---
+
+messages.sendMultiMedia#cc0110cb flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int = Updates;
+Telegram allows grouping photos into albums and generic files (audio, docuemnts) into media groups.
+To do this, messages.sendMultiMedia is used, wrapping each InputMedia constructor (uploaded or pre-existing, maximum 10 per media group) into an inputSingleMedia constructor, optionally providing a custom per-file caption in message
.
For photo albums, clients should display an album caption only if exactly one photo in the group has a caption, otherwise no album caption should be displayed, and only when viewing in detail a specific photo of the group the caption should be shown.
+Other grouped media can display a caption under each file.
document#1e87342b flags:# id:long access_hash:long file_reference:bytes date:int mime_type:string size:int thumbs:flags.0?Vector<PhotoSize> video_thumbs:flags.1?Vector<VideoSize> dc_id:int attributes:Vector<DocumentAttribute> = Document;
+
+---functions---
+
+messages.getDocumentByHash#338e2464 sha256:bytes size:int mime_type:string = Document;
+For some types of documents like GIFs, messages.getDocumentByHash can be used to search for the document on Telegram servers. +The SHA256 hash of the file is computed, and it is passed along with the file's mime type and size to the method: if the file type is correct and the file is found, a document is returned.
+photo#fb197a65 flags:# has_stickers:flags.0?true id:long access_hash:long file_reference:bytes date:int sizes:Vector<PhotoSize> video_sizes:flags.1?Vector<VideoSize> dc_id:int = Photo;
+
+photos.photo#20212ca8 photo:Photo users:Vector<User> = photos.Photo;
+
+inputPhoto#3bb3b94a id:long access_hash:long file_reference:bytes = InputPhoto;
+
+inputFile#f52ff27f id:long parts:int name:string md5_checksum:string = InputFile;
+
+inputChatUploadedPhoto#c642724e flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double = InputChatPhoto;
+inputChatPhoto#8953ad37 id:InputPhoto = InputChatPhoto;
+
+---functions---
+
+photos.updateProfilePhoto#72d4742c id:InputPhoto = photos.Photo;
+photos.uploadProfilePhoto#89f30f69 flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double = photos.Photo;
+
+messages.editChatPhoto#ca4c79d8 chat_id:int photo:InputChatPhoto = Updates;
+
+channels.editPhoto#f12e57c9 channel:InputChannel photo:InputChatPhoto = Updates;
+User profile pictures can be uploaded using the photos.uploadProfilePhoto method: the actual profile picture has to be uploaded as for normal files.
+photos.uploadProfilePhoto can also be used to reupload previously uploaded profile pictures.
Animated profile pictures are also supported, by populating the video
constructor: square MPEG4 videos up to 1080x1080
are supported, 800x800
is the recommended resolution.
+The video_start_ts
is a floating point UNIX timestamp in seconds, indicating the frame of the video that should be used as static preview.
Chat, channel and supergroup profile photos and videos can be uploaded using messages.editChatPhoto (legacy groups) or channels.editPhoto (channels, supergroups).
+Use the inputChatPhoto to reuse previously uploaded profile pictures.
There are methods available to download files which have been successfully uploaded. The schema of the types and methods used is presented below:
+upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File;
+upload.fileCdnRedirect#f18cda44 dc_id:int file_token:bytes encryption_key:bytes encryption_iv:bytes file_hashes:Vector<FileHash> = upload.File;
+
+storage.fileUnknown#aa963b05 = storage.FileType;
+storage.fileJpeg#7efe0e = storage.FileType;
+storage.fileGif#cae1aadf = storage.FileType;
+storage.filePng#a4f63c0 = storage.FileType;
+storage.fileMp3#528a0677 = storage.FileType;
+storage.fileMov#4b09ebbc = storage.FileType;
+storage.filePartial#40bc6f52 = storage.FileType;
+storage.fileMp4#b3cea0e4 = storage.FileType;
+storage.fileWebp#1081464c = storage.FileType;
+
+---functions---
+
+upload.getFile#b15a9afc flags:# precise:flags.0?true cdn_supported:flags.1?true location:InputFileLocation offset:int limit:int = upload.File;
+Any file can be downloaded by calling upload.getFile. +The data for the input parameter of the InputFileLocation type is generated as follows:
+inputFileLocation#dfdaabe1 volume_id:long local_id:int secret:long file_reference:bytes = InputFileLocation;
+inputEncryptedFileLocation#f5235d55 id:long access_hash:long = InputFileLocation;
+inputDocumentFileLocation#bad07584 id:long access_hash:long file_reference:bytes thumb_size:string = InputFileLocation;
+inputSecureFileLocation#cbc7ee28 id:long access_hash:long = InputFileLocation;
+inputTakeoutFileLocation#29be5899 = InputFileLocation;
+inputPhotoFileLocation#40181ffe id:long access_hash:long file_reference:bytes thumb_size:string = InputFileLocation;
+inputPhotoLegacyFileLocation#d83466f3 id:long access_hash:long file_reference:bytes volume_id:long local_id:int secret:long = InputFileLocation;
+inputPeerPhotoFileLocation#27d69997 flags:# big:flags.0?true peer:InputPeer volume_id:long local_id:int = InputFileLocation;
+inputStickerSetThumb#dbaeae9 stickerset:InputStickerSet volume_id:long local_id:int = InputFileLocation;
+
+inputStickerSetEmpty#ffb62b95 = InputStickerSet;
+inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
+inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
+
+inputPeerSelf#7da07ec9 = InputPeer;
+inputPeerChat#179be863 chat_id:int = InputPeer;
+inputPeerUser#7b8e7de6 user_id:int access_hash:long = InputPeer;
+inputPeerChannel#20adaef8 channel_id:int access_hash:long = InputPeer;
+
+photo#fb197a65 flags:# has_stickers:flags.0?true id:long access_hash:long file_reference:bytes date:int sizes:Vector<PhotoSize> video_sizes:flags.1?Vector<VideoSize> dc_id:int = Photo;
+document#1e87342b flags:# id:long access_hash:long file_reference:bytes date:int mime_type:string size:int thumbs:flags.0?Vector<PhotoSize> video_thumbs:flags.1?Vector<VideoSize> dc_id:int attributes:Vector<DocumentAttribute> = Document;
+
+photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize;
+photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize;
+For photos, inputPhotoFileLocation is used:
+ +For profile pictures of users, channels, supergroups and groups, since in most occasions they are encountered as simple fileLocationToBeDeprecated constructors without an associated photo, inputPeerPhotoFileLocation has to be used:
+peer
is the identifier of the peer whose photo we want to downloadvolume_id
and local_id
are extracted from the fileLocationToBeDeprecated of the desired quality (the logic for selecting the quality of profile pictures will be changed soon)For documents, inputDocumentFileLocation is used:
+ +For encrypted secret chat and telegram passport documents, respectively inputEncryptedFileLocation and inputSecureFileLocation have to be used, with parameters extracted from encryptedFile and secureFile (passport docs).
+For previews of sticker sets, inputStickerSetThumb is used (note: to download stickers and previews of stickers use the document method described above):
+stickerset
is set to the InputStickerSet constructor generated from the StickerSetvolume_id
and local_id
are extracted from the fileLocationToBeDeprecated from the thumb
PhotoSize of the StickerSet (the logic for downloading stickerset previews will be changed soon)For old deprecated photos, if the client has cached some old fileLocations with the deprecated secret
identifier, inputFileLocation is used (this is mainly used for backwards compatiblity with bot API file IDs, all user clients must use the modern inputPhotoFileLocation file IDs):
id
, file_reference
and access_hash
taken from the photo constructorThe size of each file in bytes is available, which makes it possible to download the file in parts using the parameters offset and limit, similar to the way files are uploaded.
+If precise flag is not specified, then
+If precise is specified, then
+In any case the requested part should be within one 1 MB chunk from the beginning of the file, i. e.
+The file download operation may return a FILE_REFERENCE_EXPIRED
error (or another error starting with FILE_REFERENCE_
): in this case, the file_reference
field of the input location must be refreshed.
+The file download operation may return an upload.fileCdnRedirect constructor: in this case, these instructions must be followed for downloading CDN files.
+The file download operation may also return one of the following data input errors:
fileHash#6242c773 offset:int limit:int hash:bytes = FileHash;
+
+---functions---
+
+upload.getFileHashes#c7025931 location:InputFileLocation offset:int = Vector<FileHash>;
+In order to confirm the integrity of the downloaded file, clients are recommended to verify hashes for each downloaded part, as for CDN DCs.
+upload.getFileHashes contain FileHash constructors. Each of these constructors contains the SHA-256 hash of a part of the file that starts with offset
and takes limit
bytes.
Before saving each portion of the data received from the DC into the file, the client can confirm that its hash matches the hash that was received from the master DC. If missing a hash for any file part, client developers must use the upload.getFileHashes method to obtain the missing hash.
+Scheme:
+photoSizeEmpty#e17e23c type:string = PhotoSize;
+photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize;
+photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize;
+photoStrippedSize#e0b0bc2e type:string bytes:bytes = PhotoSize;
+photoSizeProgressive#5aa86a51 type:string location:FileLocation w:int h:int sizes:Vector<int> = PhotoSize;
+photoPathSize#d8214d41 type:string bytes:bytes = PhotoSize;
+
+videoSize#e831c556 flags:# type:string location:FileLocation w:int h:int size:int video_start_ts:flags.0?double = VideoSize;
+
+document#1e87342b flags:# id:long access_hash:long file_reference:bytes date:int mime_type:string size:int thumbs:flags.0?Vector<PhotoSize> video_thumbs:flags.1?Vector<VideoSize> dc_id:int attributes:Vector<DocumentAttribute> = Document;
+photo#fb197a65 flags:# has_stickers:flags.0?true id:long access_hash:long file_reference:bytes date:int sizes:Vector<PhotoSize> video_sizes:flags.1?Vector<VideoSize> dc_id:int = Photo;
+
+photo#fb197a65 flags:# has_stickers:flags.0?true id:long access_hash:long file_reference:bytes date:int sizes:Vector<PhotoSize> video_sizes:flags.1?Vector<VideoSize> dc_id:int = Photo;
+Telegram attaches a vector of thumbnails with reduced resolution to all uploaded media.
+The server also generates a trimmed and scaled down video preview for videos, GIFs and animated profile pictures.
Each photo preview has a specific type
, indicating the resolution and image transform that was applied server-side.
Type | +Image filter | +Size | +
---|---|---|
s |
+box | +100x100 | +
m |
+box | +320x320 | +
x |
+box | +800x800 | +
y |
+box | +1280x1280 | +
w |
+box | +2560x2560 | +
a |
+crop | +160x160 | +
b |
+crop | +320x320 | +
c |
+crop | +640x640 | +
d |
+crop | +1280x1280 | +
Special types:
+Type | +Image filter | +
---|---|
i |
+strip | +
j |
+outline | +
photoStrippedSize#e0b0bc2e type:string bytes:bytes = PhotoSize;
+A photoStrippedSize (with type i
) is an extremely low-res thumbnail, embedded directly inside media location objects.
+It should be shown to the user in chat message previews, or while still downloading the most appropriately sized photoSize through the media DCs as described above.
The stripped bytes
payload should be inflated to a JPG payload as seen here ».
photoPathSize#d8214d41 type:string bytes:bytes = PhotoSize;
+Messages with animated stickers can have a compressed svg (< 300 bytes) to show the outline of the sticker before fetching the actual lottie animation.
+Animated sticker outlines will have a j
type photoPathSize thumbnail.
This specific vector thumbnail consists in an SVG path, specially encoded to save space.
+This path will be the outline of the animated sticker, and should be shown to the user while downloading the actual sticker.
As for stripped sizes, the payload should be inflated using the following algorithm:
+encoded := photoPathSize.bytes
+
+lookup := "AACAAAAHAAALMAAAQASTAVAAAZaacaaaahaaalmaaaqastava.az0123456789-,"
+
+path := "M"
+
+len := strlen(encoded)
+for (i = 0; i < len; i++) {
+ num := ord(encoded[i])
+ if (num >= 128 + 64) {
+ path += lookup[num - 128 - 64]
+ } else {
+ if (num >= 128) {
+ path += ','
+ } else if (num >= 64) {
+ path += '-'
+ }
+ path += itoa(num & 63)
+ }
+}
+path += "z"
+path
will contain the actual SVG path that can be directly inserted in the d
attribute of an svg <path> element:
<?xml version="1.0" encoding="utf-8"?>
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+ viewBox="0 0 512 512" xml:space="preserve">
+<path d="{$path}"/>
+</svg>
+videoSize#e831c556 flags:# type:string location:FileLocation w:int h:int size:int video_start_ts:flags.0?double = VideoSize;
+A videoSize constructor is typically used for [animated profile pictures]() and video previews.
+Type | +Description | +Format | +
---|---|---|
u |
+Animated profile picture | +MPEG4 | +
v |
+Video preview | +MPEG4 | +
Remote HTTP files sent by inline bots in response to inline queries and in other places are represented by WebDocument constructors. +When forwarding such remote HTTP files, they should be sent using external InputMedia constructors. +Remote HTTP files can only be downloaded directly by the client if contained in a webDocumentNoProxy constructor: in this case, the file is deemed safe to download (this is the case for HTTPS files from certain trusted domains).
+However, if the remote file is contained in a webDocument, to avoid leaking sensitive information the file must be downloaded through telegram's servers. +This can be done in a manner similar to normal files, with the difference that upload.getWebFile must be used, instead.
+upload.webFile#21e753bc size:int mime_type:string file_type:storage.FileType mtime:int bytes:bytes = upload.WebFile;
+
+storage.fileUnknown#aa963b05 = storage.FileType;
+storage.fileJpeg#7efe0e = storage.FileType;
+storage.fileGif#cae1aadf = storage.FileType;
+storage.filePng#a4f63c0 = storage.FileType;
+storage.fileMp3#528a0677 = storage.FileType;
+storage.fileMov#4b09ebbc = storage.FileType;
+storage.filePartial#40bc6f52 = storage.FileType;
+storage.fileMp4#b3cea0e4 = storage.FileType;
+storage.fileWebp#1081464c = storage.FileType;
+
+ ---functions---
+
+upload.getWebFile#24e6818d location:InputWebFileLocation offset:int limit:int = upload.WebFile;
+The InputWebFileLocation constructor is generated as follows.
+inputWebFileLocation#c239d686 url:string access_hash:long = InputWebFileLocation;
+inputWebFileGeoPointLocation#9f2221c9 geo_point:InputGeoPoint access_hash:long w:int h:int zoom:int scale:int = InputWebFileLocation;
+
+webDocument#1c570ed1 url:string access_hash:long size:int mime_type:string attributes:Vector<DocumentAttribute> = WebDocument;
+
+inputGeoPoint#48222faf flags:# lat:double long:double accuracy_radius:flags.0?int = InputGeoPoint;
+
+geoPoint#b2a2f663 flags:# long:double lat:double access_hash:long accuracy_radius:flags.0?int = GeoPoint;
+url
and access_hash
fields of the webDocument constructor.geo_point
is generated from the lat
, long
accuracy_radius
parameters of the geoPointaccess_hash
is the access hash of the geoPointw
- Map width in pixels before applying scale; 16-1024h
- Map height in pixels before applying scale; 16-1024zoom
- Map zoom level; 13-20scale
- Map scale; 1-3It is recommended that large queries (upload.getFile, upload.saveFilePart, upload.getWebFile) be handled through a separate session and a separate connection, in which no methods other than these should be executed. If this is done, then data transfer will cause less interference with getting updates and other method calls.
+How to handle file references.
Telegram allows placing chats into folders, based on their type, mute status, or other custom criteria, thanks to folder blacklists and whitelists.
+In the API, folders are called "dialog filters"; in the UI, they are typically represented as tabs.
Scheme:
+dialogFilter#7438f7e8 flags:# contacts:flags.0?true non_contacts:flags.1?true groups:flags.2?true broadcasts:flags.3?true bots:flags.4?true exclude_muted:flags.11?true exclude_read:flags.12?true exclude_archived:flags.13?true id:int title:string emoticon:flags.25?string pinned_peers:Vector<InputPeer> include_peers:Vector<InputPeer> exclude_peers:Vector<InputPeer> = DialogFilter;
+
+dialogFilterSuggested#77744d4a filter:DialogFilter description:string = DialogFilterSuggested;
+
+updateDialogFilter#26ffde7d flags:# id:int filter:flags.0?DialogFilter = Update;
+updateDialogFilterOrder#a5d72105 order:Vector<int> = Update;
+updateDialogFilters#3504914f = Update;
+
+---functions---
+
+messages.getDialogFilters#f19ed96d = Vector<DialogFilter>;
+messages.getSuggestedDialogFilters#a29cd42c = Vector<DialogFilterSuggested>;
+messages.updateDialogFilter#1ad4a04a flags:# id:int filter:flags.0?DialogFilter = Bool;
+messages.updateDialogFiltersOrder#c563c1e4 order:Vector<int> = Bool;
+
+help.getAppConfig#98914110 = JSONValue;
+On startup, clients call:
+The boolean under the dialog_filters_tooltip
JSON key in the result of help.getAppConfig can be used to determine whether a folder tooltip should be presented to the user right away.
+The UI should then show a list of suggested folder combinations.
Once configuration is finished, apps call messages.updateDialogFilter to create or update existing folders.
+As per the dialogFilter constructor, folders have multiple flags that can be combined to determine which chats should be included in (or excluded from) the folder.
+Folders can also have unlimited pinned chats, as determined by the pinned_peers
field.
To reorder existing folders, messages.updateDialogFiltersOrder should be used with the IDs of the various dialog filters.
+To delete folders, use messages.updateDialogFilter without populating the filter
flag field.
Clients can receive updateDialogFilter, updateDialogFilterOrder updates with new filter information, generated by other clients when modifying folder info.
+Clients can also receive updateDialogFilters, in which case folder info should be refetched manually using messages.getDialogFilters.
The API also has another method for identifying groups of peers, typically used only by archived chats.
+Scheme:
+inputDialogPeer#fcaafeb7 peer:InputPeer = InputDialogPeer;
+inputDialogPeerFolder#64600527 folder_id:int = InputDialogPeer;
+
+dialogPeer#e56dbf05 peer:Peer = DialogPeer;
+dialogPeerFolder#514519e2 folder_id:int = DialogPeer;
+
+updateFolderPeers#19360dc0 folder_peers:Vector<FolderPeer> pts:int pts_count:int = Update;
+
+updates#74ae4240 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq:int = Updates;
+
+---functions---
+
+folders.editPeerFolders#6847d0ab folder_peers:Vector<InputFolderPeer> = Updates;
+folders.deleteFolder#1c295881 folder_id:int = Updates;
+API peer folders are typically used only by archived chats, and are really handy for distinguishing groups of peers, since most peer-related constructors (updates, chat info) will contain the folder_id
assigned the the specified chat.
In Telegram apps, API peer folders are used only to implement the chat archive, identified by folder_id
1
; all other peers are in folder_id
0
by default; no other folder_id
is allowed at the moment.
0
folder.Both methods return an updates constructor, containing a single updateFolderPeers with the new folder_id
of moved peers.
+Clients can also receive updateFolderPeers as a normal update, generated by other clients when modifying peer folders.
Deprecated old docs. This was definitely not the droid you were looking for.
Versioning in the API is supported by so-called TL layers.
+The need to add a new object constructor or to add/remove a field in a constructor creates a backwards compatibility problem for previous versions of API clients. After all, simply changing a constructor in a schema also changes its number. To address this problem, each schema update is separated into a layer. +A layer is a collection of updated methods or constructors in a TL schema. Each layer is numbered with sequentially increasing numbers starting with 2. The first layer is the base layer -- the TL schema without any changes.
+There is helper method to let the API know that a client supports Layer layer
:
invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;
+The helper method invokeWithLayer can be used only together with initConnection: the present layer will be saved with all other parameters of the client and future calls will be using this saved value. See more below..
+It is possible to save information about the current client on the server in conjunction with an authorization key. This may help eliminate client-side problems with certain releases on certain devices or with certain localizations, as well as eliminate the need for sending layer information in each call.
+The helper method initConnection accepts client parameters. This method must be called when first calling the API after the application has restarted or in case the value of one of the parameters could have changed.
+initConnection must also be called after each auth.bindTempAuthKey.
+When calling this method, the current layer used by the client is also saved (the layer in which initConnection was wrapped is used). After a successful call to initConnection it is no longer necessary to wrap each API call in invokeWithLayerN.
+invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X;
+invokeWithoutUpdates can be used to invoke a request without subscribing the used connection for updates (this is enabled by default for file queries).
+Sometimes a client needs to transmit several send message method calls to the server all at once in a single message or in several consecutive messages. However, there is a chance that the server may execute these requests out of order (queries are handled by different servers to improve performance, which introduces a degree of randomness to the process).
+There are helper methods for making several consecutive API calls without wasting time waiting for a response:
+invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
+invokeAfterMsgs#3dc4b4f0 {X:Type} msg_ids:Vector
They may be used, for example, if a client attempts to send accumulated messages after the Internet connection has been restored after being absent for a long time. In this case, the 32-bit number 0xcb9f372d
must be added before the method number in each call, followed by a 64-bit message identifier, msg_id, which contains the previous call in the queue.
+The second method is similar, except it takes several messages that must be waited for.
If the waiting period exceeds 0.5 seconds (this value may change in the future) and no result has appeared, the method will be executed just the same. If any of the queries returns an error, all its dependent queries will also return the 400 MSG_WAIT_FAILED error.
+Important: if the helper methods invokeAfterMsg / invokeAfterMsgs are used together with invokeWithLayerN or other helper methods, invokeAfterMsg / invokeAfterMsgs must always be the outermost wrapper.
+We recommend using gzip compression when making method calls in order to reduce the amount of network traffic.
+The schema and constructor information are given in the protocol documentation.
+Before transmitting a query, the string containing the entire body of the serialized high-level query (starting with the method number) must be compressed using gzip. If the resulting string is smaller than the original, it makes sense to transmit the gzip_packed constructor.
+There is no point in doing the above when transmitting binary multimedia data (photos, videos) or small messages (up to 255 bytes).
+By default, the server compresses the response to any call as well as updates, in accordance with the rules stated above. If the gzip_packed constructor is received as a response in rpc_result, then the string that follows must be extracted and uncompressed. Processing then continues on the resulting new string.
Telegram allows sending the live geolocation of a user in a chat, optionally setting a proximity alert.
+inputGeoPointEmpty#e4c123d6 = InputGeoPoint;
+
+inputGeoPoint#48222faf flags:# lat:double long:double accuracy_radius:flags.0?int = InputGeoPoint;
+
+inputMediaGeoLive#971fa843 flags:# stopped:flags.0?true geo_point:InputGeoPoint heading:flags.2?int period:flags.1?int proximity_notification_radius:flags.3?int = InputMedia;
+
+---functions---
+
+messages.sendMedia#3491eba9 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
+
+messages.editMessage#48f71778 flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.15?int = Updates;
+To send a live geolocation, use messages.sendMedia with an inputMediaGeoLive media
.
The inputMediaGeoLive allows sending the geolocation as an inputGeoPoint with floating point latitude and longitude, with an optional accuracy_radius
in meters.
+Clients can also provide a heading
, a direction in degrees (1-360) that can be used to indicate the direction of the user, a validity period
for the current location, and a proximity_notification_radius
.
The sent geolocation should be updated perioducally using messages.editMessage at most every period
seconds, in order to implement the "live" part of live geolocations.
To stop sharing the location, pass inputGeoPointEmpty as location and set the stopped
flag to true in a last messages.editMessage call.
geoPoint#b2a2f663 flags:# long:double lat:double access_hash:long accuracy_radius:flags.0?int = GeoPoint;
+messageMediaGeoLive#b940c666 flags:# geo:GeoPoint heading:flags.0?int period:int proximity_notification_radius:flags.1?int = MessageMedia;
+
+updateGeoLiveViewed#871fb939 peer:Peer msg_id:int = Update;
+Clients will receive a message with a messageMediaGeoLive, containing the information passed by the sender; when the geolocation message is marked as read, an updateGeoLiveViewed is generated.
+Periodically, the geolocation will be updated with updateEditMessage/updateEditChannelMessage updates.
+inputWebFileGeoPointLocation#9f2221c9 geo_point:InputGeoPoint access_hash:long w:int h:int zoom:int scale:int = InputWebFileLocation;
+
+inputGeoPoint#48222faf flags:# lat:double long:double accuracy_radius:flags.0?int = InputGeoPoint;
+
+---functions---
+
+upload.getWebFile#24e6818d location:InputWebFileLocation offset:int limit:int = upload.WebFile;
+A map preview can be generated by passing the received geoPoint to upload.getWebFile, to download an image preview of the map.
+geo_point
is generated from the lat
, long
accuracy_radius
parameters of the geoPointaccess_hash
is the access hash of the geoPointw
- Map width in pixels before applying scale; 16-1024h
- Map height in pixels before applying scale; 16-1024zoom
- Map zoom level; 13-20scale
- Map scale; 1-3The image is then downloaded as specified here »
+messageActionGeoProximityReached#98e0d697 from_id:Peer to_id:Peer distance:int = MessageAction;
+
+messageService#286fa604 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction = Message;
+If:
+proximity_notification_radius
when sending a locationproximity_notification_radius
meters of the first user, and updates their location accordinglyAn updateNewMessage/updateNewChannelMessage is generated for all chat members, containing a messageService with action messageActionGeoProximityReached:
+messageActionGeoProximityReached.to_id
is the peer that enabled proximity alertsmessageActionGeoProximityReached.from_id
is the peer that is now in proximity of messageActionGeoProximityReached.to_id
messageActionGeoProximityReached.distance
is the distance between them, in metersTelegram allows mentioning other users in case of urgent duckling matters, and quickly navigating to those mentions in order to read them as swiftly as possible.
+messageEntityMention#fa04579d offset:int length:int = MessageEntity;
+messageEntityMentionName#352dca58 offset:int length:int user_id:int = MessageEntity;
+inputMessageEntityMentionName#208e68c9 offset:int length:int user_id:InputUser = MessageEntity;
+
+message#58ae39c9 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> = Message;
+
+channelParticipantsMentions#e04b5ceb flags:# q:flags.0?string top_msg_id:flags.1?int = ChannelParticipantsFilter;
+
+---functions---
+
+messages.sendMessage#520c3870 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
+
+channels.getParticipants#123e05e9 channel:InputChannel filter:ChannelParticipantsFilter offset:int limit:int hash:int = channels.ChannelParticipants;
+Mentions are implemented as message entities, passed to the messages.sendMessage method:
+Incoming messages mentioning to the current user will have the mentioned
flag set, and will contain one or more messageEntityMention and messageEntityMentionName constructors.
Graphical clients can show a list of mentionable users when the user starts entering an @
in the text bar; for this purpose, the channelParticipantsMentions filter can be used in channels.getParticipants.
+This filter can be enhanced by providing an additional query string q
(anything the user enters after @
); it will also return non-participant users, in case of channel users commenting in post comment sections.
dialog#2c171f72 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog;
+
+---functions---
+
+messages.getUnreadMentions#46578472 peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
+messages.readMentions#f0189d3 peer:InputPeer = messages.AffectedHistory;
+Graphical clients are supposed to show a blue mention indicator next to the message counter of chats in the dialog list.
+The dialog constructor contains an unread_mentions_count
field to isolate chats with unread mentions; the actual mention counter should be shown inside of the chat itself, above an @
button that can be used, by clicking multiple times, to navigate back (using messages.getUnreadMentions) through the mention history.
When the last unread mention is read, or when long-clicking on the @
button, all mentions for a chat should marked as read using messages.readMentions.
In some situations user and channel constructors have reduced set of fields present (although id
is always there) and min
flag set. This is done for performance and privacy reasons.
When receiving said constructors, the client must first check if user or chat object without min
flag is already present in local cache. If it is present, then the client should just ignore constructors with min
flag and use local one instead.
The rest of article assumes the client receives min-constructor without full object in local cache.
+The client must store the context (similar to file references) in which the user/channel was seen. Later, when the client needs to pass the user/channel as input argument (e.g. fetch profile, mute, ban etc), the context is used to generate the input*FromMessage
constructor, instead of normal inputUser
, inputChannel
or inputPeer
.
The access_hash
value, if present, is only suitable to use in inputPeerPhotoFileLocation
, to directly download the profile pictures of channels and users without having to generate an inputPeer*FromMessage
, simply using inputPeer*
with the specified access hash.
Usually min
constructors are encountered in messages inside of groups or channels.
+When a message mentioning (sender, forwarder or forwardee, et cetera) such a user or channel is found, the constuctor must be associated with the message ID of the message and with the chat where the message was seen.
Assume a message with id 34
is received from supergroup (actually channel) 123456789
.
+Said message was sent by from_id
102424212
.
+The updates container that contained the message has a user with ID 102424212
in the users
field, but it has the min
flag set, and the provided access_hash
may be absent, or otherwise can't be used to generate a typical inputPeerUser constructor to send messages or do other actions.
What the client does is associate 102424212
with the channel 123456789
and message ID 34
.
+When and if the client will need to interact with user 102424212
, it will generate one of the *FromMessage
constructors mentioned above, setting:
msg_id
to 34
peer
to the InputPeer associated with channel 123456789
user_id
to 102424212
user_id
can also be set to the IDs of users met in the fwd_header
(messages forwarded from a user can be used to interact with the original sender, if they don't have privacy settings for forwards enabled).
+Users mentioned via messageEntityMentionName in a message can also be used.
The same can be done with min
channels.
Example implementations: Telegram for iOS, tdlib.
We welcome all developers to use our API and source code to create Telegram-like messaging applications on our platform free of charge.
+++In order to ensure consistency and security across the Telegram ecosystem, +all third-party client apps must comply with the API Terms of Service.
+
In order to obtain an API id and develop your own application using the Telegram API you need to do the following:
+We will be sending important developer notifications to the phone number that you use in this process, so please use an up-to-date number connected to your active Telegram account.
+Before using the MTProto Telegram API, please note that all API client libraries are strictly monitored to prevent abuse.
+If you use the Telegram API for flooding, spamming, faking subscriber and view counters of channels, you will be banned forever.
+Due to excessive abuse of the Telegram API, all accounts that sign up or log in using unofficial Telegram API clients are automatically put under observation to avoid violations of the Terms of Service.
+If you didn't violate the Terms of Service but your account does get banned after using the API, write to recover@telegram.org explaining what you intend to do with the API, asking to unban your account.
+Please note that emails are checked by a human, so automatically generated emails will be detected and banned.
Everyone is welcome to use our open source code. We have included a sample API id with the code. This API id is limited on the server side and is not suitable for apps released to end-users — using it for anything but testing purposes will result in the API_ID_PUBLISHED_FLOOD error for your users. It is necessary that you obtain your own API id before you publish your app.
++Please remember to publish your code as well in order to comply with the GNU GPL licences.
+
Lots of Telegram API methods provide access to potentially large lists of objects, which requires pagination.
+In order to fetch only relevant subset of results for each request there is a number of available input parameters. Here is a list in order how they are applied in API.
+Typically, results are returned in antichronological order with descending object ID values.
+limit
parameterA limit on the number of objects to be returned, typically between 1 and 100. When 0 is provided the limit will often default to an intermediate value like ~20.
+offset
-based paginationFor a few methods with mostly static data this parameter allows to skip offset
elements from the beginning of list; negative values are ignored.
offset_id
-based paginationFor most methods where results are real-time data (e.g. any chat history) offset
value is not passed directly. Instead it is calculated from the passed offset_id
and add_offset
parameter values as offsetFromID(offset_id) + add_offset
, where offsetFromID(offset_id)
is a number of results from the beginning of list up to the result with ID offset_id
, inclusive.
Sample use cases:
+Loading 20 messages, older than message with ID MSGID
:
messages.getHistory({offset_id: MSGID, add_offset: 0, limit: 20})
+Loading 20 messages, newer than message with ID MSGID
:
messages.getHistory({offset_id: MSGID, add_offset: -20, limit: 20})
+Loading 20 messages around message with ID MSGID
:
messages.getHistory({offset_id: MSGID, add_offset: -10, limit: 20})
+There is a number of parameters, which are applied to the list after slicing with offset and limit, to reduce the result subset even more:
+max_id
(e.g. message ID)min_id
(e.g. message ID)max_date
:min_date
:To further reduce the result subset, there is a mechanism to avoid fetching data if the resulting list hasn't changed from the one stored on client, similar to ETag.
+When the client has cached results for API request, it can calculate the hash
value for it by taking the result IDs (message IDs or other fields with name id
) and using them to compute a 32-bit hash with the following algorithm:
hash = 0
+for id in ids:
+ hash = (((hash * 0x4F25) & 0x7FFFFFFF) + id) & 0x7FFFFFFF
+In some cases, the result container already has a hash
field, that can be used instead.
When the client passes a correct value, the API will return one of *NotModified
constructors, e.g. messages.messagesNotModified instead of the actual results.
hash
reducing using the user IDs of returned participantsAn outgoing message may be considered sent once the server has assigned it an identifier. Normally, a client would learn of this from the result of the messages.sendMessage method. +The MTProto server provides a mechanism for “quick acknowledgments". Upon receiving such an acknowledgment, the client may be certain that the call to the send message method has at least been fully received by the server and placed in a processing queue, and can inform the user that the delivery was successful. +It is possible that the server’s actual response will never be received by the client (an interrupted connection; or the app restarts at exactly the wrong time). To correctly handle these situations, you can use a special type of notification generated by the server when updates.getDifference is called: updateMessageID. When processing this notification, the client can use the random_id identifier to associate the previously transmitted message with the one delivered to the server. +If such a notification is not issued when updates.getDifference is called for one of the previously sent messages, the message must be marked as undelivered.
+Server salt is a 64-bit number added to every outgoing and incoming message. At present, a single salt’s lifespan is 1 hour, following which it is considered invalid and the server will return an error for all the messages that contain it. The error message will contain the correct salt, which may be immediately used for sending. Given this approach, there will always be a period of waiting before the client receives a new salt if it connects to the server less frequently than once an hour. +For improved performance, there is a special get_future_salts method, which fetches in advance a list of the salts that will be valid during the course of a specified period of time following the call (1 day, for example). A start time and an end time are specified for each salt. The salts overlap one another by half an hour. We recommend always using the record with the longest remaining lifespan.
+We recommend that separate connections and sessions be created for these tasks. Remember that the extra sessions must be deleted when no longer needed. +It makes sense to download files over several connections (optimally to have a pool). When uploading data to a server one connection is enough to achieve the best results.
+The file handling API is designed to perform data operations in parts. In its simplest implementation, the process of uploading files to a server looks like this: send a query, wait for a response, send the next query, etc. This approach does not optimize the use of network resources and the ping time has a huge effect. +The upload and download process is optimal when two or more queries are continuously being executed through one connection. In this arrangement, uploading to the server would look like this:
+This will help reduce the effect of ping latency and maximize the channel workload.
+Sometimes a client needs to transmit several send message method calls to the server all at once in a single message or in several consecutive messages. However, the server may execute these requests out of order (queries are handled by different servers to improve performance, which introduces a degree of randomness to the process). +This requires that dependencies be explicitly stated when processing queries by using the function
+invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
+Actually, this means padding the beginning of the query with the 32-bit number 0xcb9f372d
and the 64-bit message identifier of the query on which the current query is dependent.
Generating updates (notifications about various server events) and delivering them to the client form two different parts of the system (respectively, the messenger API and MTProto). By itself, MTProto cannot modify in any way the data transmitted to the client, and the server API cannot respond to client-MTProto connection events. +Imagine the situation where a client loses its connection (or is intentionally disconnected from the network) for some time. If lots of different events occur before a new connection is established (contacts come online, typing event messages are sent), then when a connection is established the client will receive lots of data containing all of the intervening events, despite the fact that most of the data is obsolete. +The grouping of messages has been introduced to optimize such situations. If new events occur and the client has not managed to “collect” the previously generated updates, then the server API can combine them into a single package.
+A client is able to control when the MTProto server begins to consider that the connection has been lost and grouping can begin (the earlier this occurs when there is no connection, the better for the client). This functionality is implemented through a special type of Ping message, ping_delay_disconnect, which specifies a time delay following which the server will close the current connection and start grouping messages.
+It makes sense to combine the transmission of ping_delay_disconnect with that of other recurring tasks, such as updating the user status (account.updateStatus).
+If a contact is not online, there is no need to invoke messages.setTyping.
Telegram Passport is a unified authorization method for services that require personal identification. Users can upload their documents once, then instantly share their data with services that require real-world ID (finance, ICOs, etc.). Telegram doesn‘t have access to the users’ personal information thanks to end-to-end encryption.
+This page describes the request flow that client apps must used to send the requested data to the service.
+From the perspective of a service that requires real-world ID, the process looks like this:
+See As a bot to see how to request passport data using a bot, through the MTProto API. +Look at the Passport Manual to see how to request passport data using a bot, through the simplified bot API.
+From the perspective of a user, the process looks something like this:
+See As a user to see how user client apps should send passport data to a service, through the MTProto API.
+A simplified version of this process can be used using the bot API, for more info see the Passport Manual.
+Using the MTProto API, the process is pretty much the same, up until the actual API calls.
+++Note that all binary fields are in raw binary format, unlike in the bot API where they are base64-encoded
+
Scheme:
+secureData#8aeabec3 data:bytes data_hash:bytes secret:bytes = SecureData;
+
+securePlainPhone#7d6099dd phone:string = SecurePlainData;
+securePlainEmail#21ec5a5f email:string = SecurePlainData;
+
+secureFile#e0277a62 id:long access_hash:long size:int dc_id:int date:int file_hash:bytes secret:bytes = SecureFile;
+
+secureValueTypePersonalDetails#9d2a81e3 = SecureValueType;
+secureValueTypePassport#3dac6a00 = SecureValueType;
+secureValueTypeDriverLicense#6e425c4 = SecureValueType;
+secureValueTypeIdentityCard#a0d0744b = SecureValueType;
+secureValueTypeInternalPassport#99a48f23 = SecureValueType;
+secureValueTypeAddress#cbe31e26 = SecureValueType;
+secureValueTypeUtilityBill#fc36954e = SecureValueType;
+secureValueTypeBankStatement#89137c0d = SecureValueType;
+secureValueTypeRentalAgreement#8b883488 = SecureValueType;
+secureValueTypePassportRegistration#99e3806a = SecureValueType;
+secureValueTypeTemporaryRegistration#ea02ec33 = SecureValueType;
+secureValueTypePhone#b320aadb = SecureValueType;
+secureValueTypeEmail#8e3ca7ee = SecureValueType;
+
+secureValue#187fa0ca flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?SecureFile reverse_side:flags.2?SecureFile selfie:flags.3?SecureFile translation:flags.6?Vector<SecureFile> files:flags.4?Vector<SecureFile> plain_data:flags.5?SecurePlainData hash:bytes = SecureValue;
+
+secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted;
+
+messageActionSecureValuesSentMe#1b287353 values:Vector<SecureValue> credentials:SecureCredentialsEncrypted = MessageAction;
+messageService#286fa604 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction = Message;
+
+updateNewMessage#1f2b0afd message:Message pts:int pts_count:int = Update;
+When the user confirms your request by pressing the 'Authorize' button, the MTProto API sends an updateNewMessage from the user, with a messageService constructor, containing a messageActionSecureValuesSentMe constructor that contains the encrypted Telegram Passport data.
+secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted;
+
+messageActionSecureValuesSentMe#1b287353 values:Vector<SecureValue> credentials:SecureCredentialsEncrypted = MessageAction;
+To decrypt the received data, first, decrypt the credentials contained in secureCredentialsEncrypted.
+Decrypt the credentials secret ( secret field in secureCredentialsEncrypted) using your private key (set OAEP padding option, e.g. OPENSSL_PKCS1_OAEP_PADDING
in PHP)
Use this secret and the credentials hash ( hash field in secureCredentialsEncrypted) to calculate credentials_key and credentials_iv as described below:
+ credentials_secret_hash = SHA512( credentials_secret + credentials_hash )
+ credentials_key = slice( credentials_secret_hash, 0, 32 )
+ credentials_iv = slice( credentials_secret_hash, 32, 16 )
+Decrypt the credentials data ( data field in secureCredentialsEncrypted) by AES256-CBC using these credentials_key and credentials_iv. IMPORTANT: At this step, make sure that the credentials hash is equal to SHA256( credentials_data )
Credentials data is padded with 32 to 255 random padding bytes to make its length divisible by 16 bytes. The first byte contains the length of this padding (including this byte). Remove the padding to get the data.
+++Note that all hashes are raw binary data, not hexits
+
The credentials are a JSON-serialized object, structured exactly as in the bot API ». +Since decryption credentials are E2E encrypted, apps have to store the decryption credentials as JSON and not TL payloads.
+The credentials are used as described in the Passport Manual to decrypt the files attached to the secureValue. +In this case, the container for the E2E encrypted data is in TL, while the encrypted data itself is in JSON.
+secureValue#187fa0ca flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?SecureFile reverse_side:flags.2?SecureFile selfie:flags.3?SecureFile translation:flags.6?Vector<SecureFile> files:flags.4?Vector<SecureFile> plain_data:flags.5?SecurePlainData hash:bytes = SecureValue;
+
+messageActionSecureValuesSentMe#1b287353 values:Vector<SecureValue> credentials:SecureCredentialsEncrypted = MessageAction;
+The scheme for the secureValue constructor defines the constructor that can be found in each field.
+Name | +Type | +Description | +
---|---|---|
type | +SecureValueType | +Secure passport value type | +
data | +flags.0?SecureData | +Encrypted Telegram Passport element data | +
front_side | +flags.1?SecureFile | +Encrypted passport file with the front side of the document | +
reverse_side | +flags.2?SecureFile | +Encrypted passport file with the reverse side of the document | +
selfie | +flags.3?SecureFile | +Encrypted passport file with a selfie of the user holding the document | +
translation | +flags.6?Vector<SecureFile> | +Array of encrypted passport files with translated versions of the provided documents | +
files | +flags.4?Vector<SecureFile> | +Array of encrypted passport files with photos the of the documents | +
plain_data | +flags.5?SecurePlainData | +Plaintext verified passport data | +
hash | +bytes | +Data hash | +
Here's a list of possible SecureValueTypes, and the parameters that can be set/requested when using each type.
+Type | +Allowed fields | +
---|---|
secureValueTypeEmail | +plain_data |
+
secureValueTypePhone | +plain_data |
+
secureValueTypePersonalDetails | +data |
+
secureValueTypePassport | +data , front_side , selfie , translation |
+
secureValueTypeDriverLicense | +data , front_side , reverse_side , selfie , translation |
+
secureValueTypeIdentityCard | +data , front_side , reverse_side , selfie , translation |
+
secureValueTypeInternalPassport | +data , front_side , selfie , translation |
+
secureValueTypeAddress | +data |
+
secureValueTypeUtilityBill | +files , translation |
+
secureValueTypeBankStatement | +files , translation |
+
secureValueTypeRentalAgreement | +files , translation |
+
secureValueTypePassportRegistration | +files , translation |
+
secureValueTypeTemporaryRegistration | +files , translation |
+
secureData#8aeabec3 data:bytes data_hash:bytes secret:bytes = SecureData;
+Data is an encrypted and padded JSON-serialized object of one of the specified JSON types, depending on the chosen type.
+Chosen type | +JSON object | +
---|---|
secureValueTypePersonalDetails | +PersonalDetails | +
secureValueTypePassport | +IdDocumentData | +
secureValueTypeDriverLicense | +IdDocumentData | +
secureValueTypeIdentityCard | +IdDocumentData | +
secureValueTypeInternalPassport | +IdDocumentData | +
secureValueTypeAddress | +ResidentialAddress | +
DataCredentials extracted from the credentials can then be used to decrypt encrypted data from the data field in secureData. +For more info on how to decrypt the data field, see the passport manual.
+secureFile#e0277a62 id:long access_hash:long size:int dc_id:int date:int file_hash:bytes secret:bytes = SecureFile;
+
+inputSecureFileLocation#cbc7ee28 id:long access_hash:long = InputFileLocation;
+
+---functions---
+
+upload.getFile#b15a9afc flags:# precise:flags.0?true cdn_supported:flags.1?true location:InputFileLocation offset:int limit:int = upload.File;
+Files (JPG format when decrypted, max. 10MB) are downloaded chunk by chunk as described in files », except that instead of generating an inputFileLocation, an inputFileLocation should be generated, instead.
+id
field is the id
of the secureFileaccess_hash
field is the access_hash
of the secureFileFileCredentials extracted from the credentials can then be used to decrypt downloaded encrypted data. +For more info on how to decrypt passport files, see the passport manual.
+securePlainPhone#7d6099dd phone:string = SecurePlainData;
+securePlainEmail#21ec5a5f email:string = SecurePlainData;
+The email/phone is passed in plaintext using the respective SecurePlainData constructor. +Emails and phone numbers sent using telegram passport are already verified as described in the passport manual.
+secureValueErrorData#e8a40bd9 type:SecureValueType data_hash:bytes field:string text:string = SecureValueError;
+secureValueErrorFrontSide#be3dfa type:SecureValueType file_hash:bytes text:string = SecureValueError;
+secureValueErrorReverseSide#868a2aa5 type:SecureValueType file_hash:bytes text:string = SecureValueError;
+secureValueErrorSelfie#e537ced6 type:SecureValueType file_hash:bytes text:string = SecureValueError;
+secureValueErrorFile#7a700873 type:SecureValueType file_hash:bytes text:string = SecureValueError;
+secureValueErrorFiles#666220e9 type:SecureValueType file_hash:Vector<bytes> text:string = SecureValueError;
+secureValueError#869d758f type:SecureValueType hash:bytes text:string = SecureValueError;
+secureValueErrorTranslationFile#a1144770 type:SecureValueType file_hash:bytes text:string = SecureValueError;
+secureValueErrorTranslationFiles#34636dd8 type:SecureValueType file_hash:Vector<bytes> text:string = SecureValueError;
+
+inputUser#d8292816 user_id:int access_hash:long = InputUser;
+
+
+---functions---
+
+users.setSecureValueErrors#90c894b5 id:InputUser errors:Vector<SecureValueError> = Bool;
+If the data you received contains errors, the bot can use the users.setSecureValueErrors method to inform the user and request information again. The user will not be able to resend the data, until all errors are fixed.
+Descriptions of the method parameters can be found in the method's documentation page ».
+The process starts when your app receives an event from one of the SDKs, or from a custom source.
+The SDKs trigger a passport authorization request by opening the following Telegram-specific URI:
+tg://resolve?params
+With the following query string parameters:
+Parameters | +Type | +Required | +
domain | +String | +Yes | +
bot_id | +Integer | +Yes | +
scope | +UriPassportScope | +Yes | +
public_key | +String | +Yes | +
nonce | +String | +Yes | +
callback_url | +String | +Optional | +
payload | +String | +Optional | +
Example URI, generated by the Telegram Passport Example page:
+tg://resolve?domain=telegrampassport&bot_id=543260180&scope=%7B%22v%22%3A1%2C%22d%22%3A%5B%7B%22_%22%3A%22pd%22%2C%22n%22%3A1%7D%2C%22ad%22%2C%22pn%22%2C%22em%22%2C%7B%22_%22%3A%5B%7B%22_%22%3A%22pp%22%2C%22s%22%3A1%2C%22t%22%3A1%7D%2C%22ip%22%2C%22dl%22%2C%22ic%22%5D%7D%2C%7B%22_%22%3A%5B%22ub%22%2C%22bs%22%2C%22ra%22%2C%22pr%22%2C%22tr%22%5D%7D%5D%7D&public_key=-----BEGIN%20PUBLIC%20KEY-----%0AMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv6m1zBF8lZOCqcxf8hnj%0AkvHwuWdU8s4rBWaxKXH%2FvDDUklcCS5uhSnmjhxWca9suubaG3lW4HxlCilkeJPVf%0Ajimg5Q8ZqWrR3OoOihEpcG9iJZTOEpsEk7VtEiabgacBG3Quv9JslTrDe95Fn801%0At9d21HXwgMrHeHpWDOn31Dr%2BwoEH%2BkwySUWa6L%2FZbnGwSNP7eeDTE7Amz1RMDk3t%0A8EWGq58u0IQatPcEH09aUQlKzk6MIiALkZ9ILBKCBk6d2WCokKnsdBctovNbxwSx%0AhP1qst1r%2BYc8iPBZozsDC0ZsC5jXCkcODI3OC0tkNtYzN2XKalW5R0DjDRUDmGhT%0AzQIDAQAB%0A-----END%20PUBLIC%20KEY-----%0A&nonce=b8e892dc2e0afe63424d101b964f1256_32858210_708614a4585b84872e&callback_url=https%3A%2F%2Fcore.telegram.org%2Fpassport%2Fexample%3Fpassport_ssid%3Db8e892dc2e0afe63424d101b964f1256_32858210_db259b427f200751ce&payload=nonce
+This object represents the data to be requested.
+Field | +Type | +Description | +
d | +Array of UriPassportScopeElement | +List of requested elements, each type may be used only once in the entire array of UriPassportScopeElement objects | +
v | +Integer | +Scope version, must be 1 | +
This object represents a requested element, should be one of:
+Passport document type identifiers are aliased with the following reduced type identifiers:
+Full | +Alias | +
---|---|
personal_details |
+pd |
+
passport |
+pp |
+
driver_license |
+dl |
+
identity_card |
+ic |
+
internal_passport |
+ip |
+
id_document |
+idd |
+
address |
+ad |
+
utility_bill |
+ub |
+
bank_statement |
+bs |
+
rental_agreement |
+ra |
+
passport_registration |
+pr |
+
temporary_registration |
+tr |
+
address_document |
+add |
+
phone_number |
+pn |
+
email |
+em |
+
You can use the special type "idd" as an alias for one of "pp", "dl", "ic" and the special type "add" as an alias for one of "ub", "bs", "ra".
+This object represents several elements one of which must be provided.
+Field | +Type | +Description | +
_ | +Array of UriPassportScopeElementOne | +List of elements one of which must be provided; must contain either several of “pp”, “dl”, “ic”, “ip” or several of “ub”, “bs”, “ra”, “pr”, “tr” | +
s | +Boolean | +Optional. Use this parameter if you want to request a selfie with the document from this list that the user chooses to upload. | +
t | +Boolean | +Optional. Use this parameter if you want to request a translation of the document from this list that the user chooses to upload. Note: We suggest to only request translations after you have received a valid document that requires one. | +
This object represents one particular element that must be provided. If no options are needed, String can be used instead of this object to specify the type of the element.
+Field | +Type | +Description | +
_ | +String | +Element type. One of "pd", "pp", "dl", "ic", "ip", "ad", "ub", "bs", "ra", "pr", "tr", "pn", "em" | +
s | +Boolean | +Optional. Use this parameter if you want to request a selfie with the document as well. Available for "pp", "dl", "ic" and "ip" | +
t | +Boolean | +Optional. Use this parameter if you want to request a translation of the document as well. Available for "pp", "dl", "ic", "ip", "ub", "bs", "ra", "pr" and "tr". Note: We suggest to only request translations after you have received a valid document that requires one. | +
n | +Boolean | +Optional. Use this parameter to request the first, last and middle name of the user in the language of the user's country of residence. Available for "pd" | +
You can also use the special type "idd" as an alias for one of "pp", "dl", "ic" and the special type "add" as an alias for one of "ub", "bs", "ra".
+The next step for the client app is to request the user's 2FA passport, and configure Telegram Passport/fetch and decrypt remotely saved Telegram Passport parameters as described in the Encryption article ».
+account.authorizationForm#ad2e1cd8 flags:# required_types:Vector<SecureRequiredType> values:Vector<SecureValue> errors:Vector<SecureValueError> users:Vector<User> privacy_policy_url:flags.0?string = account.AuthorizationForm;
+
+---functions---
+
+account.getAuthorizationForm#b86ba8e1 bot_id:int scope:string public_key:string = account.AuthorizationForm;
+Then, the client app passes the bot ID, scope and public key from the passport authorization request to the Telegram servers using the account.getAuthorizationForm method.
+The response will be an account.authorizationForm constructor, with info about the required document types, the URL of the service's privacy policy, as well as info about the bot to which the form should be sent. +If the form was already submitted at least once, the constructor will also contain a list of already submitted data, along with eventual errors.
+The user should accept the privacy policy and proceed to fill in the required data, and the client should encrypt and upload it as described in the Encryption article ».
+secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted;
+
+secureValueHash#ed1ecdb0 type:SecureValueType hash:bytes = SecureValueHash;
+
+---functions---
+
+account.acceptAuthorization#e7027c94 bot_id:int scope:string public_key:string value_hashes:Vector<SecureValueHash> credentials:SecureCredentialsEncrypted = Bool;
+Once the user finishes uploading the required documents and clicks on the submit button, the client calls account.acceptAuthorization, submitting the documents to the bot associated to the service.
+bot_id
, scope
and public_key
are taken from the authorization request URI.value_hashes
is used by the server to choose which document of which type to send to the bot: the type
field should be set to the document type, and the hash
field should be set to the data_hash
/file_hash
generated when uploading encrypting the data ».credentials
contains the encrypted credentials required by the service to decrypt the sent E2E encrypted secure values: it is generated as described in Passport Credentials ».Finally, the client opens the callback URL (if present).
+secureValueErrorData#e8a40bd9 type:SecureValueType data_hash:bytes field:string text:string = SecureValueError;
+secureValueErrorFrontSide#be3dfa type:SecureValueType file_hash:bytes text:string = SecureValueError;
+secureValueErrorReverseSide#868a2aa5 type:SecureValueType file_hash:bytes text:string = SecureValueError;
+secureValueErrorSelfie#e537ced6 type:SecureValueType file_hash:bytes text:string = SecureValueError;
+secureValueErrorFile#7a700873 type:SecureValueType file_hash:bytes text:string = SecureValueError;
+secureValueErrorFiles#666220e9 type:SecureValueType file_hash:Vector<bytes> text:string = SecureValueError;
+secureValueError#869d758f type:SecureValueType hash:bytes text:string = SecureValueError;
+secureValueErrorTranslationFile#a1144770 type:SecureValueType file_hash:bytes text:string = SecureValueError;
+secureValueErrorTranslationFiles#34636dd8 type:SecureValueType file_hash:Vector<bytes> text:string = SecureValueError;
+
+account.authorizationForm#ad2e1cd8 flags:# required_types:Vector<SecureRequiredType> values:Vector<SecureValue> errors:Vector<SecureValueError> users:Vector<User> privacy_policy_url:flags.0?string = account.AuthorizationForm;
+
+---functions---
+
+account.getAuthorizationForm#b86ba8e1 bot_id:int scope:string public_key:string = account.AuthorizationForm;
+If any of the values of the submitted form are rejected by the service, the bot calls the appropriate method to set information about errors.
+The user can find out about these errors directly from the service, or, if they decide to restart the process and resend the corrected data, directly from the authorization form (errors
field).
Some methods require the client to verify if the data obtained from an external source matches a certain pattern.
+For example, when requesting a login code, if the chosen verification method is a flash call, it is required that the phone number (which is the login code) matches the specified pattern.
+Same with email verification codes.
+In all cases, the pattern is a string of the same length as the string to verify: and matching is as simple as checking if all chars in the source string are the same as in the pattern string.
+Some chars in the pattern string may be censored using an asterisk *
, in this case any char in the source string is considered valid.
The pattern string can also be a single astrisk, in this case all patterns are considered valid.
+If the source string is a phone number, it has to be sanitized first to include only the following chars: 0123456789
.
Example implementation: telegram for android.
You can accept payments from Telegram users via Telegram Bots.
+++Note: This article is intended for MTProto API developers. If you're looking for a general overview of Telegram Payments, check out the Telegram blog and the bot API payment manual.
+
Telegram bots can accept payments for goods and services from users. +For more info on how payments work, check out the Telegram Blog and the bot API payment manual.
+This page will elaborate on the actions required to work with payments using the MTProto API.
+++A simplified version of the process is available only for bots using the bot API.
+
The first step for bots is enable payments as described here ».
+Then, we work with payments as follows.
+inputWebDocument#9bed434d url:string size:int mime_type:string attributes:Vector<DocumentAttribute> = InputWebDocument;
+
+labeledPrice#cb296bf8 label:string amount:long = LabeledPrice;
+
+invoice#c30aa358 flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true phone_to_provider:flags.6?true email_to_provider:flags.7?true currency:string prices:Vector<LabeledPrice> = Invoice;
+
+inputMediaInvoice#f4e096c3 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string = InputMedia;
+
+---functions---
+
+messages.sendMedia#3491eba9 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
+The user contacts the bot and requests to purchase something.
+The bot forms an inputMediaInvoice with an invoice constructor with a description of the goods or service, amount to be paid, as well as requested shipping info.
+The provider
parameter of the inputMediaInvoice constructor is where you put the token value that you've obtained earlier via Botfather. It is possible for one merchant bot to use several different tokens for different users or different goods and services.
Use the messages.sendMedia method to send the invoice.
+You can also attach an inline keyboard to the message using the reply_markup
field: if provided, the first button must be a keyboardButtonBuy button. Otherwise, an inline keyboard will be generated automatically, with a Pay 'total price'
keyboardButtonBuy as only button.
An invoice message with a pay button can only be sent to a private chat with the user. Groups and channels are not supported.
+keyboardButtonBuy#afd93fbb text:string = KeyboardButton;
+
+keyboardButtonRow#77608b83 buttons:Vector<KeyboardButton> = KeyboardButtonRow;
+replyInlineMarkup#48a30254 rows:Vector<KeyboardButtonRow> = ReplyMarkup;
+
+webDocument#1c570ed1 url:string access_hash:long size:int mime_type:string attributes:Vector<DocumentAttribute> = WebDocument;
+webDocumentNoProxy#f9c8bcc6 url:string size:int mime_type:string attributes:Vector<DocumentAttribute> = WebDocument;
+
+messageMediaInvoice#84551347 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string = MessageMedia;
+
+message#58ae39c9 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> = Message;
+
+updateNewMessage#1f2b0afd message:Message pts:int pts_count:int = Update;
+The user receives an updateNewMessage constructor from the bot, containing a messageMediaInvoice constructor with basic info about the product.
+The message will also have a replyInlineMarkup keyboard attached to it. +The the first button of the keyboard will always be a keyboardButtonBuy button.
+invoice#c30aa358 flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true phone_to_provider:flags.6?true email_to_provider:flags.7?true currency:string prices:Vector<LabeledPrice> = Invoice;
+
+paymentRequestedInfo#909c3f94 flags:# name:flags.0?string phone:flags.1?string email:flags.2?string shipping_address:flags.3?PostAddress = PaymentRequestedInfo;
+
+paymentSavedCredentialsCard#cdc27a1f id:string title:string = PaymentSavedCredentials;
+
+payments.paymentForm#3f56aea3 flags:# can_save_credentials:flags.2?true password_missing:flags.3?true bot_id:int invoice:Invoice provider_id:int url:string native_provider:flags.4?string native_params:flags.4?DataJSON saved_info:flags.0?PaymentRequestedInfo saved_credentials:flags.1?PaymentSavedCredentials users:Vector<User> = payments.PaymentForm;
+
+---functions---
+
+payments.getPaymentForm#99f09745 msg_id:int = payments.PaymentForm;
+If the user clicks on the keyboardButtonBuy button, the client proceeds to call payments.getPaymentForm with the message ID of the invoice preview message to get the payment form.
+The returned form will contain fields that should be passed to the payment provider along with the full invoice. +The payment form also contains info about previously saved payment credentials and order information (name, phone number, email, shipping address & so on).
+The full invoice contains info about the information required for the order, the price and the currency, and whether this is a test
order.
invoice#c30aa358 flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true phone_to_provider:flags.6?true email_to_provider:flags.7?true currency:string prices:Vector<LabeledPrice> = Invoice;
+
+postAddress#1e8caaeb street_line1:string street_line2:string city:string state:string country_iso2:string post_code:string = PostAddress;
+
+paymentRequestedInfo#909c3f94 flags:# name:flags.0?string phone:flags.1?string email:flags.2?string shipping_address:flags.3?PostAddress = PaymentRequestedInfo;
+
+payments.validatedRequestedInfo#d1451883 flags:# id:flags.0?string shipping_options:flags.1?Vector<ShippingOption> = payments.ValidatedRequestedInfo;
+
+---functions---
+
+payments.validateRequestedInfo#770a8e74 flags:# save:flags.0?true msg_id:int info:PaymentRequestedInfo = payments.ValidatedRequestedInfo;
+If any data at all is requested by the invoice (name_requested
, phone_requested
, email_requested
, shipping_address_requested
), the user must call payments.validateRequestedInfo, providing the required data (as usual, msg_id
is the ID of the invoice message).
+The user can choose to save order information for future use by setting the save
flag.
+Data can be autofilled as described in autofill.
If no errors are found in the submitted info, the response of the method will contain an id
flag, to be used later to complete the payment.
If the flexible
flag of the invoice is set, calling the payments.validateRequestedInfo method will send a shipping query update to the bot, to which the bot will reply with the available shipping options for the specified address as described here ».
+The return value in this case will also contain a shipping_options
field with the available shipping options.
If any errors are found in the submmitted data, a service notification will be sent to the user, with a description of the error from the bot.
+payments.savedInfo#fb8fe43c flags:# has_saved_credentials:flags.1?true saved_info:flags.0?PaymentRequestedInfo = payments.SavedInfo;
+
+---functions---
+
+payments.getSavedInfo#227d824b = payments.SavedInfo;
+payments.clearSavedInfo#d83d70c1 flags:# credentials:flags.0?true info:flags.1?true = Bool;
+The requested fields can be autofilled with the info provided in the saved_info
field of the payment form, or with the info fetched manually using payments.getSavedInfo.
Saved order information can also be cleared using payments.clearSavedInfo.
+labeledPrice#cb296bf8 label:string amount:long = LabeledPrice;
+
+shippingOption#b6213cdf id:string title:string prices:Vector<LabeledPrice> = ShippingOption;
+
+updateBotShippingQuery#e0cdc940 query_id:long user_id:int payload:bytes shipping_address:PostAddress = Update;
+
+---functions---
+
+messages.setBotShippingResults#e5f672fa flags:# query_id:long error:flags.0?string shipping_options:flags.1?Vector<ShippingOption> = Bool;
+If a shipping address was requested and the bot included the parameter flexible
, when the user validates order information the Telegram API will send an updateBotShippingQuery to the bot.
+The bot must respond using messages.setBotShippingResults either with a list of possible delivery options and the relevant delivery prices, or with an error (for example, if delivery to the specified address is not possible).
The returned shipping options or the shipping error will be returned to the user while validating order information.
+inputPaymentCredentialsSaved#c10eb2cf id:string tmp_password:bytes = InputPaymentCredentials;
+inputPaymentCredentials#3417d728 flags:# save:flags.0?true data:DataJSON = InputPaymentCredentials;
+inputPaymentCredentialsApplePay#aa1c39f payment_data:DataJSON = InputPaymentCredentials;
+inputPaymentCredentialsAndroidPay#ca05d50e payment_token:DataJSON google_transaction_id:string = InputPaymentCredentials;
+
+
+payments.paymentForm#3f56aea3 flags:# can_save_credentials:flags.2?true password_missing:flags.3?true bot_id:int invoice:Invoice provider_id:int url:string native_provider:flags.4?string native_params:flags.4?DataJSON saved_info:flags.0?PaymentRequestedInfo saved_credentials:flags.1?PaymentSavedCredentials users:Vector<User> = payments.PaymentForm;
+inputPaymentCredentials#3417d728 flags:# save:flags.0?true data:DataJSON = InputPaymentCredentials;
+
+payments.paymentForm#3f56aea3 flags:# can_save_credentials:flags.2?true password_missing:flags.3?true bot_id:int invoice:Invoice provider_id:int url:string native_provider:flags.4?string native_params:flags.4?DataJSON saved_info:flags.0?PaymentRequestedInfo saved_credentials:flags.1?PaymentSavedCredentials users:Vector<User> = payments.PaymentForm;
+Typically, payment takes place by opening the url
in the specified payment form, which leads to a payment form on the website of the payment gateway.
+Once the user finishes entering their payment credentials, a payment_form_submit
web event is generated by the payment gateway, containing data
and title
JSON fields.
The title
is used by the client app to represent the payment credentials (typically a censored version of credit card information).
+The data
is used to generate an inputPaymentCredentials constructor.
+Eventually, you can set the save
flag to save the credit card info for future use, only if 2FA is enabled.
Telegram does not have access to your card information. Credit card details will be handled only by the payment system.
+inputPaymentCredentials#3417d728 flags:# save:flags.0?true data:DataJSON = InputPaymentCredentials;
+
+payments.paymentForm#3f56aea3 flags:# can_save_credentials:flags.2?true password_missing:flags.3?true bot_id:int invoice:Invoice provider_id:int url:string native_provider:flags.4?string native_params:flags.4?DataJSON saved_info:flags.0?PaymentRequestedInfo saved_credentials:flags.1?PaymentSavedCredentials users:Vector<User> = payments.PaymentForm;
+Most telegram apps support working natively with the native APIs of some payment providers, without opening the website of the payment and receiving a JS event.
+This is done using the JSON native_params
parameters field of the payments.paymentForm constructor, which contains an object, which can contain one or more of the following fields:
publishable_key
: Stripe API publishable keyapple_pay_merchant_id
: Apple Pay merchant IDandroid_pay_public_key
: Android Pay public keyandroid_pay_bgcolor
: Android Pay form background colorandroid_pay_inverse
: Whether to use the dark theme in the Android Pay formneed_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 providedThe payment gateway to use is decided based on the value of the native_provider
field.
inputPaymentCredentials#3417d728 flags:# save:flags.0?true data:DataJSON = InputPaymentCredentials;
+
+payments.paymentForm#3f56aea3 flags:# can_save_credentials:flags.2?true password_missing:flags.3?true bot_id:int invoice:Invoice provider_id:int url:string native_provider:flags.4?string native_params:flags.4?DataJSON saved_info:flags.0?PaymentRequestedInfo saved_credentials:flags.1?PaymentSavedCredentials users:Vector<User> = payments.PaymentForm;
+If the native_provider
field is set and equal to stripe
, the client can make use of the native Stripe token APIs with the publishable_key
from the native_params
to add a payment method to Stripe, and then use the token type
and id
to generate a JSON object:
{"type":"token.type", "id":"token.id"}"
+The generated JSON object can then be passed to the data
field of the inputPaymentCredentials.
+Eventually, you can set the save
flag to save the credit card info for future use, only if 2FA is enabled.
Telegram does not have access to your card information. Credit card details will be handled only by the payment system.
+Example implementation: Telegram for Android.
+inputPaymentCredentialsApplePay#aa1c39f payment_data:DataJSON = InputPaymentCredentials;
+On iOS devices, Apple Pay can be used to generate payment data, which is then sent using the inputPaymentCredentialsApplePay constructor.
+Example implementation: Telegram for iOS.
+inputPaymentCredentialsAndroidPay#ca05d50e payment_token:DataJSON google_transaction_id:string = InputPaymentCredentials;
+On Android devices, Google Pay can be used to generate payment data, which is then sent using the inputPaymentCredentialsAndroidPay constructor.
+Example implementation: Telegram for Android.
+inputPaymentCredentialsSaved#c10eb2cf id:string tmp_password:bytes = InputPaymentCredentials;
+
+paymentSavedCredentialsCard#cdc27a1f id:string title:string = PaymentSavedCredentials;
+
+payments.paymentForm#3f56aea3 flags:# can_save_credentials:flags.2?true password_missing:flags.3?true bot_id:int invoice:Invoice provider_id:int url:string native_provider:flags.4?string native_params:flags.4?DataJSON saved_info:flags.0?PaymentRequestedInfo saved_credentials:flags.1?PaymentSavedCredentials users:Vector<User> = payments.PaymentForm;
+
+account.tmpPassword#db64fd34 tmp_password:bytes valid_until:int = account.TmpPassword;
+
+---functions---
+
+account.getTmpPassword#449e0b51 password:InputCheckPasswordSRP period:int = account.TmpPassword;
+To reuse saved payment methods, the saved_credentials
field of the payment form is used.
+The title
of the paymentSavedCredentialsCard can be used to preview a censored version of credit card info.
+The id
field is provided by the payment provider directly to the Telegram servers when saving the payment method, and identifies the payment method.
+Full credit card info is not saved on Telegram Servers, and cannot be fetched by the user.
In order to use the saved payment method, 2FA must be enabled: the user must verify their identity by entering their 2FA password, which is then used as described in the SRP docs to generate SRP parameters which must be passed to account.getTmpPassword.
+The generated temporary password can then be used to make payments using the saved credentials using the inputPaymentCredentialsSaved constructor.
+id
field is the paymentSavedCredentialsCard id
.tmp_password
is the temporary payment password generated by the server, if the user provided a correct 2FA password.Example implementation: Telegram for Android.
+inputPaymentCredentialsSaved#c10eb2cf id:string tmp_password:bytes = InputPaymentCredentials;
+inputPaymentCredentials#3417d728 flags:# save:flags.0?true data:DataJSON = InputPaymentCredentials;
+inputPaymentCredentialsApplePay#aa1c39f payment_data:DataJSON = InputPaymentCredentials;
+inputPaymentCredentialsAndroidPay#ca05d50e payment_token:DataJSON google_transaction_id:string = InputPaymentCredentials;
+
+payments.paymentResult#4e5f810d updates:Updates = payments.PaymentResult;
+payments.paymentVerificationNeeded#d8411139 url:string = payments.PaymentResult;
+
+---functions---
+
+payments.sendPaymentForm#2b8879b3 flags:# msg_id:int requested_info_id:flags.0?string shipping_option_id:flags.1?string credentials:InputPaymentCredentials = payments.PaymentResult;
+After verifying order information, the final step for the client is to call payments.sendPaymentForm, with the following parameters:
+msg_id
is set to the ID of the invoice messagerequested_info_id
is set to the id
of the verified order information, if it was requestedshipping_option_id
is set to the selected delivery option, if shipping was requested.credentials
are the payment credentials generated by the payment provider, required to complete the order.Payment method info can also be saved to the Telegram Servers and reused, by setting the save
flag of inputPaymentCredentials when sending the form.
+This is only possible on accounts with 2FA enabled.
The bot then replies to the received precheckout query, finally the user proceeds to checkout.
+Please note that if the result of the method is a payments.paymentVerificationNeeded, before proceeding to checkout the payment provider requires the user to verify his identity by opening the provided url
and following instructions.
+Once the user finishes working with the webpage, the client can proceed to checkout.
Eventual errors are returned in the form of RPC errors, with the description of the error by the bot contained in service updates.
+paymentRequestedInfo#909c3f94 flags:# name:flags.0?string phone:flags.1?string email:flags.2?string shipping_address:flags.3?PostAddress = PaymentRequestedInfo;
+
+updateBotPrecheckoutQuery#5d2f3aa9 flags:# query_id:long user_id:int payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string currency:string total_amount:long = Update;
+
+---functions---
+
+messages.setBotPrecheckoutResults#9c2dd95 flags:# success:flags.1?true query_id:long error:flags.0?string = Bool;
+The user enters their payment information as described above and presses the final pay button. +At this moment the Telegram API sends an updateBotPrecheckoutQuery constructor that contains all the available information about the order to the bot. +The bot must reply using messages.setBotPrecheckoutResults within 10 seconds after receiving this update or the transaction is canceled.
+The bot may return an error if it can‘t process the order for any reason. We highly recommend specifying a reason for failure to complete the order in human readable form (e.g. "Sorry, we’re all out of rubber ducks! Would you be interested in a steel bear instead?"). Telegram will display this reason to the user.
+keyboardButtonBuy#afd93fbb text:string = KeyboardButton;
+
+keyboardButtonRow#77608b83 buttons:Vector<KeyboardButton> = KeyboardButtonRow;
+replyInlineMarkup#48a30254 rows:Vector<KeyboardButtonRow> = ReplyMarkup;
+
+messageMediaInvoice#84551347 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string = MessageMedia;
+
+message#58ae39c9 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> = Message;
+
+updateNewMessage#1f2b0afd message:Message pts:int pts_count:int = Update;
+
+payments.paymentReceipt#500911e1 flags:# date:int bot_id:int invoice:Invoice provider_id:int info:flags.0?PaymentRequestedInfo shipping:flags.1?ShippingOption currency:string total_amount:long credentials_title:string users:Vector<User> = payments.PaymentReceipt;
+
+---functions---
+
+payments.getPaymentReceipt#a092a980 msg_id:int = payments.PaymentReceipt;
+In case the bot confirms the order, Telegram requests the payment provider to complete the transaction. If the payment information was entered correctly and the payment goes through, the Telegram API will modify the invoice message and send a service message as described below. Once your bot receives this message, it should proceed with delivering the goods or services purchased by the user.
+If all is OK, the user receives a payments.paymentResult in reply to the payments.sendPaymentForm query, containing info about the updated invoice message in the form of an updateEditMessage.
+The invoice message will be updated as follows: the attached messageMediaInvoice will now have a receipt_msg_id
field.
+Clients should treat invoice messages with a receipt_msg_id
field as receipt messages, locally modifying the label of the keyboardButtonBuy button to a localized version of the word Receipt
.
+From this point, clicking on the Receipt
button should trigger a call to payments.getPaymentReceipt, providing the receipt_msg_id
to the msg_id
field, which will return info about the transaction.
The payment will also generate one service message of type messageActionPaymentSent or messageActionPaymentSentMe, replying to the invoice. +For bots, the service message will be of type messageActionPaymentSentMe, for users it will be a messageActionPaymentSent.
+messageActionPaymentSentMe#8f31b327 flags:# currency:string total_amount:long payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string charge:PaymentCharge = MessageAction;
+messageActionPaymentSent#40699cd0 currency:string total_amount:long = MessageAction;
++This article is about Perfect Forward Secrecy in cloud chats, see also PFS in Secret Chats.
+
Telegram supports Perfect Forward Secrecy (PFS).
+To make this possible, the client generates a permanent authorization key using p_q_inner_data and a temporary key using p_q_inner_data_temp. (See Creating an Authorization Key for more info.) These 2 operations may be done in parallel and even using the same connection. The client must save an expires_at unix timestamp expires_at = time + expires_in
.
Important: in order to achieve PFS, the client must never use the permanent auth_key_id directly. Every message that is sent to MTProto, must be encrypted by a temp_auth_key_id, that was bound to the perm_auth_key_id.
+An unbound temp_auth_key_id may only be used with the following methods:
+ +In order to bind a temporary authorization key to the permanent key the client creates a special binding message and executes the auth.bindTempAuthKey method using temp_auth_key. Once auth.bindTempAuthKey has been executed successfully, the client may signUp / signIn using other auth.* methods and continue using the API as usual; the client must also rewrite client info using initConnection after each binding. Each permanent key may only be bound to one temporary key at a time, binding a new temporary key overwrites the previous one.
+Once the temporary key expires, the client needs to generate a new temporary key using p_q_inner_data_temp. Then it needs to re-bind that new temporary key to the initial permanent key. A new key can also be generated in advance, so that the client has a new key ready by the time the old one has expired.
+For additional security, the client can store the temporary authorization key in RAM only and never save it in persistent storage.
+A temporary authorization key may expire at any moment before expires_at, since such keys are also stored only in the RAM on the server-side. Be prepared to handle resulting MTProto errors correctly (non-existent auth_key_id results in a 404 error).
Telegram allows pinning multiple messages on top of a specific chat.
+message#58ae39c9 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> = Message;
+
+updatePinnedMessages#ed85eab5 flags:# pinned:flags.0?true peer:Peer messages:Vector<int> pts:int pts_count:int = Update;
+updatePinnedChannelMessages#8588878b flags:# pinned:flags.0?true channel_id:int messages:Vector<int> pts:int pts_count:int = Update;
+
+---functions---
+
+messages.updatePinnedMessage#d2aaf7ec flags:# silent:flags.0?true unpin:flags.1?true pm_oneside:flags.2?true peer:InputPeer id:int = Updates;
+messages.unpinAllMessages#f025bc8b peer:InputPeer = messages.AffectedHistory;
+
+messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
+channels.getMessages#ad8c9a23 channel:InputChannel id:Vector<InputMessage> = messages.Messages;
+The messages.updatePinnedMessage method can be used to pin or unpin a specific message in an arbitrary chat.
+The unpin
flags specifies whether to unpin or pin the message, and pm_oneside
specifies whether the message should only be pinned on the local side of a one-to-one chat.
messages.unpinAllMessages can be used to unpin all messages in a chat.
+When (un)pinning messages, a updatePinnedMessages or updatePinnedChannelMessages update will be emitted, containing IDs of pinned or unpinned messages.
+Pinned messages will also have the will also have the pinned
flag of message set.
userFull#edf17c12 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int = UserFull;
+chatFull#1b7c9db3 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int = ChatFull;
+channelFull#f0e6672a flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int = ChatFull;
+
+inputMessagesFilterPinned#1bb00451 = MessagesFilter;
+
+---functions---
+
+messages.search#c352eec flags:# peer:InputPeer q:string from_id:flags.0?InputPeer top_msg_id:flags.1?int filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
+The pinned_msg_id
of userFull, chatFull, channelFull contains the ID of only the latest pinned message.
+To obtain a full list, use messages.search with inputMessagesFilterPinned filter.
Telegram allows sending polls and quizes, that can be voted on by thousands, if not milions of users in chats and channels.
+pollAnswer#6ca9c2e9 text:string option:bytes = PollAnswer;
+
+poll#86e18161 id:long flags:# closed:flags.0?true public_voters:flags.1?true multiple_choice:flags.2?true quiz:flags.3?true question:string answers:Vector<PollAnswer> close_period:flags.4?int close_date:flags.5?int = Poll;
+
+inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> solution:flags.1?string solution_entities:flags.1?Vector<MessageEntity> = InputMedia;
+
+---functions---
+
+messages.sendMedia#3491eba9 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
+To send a poll in a chat, call messages.sendMedia, providing an inputMediaPoll:
+poll
is the actual poll constructor, containing:
question
- The poll title, aka the poll's titleanswers
- A vector of possible answers (2-10), each with a visible title text
, and a unique option
identifier (1-100 bytes)closed
- Whether the poll is closedpublic_voters
- Whether cast votes are publicly visible to all users (non-anonymous poll) multiple_choice
- Whether multiple options can be chosen as answerquiz
- Whether this is a quiz with correct answer IDs specified in inputMediaPoll.correct_answers
close_period
- Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with close_date
.close_date
- Point in time (Unix timestamp) 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
. correct_answers
- For quizes, option ID of the only correct answer
solution
- Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters with at most 2 line feeds
solution_entities
- Styled text message entities for the solution
explanation
In order to prematurely close the poll, preventing further votes, use messages.editMessage, setting the poll.closed
flag to true.
pollAnswerVoters#3b6ddad2 flags:# chosen:flags.0?true correct:flags.1?true option:bytes voters:int = PollAnswerVoters;
+
+pollResults#badcc1a3 flags:# min:flags.0?true results:flags.1?Vector<PollAnswerVoters> total_voters:flags.2?int recent_voters:flags.3?Vector<int> solution:flags.4?string solution_entities:flags.4?Vector<MessageEntity> = PollResults;
+
+poll#86e18161 id:long flags:# closed:flags.0?true public_voters:flags.1?true multiple_choice:flags.2?true quiz:flags.3?true question:string answers:Vector<PollAnswer> close_period:flags.4?int close_date:flags.5?int = Poll;
+
+messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia;
+
+updateMessagePoll#aca1657b flags:# poll_id:long poll:flags.0?Poll results:PollResults = Update;
+
+---functions---
+
+messages.sendVote#10ea6184 peer:InputPeer msg_id:int options:Vector<bytes> = Updates;
+When receiving a message with a messageMediaPoll, users can vote in it using messages.sendVote, specifying the chosen option
identifiers.
The method will return an updateMessagePoll, containing an updated pollResults constructor, with the chosen
flag set on the options we chose, and the correct
flag set on the correct answers.
pollAnswerVoters#3b6ddad2 flags:# chosen:flags.0?true correct:flags.1?true option:bytes voters:int = PollAnswerVoters;
+
+pollResults#badcc1a3 flags:# min:flags.0?true results:flags.1?Vector<PollAnswerVoters> total_voters:flags.2?int recent_voters:flags.3?Vector<int> solution:flags.4?string solution_entities:flags.4?Vector<MessageEntity> = PollResults;
+
+updateMessagePoll#aca1657b flags:# poll_id:long poll:flags.0?Poll results:PollResults = Update;
+
+---functions---
+
+messages.getPollResults#73bb643b peer:InputPeer msg_id:int = Updates;
+Regularly, if new users have voted in polls available to the user, they will receive an updateMessagePoll, with updated pollResults.
+The same constructor can also be fetched manually using messages.getPollResults.
+messageUserVote#a28e5559 user_id:int option:bytes date:int = MessageUserVote;
+messageUserVoteInputOption#36377430 user_id:int date:int = MessageUserVote;
+messageUserVoteMultiple#e8fe0de user_id:int options:Vector<bytes> date:int = MessageUserVote;
+
+messages.votesList#823f649 flags:# count:int votes:Vector<MessageUserVote> users:Vector<User> next_offset:flags.0?string = messages.VotesList;
+
+updateMessagePollVote#42f88f2c poll_id:long user_id:int options:Vector<bytes> = Update;
+
+---functions---
+
+messages.getPollVotes#b86e380e flags:# peer:InputPeer id:int option:flags.0?bytes offset:flags.1?string limit:int = messages.VotesList;
+messages.getPollVotes can be used to get poll results for non-anonymous polls, to see how each user voted for a poll option.
+Bots will also receive an updateMessagePollVote every time a user their answer in a non-anonymous poll. Bots receive new votes only in polls that were sent by the bot itself.
To be able to send APNS notifications to Apple servers or GCM notifications to Google servers, application certificates (APNS) or an application key (GCM) must be specified in the application settings.
+To subscribe to notifications, the client must invoke the account.registerDevice query, passing in token_type and token as parameters that identify the current device. It is useful to repeat this query at least once every 24 hours or when restarting the application. Use account.unregisterDevice to unsubscribe.
+The following modes are supported:
+1
- APNS (device token for apple push)2
- FCM (firebase token for google firebase)3
- MPNS (channel URI for microsoft push)4
- Deprecated: Simple push (endpoint for firefox's simple push API)5
- Ubuntu phone (token for ubuntu push)6
- Blackberry (token for blackberry push)7
- MTProto separate session8
- 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 with the following keys:
endpoint
: Absolute URL exposed by the push service where the application server can send push messageskeys
: P-256 elliptic curve Diffie-Hellman parameters in the following objectp256dh
: Base64url-encoded P-256 elliptic curve Diffie-Hellman public keyauth
: Base64url-encoded authentication secretFor FCM and APNS VoIP, an optional encryption key used to encrypt push notifications can be passed to account.registerDevice (secret
). This key (auth_key
) is used to encrypt the payloads using MTProto v2.
The FCM payload will be a JSON payload, containing a p
field with the base64-encoded encrypted MTProto payload. After decryption, the result will be a JSON object, prefixed by a 32-bit little-endian integer with the length of the JSON payload. As usual, make sure to follow all security checks as described in the MTProto docs.
As mentioned above, payloads can also be encrypted using P-256 Elliptic Curve Diffie-Hellman when using web push.
+An (optionally encrypted) notification is provided as a JSON object in the following format:
+{
+ "data": {
+ "loc_key": "CHAT_MESSAGE_CONTACT",
+ "loc_args": ["John Doe", "My magical group", "Contact Exchange"],
+ "user_id": 14124122,
+ "custom": {
+ "chat_id": 241233,
+ "msg_id": 123
+ },
+ "sound": "sound1.mp3",
+ }
+}
+Each notification has several parameters that describe it.
+data.loc_key - A string literal in the form /[A-Z_0-9]+/
, which summarizes the notification. For example, CHAT_MESSAGE_TEXT
.
data.loc_args - A list or arguments which, when inserted into a template, produce a readable notification.
+data.custom - Parameters which are be passed into the application when a notification is opened.
+data.sound - The name of an audio file to be played.
+data.user_id - ID of the account to which the PUSH notification should be delivered, in case of clients with multiple accounts active and running.
+In principle, data.loc_key, data.custom, and an Internet connection are sufficient to generate a notification. Obviously, if possible, when generating a visual notification you need not use all of the transmitted data and may rely on the information already stored on the client. But if a user or a chat is not cached locally, the values passed in loc_args may also be used. data.user_id is the ID of the account to which the PUSH notification should be delivered, in case of clients with multiple accounts active and running.
+The following notifications can be used to update app settings.
+Type | +Extra custom arguments | +Description | +
---|---|---|
DC_UPDATE | +dc - number of the data-center addr - server address with port number in the format 111.112.113.114:443 |
+In case the client gets this notification, it is necessary to add the received server address to the list of possible addresses. In case the address of the first DC was passed (dc=1 ), it is recommended to call it immediately using help.getConfig to update dc-configuration. |
+
MESSAGE_DELETED | +channel_id: For channels and supergroups, Channel/supergroup identifier chat_id: For chats, Chat identifier from_id: For PMs, Author identifier messages: Comma-separated IDs of messages that were deleted |
+Messages were deleted, remove multiple notifications for this chat | +
READ_HISTORY | +channel_id: For channels and supergroups, Channel/supergroup identifier chat_id: For chats, Chat identifier from_id: For PMs, Author identifier max_id: Maximum ID of read messages |
+Message history was read, remove multiple notifications for this chat | +
GEO_LIVE_PENDING | ++ | Any of the live locations currently being shared should be updated | +
SESSION_REVOKE | ++ | Logout and remove DB for specified session by data.user_id, only apply if coming from an MTProto-encrypted payload | +
MESSAGE_MUTED | ++ | Sent rarely, every 10th message in chats or once per 15 sec in PM, to update badge or download messages | +
Type | +Template example | +Arguments | +Extra custom arguments | +
---|---|---|---|
MESSAGE_PLAYLIST | +{1} sent you {2} music files | +1. User name 2. Number of audio files that were sent |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_DOCS | +{1} sent you {2} files | +1. User name 2. Number of documents that were sent |
+attachb64: Base64-encoded version of the attached media (related to the first message) chat_from_id: Groups only, message author identifier (related to the first message) edit_date: When was the message last edited (related to the first message) from_id: Author identifier (related to the first message) mention: Whether the user was mentioned in the message (related to the first message) msg_id: ID of the message (related to the first message) silent: Whether the message was posted silently (no notification should be issued) (related to the first message) |
+
CHAT_MESSAGE_PLAYLIST | +{1} sent {3} music files to the group {2} | +1. User name 2. Group name 3. Number of audio files that were sent |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier chat_id: Chat identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
CHAT_MESSAGE_DOCS | +{1} sent {3} files to the group {2} | +1. User name 2. Group name 3. Number of documents that were sent |
+attachb64: Base64-encoded version of the attached media (related to the first message) chat_from_id: Groups only, message author identifier (related to the first message) chat_id: Chat identifier (related to the first message) edit_date: When was the message last edited (related to the first message) mention: Whether the user was mentioned in the message (related to the first message) msg_id: ID of the message (related to the first message) silent: Whether the message was posted silently (no notification should be issued) (related to the first message) |
+
CHANNEL_MESSAGE_PLAYLIST | +{1} posted {2} music files | +1. Channel name 2. Number of audio files that were posted |
+attachb64: Base64-encoded version of the attached media channel_id: Channel/supergroup identifier chat_from_id: Groups only, message author identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
CHANNEL_MESSAGE_DOCS | +{1} posted {2} files | +1. Channel name 2. Number of documents that were posted |
+attachb64: Base64-encoded version of the attached media (related to the first message) channel_id: Channel/supergroup identifier (related to the first message) chat_from_id: Groups only, message author identifier (related to the first message) edit_date: When was the message last edited (related to the first message) mention: Whether the user was mentioned in the message (related to the first message) msg_id: ID of the message (related to the first message) silent: Whether the message was posted silently (no notification should be issued) (related to the first message) |
+
MESSAGE_TEXT | +{1}: {2} | +1. Message author 2. Message body |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_NOTEXT | +{1} sent you a message | +1. Message author | +attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_PHOTO | +{1} sent you a photo | +1. Message author | +attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_PHOTO_SECRET | +{1} sent you a self-destructing photo | +1. User name | +attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_VIDEO | +{1} sent you a video | +1. Message author | +attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_VIDEO_SECRET | +{1} sent you a self-destructing video | +1. User name | +attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_SCREENSHOT | +{1} took a screenshot | +1. User name | +attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_ROUND | +{1} sent you a video message | +1. User name | +attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_DOC | +{1} sent you a file | +1. User name | +attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_STICKER | +{1} sent you a {2}sticker | +1. User name 2. Sticker emoji with included trailing space or empty string |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_AUDIO | +{1} sent you a voice message | +1. Message author | +attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_CONTACT | +{1} shared a contact {2} with you | +1. User name 2. Contact name |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_QUIZ | +{1} sent you a quiz {2} | +1. User name 2. Quiz name |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_GEO | +{1} sent you a map | +1. Message author | +attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_GEOLIVE | +{1} started sharing their live location | +1. User name | +attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_POLL | +{1} sent you a poll {2} | +1. User name 2. Poll name |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_GIF | +{1} sent you a GIF | +1. User name | +attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_GAME | +{1} invited you to play {2} | +1. User name 2. Game name |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_GAME_SCORE | +{1} scored {3} in game {2} | +1. User name 2. Game name 3. Score |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_INVOICE | +{1} sent you an invoice for {2} | +1. User name 2. Product |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier edit_date: When was the message last edited from_id: Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
MESSAGE_FWDS | +{1} forwarded you {2} messages | +1. User name 2. Number of messages that were forwarded |
+attachb64: Base64-encoded version of the attached media (related to the first message) chat_from_id: Groups only, message author identifier (related to the first message) edit_date: When was the message last edited (related to the first message) from_id: Author identifier (related to the first message) mention: Whether the user was mentioned in the message (related to the first message) msg_id: ID of the message (related to the first message) silent: Whether the message was posted silently (no notification should be issued) (related to the first message) |
+
MESSAGE_PHOTOS | +{1} sent you {2} photos | +1. User name 2. Number of photos that were sent |
+attachb64: Base64-encoded version of the attached media (related to the first message) chat_from_id: Groups only, message author identifier (related to the first message) edit_date: When was the message last edited (related to the first message) from_id: Author identifier (related to the first message) mention: Whether the user was mentioned in the message (related to the first message) msg_id: ID of the message (related to the first message) silent: Whether the message was posted silently (no notification should be issued) (related to the first message) |
+
MESSAGE_VIDEOS | +{1} sent you {2} videos | +1. User name 2. Number of videos that were sent |
+attachb64: Base64-encoded version of the attached media (related to the first message) chat_from_id: Groups only, message author identifier (related to the first message) edit_date: When was the message last edited (related to the first message) from_id: Author identifier (related to the first message) mention: Whether the user was mentioned in the message (related to the first message) msg_id: ID of the message (related to the first message) silent: Whether the message was posted silently (no notification should be issued) (related to the first message) |
+
MESSAGES | +{1} sent you an album | +1. User name | +from_id: author identifier | +
CHANNEL_MESSAGE_TEXT | +{1}: {2} | +1. Channel name 2. Message body |
+attachb64: Base64-encoded version of the attached media channel_id: Channel/supergroup identifier chat_from_id: Groups only, message author identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHANNEL_MESSAGE_NOTEXT | +{1} posted a message | +1. Channel name | +attachb64: Base64-encoded version of the attached media channel_id: Channel/supergroup identifier chat_from_id: Groups only, message author identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHANNEL_MESSAGE_PHOTO | +{1} posted a photo | +1. Channel name | +attachb64: Base64-encoded version of the attached media channel_id: Channel/supergroup identifier chat_from_id: Groups only, message author identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHANNEL_MESSAGE_VIDEO | +{1} posted a video | +1. Channel name | +attachb64: Base64-encoded version of the attached media channel_id: Channel/supergroup identifier chat_from_id: Groups only, message author identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHANNEL_MESSAGE_ROUND | +{1} posted a video message | +1. Channel name | +attachb64: Base64-encoded version of the attached media channel_id: Channel/supergroup identifier chat_from_id: Groups only, message author identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHANNEL_MESSAGE_DOC | +{1} posted a file | +1. Message author | +attachb64: Base64-encoded version of the attached media channel_id: Channel/supergroup identifier chat_from_id: Groups only, message author identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHANNEL_MESSAGE_STICKER | +{1} posted a {2}sticker | +1. Channel name 2. Sticker emoji with included trailing space or empty string |
+attachb64: Base64-encoded version of the attached media channel_id: Channel/supergroup identifier chat_from_id: Groups only, message author identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHANNEL_MESSAGE_AUDIO | +{1} posted a voice message | +1. Message author | +attachb64: Base64-encoded version of the attached media channel_id: Channel/supergroup identifier chat_from_id: Groups only, message author identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHANNEL_MESSAGE_CONTACT | +{1} posted a contact {2} | +1. Message author 2. Contact name |
+attachb64: Base64-encoded version of the attached media channel_id: Channel/supergroup identifier chat_from_id: Groups only, message author identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHANNEL_MESSAGE_GEO | +{1} posted a map | +1. Channel name | +attachb64: Base64-encoded version of the attached media channel_id: Channel/supergroup identifier chat_from_id: Groups only, message author identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHANNEL_MESSAGE_GEOLIVE | +{1} posted a live location | +1. Channel name | +attachb64: Base64-encoded version of the attached media channel_id: Channel/supergroup identifier chat_from_id: Groups only, message author identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHANNEL_MESSAGE_POLL | +{1} posted a poll {2} | +1. Channel name 2. Poll name |
+attachb64: Base64-encoded version of the attached media channel_id: Channel/supergroup identifier chat_from_id: Groups only, message author identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHANNEL_MESSAGE_QUIZ | +{1} posted a quiz {2} | +1. Channel name 2. Quiz name |
+attachb64: Base64-encoded version of the attached media channel_id: Channel/supergroup identifier chat_from_id: Groups only, message author identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHANNEL_MESSAGE_GIF | +{1} posted a GIF | +1. Channel name | +attachb64: Base64-encoded version of the attached media channel_id: Channel/supergroup identifier chat_from_id: Groups only, message author identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHANNEL_MESSAGE_GAME | +{1} invited you to play {2} | +1. Message author 2. Game name |
+attachb64: Base64-encoded version of the attached media channel_id: Channel/supergroup identifier chat_from_id: Groups only, message author identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHANNEL_MESSAGE_GAME_SCORE | +{1} scored {3} in game {2} | +1. User 2. Game name 3. Score |
+attachb64: Base64-encoded version of the attached media channel_id: Channel/supergroup identifier chat_from_id: Groups only, message author identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHANNEL_MESSAGE_FWDS | +{1} posted {2} forwarded messages | +1. Message author 2. Number of forwarded messages |
+attachb64: Base64-encoded version of the attached media (related to the first message) channel_id: Channel/supergroup identifier (related to the first message) chat_from_id: Groups only, message author identifier (related to the first message) edit_date: When was the message last edited (related to the first message) mention: Whether the user was mentioned in the message (related to the first message) msg_id: ID of the message (related to the first message) silent: Whether the message was posted silently (no notification should be issued) (related to the first message) |
+
CHANNEL_MESSAGE_PHOTOS | +{1} posted {2} photos | +1. Channel name 2. Number of photos that was sent |
+attachb64: Base64-encoded version of the attached media (related to the first message) channel_id: Channel/supergroup identifier (related to the first message) chat_from_id: Groups only, message author identifier (related to the first message) edit_date: When was the message last edited (related to the first message) mention: Whether the user was mentioned in the message (related to the first message) msg_id: ID of the message (related to the first message) silent: Whether the message was posted silently (no notification should be issued) (related to the first message) |
+
CHANNEL_MESSAGE_VIDEOS | +{1} posted {2} videos | +1. Channel name 2. Number of videos that were posted |
+attachb64: Base64-encoded version of the attached media (related to the first message) channel_id: Channel/supergroup identifier (related to the first message) chat_from_id: Groups only, message author identifier (related to the first message) edit_date: When was the message last edited (related to the first message) mention: Whether the user was mentioned in the message (related to the first message) msg_id: ID of the message (related to the first message) silent: Whether the message was posted silently (no notification should be issued) (related to the first message) |
+
CHANNEL_MESSAGES | +{1} posted an album | +1. Message author | +attachb64: Base64-encoded version of the attached media (related to the first message) channel_id: Channel/supergroup identifier (related to the first message) chat_from_id: Groups only, message author identifier (related to the first message) edit_date: When was the message last edited (related to the first message) mention: Whether the user was mentioned in the message (related to the first message) msg_id: ID of the message (related to the first message) silent: Whether the message was posted silently (no notification should be issued) (related to the first message) |
+
CHAT_MESSAGE_TEXT | +{1}@{2}: {3} | +1. Message author 2. Chat name 3. Message body |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier chat_id: Chat identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHAT_MESSAGE_NOTEXT | +{1} sent a message to the group {2} | +1. Message author 2. Chat name |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier chat_id: Chat identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHAT_MESSAGE_PHOTO | +{1} sent a photo to the group {2} | +1. Message author 2. Chat name |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier chat_id: Chat identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHAT_MESSAGE_VIDEO | +{1} sent a video to the group {2} | +1. Message author 2. Chat name |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier chat_id: Chat identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHAT_MESSAGE_ROUND | +{1} sent a video message to the group {2} | +1. User name 2. Group name |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier chat_id: Chat identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHAT_MESSAGE_DOC | +{1} sent a file to the group {2} | +1. User name 2. Group name |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier chat_id: Chat identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHAT_MESSAGE_STICKER | +{1} sent a {3}sticker to the group {2} | +1. User name 2. Group name 3. Sticker emoji with included trailing space or empty string |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier chat_id: Chat identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHAT_MESSAGE_AUDIO | +{1} sent a voice message to the group {2} | +1. Message author 2. Chat name |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier chat_id: Chat identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHAT_MESSAGE_CONTACT | +{1} shared a contact {3} in the group {2} | +1. User name 2. Group name 3. Contact name |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier chat_id: Chat identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHAT_MESSAGE_GEO | +{1} sent a map to the group {2} | +1. Message author 2. Chat name |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier chat_id: Chat identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHAT_MESSAGE_GEOLIVE | +{1} started sharing their live location with {2} | +1. User name 2. Group name |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier chat_id: Chat identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHAT_MESSAGE_POLL | +{1} sent a poll {3} to the group {2} | +1. User name 2. Group name 3. Poll name |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier chat_id: Chat identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHAT_MESSAGE_QUIZ | +{1} sent a quiz {3} to the group {2} | +1. User name 2. Group name 3. Quiz name |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier chat_id: Chat identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHAT_MESSAGE_GIF | +{1} sent a GIF to the group {2} | +1. User name 2. Group name |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier chat_id: Chat identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHAT_MESSAGE_GAME | +{1} invited the group {2} to play {3}. | +1. User name 2. Group name 3. Game name |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier chat_id: Chat identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHAT_MESSAGE_GAME_SCORE | +{1} scored {4} in game {3} in the group {2} | +1. User name 2. Group name 3. Game name 4. Score |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier chat_id: Chat identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHAT_MESSAGE_INVOICE | +{1} sent an invoice to the group {2} for {3} | +1. User name 2. Group name 3. Product name |
+attachb64: Base64-encoded version of the attached media chat_from_id: Groups only, message author identifier chat_id: Chat identifier edit_date: When was the message last edited mention: Whether the user was mentioned in the message msg_id: ID of the message scheduled: Whether this is a scheduled message silent: Whether the message was posted silently (no notification should be issued) |
+
CHAT_CREATED | +{1} invited you to the group {2} | +1. Message author 2. Chat name |
+chat_id: chat identifier | +
CHAT_TITLE_EDITED | +{1} edited the group's {2} name | +1. User name 2. Group name |
+chat_id: chat identifier | +
CHAT_PHOTO_EDITED | +{1} edited the group's {2} photo | +1. Message author 2. Chat name |
+chat_from_id: Message author identifier chat_id: chat identifier msg_id: ID of the message |
+
CHAT_ADD_MEMBER | +{1} invited {3} to the group {2} | +1. Message author 2. Chat name 3. New participant name |
+chat_id: chat identifier | +
CHAT_ADD_YOU | +{1} invited you to the group {2} | +1. User name 2. Group name |
+chat_id: chat identifier | +
CHAT_DELETE_MEMBER | +{1} kicked {3} from the group {2} | +1. Message author 2. Chat name 3. Dropped participant name |
+chat_id: chat identifier | +
CHAT_DELETE_YOU | +{1} kicked you from the group {2} | +1. Message author 2. Chat name |
+chat_id: chat identifier | +
CHAT_LEFT | +{1} has left the group {2} | +1. Message author 2. Chat name |
+chat_id: chat identifier | +
CHAT_RETURNED | +{1} has returned to the group {2} | +1. Message author 2. Chat name |
+chat_id: chat identifier | +
CHAT_JOINED | +{1} has joined the group {2} | +1. User name 2. Group name |
+chat_id: chat identifier | +
CHAT_MESSAGE_FWDS | +{1} forwarded {3} messages to the group {2} | +1. User name 2. Group name 3. Number of messages that were forwarded |
+attachb64: Base64-encoded version of the attached media (related to the first message) chat_from_id: Groups only, message author identifier (related to the first message) chat_id: Chat identifier (related to the first message) edit_date: When was the message last edited (related to the first message) mention: Whether the user was mentioned in the message (related to the first message) msg_id: ID of the message (related to the first message) silent: Whether the message was posted silently (no notification should be issued) (related to the first message) |
+
CHAT_MESSAGE_PHOTOS | +{1} sent {3} photos to the group {2} | +1. User name 2. Group name 3. Number of photos that were sent |
+attachb64: Base64-encoded version of the attached media (related to the first message) chat_from_id: Groups only, message author identifier (related to the first message) chat_id: Chat identifier (related to the first message) edit_date: When was the message last edited (related to the first message) mention: Whether the user was mentioned in the message (related to the first message) msg_id: ID of the message (related to the first message) silent: Whether the message was posted silently (no notification should be issued) (related to the first message) |
+
CHAT_MESSAGE_VIDEOS | +{1} sent {3} videos to the group {2} | +1. User name 2. Group name 3. Number of videos that were sent |
+attachb64: Base64-encoded version of the attached media (related to the first message) chat_from_id: Groups only, message author identifier (related to the first message) chat_id: Chat identifier (related to the first message) edit_date: When was the message last edited (related to the first message) mention: Whether the user was mentioned in the message (related to the first message) msg_id: ID of the message (related to the first message) silent: Whether the message was posted silently (no notification should be issued) (related to the first message) |
+
CHAT_MESSAGES | +{1} sent an album to the group {2} | +1. User name 2. Group name |
+chat_from_id: Message author identifier chat_id: chat identifier mention: Whether the user was mentioned in the message |
+
PINNED_TEXT | +{1} pinned "{2}" | +1. User name 2. Message body |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_TEXT | +{1} pinned "{3}" in the group {2} | +1. User name 2. Group name 3. Message body |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_NOTEXT | +{1} pinned a message | +1. User name | +attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_NOTEXT | +{1} pinned a message in the group {2} | +1. User name 2. Group name |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_PHOTO | +{1} pinned a photo | +1. User name | +attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_PHOTO | +{1} pinned a photo in the group {2} | +1. User name 2. Group name |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_VIDEO | +{1} pinned a video | +1. User name | +attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_VIDEO | +{1} pinned a video in the group {2} | +1. User name 2. Group name |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_ROUND | +{1} pinned a video message | +1. User name | +attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_ROUND | +{1} pinned a video message in the group {2} | +1. User name 2. Group name |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_DOC | +{1} pinned a file | +1. User name | +attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_DOC | +{1} pinned a file in the group {2} | +1. User name 2. Group name |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_STICKER | +{1} pinned a {2}sticker | +1. User name 2. Sticker emoji with included trailing space or empty string |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_STICKER | +{1} pinned a {3}sticker in the group {2} | +1. User name 2. Group name 3. Sticker emoji with included trailing space or empty string |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_AUDIO | +{1} pinned a voice message | +1. User name | +attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_AUDIO | +{1} pinned a voice message in the group {2} | +1. User name 2. Group name |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_CONTACT | +{1} pinned a contact {2} | +1. User name 2. Contact name |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_CONTACT | +{1} pinned a contact {3} in the group {2} | +1. User name 2. Group name 3. Contact name |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_GEO | +{1} pinned a map | +1. User name | +attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_GEO | +{1} pinned a map in the group {2} | +1. User name 2. Group name |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_GEOLIVE | +{1} pinned a live location | +1. User name | +attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_GEOLIVE | +{1} pinned a live location in the group {2} | +1. User name 2. Group name |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_POLL | +{1} pinned a poll {2} | +1. User name 2. Poll name |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_POLL | +{1} pinned a poll {3} in the group {2} | +1. User name 2. Group name 3. Poll name |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_QUIZ | +{1} pinned a quiz {2} | +1. User name 2. Quiz name |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_QUIZ | +{1} pinned a quiz {3} in the group {2} | +1. User name 2. Group name 3. Quiz name |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_GAME | +{1} pinned a game | +1. User name | +attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_GAME | +{1} pinned a game in the group {2} | +1. User name 2. Group name |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_GAME_SCORE | +{1} pinned a game score | +1. User name | +attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_GAME_SCORE | +{1} pinned a game score in the group {2} | +1. User name 2. Group name |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_INVOICE | +{1} pinned an invoice | +1. User name | +attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_INVOICE | +{1} pinned an invoice in the group {2} | +1. User name 2. Group name |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_GIF | +{1} pinned a GIF | +1. User name | +attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
PINNED_GIF | +{1} pinned a GIF in the group {2} | +1. User name 2. Group name |
+attachb64: Base64-encoded version of the attached media channel_id: For channels and supergroups, Channel/supergroup identifier chat_from_id: Groups only, message author identifier chat_id: For chats, Chat identifier edit_date: When was the message last edited from_id: For PMs, Author identifier mention: Whether the user was mentioned in the message msg_id: ID of the message silent: Whether the message was posted silently (no notification should be issued) |
+
CONTACT_JOINED | +{1} joined Telegram! | +1. Contact name | +contact_id: contact identifier | +
AUTH_UNKNOWN | +New login from unrecognized device {1} | +1. Device name | ++ |
AUTH_REGION | +New login from unrecognized device {1}, location: {2} | +1. Device name 2. Location |
++ |
ENCRYPTION_REQUEST | +You have a new message | ++ | encryption_id: secret chat identifier | +
ENCRYPTION_ACCEPT | +You have a new message | ++ | encryption_id: secret chat identifier | +
ENCRYPTED_MESSAGE | +You have a new message | ++ | encryption_id: secret chat identifier random_id: message identifier |
+
LOCKED_MESSAGE | +You have a new message | ++ | + |
PHONE_CALL_REQUEST | +{1} is calling you! | +1. User name | +call_ah: Call access hash call_id: Call ID |
+
PHONE_CALL_MISSED | +You missed a call from {1} | +1. User name | ++ |
MESSAGE_ANNOUNCEMENT | +{1} | +1. Announcement | +announcement: Announcement: roughly equivalent to a message received from the service notifications (Telegram Notifications, id 777000 ) user, but must be delivered via push notifications, without contacting the API. |
+
QR code login flow.
+Related TL schema:
+auth.loginToken#629f1980 expires:int token:bytes = auth.LoginToken;
+auth.loginTokenMigrateTo#68e9916 dc_id:int token:bytes = auth.LoginToken;
+auth.loginTokenSuccess#390d5c5e authorization:auth.Authorization = auth.LoginToken;
+
+updateLoginToken#564fe691 = Update;
+
+authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true hash:long device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization;
+
+---functions---
+
+auth.exportLoginToken#b1b41517 api_id:int api_hash:string except_ids:Vector<int> = auth.LoginToken;
+auth.acceptLoginToken#e894ad4d token:bytes = Authorization;
+auth.importLoginToken#95ac5ce4 token:bytes = auth.LoginToken;
+First of all, auth.exportLoginToken must be called by the app that wants to log in to an existing Telegram account.
+The method will return an auth.loginToken constructor, containing a binary login token
and an expiry date (usually 30 seconds).
The login token
must be encoded using base64url, embedded in a tg://login?token=base64encodedtoken
URL and shown in the form of a QR code to the user.
+After the expiration of the current QR code, the auth.exportLoginToken method must be recalled and a new QR code must be generated automatically.
In order to log in, the QR code must be scanned and accepted by an already logged-in Telegram app using auth.acceptLoginToken.
+The token must be extracted from the tg://login
URI and base64url-decoded before using it in the method.
Possible errors returned by the method are:
+AUTH_TOKEN_INVALID
, an invalid authorization token was providedAUTH_TOKEN_EXPIRED
, the provided authorization token has expired and the updated QR-code must be re-scannedAUTH_TOKEN_ALREADY_ACCEPTED
, the authorization token was already usedThe method will return an authorization object, containing info about the app and session that we just authorized.
+After the logged-in app calls auth.acceptLoginToken and accepts the login token, the app that is trying to login will receive an updateLoginToken update, which should trigger a second call to the auth.exportLoginToken method.
+This second call should then return an auth.loginTokenSuccess constructor, indicating successful login, essentially allowing further authorized interaction with the API.
+If, however, there is a DC mismatch between the two apps, auth.loginTokenMigrateTo is returned instead, to which the app that is trying to login should respond by calling auth.importLoginToken with the specified token
, to the specified DC.
This call should then finally return a auth.loginTokenSuccess constructor.
Both supergroups and channels offer a so-called admin log, a log of recent relevant supergroup and channel actions, like the modification of group/channel settings or information on behalf of an admin, user kicks and bans, and more.
+channelAdminLogEventActionChangeTitle#e6dfb825 prev_value:string new_value:string = ChannelAdminLogEventAction;
+channelAdminLogEventActionChangeAbout#55188a2e prev_value:string new_value:string = ChannelAdminLogEventAction;
+channelAdminLogEventActionChangeUsername#6a4afc38 prev_value:string new_value:string = ChannelAdminLogEventAction;
+channelAdminLogEventActionChangePhoto#434bd2af prev_photo:Photo new_photo:Photo = ChannelAdminLogEventAction;
+channelAdminLogEventActionToggleInvites#1b7907ae new_value:Bool = ChannelAdminLogEventAction;
+channelAdminLogEventActionToggleSignatures#26ae0971 new_value:Bool = ChannelAdminLogEventAction;
+channelAdminLogEventActionUpdatePinned#e9e82c18 message:Message = ChannelAdminLogEventAction;
+channelAdminLogEventActionEditMessage#709b2405 prev_message:Message new_message:Message = ChannelAdminLogEventAction;
+channelAdminLogEventActionDeleteMessage#42e047bb message:Message = ChannelAdminLogEventAction;
+channelAdminLogEventActionParticipantJoin#183040d3 = ChannelAdminLogEventAction;
+channelAdminLogEventActionParticipantLeave#f89777f2 = ChannelAdminLogEventAction;
+channelAdminLogEventActionParticipantInvite#e31c34d8 participant:ChannelParticipant = ChannelAdminLogEventAction;
+channelAdminLogEventActionParticipantToggleBan#e6d83d7e prev_participant:ChannelParticipant new_participant:ChannelParticipant = ChannelAdminLogEventAction;
+channelAdminLogEventActionParticipantToggleAdmin#d5676710 prev_participant:ChannelParticipant new_participant:ChannelParticipant = ChannelAdminLogEventAction;
+channelAdminLogEventActionChangeStickerSet#b1c3caa7 prev_stickerset:InputStickerSet new_stickerset:InputStickerSet = ChannelAdminLogEventAction;
+channelAdminLogEventActionTogglePreHistoryHidden#5f5c95f1 new_value:Bool = ChannelAdminLogEventAction;
+channelAdminLogEventActionDefaultBannedRights#2df5fc0a prev_banned_rights:ChatBannedRights new_banned_rights:ChatBannedRights = ChannelAdminLogEventAction;
+channelAdminLogEventActionStopPoll#8f079643 message:Message = ChannelAdminLogEventAction;
+channelAdminLogEventActionChangeLinkedChat#a26f881b prev_value:int new_value:int = ChannelAdminLogEventAction;
+channelAdminLogEventActionChangeLocation#e6b76ae prev_value:ChannelLocation new_value:ChannelLocation = ChannelAdminLogEventAction;
+channelAdminLogEventActionToggleSlowMode#53909779 prev_value:int new_value:int = ChannelAdminLogEventAction;
+
+channelAdminLogEvent#3b5a3e40 id:long date:int user_id:int action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
+
+channels.adminLogResults#ed8af74d events:Vector<ChannelAdminLogEvent> chats:Vector<Chat> users:Vector<User> = channels.AdminLogResults;
+
+channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true = ChannelAdminLogEventsFilter;
+
+---functions---
+
+channels.getAdminLog#33ddf480 flags:# channel:InputChannel q:string events_filter:flags.0?ChannelAdminLogEventsFilter admins:flags.1?Vector<InputUser> max_id:long min_id:long limit:int = channels.AdminLogResults;
+channels.getAdminLog can be used to list recent admin activity.
+A channelAdminLogEventsFilter can be used to filter out actions of a certain type, and the admins
field can be used to show only actions by certain admins.
+q
can also be used to filter only logs matching a query string.
Let's consider several typical interaction scenarios for two users.
+User A knows phone number of B (for instance, A has B's number in the phone book). But B does not know А's number. User A sends message to B. Immediately prior to sending a message relevant check is executed at the server side and the link between A and B will change automatically: A's number becomes available for B (user A is then presented as constructor userRequest). +The same happens if user B not having A's number sends a message to the latter. A's number also becomes available to B.
+User A has found user B in a chat, geochat or any other way. For both of them phone numbers are not available (in API - constructor userForeign). A starts chat with B. When messaging A's number will not become available for B and vice versa. To provide B with A's number it is necessary to send him personal message with contact info (phone number should be transferred in constructor inputMediaContact using method messages.sendMedia). +Now B having personal message with A's phone number can store it in his phone book and import it using method contacts.importContacts to have it in contacts. With all that, it is not mandatory for him to send his number: after any message later between them B's number will be available for A (see One user knows the other's number).
+This is quite obvious: messaging does not change links since target state has been reached.
+Since when sending messages to a current user updates on changed links are not sent constructors containing updated links were added to resulting types of messaging methods:
+messages.statedMessagesLinks messages:Vector<Message> chats:Vector<Chat> users:Vector<User> links:Vector<contacts.Link> pts:int seq:int = messages.StatedMessages;
+messages.statedMessageLink message:Message chats:Vector<Chat> users:Vector<User> links:Vector<contacts.Link> pts:int seq:int = messages.StatedMessage;
+messages.sentMessageLink id:int date:int pts:int seq:int links:Vector<contacts.Link> = messages.SentMessage;
+To indicate for clients that these constructors are supported one should use 3rd layer.
Channels and supergroups allow setting granular permissions both for admins and specific users. +Channels, supergroups and legacy groups also allow setting global granular permissions for users.
+They can be modified as follows:
+channels.editAdmin can be used to modify the admin rights of a user in a channel or supergroup. +Legacy groups do not allow setting granular admin permissions, messages.editChatAdmin has to be used, instead.
+Permissions are defined by the chatAdminRights constructor, some admin rights can only be used for channels, others both for channels and supergroups (see the constructor page).
+channels.editBanned can be used to modify the rights of a user in a channel or supergroup, to ban/kick a user from the group, or restrict the user from doing certain things. +Legacy groups do not allow setting granular user permissions for single users, single users can only be removed from groups using messages.deleteChatUser: however, setting global granular permissions with legacy groups is supported.
+Permissions are defined by the chatBannedRights constructor, for more info see the constructor page.
+messages.editChatDefaultBannedRights can be used to modify the rights of all users in a channel, supergroup or legacy group, to restrict them from doing certain things.
+Permissions are defined by the chatBannedRights constructor: all flags can be used except for view_messages
, for more info see the constructor page.
Telegram allows scheduling messages.
+message#58ae39c9 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> = Message;
+
+updateNewScheduledMessage#39a51dfb message:Message = Update;
+updateDeleteScheduledMessages#90866cee peer:Peer messages:Vector<int> = Update;
+
+---functions---
+
+messages.sendMessage#520c3870 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
+
+messages.sendMedia#3491eba9 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
+To schedule a message, simply provide a future unixtime in the schedule_date
flag of messages.sendMessage or messages.sendMedia.
The specified message or media will be added to a server-side schedule queue for the current chat, and will be automatically sent at the specified time.
+The method call generates the following updates:
schedule_date
, an updateNewMessage or updateNewChannelMessage with the from_scheduled
flag set, indicating to the sender that the specified scheduled message was sent. schedule_date
, an updateDeleteScheduledMessages, indicating that the message was flushed from the schedule queue.If the schedule_date
is less than 10 seconds in the future, the message will be sent immediately, generating a normal updateNewMessage/updateNewChannelMessage .
updateNewScheduledMessage#39a51dfb message:Message = Update;
+updateDeleteScheduledMessages#90866cee peer:Peer messages:Vector<int> = Update;
+
+---functions---
+
+messages.getScheduledHistory#e2c2685b peer:InputPeer hash:int = messages.Messages;
+messages.getScheduledMessages#bdbb0464 peer:InputPeer id:Vector<int> = messages.Messages;
+messages.sendScheduledMessages#bd38850a peer:InputPeer id:Vector<int> = Updates;
+messages.deleteScheduledMessages#59ae2b16 peer:InputPeer id:Vector<int> = Updates;
+
+messages.editMessage#48f71778 flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.15?int = Updates;
+Clients can manually edit the schedule queue of a certain chat, providing the scheduled message ID obtained from updateNewScheduledMessage.
+Modifying scheduled messages will generate an updateNewScheduledMessage with the same ID, and updated information.
+Deleting scheduled messages will generate an updateDeleteScheduledMessages.
Telegram allows applying detailed message filters while looking for messages in chats.
+inputMessagesFilterEmpty#57e2f66c = MessagesFilter;
+inputMessagesFilterPhotos#9609a51c = MessagesFilter;
+inputMessagesFilterVideo#9fc00e65 = MessagesFilter;
+inputMessagesFilterPhotoVideo#56e9f0e4 = MessagesFilter;
+inputMessagesFilterDocument#9eddf188 = MessagesFilter;
+inputMessagesFilterUrl#7ef0dd87 = MessagesFilter;
+inputMessagesFilterGif#ffc86587 = MessagesFilter;
+inputMessagesFilterVoice#50f5c392 = MessagesFilter;
+inputMessagesFilterMusic#3751b49e = MessagesFilter;
+inputMessagesFilterChatPhotos#3a20ecb8 = MessagesFilter;
+inputMessagesFilterPhoneCalls#80c99768 flags:# missed:flags.0?true = MessagesFilter;
+inputMessagesFilterRoundVoice#7a7c17a4 = MessagesFilter;
+inputMessagesFilterRoundVideo#b549da53 = MessagesFilter;
+inputMessagesFilterMyMentions#c1f8e69a = MessagesFilter;
+inputMessagesFilterGeo#e7026d0d = MessagesFilter;
+inputMessagesFilterContacts#e062db83 = MessagesFilter;
+inputMessagesFilterPinned#1bb00451 = MessagesFilter;
+
+messages.messages#8c718e87 messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
+messages.messagesSlice#3a54685e flags:# inexact:flags.1?true count:int next_rate:flags.0?int offset_id_offset:flags.2?int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
+messages.channelMessages#64479808 flags:# inexact:flags.1?true pts:int count:int offset_id_offset:flags.2?int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
+messages.messagesNotModified#74535f21 count:int = messages.Messages;
+
+---functions---
+
+messages.search#c352eec flags:# peer:InputPeer q:string from_id:flags.0?InputPeer top_msg_id:flags.1?int filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
+
+messages.searchGlobal#4bc6589a flags:# folder_id:flags.0?int q:string filter:MessagesFilter min_date:int max_date:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
+When using messages.search or messages.searchGlobal, a certain message filter may be applied.
+This allows the server to filter messages based on a text query, and even on their type, and this feature is often used by graphical clients to implement features like the chat gallery, chat profile pictures and more.
+Available filters:
The returned messages.Messages constructors contain parameters for pagination, the messages themselves and two offset_id_offset
/count
parameters that can be used to display a progress/total
counter like photo 134 of 200
.
+For example, when displaying the chat photo gallery, we could display a photo ${offset_id_offset} of ${count}
indicator on top.
messages.searchCounter#e844ebff flags:# inexact:flags.1?true filter:MessagesFilter count:int = messages.SearchCounter;
+
+---functions---
+
+messages.getSearchCounters#732eef00 peer:InputPeer filters:Vector<MessagesFilter> = Vector<messages.SearchCounter>;
+Chat counters with filters can also be returned without fetching the actual messages, as seen in the scheme above.
Telegram uses the Secure Remote Password protocol version 6a to implement 2FA.
+Example impementation: tdlib.
+To login to an account protected by a 2FA password or to perform some other actions (like changing channel owner), you will need to verify the user's knowledge of the current 2FA account password.
+To do this, first the client needs to obtain SRP parameters and the KDF algorithm to use to check the validity of the password via account.getPassword method. For now, only the passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow algorithm is supported, so we'll only explain that.
+Then, after the user provides a password, the client should generate an InputCheckPasswordSRP object using SRP and a specific KDF algorithm as shown below and pass it to appropriate method (e.g. auth.checkPassword in case of authorization).
+This extension of the SRP protocol uses the password-based PBKDF2 with 100000 iterations using sha512 (PBKDF2HMACSHA512iter100000
).
PBKDF2 is used to additionally rehash the x
parameter, obtained using a method similar to the one described in RFC 2945 (H(s | H ( I | password | I) | s)
instead of H(s | H ( I | ":" | password)
) (see below).
Here, |
denotes concatenation and +
denotes the arithmetical operator +
.
In all cases where concatenation of numbers passed to hashing functions is done, the numbers must be used in big-endian form, padded to 2048 bits; all maths is modulo p
.
Instead of I
, salt1
will be used (see SRP protocol).
Instead of s
, salt2
will be used (see SRP protocol).
The main hashing function H
is sha256:
H(data) := sha256(data)
The salting hashing function SH
is defined as follows:
SH(data, salt) := H(salt | data | salt)
The primary password hashing function is defined as follows:
+PH1(password, salt1, salt2) := SH(SH(password, salt1), salt2)
The secondary password hashing function is defined as follows:
+PH2(password, salt1, salt2) := SH(pbkdf2(sha512, PH1(password, salt1, salt2), salt1, 100000), salt2)
Client-side, the following parameters are extracted from the passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow object, contained in the account.password object.
+g := algo.g
p := algo.p
The client is expected to check whether p is a safe 2048-bit prime (meaning that both p and (p-1)/2 are prime, and that 2^2047 < p < 2^2048
), and that g generates a cyclic subgroup of prime order (p-1)/2, i.e. is a quadratic residue mod p. Since g is always equal to 2, 3, 4, 5, 6 or 7, this is easily done using quadratic reciprocity law, yielding a simple condition on p mod 4g — namely, p mod 8 = 7 for g = 2; p mod 3 = 2 for g = 3; no extra condition for g = 4; p mod 5 = 1 or 4 for g = 5; p mod 24 = 19 or 23 for g = 6; and p mod 7 = 3, 5 or 6 for g = 7. After g and p have been checked by the client, it makes sense to cache the result, so as to avoid repeating lengthy computations in future. This cache might be shared with one used for Authorization Key generation.
If the client has an inadequate random number generator, it makes sense to use the secure_random of account.password as additional seed.
+password := (user-provided password)
salt1 := algo.salt1
salt2 := algo.salt2
g_b := srp_B
srp_B
and srp_id
are extracted from the account.password object.The k
parameter is generated, both on client and server:
k := H(p | g)
The shared param u
is generated: the client does this, and the server does the same with the g_a
we will send him later (see below)
u := H(g_a | g_b)
The final parameters are generated client-side only:
+x := PH2(password, salt1, salt2)
v := pow(g, x) mod p
The server already has v
, from when we set the password.
A final shared param is generated, for commodity:
+k_v := (k * v) mod p
Finally, the key exchange process starts on both parties.
+The client computes a 2048-bit number a (using sufficient entropy or the server’s random; see above) and generates:
+g_a := pow(g, a) mod p
.The server computes a 2048-bit number b using sufficient entropy and generates the g_b
parameter that was sent to us (see above).
g_b := (k_v + (pow(g, b) mod p)) mod p
Finally, the SRP session keys are generated:
+Client side:
+t := (g_b - k_v) mod p
(positive modulo, if the result is negative increment by p
) s_a := pow(t, a + u * x) mod p
k_a := H(s_a)
Server side:
+s_b := pow(g_a * (pow(v, u) mod p), b) mod p
k_b := H(s_b)
Since:
+g_b := (k_v + (pow(g, b) mod p)) mod p
t := (g_b - k_v) mod p
t := ((k_v + (pow(g, b) mod p)) - k_v) mod p
t := pow(g, b) mod p
s_a := pow(t, a + u * x) mod p
s_a := pow(pow(g, b) mod p, a + u * x) mod p
And:
+g_a := pow(g, a) mod p
v := pow(g, x) mod p
s_b := pow(g_a * (pow(v, u) mod p), b) mod p
s_b := pow((pow(g, a) mod p) * (pow(pow(g, x) mod p, u) mod p), b) mod p
s_b := pow(pow(g, a + x * u) mod p, b) mod p
s_b := pow(pow(g, b) mod p, a + u * x) mod p
s_a := pow(pow(g, b) mod p, a + u * x) mod p
This means:
+s_b === s_a
k_b === k_a
Finally, as per SRP:
+M1 := H(H(p) xor H(g) | H(salt1) | H(salt2) | g_a | g_b | k_a)
M1
is passed to inputCheckPasswordSRP, along with g_a
(as A
parameter) and the srp_id
, extracted from the account.password object.
The server then computes:
+M2 := H(H(p) xor H(g) | H(salt1) | H(salt2) | g_a | g_b | k_b)
Since we said that:
+s_b === s_a
k_b === k_a
This means, if everything was done correctly,
+M1 === M2
If the password isn't correct, 400 PASSWORD_HASH_INVALID will be returned.
+To set a new 2FA password use the account.updatePasswordSettings method.
If a password is already set, generate an InputCheckPasswordSRP object as per checking passwords with SRP, and insert it in the password
field of the account.updatePasswordSettings method.
To remove the current password, pass an empty new_password_hash
in the account.PasswordInputSettings object.
To set a new password, use the SRP parameters and the KDF algorithm obtained using account.getPassword when generating the password
field.
Then generate a new new_password_hash
using the KDF algorithm specified in the new_settings
, just append 32 sufficiently random bytes to the salt1
, first.
Proceed as for checking passwords with SRP, just stop at the generation of the v
parameter, and use it as new_password_hash
:
v := pow(g, x) mod p
As usual in big endian form, padded to 2048 bits.
+When setting up two-factor authorization, it is recommended to set up a recovery email, to allow recovery of the password through the user's email address, in case they forget it.
+To set up a recovery email, it must first be verified.
This can be done directly when setting the new password using account.updatePasswordSettings by setting the email parameter and flag in the account.passwordInputSettings constructor.
If the email isn't verified, an EMAIL_UNCONFIRMED_X 400 error will be returned, where X is the length of the verification code that was just sent to the email.
Use account.confirmPasswordEmail to enter the received verification code and enable the recovery email.
Use account.resendPasswordEmail to resend the verification code.
Use account.cancelPasswordEmail to cancel the verification code.
To get the current recovery email, use account.getPasswordSettings.
+In order to recover a forgotten 2FA password, an email must be sent to the previously specified address using the auth.requestPasswordRecovery method.
Then use auth.recoverPassword with the received code to delete the current 2FA password, to set a new one follow these instructions.
Telegram offers detailed channel statistics for channels and supergroups.
+Scheme:
+statsDateRangeDays#b637edaf min_date:int max_date:int = StatsDateRangeDays;
+
+statsAbsValueAndPrev#cb43acde current:double previous:double = StatsAbsValueAndPrev;
+
+statsPercentValue#cbce2fe0 part:double total:double = StatsPercentValue;
+
+statsGraphAsync#4a27eb2d token:string = StatsGraph;
+statsGraphError#bedc9822 error:string = StatsGraph;
+statsGraph#8ea464b6 flags:# json:DataJSON zoom_token:flags.0?string = StatsGraph;
+
+messageInteractionCounters#ad4fc9bd msg_id:int views:int forwards:int = MessageInteractionCounters;
+
+stats.broadcastStats#bdf78394 period:StatsDateRangeDays followers:StatsAbsValueAndPrev views_per_post:StatsAbsValueAndPrev shares_per_post:StatsAbsValueAndPrev enabled_notifications:StatsPercentValue growth_graph:StatsGraph followers_graph:StatsGraph mute_graph:StatsGraph top_hours_graph:StatsGraph interactions_graph:StatsGraph iv_interactions_graph:StatsGraph views_by_source_graph:StatsGraph new_followers_by_source_graph:StatsGraph languages_graph:StatsGraph recent_message_interactions:Vector<MessageInteractionCounters> = stats.BroadcastStats;
+
+---functions---
+
+stats.getBroadcastStats#ab42441a flags:# dark:flags.0?true channel:InputChannel = stats.BroadcastStats;
+stats.loadAsyncGraph#621d5fa0 flags:# token:string x:flags.0?long = StatsGraph;
+Administrators of channels of a certain size (the exact limit is a server-side config, returned in the can_view_stats
flag of channelFull) can call stats.getBroadcastStats to get detailed channel statistics.
+The returned stats.broadcastStats contains multiple statistics:
period
). period
typically depends on channel activity.followers
)total_viewcount/postcount
, for posts posted during the period in consideration (views_per_post
).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
). total_sharecount/postcount
, for posts posted during the period in consideration (shares_per_post
).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
).enabled_notifications
)Scheme:
+statsGroupTopPoster#18f3d0f7 user_id:int messages:int avg_chars:int = StatsGroupTopPoster;
+statsGroupTopInviter#31962a4c user_id:int invitations:int = StatsGroupTopInviter;
+statsGroupTopAdmin#6014f412 user_id:int deleted:int kicked:int banned:int = StatsGroupTopAdmin;
+
+stats.megagroupStats#ef7ff916 period:StatsDateRangeDays members:StatsAbsValueAndPrev messages:StatsAbsValueAndPrev viewers:StatsAbsValueAndPrev posters:StatsAbsValueAndPrev growth_graph:StatsGraph members_graph:StatsGraph new_members_by_source_graph:StatsGraph languages_graph:StatsGraph messages_graph:StatsGraph actions_graph:StatsGraph top_hours_graph:StatsGraph weekdays_graph:StatsGraph top_posters:Vector<StatsGroupTopPoster> top_admins:Vector<StatsGroupTopAdmin> top_inviters:Vector<StatsGroupTopInviter> users:Vector<User> = stats.MegagroupStats;
+
+---functions---
+
+stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel = stats.MegagroupStats;
+Administrators of supergroups of a certain size (the exact limit is a server-side config, returned in the can_view_stats
flag of channelFull) can call stats.getMegagroupStats to get detailed supergroup statistics.
+The returned stats.broadcastStats contains multiple statistics, see the constructor page for more info ».
stats.messageStats#8999f295 views_graph:StatsGraph = stats.MessageStats;
+
+---functions---
+
+stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
+Administrators of channels of a certain size (the exact limit is a server-side config, returned in the can_view_stats
flag of channelFull) can call stats.getMessageStats to get statistics of a specific message.
+The returned stats.messageStats contains the view graph of the message.
There are four available visualizations for graph types:
+ +Graph modifiers (see various graphs for examples):
+y_scaled
- Indicates that each of the two (!) lines in a step graph must be visualized on its own scale, with two different tick axes on the left and right parts of the graphpercentage
- Indicates whether value percentages should be shown in labelsstacked
- Depending on the graph type, indicates stacking of multiple columns in the same graphstatsGraphAsync#4a27eb2d token:string = StatsGraph;
+statsGraphError#bedc9822 error:string = StatsGraph;
+statsGraph#8ea464b6 flags:# json:DataJSON zoom_token:flags.0?string = StatsGraph;
+
+dataJSON#7d748d04 data:string = DataJSON;
+
+---functions---
+
+stats.loadAsyncGraph#621d5fa0 flags:# token:string x:flags.0?long = StatsGraph;
+Certain graphs are not directly sent in the stats.broadcastStats constructor to reduce server load: instead, those graphs will be sent as a statsGraphAsync constructor, and should be fetched separately using stats.loadAsyncGraph.
+After obtaining the full statsGraph constructor, clients should parse the JSON graph object in the json
field.
Object structure:
+Key | +Graph title | +
---|---|
columns | +Array of all data column arrays in the chart. Each column array has its label at position 0, followed by values. | +
types | +Graphs types for each of the column arrays (object, "label": "type" ): - line - line graph - area - area graph - step - step graph - bar - bar graph - x - x axis values for each of the charts at the corresponding positions, UNIX timestamps in milliseconds. |
+
colors | +Color for each type (object, "label": "colorKey#AAAAAA" ; see chart colors). |
+
names | +Localized name for each variable (object, "label": "Name" ) |
+
subchart | +Object indicating the default zoom range for the graph, this is object's structure: - show - Whether to use the specified zoom range (boolean) - defaultZoom - An array with two x values, indicating the two ends of the default zoom range |
+
y_scaled | +Indicates that each of the two (!) lines in a step graph must be visualized on its own scale, with two different tick axes on the left and right parts of the graph (boolean, see various graphs for examples) | +
percentage | +Indicates whether value percentages should be shown in labels (boolean, see various graphs for examples) | +
stacked | +Depending on the graph type, indicates stacking of multiple columns in the same graph (boolean, see various graphs for examples) | +
The following chart restrictions apply:
+bar
chart type and stacked
option are always used together.percentage
is always used with the area
graph.
+
+statsGraphError#bedc9822 error:string = StatsGraph;
+statsGraph#8ea464b6 flags:# json:DataJSON zoom_token:flags.0?string = StatsGraph;
+
+dataJSON#7d748d04 data:string = DataJSON;
+
+---functions---
+
+stats.loadAsyncGraph#621d5fa0 flags:# token:string x:flags.0?long = StatsGraph;
+Graphs that support zooming will contain a zoom_token
in the statsGraph constructor.
+Said token should be then used as token
in a new stats.loadAsyncGraph call triggered when the user clicks on the label, related to a certain x axis in the graph (see graph examples).
+The x
coordinate of the label should be provided to the x
parameter; the method will then return (if available) a more detailed subgraph.
+If not enough data is available, a localized statsGraphError will be returned.
Typical zoom visualization rules:
+percentage
graph (even if a zoom_token
is not available) => piechartChart colors can be provided as a color key, followed by the primary color value in hex format:
+red#e05356
+The color key can be one of red
, lightblue
, lightgreen
, golden
, green
, orange
, blue
, indigo
.
+Apps can choose to use a color value specified by the currently loaded theme: for example, the android app uses statisticChartLine_*
themekeys for each of the color keys, check out the assets directory for a bunch of default themes with various colors for channel statistics.
However, the server may also choose to return just a plain color value in hex format:
+#e05356
+In this case, the dark
flag of the stats.getBroadcastStats method can be used to choose the palette of returned colors.
Simple single line graph
+Step graph, always "stacked" (to indicate multiple lines)
+Bar graph with multiple lines, always "stacked" (to indicate actual stacked bars, biggest bars first)
+Mixed bar/line graph, always "stacked" (to indicate actual stacked bars, biggest bars first)
+Piechart, typically obtained only when zooming into percentage graphs
We welcome all developers to use our API and source code to create Telegram-like messaging applications on our platform free of charge. In order to ensure consistency and security across the Telegram ecosystem, all third-party client apps must comply with the following Terms of Service.
+1.1. Telegram is a privacy-oriented platform. All client apps must, therefore, guard their users' privacy with utmost care and comply with our Security Guidelines.
+1.2. Developers are welcome to add new features or improve and extend existing Telegram features provided that these modifications do not violate these Terms of Service.
+1.3. As a client developer, you must make sure that all the basic features of the main Telegram apps function correctly and in an expected way both in your app and when users of your app communicate with other Telegram users. It is forbidden to force users of other Telegram clients to download your app in order to view certain messages and content sent using your app.
+1.4. It is forbidden to interfere with the basic functionality of Telegram. This includes but is not limited to: making actions on behalf of the user without the user's knowledge and consent, preventing self-destructing content from disappearing, preventing last seen and online statuses from being displayed correctly, tampering with the 'read' statuses of messages (e.g. implementing a 'ghost mode'), preventing typing statuses from being sent/displayed, etc.
2.1. You must obtain your own api_id for your application.
+2.2. We offer our API free of charge, but your users must be aware of the fact that your app uses the Telegram API and is part of the Telegram ecosystem. This fact must be featured prominently in the app's description in the app stores and in the in-app intro if your app has it.
+2.3. To avoid confusion, the title of your app must not include the word “Telegram”. An exception can be made if the word “Telegram” is preceded with the word “Unofficial” in the title.
+2.4. You must not use the official Telegram logo for your app. Both the Telegram brand and its logo are registered trademarks protected by law in almost every country.
3.1. Developers are allowed to monetize their coding efforts through advertising or other legitimate means.
+3.2. If you decide to monetize your app, you must clearly mention all the methods of monetization that are used in your app in all its app store descriptions.
4.1. If your app violates these terms, we will notify the Telegram account responsible for the app about the breach of terms.
+4.2. If you do not update the app to fix the highlighted issues within 10 days, we will have to discontinue your access to Telegram API and contact the app stores about the removal of your apps that are using the Telegram API in violation of these terms.
We reserve the right to expand these terms and guidelines as the need arises. We will inform client developers of such changes via an in-app notification to their accounts connected to the app in question.
++ +
Telegram allows commenting on a channel post or on a generic supergroup message, thanks to message threads.
+Schema:
+messageReplyHeader#a6d57763 flags:# reply_to_msg_id:int reply_to_peer_id:flags.0?Peer reply_to_top_id:flags.1?int = MessageReplyHeader;
+
+messageReplies#4128faac flags:# comments:flags.0?true replies:int replies_pts:int recent_repliers:flags.1?Vector<Peer> channel_id:flags.0?int max_id:flags.2?int read_max_id:flags.3?int = MessageReplies;
+
+message#58ae39c9 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> = Message;
+
+---functions---
+
+messages.search#c352eec flags:# peer:InputPeer q:string from_id:flags.0?InputPeer top_msg_id:flags.1?int filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
+Threads are usually automatically created when replying to any message in a group.
+For example, all replies to a message with ID 420
are associated to thread with ID 420
, unique to this group; this thread ID is contained in the reply_to_top_id
field of reply_to
messageReplyHeader, along with an eventual reply_to_msg_id
, for replies to messages within a thread.
+Replies to messages in a thread are part of the same thread, and do not spawn new threads.
When receiving a message from a group that is also the top of a thread (the message with ID 420
), the replies
optional field will contain a messageReplies constructor, containing the message ID and PTS of the latest reply in the thread, and the message ID of the latest read thread reply, along with the total number of replies in the thread.
Replies to a thread can also be manually fetched using messages.search, providing to top_msg_id
the thread ID.
messageReplies#4128faac flags:# comments:flags.0?true replies:int replies_pts:int recent_repliers:flags.1?Vector<Peer> channel_id:flags.0?int max_id:flags.2?int read_max_id:flags.3?int = MessageReplies;
+The same messageReplies constructor seen above will also be contained in channel posts, this time containing information about the comment section of a specific channel post.
+The comment section of a channel post is simply the message thread of the automatically forwarded channel message in the linked discussion supergroup; the ID of the linked discussion supergroup will be contained in the messageReplies.channel_id
field.
For channel posts, the recent_repliers
field will also contain information about the last few comment posters for a specific thread, to show a small list of commenter profile pictures in client previews.
messageFwdHeader#5f777dce flags:# from_id:flags.0?Peer from_name:flags.5?string date:int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int psa_type:flags.6?string = MessageFwdHeader;
+
+messageReplyHeader#a6d57763 flags:# reply_to_msg_id:int reply_to_peer_id:flags.0?Peer reply_to_top_id:flags.1?int = MessageReplyHeader;
+
+message#58ae39c9 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> = Message;
+
+updateNewMessage#1f2b0afd message:Message pts:int pts_count:int = Update;
+updateNewChannelMessage#62ba04d9 message:Message pts:int pts_count:int = Update;
+
+---functions---
+
+contacts.blockFromReplies#29a8962c flags:# delete_message:flags.0?true delete_history:flags.1?true report_spam:flags.2?true msg_id:int = Updates;
+
+contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer;
+Since a user can comment in channel posts without joining the actual discussion supergroup, there must be a way for them to receive notifications about replies in comment sections.
+For this reason, a special @replies
username is provided.
+Its ID for main and testing endpoints can be seen in the tdlib sources.
When someone replies to one of our messages in the comment section of a channel post, and the user is not subscribed to the discussion group, the client will receive two updates:
+id
set to the ID of the replyfrom_id
set to the peer that replied to uspeer_id
set to the peer of the discussion groupreply_to.reply_to_msg_id
set to the ID of our messagereply_to.reply_to_top_id
set to the thread ID. id
set to the common ID sequence for usersfrom_id
set to the peer of @replies
peer_id
set to our own peerfwd_from.saved_from_msg_id
set to the ID of the replyfwd_from.from_id
set to the the peer that replied to usreply_to.reply_to_peer_id
set to the peer of the discussion groupreply_to.reply_to_msg_id
set to the ID of our messagereply_to.reply_to_top_id
set to the thread IDClients should display messages coming from @replies
as a read-only supergroup, with each reply displayed as a separate message from the author of the reply, with a "View in chat" button like for channel comments.
If enabled, the rating of top peers indicates the relevance of a frequently used peer in a certain category (frequently messaged users, frequently used bots, inline bots, frequently visited channels and so on).
+Schema:
+topPeer#edcdc05b peer:Peer rating:double = TopPeer;
+
+topPeerCategoryBotsPM#ab661b5b = TopPeerCategory;
+topPeerCategoryBotsInline#148677e2 = TopPeerCategory;
+topPeerCategoryCorrespondents#637b7ed = TopPeerCategory;
+topPeerCategoryGroups#bd17a14a = TopPeerCategory;
+topPeerCategoryChannels#161d9628 = TopPeerCategory;
+topPeerCategoryPhoneCalls#1e76a78c = TopPeerCategory;
+topPeerCategoryForwardUsers#a8406ca9 = TopPeerCategory;
+topPeerCategoryForwardChats#fbeec0f0 = TopPeerCategory;
+
+topPeerCategoryPeers#fb834291 category:TopPeerCategory count:int peers:Vector<TopPeer> = TopPeerCategoryPeers;
+
+contacts.topPeersNotModified#de266ef5 = contacts.TopPeers;
+contacts.topPeers#70b772a8 categories:Vector<TopPeerCategoryPeers> chats:Vector<Chat> users:Vector<User> = contacts.TopPeers;
+contacts.topPeersDisabled#b52c939d = contacts.TopPeers;
+
+---functions---
+
+contacts.getTopPeers#d4982db5 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true forward_users:flags.4?true forward_chats:flags.5?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:int = contacts.TopPeers;
+The rate delta is computed by taking the time delta between the last time the user used a certain peer and the last time the rating for that peer was received and dividing it by the exponential decay from config.
+Example:
+Client-side, every time a user opens chat 123456789
the following operation must be done on the cached top peer info.
dateOpened
indicates when was the peer usednormalizeRate
is an arbitrary time in the recent past.
+When ratings are received from the server using contacts.getTopPeers and the scheme described above, it is the time when they were received.topPeer.rating += e^((dateOpened - normalizeRate) / config.rating_e_decay)
When a client is being actively used, events will occur that affect the current user and that they must learn about as soon as possible, e.g. when a new message is received. To eliminate the need for the client itself to periodically download these events, there is an update delivery mechanism in which the server sends the user notifications over one of its available connections with the client.
+Update events are sent to an authorized user into the last active connection (except for connections needed for downloading / uploading files).
+So to start receiving updates the client needs to init connection and call API method, e.g. to fetch current state.
+All events are received from the socket as a sequence of TL-serialized Updates objects, which might be optionally gzip-compressed in the same way as responses to queries.
+Each Updates object may contain single or multiple Update objects, representing different events happening.
+In order to apply all updates in precise order and to guarantee that no update is missed or applied twice there is seq
attribute in Updates constructors, and pts
(with pts_count
) or qts
attributes in Update constructors. The client must use those attributes values in combination with locally stored state to correctly apply incoming updates.
When a gap in updates sequence occurs, it must be filled via calling one of the API methods. More below »
+As said earlier, each payload with updates has a TL-type Updates. It can be seen from the schema below that this type has several constructors.
+updatesTooLong#e317af7e = Updates;
+updateShort#78d4dec1 update:Update date:int = Updates;
+updateShortMessage#2296d2c8 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int user_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader entities:flags.7?Vector<MessageEntity> = Updates;
+updateShortChatMessage#402d5dbb flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int from_id:int chat_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader entities:flags.7?Vector<MessageEntity> = Updates;
+updateShortSentMessage#11f1331c flags:# out:flags.1?true id:int pts:int pts_count:int date:int media:flags.9?MessageMedia entities:flags.7?Vector<MessageEntity> = Updates;
+updatesCombined#725b04c3 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq_start:int seq:int = Updates;
+updates#74ae4240 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq:int = Updates;
+updatesTooLong indicates that there are too many events pending to be pushed to the client, so one needs to fetch them manually.
+Events inside updateShort constructors, normally, have lower priority and are broadcast to a large number of users, i.e. one of the chat participants started entering text in a big conversation (updateChatUserTyping).
+The updateShortMessage, updateShortSentMessage and updateShortChatMessage constructors are redundant but help significantly reduce the transmitted message size for 90% of the updates. They should be transformed to updateShort upon receiving.
+Two remaining constructors updates and updatesCombined are part of the Updates sequence. Both of them have seq
attribute, which indicates the remote Updates state after the generation of the Updates, and seq_start
indicates the remote Updates state after the first of the Updates in the packet is generated. For updates, seq_start
attribute is omitted, because it is assumed that it is always equal to seq
.
Each event related to a message box (message created, message edited, message deleted, etc) is identified by a unique autoincremented pts (or qts in case of secret chats).
+Each message box can be considered as some server-side DB table that stores messages and events associated with them. +All boxes are completely independent, and each pts sequence is tied to just one box (see below).
+Update object may contain info about multiple events (for example, updateDeleteMessages).
+That's why all single updates might have pts_count parameter indicating the number of events contained in the received update (with some exceptions, in this case, the pts_count is considered to be 0
).
Each channel and supergroup has its message box and its event sequence as a result; private chats and legacy groups of one user have another common event sequence. +User's Secret chats have yet another common secret event sequence.
+To recap, the client has to take care of the integrity of the following sequences to properly handle updates:
+The common update state is represented by the updates.State constructor. +When the user logs in for the first time, call to updates.getState has to be made to store the latest update state (which will not be the absolute initial state, just the latest state at the current time). +The common update state can also be fetched from updates.differenceTooLong.
+The channel update state is represented simply by the pts of the event sequence: when first logging in, the initial channel state can be obtained from the dialog constructor when fetching dialogs, from the full channel info, or it can be received as an updateChannelTooLong update.
+The secret chat update state is represented by the qts of the secret event sequence, it is contained in the updates.State of the common update state.
+The Updates sequence state is represented by the date and seq of the Updates sequence, it is contained in the updates.State of the common update state.
+Update handling in Telegram clients consists of receiving events, making sure there were no gaps and no events were missed based on the locally stored state of the correspondent event sequence, and then updating the locally stored state based on the parameters received.
+When the client receives payload with serialized updates, first of all, it needs to walk through all of the nested Update objects and check if they belong to any of message box sequences (have pts
or qts
parameters). Those updates need to be handled separately according to corresponding local state and new pts
/qts
values. Details below »
After message box updates are handled, if there are any other updates remaining the client needs to handle them with respect to seq
. Details below »
pts
: checking and applyingHere, local_pts
will be the local state, pts
will be the remote state, pts_count
will be the number of events in the update.
If local_pts + pts_count === pts
, the update can be applied.
+If local_pts + pts_count > pts
, the update was already applied, and must be ignored.
+If local_pts + pts_count < pts
, there's an update gap that must be filled.
For example, let's assume the client has the following local state for the channel 123456789
:
local_pts = 131
+Now let's assume an updateNewChannelMessage from channel 123456789
is received with pts = 132
and pts_count=1
.
+Since local_pts + pts_count === pts
, the total number of events since the last stored state is, in fact, equal to pts_count
: this means the update can be safely accepted and the remote pts
applied:
local_pts = 132
+Since:
+pts
indicates the server state after the new channel message events are generatedpts_count
indicates the number of events in the new channel updatepts_before = pts - pts_count = 131
, which is, in fact, equal to our local state.Now let's assume an updateNewChannelMessage from channel 123456789
is received with pts = 132
and pts_count=1
.
+Since local_pts + pts_count > pts
(133 > 132
), the update is skipped because we've already handled this update (in fact, our current local_pts
was set by this same update, and it was resent twice due to network issues or other issues).
Now let's assume an updateDeleteChannelMessages from channel 123456789
is received with pts = 140
and pts_count=5
.
+Since local_pts + pts_count < pts
(137 < 140
), this means that updates were missed, and the gap must be recovered.
The whole process is very similar for secret chats, but there is qts
instead of pts
, and events are never grouped, so it's assumed that qts_count
is always equal to 1.
seq
: checking and applyingOn top level when handling received updates and updatesCombined there are three possible cases:
+If local_seq + 1 === seq_start
, the updates can be applied.
+If local_seq + 1 > seq_start
, the updates were already applied, and must be ignored.
+If local_seq + 1 < seq_start
, there's an updates gap that must be filled (updates.getDifference must be used as with common and secret event sequences).
If the updates were applied, local Updates state must be updated with seq
and date
from the constructor.
For all the other Updates type constructors there is no need to check seq
or change a local state.
To do this, updates.getDifference (common/secret state) or updates.getChannelDifference (channel state) with the respective local states must be called. +These methods should also be called on startup, to fetch new updates (preferably with some flags to reduce server load, see the method's docs). +Manually obtaining updates is also required in the following situations:
+When calling updates.getDifference if the updates.differenceSlice constructor is returned in response, the full difference was too large to be received in one request. The intermediate status, intermediate_state, must be saved on the client and the query must be repeated, using the intermediate status as the current status.
+To fetch the updates difference of a channel, updates.getChannelDifference is used.
+If the difference is too large to be received in one request, the final
flag of the result is not set (see docs).
+The intermediate status, represented by the pts, must be saved on the client and the query must be repeated, using the intermediate status as the current status.
For perfomance reasons and for better user experience, client can set maximum gap size to be filled: pts_total_limit
parameter of updates.getDifference and limit
parameter for updates.getChannelDifference can be used.
If the gap is too large and there are too many updates to fetch, a *TooLong
constructor will be returned. In this case, the client must re-fetch the state, re-start fetching updates from that state and follow the instructions that can be found here.
It is recommended to use limit 10-100
for channels and 1000-10000
otherwise.
Implementations also have to take care to postpone updates received via the socket while filling gaps in the event and Update sequences, as well as avoid filling gaps in the same sequence.
+Example implementations: tdlib, MadelineProto.
+An interesting and easy way this can be implemented, instead of using various locks, is by running background loops, like in MadelineProto ».
+If a client does not have an active connection at the time of an event, PUSH Notifications will also be useful.
Bots may ask users to login to a certain website via Telegram when clicking on certain URL buttons in inline keyboards.
+When the user clicks on keyboardButtonUrlAuth, messages.requestUrlAuth should be called, providing the button_id
of the button and the ID and peer of the container message.
+The returned urlAuthResultRequest object will contain more details about the authorization request:
domain
parameter will contain the domain name of the website on which the user will log in (example: comments.app).bot
parameter will contain info about the bot which will be used for user authorization (example: DiscussBot).request_write_access
will be set if the bot would like to send messages to the user.The info should be shown in a prompt:
+ +If the user agrees to login to the URL, messages.acceptUrlAuth should be called (eventually setting the write_allowed
if the permission was requested and the user consented).
+The result will be a urlAuthResultAccepted with the final URL to open, which will include a query string with the requested info and a hash that must be verified upon receival by the service.
urlAuthResultDefault could also be returned, instead, in which case the url
of the keyboardButtonUrlAuth must be opened, instead.
+The same must be done if the user opens the link while refusing the authorization request.
When interacting with HTML5 games and the websites of payment gateways, Telegram apps should expose APIs to allow receiving data and events from the websites.
+Games and payment gateways can generate events that are meant to be received by the Telegram apps.
+Typically events are generated by using the postEvent
method of the GamingCommunication library.
+The postEvent
function will try sending the event to the Telegram app in a number of different ways.
In mobile apps, the event receiver API should be typically exposed as a window.TelegramWebviewProxy
object with a postEvent
method.
window.TelegramWebviewProxy.postEvent(eventType, eventData)
+Alternatively, a window.external.notify
method can be exposed, accepting a string JSON payload with the event type and payload:
window.external.notify(JSON.stringify({eventType: eventType, eventData: eventData}));
+Finally, web MTProto clients that need to open a game or process a payment in an iframe can use the postMessage API to receive events from iframes.
+The GamingCommunication library by defaultwill use '*'
as targetOrigin
, sending messages to parent pages regardless of the origin of the embedder.
window.parent.postMessage(JSON.stringify({eventType: eventType, eventData: eventData}), targetOrigin);
+eventType
is a simple string indicating the event type, and eventData
is a payload with an object that will be parsed by the Telegram app.
eventType | +eventData | +Description | +
---|---|---|
payment_form_submit |
+JSON object with data and title fields |
+title is the censored credit card title.data is a service-specific JSON payload with information about the payment credentials provided by the user to the payment system.Neither Telegram, nor bots will have access to your credit card information. Credit card details will be handled only by the payment system. |
+
share_score |
+null | +Will be called by games when the user explicitly clicks on the share score button to share the game, along with his score. Typically done by using messages.forwardMessages on the game message with the with_my_score flag. |
+
share_game |
+null | +Will be called by games when the user explicitly clicks on the share game button to share the game, without sharing his score. Typically done by using messages.forwardMessages on the game message without the with_my_score flag, or by sharing the game's short URL. |
+
game_over |
+null | +Can be called by games when the user loses a game | +
game_loaded |
+null | +Can be called by games once the game fully loads | +
resize_frame |
+JSON object with height field |
+Called by supported pages inside of IV iframe embeds, indicates the new size of the embed frame. | +
Bots are third-party applications that run inside Telegram. Users can interact with bots by sending them messages, commands and inline requests. You control your bots using HTTPS requests to our Bot API.
+To name just a few things, you could use bots to:
+Get customized notifications and news. A bot can act as a smart newspaper, sending you relevant content as soon as it's published.
+Integrate with other services. A bot can enrich Telegram chats with content from external services.
Gmail Bot, GIF bot, IMDB bot, Wiki bot, Music bot, Youtube bot, GitHubBot
Accept payments from Telegram users. A bot can offer paid services or work as a virtual storefront. Read more »
Demo Shop Bot, Demo Store
Create custom tools. A bot may provide you with alerts, weather forecasts, translations, formatting or other services.
Markdown bot, Sticker bot, Vote bot, Like bot
Build single- and multiplayer games. A bot can offer rich HTML5 experiences, from simple arcades and puzzles to 3D-shooters and real-time strategy games.
GameBot, Gamee
Build social services. A bot could connect people looking for conversation partners based on common interests or proximity.
+Do virtually anything else. Except for dishes — bots are terrible at doing the dishes.
+At the core, Telegram Bots are special accounts that do not require an additional phone number to set up. Users can interact with bots in two ways:
+Messages, commands and requests sent by users are passed to the software running on your servers. Our intermediary server handles all encryption and communication with the Telegram API for you. You communicate with this server via a simple HTTPS-interface that offers a simplified version of the Telegram API. We call that interface our Bot API.
+++A detailed description of the Bot API is available on this page »
+
There's a… bot for that. Just talk to BotFather (described below) and follow a few simple steps. Once you've created a bot and received your authorization token, head down to the Bot API manual to see what you can teach your bot to do.
+++You may also like to check out some code examples here »
+
t.me/<bot_username>
links or username search to find your bot.Telegram bots are unique in many ways — we offer two kinds of keyboards, additional interfaces for default commands and deep linking as well as text formatting, integrated payments and more.
+Users can interact with your bot via inline queries straight from the text input field in any chat. All they need to do is start a message with your bot's username and then type a query.
+Having received the query, your bot can return some results. As soon as the user taps one of them, it is sent to the user's currently opened chat. This way, people can request content from your bot in any of their chats, groups or channels.
+Check out this blog to see a sample inline bot in action. You can also try the @sticker and @music bots to see for yourself.
+ + +We've also implemented an easy way for your bot to switch between inline and PM modes.
++ ++
You can use bots to accept payments from Telegram users around the world.
+@ShopBot ...
in any chat for an inline invoice.+ ++
Bots can offer their users HTML5 games to play solo or to compete against each other in groups and one-on-one chats. The platform allows your bot to keep track of high scores for every game played in every chat. Whenever there’s a new leader in the game, other playing members in the chat are notified that they need to step it up.
+ + +Since the underlying technology is HTML5, the games can be anything from simple arcades and puzzles to multiplayer 3D-shooters and real-time strategy games. Our team has created a couple of simple demos for you to try out:
+ +You can also check out the @gamee bot that has more than 20 games.
++ ++
Traditional chat bots can of course be taught to understand human language. But sometimes you want some more formal input from the user — and this is where custom keyboards can become extremely useful.
+Whenever your bot sends a message, it can pass along a special keyboard with predefined reply options (see ReplyKeyboardMarkup). Telegram apps that receive the message will display your keyboard to the user. Tapping any of the buttons will immediately send the respective command. This way you can drastically simplify user interaction with your bot.
+We currently support text and emoji for your buttons. Here are some custom keyboard examples:
+++For more technical information on custom keyboards, please consult the Bot API manual (see sendMessage).
+
There are times when you'd prefer to do things without sending any messages to the chat. For example, when your user is changing settings or flipping through search results. In such cases you can use Inline Keyboards that are integrated directly into the messages they belong to.
+Unlike with custom reply keyboards, pressing buttons on inline keyboards doesn't result in messages sent to the chat. Instead, inline keyboards support buttons that work behind the scenes: callback buttons, URL buttons and switch to inline buttons.
+ + +When callback buttons are used, your bot can update its existing messages (or just their keyboards) so that the chat remains tidy. Check out these sample bots to see inline keyboards in action: @music, @vote, @like.
++ ++
Commands present a more flexible way to communicate with your bot. The following syntax may be used:
+/command
+A command must always start with the '/' symbol and may not be longer than 32 characters. Commands can use latin letters, numbers and underscores. Here are a few examples:
+/get_messages_stats
+/set_timer 10min Alarm!
+/get_timezone London, UK
+Messages that start with a slash are always passed to the bot (along with replies to its messages and messages that @mention the bot by username). Telegram apps will:
+If multiple bots are in a group, it is possible to add bot usernames to commands in order to avoid confusion:
+/start@TriviaBot
+/start@ApocalypseBot
+This is done automatically when commands are selected via the list of suggestions. Please remember that your bot needs to be able to process commands that are followed by its username.
+In order to make it easier for users to navigate the bot multiverse, we ask all developers to support a few basic commands. Telegram apps will have interface shortcuts for these commands.
+Users will see a Start button when they first open a conversation with your bot. Help and Settings links will be available in the menu on the bot's profile page.
+You can use bold, italic or fixed-width text, as well as inline links in your bots' messages. Telegram clients will render them accordingly.
++ ++
Bots are frequently added to groups in order to augment communication between human users, e.g. by providing news, notifications from external services or additional search functionality. This is especially true for work-related groups. Now, when you share a group with a bot, you tend to ask yourself “How can I be sure that the little rascal isn't selling my chat history to my competitors?” The answer is — privacy mode.
+A bot running in privacy mode will not receive all messages that people send to the group. Instead, it will only receive:
+On one hand, this helps some of us sleep better at night (in our tinfoil nightcaps), on the other — it allows the bot developer to save a lot of resources, since they won't need to process tens of thousands irrelevant messages each day.
+Privacy mode is enabled by default for all bots, except bots that were added to the group as admins (bot admins always receive all messages). It can be disabled, so that the bot receives all messages like an ordinary user (the bot will need to be re-added to the group for this change to take effect). We only recommend doing this in cases where it is absolutely necessary for your bot to work — users can always see a bot's current privacy setting in the group members list. In most cases, using the force reply option for the bot's messages should be more than enough.
+So what messages exactly will my bot get? »
+Telegram bots have a deep linking mechanism, that allows for passing additional parameters to the bot on startup. It could be a command that launches the bot — or an auth token to connect the user's Telegram account to their account on some external service.
+Each bot has a link that opens a conversation with it in Telegram — https://t.me/<bot username>
. You can add the parameters start or startgroup to this link, with values up to 64 characters long. For example:
https://t.me/triviabot?startgroup=test
+A-Z
, a-z
, 0-9
, _
and -
are allowed. We recommend using base64url to encode parameters with binary and other types of content.
Following a link with the start parameter will open a one-on-one conversation with the bot, showing a START button in the place of the input field. If the startgroup parameter is used, the user is prompted to select a group to add the bot to. As soon as a user confirms the action (presses the START button in their app or selects a group to add the bot to), your bot will receive a message from that user in this format:
+/start PAYLOAD
+PAYLOAD
stands for the value of the start or startgroup parameter that was passed in the link.
Suppose the website example.com would like to send notifications to its users via a Telegram bot. Here's what they could do to enable notifications for a user with the ID 123
.
$memcache_key = "vCH1vGWJxfSeofSAs0K5PA"
123
with the key $memcache_key
into Memcache for 3600 seconds (one hour) https://t.me/ExampleComBot?start=vCH1vGWJxfSeofSAs0K5PA
/start
. If the key exists, record the chat_id passed to the webhook as telegram_chat_id for the user 123
. Remove the key from Memcache.123
, check if they have the field telegram_chat_id. If yes, use the sendMessage method in the Bot API to send them a message in Telegram.Some bots need extra data from the user to work properly. For example, knowing the user's location helps provide more relevant geo-specific results. The user's phone number can be very useful for integrations with other services, like banks, etc.
+Bots can ask a user for their location and phone number using special buttons. Note that both phone number and location request buttons will only work in private chats.
+ + +When these buttons are pressed, Telegram clients will display a confirmation alert that tells the user what's about to happen.
++ ++
++Jump to top to learn everything about Telegram bots »
+
BotFather is the one bot to rule them all. It will help you create new bots and change settings for existing ones.
+Use the /newbot command to create a new bot. The BotFather will ask you for a name and username, then generate an authorization token for your new bot.
+The name of your bot is displayed in contact details and elsewhere.
+The Username is a short name, to be used in mentions and t.me links. Usernames are 5-32 characters long and are case insensitive, but may only include Latin characters, numbers, and underscores. Your bot's username must end in 'bot', e.g. 'tetris_bot' or 'TetrisBot'.
+The token is a string along the lines of 110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw
that is required to authorize the bot and send requests to the Bot API. Keep your token secure and store it safely, it can be used by anyone to control your bot.
If your existing token is compromised or you lost it for some reason, use the /token command to generate a new one.
+The remaining commands are pretty self-explanatory:
+Edit bots
+/
in the chat with your bot. Each command has a name (must start with a slash ‘/’, alphanumeric plus underscores, no more than 32 characters, case-insensitive), parameters, and a text description. Users will see the list of commands whenever they type '/' in a conversation with your bot.Edit settings
+Manage games
+++Please note, that it may take a few minutes for changes to take effect.
+
Millions choose Telegram for its speed. To stay competitive in this environment, your bot also needs to be responsive. In order to help developers keep their bots in shape, Botfather will send status alerts if it sees something is wrong.
+We will be checking the number of replies and the request/response conversion rate for popular bots (~300 requests per minute: but don't write this down as the value may change in the future). If we get abnormally low readings, you will receive a notification from Botfather.
+By default, you will only get one alert per bot per hour. Each alert has the following buttons:
+We will currently notify you about the following issues:
+1.
+Too few **private messages** are sent compared to previous weeks: **{value}**
+Your bot is sending much fewer messages than it did in the previous weeks. This is useful for newsletter-style bots that send out messages without prompts from the users. The larger the value, the more significant the difference.
+2.
+Too few replies to incoming **private messages**. Conversion rate: **{value}**
+Your bot is not replying to all messages that are being sent to it (the request/response conversion rate for your bot was too low for at least two of the last three 5-minute periods). To provide a good user experience, please respond to all messages that are sent to your bot. Respond to message updates by calling send… methods (e.g. sendMessage).
+3.
+Too few answers to **inline queries**. Conversion rate: **{value}**
+Your bot is not replying to all inline queries that are being sent to it, calculated in the same way as above. Respond to inline_query updates by calling answerInlineQuery.
+4.
+Too few answers to **callback queries**. Conversion rate: **{value}**
+Too few answers to **callback game queries**. Conversion rate: **{value}**
+Your bot is not replying to all callback queries that are being sent to it (with or without games), calculated in the same way as above. Respond to callback_query updates by calling answerCallbackQuery.
+++Please note that the status alerts feature is still being tested and will be improved in the future.
+
That's it for the introduction. You are now definitely ready to proceed to the BOT API MANUAL.
+If you've got any questions, please check out our Bot FAQ »
+++Howdy! This text assumes that you‘re familiar with Telegram’s bot platform.
+
If this is not the case, kindly check out our Introduction to Bots.
Today we‘re introducing the biggest change to Telegram’s Bot Platform since June 2015. These new tools will help you create fluid and intuitive interfaces for your bots. And bots are becoming a lot more capable. They can now send any type of content supported on Telegram, provide location-based services and integrate with other services deeply based on users' phone numbers.
+If you'd like a more concise changelog, you can find one in the Bot API Manual.
+To begin with, we're adding a new type of keyboard that is integrated directly into the message it belongs to. Inline keyboards are available for messages sent both in chat mode and inline mode.
+ + +Unlike with custom reply keyboards, pressing buttons on inline keyboards doesn't result in messages sent to the chat. Instead, inline keyboards support buttons that work behind the scenes: callback buttons, URL buttons and switch to inline buttons.
++ ++
When a user presses a callback button, no messages are sent to the chat. Instead, your bot simply receives the relevant query. Upon receiving the query, your bot can display some result in a notification at the top of the chat screen or in an alert.
+ + +Sample bot
@music – This sample music bot uses inline callback buttons to flip pages and reload random results.
Read on to updating messages to find out how callback buttons can get even cooler.
+Buttons of this type have a small arrow icon to help the user understand that tapping on a URL button will open an external link. Naturally, we'll show them a confirmation alert before opening the link in the browser.
+ + +Pressing a switch to inline button prompts the user to select a chat, opens it and inserts the bot's username into the input field. You can also pass a query that will be inserted along with the username – this way your users will immediately get some inline results they can share.
+ + +Sample bot
@sticker – This sticker search bot offers a ‘switch to inline’ button to teach users how to use it in inline mode.
Since inline keyboards don‘t send additional messages to the chat, it made sense to give bots a way of manipulating their existing messages, so that they don’t have to send a new message each time they need to update something. This helps reduce clutter and build more fluid interfaces.
+Sample bot
@music – Watch how the music bot updates its messages with search results when you press the navigation buttons.
+ ++
Some bots need extra data from the user to work properly. For example, knowing the user‘s location helps provide more relevant geo-specific results. The user’s phone number can be very useful for integrations with other services, like banks, etc.
+We've added an easy way for bots to ask the user for their location and phone number using special buttons. Note that both phone number and location request buttons will only work in private chats.
+ + +When these buttons are pressed, Telegram clients will display a confirmation alert that tells the user what's about to happen.
++ ++
Inline bots can also request location data from their users. Use the /setinlinegeo
command with @BotFather to enable this. Your bot will ask the user for permission to access their location whenever they send an inline request.
Sample bot
@foursquare – This bot will ask for permission to access the user's location, then provide geo-targeted results.
Speaking of inline bots, they are also getting a major upgrade today.
+Inline bots now support all types of content available in Telegram (19 in all), they are now capable of sending stickers, videos, music, locations, documents and more.
+ + +Sample bots
@sticker – This sticker bot will accept one or more emoji and search for relevant stickers.
@music – The music bot allows users to send mp3 tracks from a database of public domain classical music.
+ ++
Some inline bots can benefit from an initial setup process, like connecting them to an account on an external service (e.g., YouTube). We've added an easy way of switching between the private chat with a bot and whatever chat the user wants to share inline results in.
+ + +You can now display a special ‘Switch to PM’ button above the inline results (or instead of them). This button will open a private chat with the bot and pass a parameter of your choosing, so that you can prompt the user for the relevant setup actions. Once done, you can use an inline keyboard with a switch_inline_query button to send the user back to the original chat.
+Sample bots
@youtube – Shows a ‘Sign in to YouTube’ button, then suggests personalized results.
+ ++
Since sending content via inline bots works differently from sending ordinary messages, we‘ve changed the interface a little. There’s hardly a more effective way of explaining that there‘s no need to hit ’Send':
+ + +Tapping on the cross icon once will clear the query, tapping twice will give the ‘Send’ button back to the user.
+As a dessert, we‘re beginning to roll out tools that will allow you to create bot solutions for group admins. As the first step, we’ve added methods to remove members from groups and supergroups.
++ ++
And that's about it for now. Stay tuned for more updates and subscribe to our official @Botnews channel on Telegram.
++ ++
++The Bot API is an HTTP-based interface created for developers keen on building bots for Telegram.
+
To learn how to create and set up a bot, please consult our Introduction to Bots »
You will find all changes to our Bot API on this page.
+++Subscribe to @BotNews to be the first to know about the latest updates and join the discussion in @BotTalk
+
Bot API 5.2
+++WARNING!
+
After the next Bot API update (Bot API 5.3) there will be a one-time change of the value of the fieldfile_unique_id
in objects of the type PhotoSize and of the fieldssmall_file_unique_id
andbig_file_unique_id
in objects of the type ChatPhoto.
++WARNING!
+
Service messages about non-bot users joining the chat will be soon removed from large groups. We recommend using the “chat_member” update as a replacement.
++WARNING!
+
After one of the upcoming Bot API updates, user identifiers will become bigger than2^31 - 1
and it will be no longer possible to store them in a signed 32-bit integer type. User identifiers will have up to 52 significant bits, so a 64-bit integer or double-precision float type would still be safe for storing them. Please make sure that your code can correctly handle such user identifiers.
Bot API 5.1
+Added two new update types
+Improved Invite Links
+Voice Chat Info
+And More
+++WARNING!
+
After one of the upcoming Bot API updates, some user identifiers will become bigger than2^31 - 1
and it will be no longer possible to store them in a signed 32-bit integer type. User identifiers will have up to 52 significant bits, so a 64-bit integer or double-precision float type would still be safe for storing them. Please make sure that your code can correctly handle such user identifiers.
Introducing Bot API 5.0
+Run Your Own Bot API Server
+Transfer Bot Ownership
+Webhooks
+Working with Groups
+Working with Files
+Multiple Pinned Messages
+File Albums
+Live Locations
+Anonymous Admins
+And More
+And Last but not Least
+Bot API 4.9
+Bot API 4.8
+Bot API 4.7
+Bot API 4.6
+Bot API 4.5
+<u>/<ins>
(for underlined text) and <s>/<strike>/<del>
(for strikethrough text) in parse mode HTML.__
(for underlined text) and ~
(for strikethrough text). Parse mode Markdown remains unchanged for backward compatibility.Bot API 4.4
+149.154.160.0/20
and 91.108.4.0/22
. Most users won't need to do anything to continue receiving webhooks. If you control inbound access with a firewall, you may need to update your configuration. You can always find the list of actual IP addresses of servers used to send webhooks there: https://core.telegram.org/bots/webhooks.Bot API 4.3
+Also in this update:
+reply_markup
to the Message object, containing the inline keyboard attached to the message.149.154.160.0/20
and 91.108.4.0/22
. Most users won't need to do anything to continue receiving webhooks. If you control inbound access with a firewall, you may need to update your configuration. You can always find the list of actual IP addresses of servers used to send webhooks there: https://core.telegram.org/bots/webhooks.Bot API 4.2
+149.154.160.0/20
and 91.108.4.0/22
. Most users won't need to do anything to continue receiving webhooks. If you control inbound access with a firewall, you may need to update your configuration. You can always find the list of actual IP addresses of servers used to send webhooks there: https://core.telegram.org/bots/webhooks.Bot API 4.1
+Bot API 4.0.
+tg://
URLs now can be used in inline keyboard url buttons and text_link
message entities. Retry-After
response header to configure the delay after which the Bot API will retry the request after an unsuccessful response from a webhook.410 Gone
for all requests for more than 23 hours successively, it can be automatically removed.Bot API 3.6.
+Bot API 3.5.
+Bot API 3.4.
+Bot API 3.3.
+Bot API 3.2. Teach your bot to handle stickers and sticker sets.
+Bot API 3.1. Build your own robotic police force for supergoups with these new methods for admin bots:
+Introducing Bot API 3.0.
+NEW Payment Platform
+See Introduction to Bot Payments for a brief overview. If you're not a developer, you may like this user-friendly blog post better.
+NEW Video Messages
+NEW Multilingual Bots
+More power to admin bots
+Minor Changes
+Introducing Bot API 2.3.1, a nifty little update that will give you more control over how your bot gets its updates.
+Bot API 2.3
+Your bot can now get updates about posts in channels. Added new fields channel_post and edited_channel_post to Update.
+You can now update high scores to a lower value by using the new force parameter in setGameScore. Handy for punishing cheaters or fixing errors in your game's High Score table.
+Bot API 2.2. Introducing a new Gaming Platform! See this introduction for a brief overview.
If you're not a developer, you may like this user-friendly blog post better.
Other changes
+New method getWebhookInfo to check current webhook status.
+Added the option to specify an HTTP URL for a file in all methods where InputFile or file_id can be used (except voice messages). Telegram will get the file from the specified URL and send it to the user. Files must be smaller than 5 MB for photos and smaller than 20 MB for all other types of content.
+Introducing Bot API 2.0. Check out this page for a review of this major update.
+Inline bots
+/setinlinegeo
, added field location to the InlineQuery object, added fields location and inline_message_id to the ChosenInlineResult object.Miscellaneous
+setinlinefeedback
command for Botfather, new type ChosenInlineResult, new field chosen_inline_result to the Update object./setinline
to @BotFather.@channelusername
) in the place of chat_id in all methods (and instead of from_chat_id in forwardMessage). For this to work, the bot must be an administrator in the channel (and that's exactly what Telegram clients don't support yet — adding bots as administrators coming soon).The bot platform was officially launched.
++ ++
++The Bot API is an HTTP-based interface created for developers keen on building bots for Telegram.
+
To learn how to create and set up a bot, please consult our Introduction to Bots and Bot FAQ.
++Subscribe to @BotNews to be the first to know about the latest updates and join the discussion in @BotTalk
+
Bot API 5.2
+++WARNING!
+
After the next Bot API update (Bot API 5.3) there will be a one-time change of the value of the fieldfile_unique_id
in objects of the type PhotoSize and of the fieldssmall_file_unique_id
andbig_file_unique_id
in objects of the type ChatPhoto.
++WARNING!
+
Service messages about non-bot users joining the chat will be soon removed from large groups. We recommend using the “chat_member” update as a replacement.
++WARNING!
+
After one of the upcoming Bot API updates, user identifiers will become bigger than2^31 - 1
and it will be no longer possible to store them in a signed 32-bit integer type. User identifiers will have up to 52 significant bits, so a 64-bit integer or double-precision float type would still be safe for storing them. Please make sure that your code can correctly handle such user identifiers.
Bot API 5.1
+Added two new update types
+Improved Invite Links
+Voice Chat Info
+And More
+++WARNING!
+
After one of the upcoming Bot API updates, some user identifiers will become bigger than2^31 - 1
and it will be no longer possible to store them in a signed 32-bit integer type. User identifiers will have up to 52 significant bits, so a 64-bit integer or double-precision float type would still be safe for storing them. Please make sure that your code can correctly handle such user identifiers.
Introducing Bot API 5.0
+Run Your Own Bot API Server
+Transfer Bot Ownership
+Webhooks
+Working with Groups
+Working with Files
+Multiple Pinned Messages
+File Albums
+Live Locations
+Anonymous Admins
+And More
+And Last but bot Least
+Each bot is given a unique authentication token when it is created. The token looks something like 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
, but we'll use simply <token> in this document instead. You can learn about obtaining tokens and generating new ones in this document.
All queries to the Telegram Bot API must be served over HTTPS and need to be presented in this form: https://api.telegram.org/bot<token>/METHOD_NAME
. Like this for example:
https://api.telegram.org/bot123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11/getMe
+We support GET and POST HTTP methods. We support four ways of passing parameters in Bot API requests:
+The response contains a JSON object, which always has a Boolean field 'ok' and may have an optional String field 'description' with a human-readable description of the result. If 'ok' equals true, the request was successful and the result of the query can be found in the 'result' field. In case of an unsuccessful request, 'ok' equals false and the error is explained in the 'description'. An Integer 'error_code' field is also returned, but its contents are subject to change in the future. Some errors may also have an optional field 'parameters' of the type ResponseParameters, which can help to automatically handle the error.
+If you're using webhooks, you can perform a request to the Bot API while sending an answer to the webhook. Use either application/json or application/x-www-form-urlencoded or multipart/form-data response content type for passing parameters. Specify the method to be invoked in the method parameter of the request. It's not possible to know that such a request was successful or get its result.
+++Please see our FAQ for examples.
+
The Bot API server source code is available at telegram-bot-api. You can run it locally and send the requests to your own server instead of https://api.telegram.org
. If you switch to a local Bot API server, your bot will be able to:
The majority of bots will be OK with the default configuration, running on our servers. But if you feel that you need one of these features, you're welcome to switch to your own at any time.
+There are two mutually exclusive ways of receiving updates for your bot — the getUpdates method on one hand and Webhooks on the other. Incoming updates are stored on the server until the bot receives them either way, but they will not be kept longer than 24 hours.
+Regardless of which option you choose, you will receive JSON-serialized Update objects as a result.
+This object represents an incoming update.
At most one of the optional parameters can be present in any given update.
Field | +Type | +Description | +
---|---|---|
update_id | +Integer | +The update's unique identifier. Update identifiers start from a certain positive number and increase sequentially. This ID becomes especially handy if you're using Webhooks, since it allows you to ignore repeated updates or to restore the correct update sequence, should they get out of order. If there are no new updates for at least a week, then identifier of the next update will be chosen randomly instead of sequentially. | +
message | +Message | +Optional. New incoming message of any kind — text, photo, sticker, etc. | +
edited_message | +Message | +Optional. New version of a message that is known to the bot and was edited | +
channel_post | +Message | +Optional. New incoming channel post of any kind — text, photo, sticker, etc. | +
edited_channel_post | +Message | +Optional. New version of a channel post that is known to the bot and was edited | +
inline_query | +InlineQuery | +Optional. New incoming inline query | +
chosen_inline_result | +ChosenInlineResult | +Optional. 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. | +
callback_query | +CallbackQuery | +Optional. New incoming callback query | +
shipping_query | +ShippingQuery | +Optional. New incoming shipping query. Only for invoices with flexible price | +
pre_checkout_query | +PreCheckoutQuery | +Optional. New incoming pre-checkout query. Contains full information about checkout | +
poll | +Poll | +Optional. New poll state. Bots receive only updates about stopped polls and polls, which are sent by the bot | +
poll_answer | +PollAnswer | +Optional. A user changed their answer in a non-anonymous poll. Bots receive new votes only in polls that were sent by the bot itself. | +
my_chat_member | +ChatMemberUpdated | +Optional. The bot's chat member status was updated in a chat. For private chats, this update is received only when the bot is blocked or unblocked by the user. | +
chat_member | +ChatMemberUpdated | +Optional. A chat member's status was updated in a chat. The bot must be an administrator in the chat and must explicitly specify “chat_member” in the list of allowed_updates to receive these updates. | +
Use this method to receive incoming updates using long polling (wiki). An Array of Update objects is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
offset | +Integer | +Optional | +Identifier of the first update to be returned. Must be greater by one than the highest among the identifiers of previously received updates. By default, updates starting with the earliest unconfirmed update are returned. An update is considered confirmed as soon as getUpdates is called with an offset higher than its update_id. The negative offset can be specified to retrieve updates starting from -offset update from the end of the updates queue. All previous updates will forgotten. | +
limit | +Integer | +Optional | +Limits the number of updates to be retrieved. Values between 1-100 are accepted. Defaults to 100. | +
timeout | +Integer | +Optional | +Timeout in seconds for long polling. Defaults to 0, i.e. usual short polling. Should be positive, short polling should be used for testing purposes only. | +
allowed_updates | +Array of String | +Optional | +A JSON-serialized list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all update types except chat_member (default). If not specified, the previous setting will be used. Please note that this parameter doesn't affect updates created before the call to the getUpdates, so unwanted updates may be received for a short period of time. |
+
++Notes
+
1. This method will not work if an outgoing webhook is set up.
2. In order to avoid getting duplicate updates, recalculate offset after each server response.
Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an update for the bot, we will send an HTTPS POST request to the specified url, containing a JSON-serialized Update. In case of an unsuccessful request, we will give up after a reasonable amount of attempts. Returns True on success.
+If you'd like to make sure that the Webhook request comes from Telegram, we recommend using a secret path in the URL, e.g. https://www.example.com/<token>
. Since nobody else knows your bot's token, you can be pretty sure it's us.
Parameter | +Type | +Required | +Description | +
---|---|---|---|
url | +String | +Yes | +HTTPS url to send updates to. Use an empty string to remove webhook integration | +
certificate | +InputFile | +Optional | +Upload your public key certificate so that the root certificate in use can be checked. See our self-signed guide for details. | +
ip_address | +String | +Optional | +The fixed IP address which will be used to send webhook requests instead of the IP address resolved through DNS | +
max_connections | +Integer | +Optional | +Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server, and higher values to increase your bot's throughput. | +
allowed_updates | +Array of String | +Optional | +A JSON-serialized list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all update types except chat_member (default). If not specified, the previous setting will be used. Please note that this parameter doesn't affect updates created before the call to the setWebhook, so unwanted updates may be received for a short period of time. |
+
drop_pending_updates | +Boolean | +Optional | +Pass True to drop all pending updates | +
++Notes
+
1. You will not be able to receive updates using getUpdates for as long as an outgoing webhook is set up.
2. To use a self-signed certificate, you need to upload your public key certificate using certificate parameter. Please upload as InputFile, sending a String will not work.
3. Ports currently supported for Webhooks: 443, 80, 88, 8443.NEW! If you're having any trouble setting up webhooks, please check out this amazing guide to Webhooks.
+
Use this method to remove webhook integration if you decide to switch back to getUpdates. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
drop_pending_updates | +Boolean | +Optional | +Pass True to drop all pending updates | +
Use this method to get current webhook status. Requires no parameters. On success, returns a WebhookInfo object. If the bot is using getUpdates, will return an object with the url field empty.
+Contains information about the current status of a webhook.
+Field | +Type | +Description | +
---|---|---|
url | +String | +Webhook URL, may be empty if webhook is not set up | +
has_custom_certificate | +Boolean | +True, if a custom certificate was provided for webhook certificate checks | +
pending_update_count | +Integer | +Number of updates awaiting delivery | +
ip_address | +String | +Optional. Currently used webhook IP address | +
last_error_date | +Integer | +Optional. Unix time for the most recent error that happened when trying to deliver an update via webhook | +
last_error_message | +String | +Optional. Error message in human-readable format for the most recent error that happened when trying to deliver an update via webhook | +
max_connections | +Integer | +Optional. Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery | +
allowed_updates | +Array of String | +Optional. A list of update types the bot is subscribed to. Defaults to all update types except chat_member | +
All types used in the Bot API responses are represented as JSON-objects.
+It is safe to use 32-bit signed integers for storing all Integer fields unless otherwise noted.
+++Optional fields may be not returned when irrelevant.
+
This object represents a Telegram user or bot.
+Field | +Type | +Description | +
---|---|---|
id | +Integer | +Unique identifier for this user or bot. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a 64-bit integer or double-precision float type are safe for storing this identifier. | +
is_bot | +Boolean | +True, if this user is a bot | +
first_name | +String | +User's or bot's first name | +
last_name | +String | +Optional. User's or bot's last name | +
username | +String | +Optional. User's or bot's username | +
language_code | +String | +Optional. IETF language tag of the user's language | +
can_join_groups | +Boolean | +Optional. True, if the bot can be invited to groups. Returned only in getMe. | +
can_read_all_group_messages | +Boolean | +Optional. True, if privacy mode is disabled for the bot. Returned only in getMe. | +
supports_inline_queries | +Boolean | +Optional. True, if the bot supports inline queries. Returned only in getMe. | +
This object represents a chat.
+Field | +Type | +Description | +
---|---|---|
id | +Integer | +Unique identifier for this chat. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this identifier. | +
type | +String | +Type of chat, can be either “private”, “group”, “supergroup” or “channel” | +
title | +String | +Optional. Title, for supergroups, channels and group chats | +
username | +String | +Optional. Username, for private chats, supergroups and channels if available | +
first_name | +String | +Optional. First name of the other party in a private chat | +
last_name | +String | +Optional. Last name of the other party in a private chat | +
photo | +ChatPhoto | +Optional. Chat photo. Returned only in getChat. | +
bio | +String | +Optional. Bio of the other party in a private chat. Returned only in getChat. | +
description | +String | +Optional. Description, for groups, supergroups and channel chats. Returned only in getChat. | +
invite_link | +String | +Optional. Primary invite link, for groups, supergroups and channel chats. Returned only in getChat. | +
pinned_message | +Message | +Optional. The most recent pinned message (by sending date). Returned only in getChat. | +
permissions | +ChatPermissions | +Optional. Default chat member permissions, for groups and supergroups. Returned only in getChat. | +
slow_mode_delay | +Integer | +Optional. For supergroups, the minimum allowed delay between consecutive messages sent by each unpriviledged user. Returned only in getChat. | +
message_auto_delete_time | +Integer | +Optional. The time after which all messages sent to the chat will be automatically deleted; in seconds. Returned only in getChat. | +
sticker_set_name | +String | +Optional. For supergroups, name of group sticker set. Returned only in getChat. | +
can_set_sticker_set | +Boolean | +Optional. True, if the bot can change the group sticker set. Returned only in getChat. | +
linked_chat_id | +Integer | +Optional. Unique identifier for the linked chat, i.e. the discussion group identifier for a channel and vice versa; for supergroups and channel chats. This identifier may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier. Returned only in getChat. | +
location | +ChatLocation | +Optional. For supergroups, the location to which the supergroup is connected. Returned only in getChat. | +
This object represents a message.
+Field | +Type | +Description | +
---|---|---|
message_id | +Integer | +Unique message identifier inside this chat | +
from | +User | +Optional. Sender, empty for messages sent to channels | +
sender_chat | +Chat | +Optional. Sender of the message, sent on behalf of a chat. The channel itself for channel messages. The supergroup itself for messages from anonymous group administrators. The linked channel for messages automatically forwarded to the discussion group | +
date | +Integer | +Date the message was sent in Unix time | +
chat | +Chat | +Conversation the message belongs to | +
forward_from | +User | +Optional. For forwarded messages, sender of the original message | +
forward_from_chat | +Chat | +Optional. For messages forwarded from channels or from anonymous administrators, information about the original sender chat | +
forward_from_message_id | +Integer | +Optional. For messages forwarded from channels, identifier of the original message in the channel | +
forward_signature | +String | +Optional. For messages forwarded from channels, signature of the post author if present | +
forward_sender_name | +String | +Optional. Sender's name for messages forwarded from users who disallow adding a link to their account in forwarded messages | +
forward_date | +Integer | +Optional. For forwarded messages, date the original message was sent in Unix time | +
reply_to_message | +Message | +Optional. For replies, the original message. Note that the Message object in this field will not contain further reply_to_message fields even if it itself is a reply. | +
via_bot | +User | +Optional. Bot through which the message was sent | +
edit_date | +Integer | +Optional. Date the message was last edited in Unix time | +
media_group_id | +String | +Optional. The unique identifier of a media message group this message belongs to | +
author_signature | +String | +Optional. Signature of the post author for messages in channels, or the custom title of an anonymous group administrator | +
text | +String | +Optional. For text messages, the actual UTF-8 text of the message, 0-4096 characters | +
entities | +Array of MessageEntity | +Optional. For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text | +
animation | +Animation | +Optional. Message is an animation, information about the animation. For backward compatibility, when this field is set, the document field will also be set | +
audio | +Audio | +Optional. Message is an audio file, information about the file | +
document | +Document | +Optional. Message is a general file, information about the file | +
photo | +Array of PhotoSize | +Optional. Message is a photo, available sizes of the photo | +
sticker | +Sticker | +Optional. Message is a sticker, information about the sticker | +
video | +Video | +Optional. Message is a video, information about the video | +
video_note | +VideoNote | +Optional. Message is a video note, information about the video message | +
voice | +Voice | +Optional. Message is a voice message, information about the file | +
caption | +String | +Optional. Caption for the animation, audio, document, photo, video or voice, 0-1024 characters | +
caption_entities | +Array of MessageEntity | +Optional. For messages with a caption, special entities like usernames, URLs, bot commands, etc. that appear in the caption | +
contact | +Contact | +Optional. Message is a shared contact, information about the contact | +
dice | +Dice | +Optional. Message is a dice with random value | +
game | +Game | +Optional. Message is a game, information about the game. More about games » | +
poll | +Poll | +Optional. Message is a native poll, information about the poll | +
venue | +Venue | +Optional. Message is a venue, information about the venue. For backward compatibility, when this field is set, the location field will also be set | +
location | +Location | +Optional. Message is a shared location, information about the location | +
new_chat_members | +Array of User | +Optional. New members that were added to the group or supergroup and information about them (the bot itself may be one of these members) | +
left_chat_member | +User | +Optional. A member was removed from the group, information about them (this member may be the bot itself) | +
new_chat_title | +String | +Optional. A chat title was changed to this value | +
new_chat_photo | +Array of PhotoSize | +Optional. A chat photo was change to this value | +
delete_chat_photo | +True | +Optional. Service message: the chat photo was deleted | +
group_chat_created | +True | +Optional. Service message: the group has been created | +
supergroup_chat_created | +True | +Optional. Service message: the supergroup has been created. This field can't be received in a message coming through updates, because bot can't be a member of a supergroup when it is created. It can only be found in reply_to_message if someone replies to a very first message in a directly created supergroup. | +
channel_chat_created | +True | +Optional. Service message: the channel has been created. This field can't be received in a message coming through updates, because bot can't be a member of a channel when it is created. It can only be found in reply_to_message if someone replies to a very first message in a channel. | +
message_auto_delete_timer_changed | +MessageAutoDeleteTimerChanged | +Optional. Service message: auto-delete timer settings changed in the chat | +
migrate_to_chat_id | +Integer | +Optional. The group has been migrated to a supergroup with the specified identifier. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this identifier. | +
migrate_from_chat_id | +Integer | +Optional. The supergroup has been migrated from a group with the specified identifier. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this identifier. | +
pinned_message | +Message | +Optional. Specified message was pinned. Note that the Message object in this field will not contain further reply_to_message fields even if it is itself a reply. | +
invoice | +Invoice | +Optional. Message is an invoice for a payment, information about the invoice. More about payments » | +
successful_payment | +SuccessfulPayment | +Optional. Message is a service message about a successful payment, information about the payment. More about payments » | +
connected_website | +String | +Optional. The domain name of the website on which the user has logged in. More about Telegram Login » | +
passport_data | +PassportData | +Optional. Telegram Passport data | +
proximity_alert_triggered | +ProximityAlertTriggered | +Optional. Service message. A user in the chat triggered another user's proximity alert while sharing Live Location. | +
voice_chat_scheduled | +VoiceChatScheduled | +Optional. Service message: voice chat scheduled | +
voice_chat_started | +VoiceChatStarted | +Optional. Service message: voice chat started | +
voice_chat_ended | +VoiceChatEnded | +Optional. Service message: voice chat ended | +
voice_chat_participants_invited | +VoiceChatParticipantsInvited | +Optional. Service message: new participants invited to a voice chat | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message. login_url buttons are represented as ordinary url buttons. |
+
This object represents a unique message identifier.
+Field | +Type | +Description | +
---|---|---|
message_id | +Integer | +Unique message identifier | +
This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the entity. Can be “mention” (@username ), “hashtag” (#hashtag ), “cashtag” ($USD ), “bot_command” (/start@jobs_bot ), “url” (https://telegram.org ), “email” (do-not-reply@telegram.org ), “phone_number” (+1-212-555-0123 ), “bold” (bold text), “italic” (italic text), “underline” (underlined text), “strikethrough” (strikethrough text), “code” (monowidth string), “pre” (monowidth block), “text_link” (for clickable text URLs), “text_mention” (for users without usernames) |
+
offset | +Integer | +Offset in UTF-16 code units to the start of the entity | +
length | +Integer | +Length of the entity in UTF-16 code units | +
url | +String | +Optional. For “text_link” only, url that will be opened after user taps on the text | +
user | +User | +Optional. For “text_mention” only, the mentioned user | +
language | +String | +Optional. For “pre” only, the programming language of the entity text | +
This object represents one size of a photo or a file / sticker thumbnail.
+Field | +Type | +Description | +
---|---|---|
file_id | +String | +Identifier for this file, which can be used to download or reuse the file | +
file_unique_id | +String | +Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. | +
width | +Integer | +Photo width | +
height | +Integer | +Photo height | +
file_size | +Integer | +Optional. File size | +
This object represents an animation file (GIF or H.264/MPEG-4 AVC video without sound).
+Field | +Type | +Description | +
---|---|---|
file_id | +String | +Identifier for this file, which can be used to download or reuse the file | +
file_unique_id | +String | +Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. | +
width | +Integer | +Video width as defined by sender | +
height | +Integer | +Video height as defined by sender | +
duration | +Integer | +Duration of the video in seconds as defined by sender | +
thumb | +PhotoSize | +Optional. Animation thumbnail as defined by sender | +
file_name | +String | +Optional. Original animation filename as defined by sender | +
mime_type | +String | +Optional. MIME type of the file as defined by sender | +
file_size | +Integer | +Optional. File size | +
This object represents an audio file to be treated as music by the Telegram clients.
+Field | +Type | +Description | +
---|---|---|
file_id | +String | +Identifier for this file, which can be used to download or reuse the file | +
file_unique_id | +String | +Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. | +
duration | +Integer | +Duration of the audio in seconds as defined by sender | +
performer | +String | +Optional. Performer of the audio as defined by sender or by audio tags | +
title | +String | +Optional. Title of the audio as defined by sender or by audio tags | +
file_name | +String | +Optional. Original filename as defined by sender | +
mime_type | +String | +Optional. MIME type of the file as defined by sender | +
file_size | +Integer | +Optional. File size | +
thumb | +PhotoSize | +Optional. Thumbnail of the album cover to which the music file belongs | +
This object represents a general file (as opposed to photos, voice messages and audio files).
+Field | +Type | +Description | +
---|---|---|
file_id | +String | +Identifier for this file, which can be used to download or reuse the file | +
file_unique_id | +String | +Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. | +
thumb | +PhotoSize | +Optional. Document thumbnail as defined by sender | +
file_name | +String | +Optional. Original filename as defined by sender | +
mime_type | +String | +Optional. MIME type of the file as defined by sender | +
file_size | +Integer | +Optional. File size | +
This object represents a video file.
+Field | +Type | +Description | +
---|---|---|
file_id | +String | +Identifier for this file, which can be used to download or reuse the file | +
file_unique_id | +String | +Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. | +
width | +Integer | +Video width as defined by sender | +
height | +Integer | +Video height as defined by sender | +
duration | +Integer | +Duration of the video in seconds as defined by sender | +
thumb | +PhotoSize | +Optional. Video thumbnail | +
file_name | +String | +Optional. Original filename as defined by sender | +
mime_type | +String | +Optional. Mime type of a file as defined by sender | +
file_size | +Integer | +Optional. File size | +
This object represents a video message (available in Telegram apps as of v.4.0).
+Field | +Type | +Description | +
---|---|---|
file_id | +String | +Identifier for this file, which can be used to download or reuse the file | +
file_unique_id | +String | +Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. | +
length | +Integer | +Video width and height (diameter of the video message) as defined by sender | +
duration | +Integer | +Duration of the video in seconds as defined by sender | +
thumb | +PhotoSize | +Optional. Video thumbnail | +
file_size | +Integer | +Optional. File size | +
This object represents a voice note.
+Field | +Type | +Description | +
---|---|---|
file_id | +String | +Identifier for this file, which can be used to download or reuse the file | +
file_unique_id | +String | +Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. | +
duration | +Integer | +Duration of the audio in seconds as defined by sender | +
mime_type | +String | +Optional. MIME type of the file as defined by sender | +
file_size | +Integer | +Optional. File size | +
This object represents a phone contact.
+Field | +Type | +Description | +
---|---|---|
phone_number | +String | +Contact's phone number | +
first_name | +String | +Contact's first name | +
last_name | +String | +Optional. Contact's last name | +
user_id | +Integer | +Optional. Contact's user identifier in Telegram. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a 64-bit integer or double-precision float type are safe for storing this identifier. | +
vcard | +String | +Optional. Additional data about the contact in the form of a vCard | +
This object represents an animated emoji that displays a random value.
+Field | +Type | +Description | +
---|---|---|
emoji | +String | +Emoji on which the dice throw animation is based | +
value | +Integer | +Value of the dice, 1-6 for “”, “” and “” base emoji, 1-5 for “” and “” base emoji, 1-64 for “” base emoji | +
This object contains information about one answer option in a poll.
+Field | +Type | +Description | +
---|---|---|
text | +String | +Option text, 1-100 characters | +
voter_count | +Integer | +Number of users that voted for this option | +
This object represents an answer of a user in a non-anonymous poll.
+Field | +Type | +Description | +
---|---|---|
poll_id | +String | +Unique poll identifier | +
user | +User | +The user, who changed the answer to the poll | +
option_ids | +Array of Integer | +0-based identifiers of answer options, chosen by the user. May be empty if the user retracted their vote. | +
This object contains information about a poll.
+Field | +Type | +Description | +
---|---|---|
id | +String | +Unique poll identifier | +
question | +String | +Poll question, 1-300 characters | +
options | +Array of PollOption | +List of poll options | +
total_voter_count | +Integer | +Total number of users that voted in the poll | +
is_closed | +Boolean | +True, if the poll is closed | +
is_anonymous | +Boolean | +True, if the poll is anonymous | +
type | +String | +Poll type, currently can be “regular” or “quiz” | +
allows_multiple_answers | +Boolean | +True, if the poll allows multiple answers | +
correct_option_id | +Integer | +Optional. 0-based identifier of the correct answer option. Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot. | +
explanation | +String | +Optional. Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters | +
explanation_entities | +Array of MessageEntity | +Optional. Special entities like usernames, URLs, bot commands, etc. that appear in the explanation | +
open_period | +Integer | +Optional. Amount of time in seconds the poll will be active after creation | +
close_date | +Integer | +Optional. Point in time (Unix timestamp) when the poll will be automatically closed | +
This object represents a point on the map.
+Field | +Type | +Description | +
---|---|---|
longitude | +Float | +Longitude as defined by sender | +
latitude | +Float | +Latitude as defined by sender | +
horizontal_accuracy | +Float number | +Optional. The radius of uncertainty for the location, measured in meters; 0-1500 | +
live_period | +Integer | +Optional. Time relative to the message sending date, during which the location can be updated, in seconds. For active live locations only. | +
heading | +Integer | +Optional. The direction in which user is moving, in degrees; 1-360. For active live locations only. | +
proximity_alert_radius | +Integer | +Optional. Maximum distance for proximity alerts about approaching another chat member, in meters. For sent live locations only. | +
This object represents a venue.
+Field | +Type | +Description | +
---|---|---|
location | +Location | +Venue location. Can't be a live location | +
title | +String | +Name of the venue | +
address | +String | +Address of the venue | +
foursquare_id | +String | +Optional. Foursquare identifier of the venue | +
foursquare_type | +String | +Optional. Foursquare type of the venue. (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.) | +
google_place_id | +String | +Optional. Google Places identifier of the venue | +
google_place_type | +String | +Optional. Google Places type of the venue. (See supported types.) | +
This object represents the content of a service message, sent whenever a user in the chat triggers a proximity alert set by another user.
+Field | +Type | +Description | +
---|---|---|
traveler | +User | +User that triggered the alert | +
watcher | +User | +User that set the alert | +
distance | +Integer | +The distance between the users | +
This object represents a service message about a change in auto-delete timer settings.
+Field | +Type | +Description | +
---|---|---|
message_auto_delete_time | +Integer | +New auto-delete time for messages in the chat | +
This object represents a service message about a voice chat scheduled in the chat.
+Field | +Type | +Description | +
---|---|---|
start_date | +Integer | +Point in time (Unix timestamp) when the voice chat is supposed to be started by a chat administrator | +
This object represents a service message about a voice chat started in the chat. Currently holds no information.
+This object represents a service message about a voice chat ended in the chat.
+Field | +Type | +Description | +
---|---|---|
duration | +Integer | +Voice chat duration; in seconds | +
This object represents a service message about new members invited to a voice chat.
+Field | +Type | +Description | +
---|---|---|
users | +Array of User | +Optional. New members that were invited to the voice chat | +
This object represent a user's profile pictures.
+Field | +Type | +Description | +
---|---|---|
total_count | +Integer | +Total number of profile pictures the target user has | +
photos | +Array of Array of PhotoSize | +Requested profile pictures (in up to 4 sizes each) | +
This object represents a file ready to be downloaded. The file can be downloaded via the link https://api.telegram.org/file/bot<token>/<file_path>
. It is guaranteed that the link will be valid for at least 1 hour. When the link expires, a new one can be requested by calling getFile.
++Maximum file size to download is 20 MB
+
Field | +Type | +Description | +
---|---|---|
file_id | +String | +Identifier for this file, which can be used to download or reuse the file | +
file_unique_id | +String | +Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. | +
file_size | +Integer | +Optional. File size, if known | +
file_path | +String | +Optional. File path. Use https://api.telegram.org/file/bot<token>/<file_path> to get the file. |
+
This object represents a custom keyboard with reply options (see Introduction to bots for details and examples).
+Field | +Type | +Description | +
---|---|---|
keyboard | +Array of Array of KeyboardButton | +Array of button rows, each represented by an Array of KeyboardButton objects | +
resize_keyboard | +Boolean | +Optional. Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). Defaults to false, in which case the custom keyboard is always of the same height as the app's standard keyboard. | +
one_time_keyboard | +Boolean | +Optional. 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. Defaults to false. | +
selective | +Boolean | +Optional. 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. |
+
This object represents one button of the reply keyboard. For simple text buttons String can be used instead of this object to specify text of the button. Optional fields request_contact, request_location, and request_poll are mutually exclusive.
+Field | +Type | +Description | +
---|---|---|
text | +String | +Text of the button. If none of the optional fields are used, it will be sent as a message when the button is pressed | +
request_contact | +Boolean | +Optional. If True, the user's phone number will be sent as a contact when the button is pressed. Available in private chats only | +
request_location | +Boolean | +Optional. If True, the user's current location will be sent when the button is pressed. Available in private chats only | +
request_poll | +KeyboardButtonPollType | +Optional. If specified, the user will be asked to create a poll and send it to the bot when the button is pressed. Available in private chats only | +
Note: request_contact and request_location options will only work in Telegram versions released after 9 April, 2016. Older clients will display unsupported message.
Note: request_poll option will only work in Telegram versions released after 23 January, 2020. Older clients will display unsupported message.
This object represents type of a poll, which is allowed to be created and sent when the corresponding button is pressed.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Optional. If quiz is passed, the user will be allowed to create only polls in the quiz mode. If regular is passed, only regular polls will be allowed. Otherwise, the user will be allowed to create a poll of any type. | +
Upon receiving a message with this object, Telegram clients will remove the current custom keyboard and display the default letter-keyboard. By default, custom keyboards are displayed until a new keyboard is sent by a bot. An exception is made for one-time keyboards that are hidden immediately after the user presses a button (see ReplyKeyboardMarkup).
+Field | +Type | +Description | +
---|---|---|
remove_keyboard | +True | +Requests clients to remove the custom keyboard (user will not be able to summon this keyboard; if you want to hide the keyboard from sight but keep it accessible, use one_time_keyboard in ReplyKeyboardMarkup) | +
selective | +Boolean | +Optional. Use this parameter 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. |
+
This object represents an inline keyboard that appears right next to the message it belongs to.
+Field | +Type | +Description | +
---|---|---|
inline_keyboard | +Array of Array of InlineKeyboardButton | +Array of button rows, each represented by an Array of InlineKeyboardButton objects | +
Note: This will only work in Telegram versions released after 9 April, 2016. Older clients will display unsupported message.
+This object represents one button of an inline keyboard. You must use exactly one of the optional fields.
+Field | +Type | +Description | +
---|---|---|
text | +String | +Label text on the button | +
url | +String | +Optional. HTTP or tg:// url to be opened when button is pressed | +
login_url | +LoginUrl | +Optional. An HTTP URL used to automatically authorize the user. Can be used as a replacement for the Telegram Login Widget. | +
callback_data | +String | +Optional. Data to be sent in a callback query to the bot when button is pressed, 1-64 bytes | +
switch_inline_query | +String | +Optional. If set, 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. Can be empty, in which case just the bot's username will be inserted. Note: This offers an easy way for users to start using your bot in inline mode when they are currently in a private chat with it. Especially useful when combined with switch_pm… actions – in this case the user will be automatically returned to the chat they switched from, skipping the chat selection screen. |
+
switch_inline_query_current_chat | +String | +Optional. If set, pressing the button will insert the bot's username and the specified inline query in the current chat's input field. Can be empty, in which case only the bot's username will be inserted. This offers a quick way for the user to open your bot in inline mode in the same chat – good for selecting something from multiple options. |
+
callback_game | +CallbackGame | +Optional. Description of the game that will be launched when the user presses the button. NOTE: This type of button must always be the first button in the first row. |
+
pay | +Boolean | +Optional. Specify True, to send a Pay button. NOTE: This type of button must always be the first button in the first row. |
+
This object represents a parameter of the inline keyboard button used to automatically authorize a user. Serves as a great replacement for the Telegram Login Widget when the user is coming from Telegram. All the user needs to do is tap/click a button and confirm that they want to log in:
+ + +Telegram apps support these buttons as of version 5.7.
+++Sample bot: @discussbot
+
Field | +Type | +Description | +
---|---|---|
url | +String | +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. |
+
forward_text | +String | +Optional. New text of the button in forwarded messages. | +
bot_username | +String | +Optional. 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. | +
request_write_access | +Boolean | +Optional. Pass True to request the permission for your bot to send messages to the user. | +
This object represents an incoming callback query from a callback button in an inline keyboard. If the button that originated the query was attached to a message sent by the bot, the field message will be present. If the button was attached to a message sent via the bot (in inline mode), the field inline_message_id will be present. Exactly one of the fields data or game_short_name will be present.
+Field | +Type | +Description | +
---|---|---|
id | +String | +Unique identifier for this query | +
from | +User | +Sender | +
message | +Message | +Optional. Message with the callback button that originated the query. Note that message content and message date will not be available if the message is too old | +
inline_message_id | +String | +Optional. Identifier of the message sent via the bot in inline mode, that originated the query. | +
chat_instance | +String | +Global identifier, uniquely corresponding to the chat to which the message with the callback button was sent. Useful for high scores in games. | +
data | +String | +Optional. Data associated with the callback button. Be aware that a bad client can send arbitrary data in this field. | +
game_short_name | +String | +Optional. Short name of a Game to be returned, serves as the unique identifier for the game | +
++NOTE: After the user presses a callback button, Telegram clients will display a progress bar until you call answerCallbackQuery. It is, therefore, necessary to react by calling answerCallbackQuery even if no notification to the user is needed (e.g., without specifying any of the optional parameters).
+
Upon receiving a message with this object, Telegram clients will display a reply interface to the user (act as if the user has selected the bot's message and tapped 'Reply'). This can be extremely useful if you want to create user-friendly step-by-step interfaces without having to sacrifice privacy mode.
+Field | +Type | +Description | +
---|---|---|
force_reply | +True | +Shows reply interface to the user, as if they manually selected the bot's message and tapped 'Reply' | +
selective | +Boolean | +Optional. Use this parameter if you want to force reply from 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 poll bot for groups runs in privacy mode (only receives commands, replies to its messages and mentions). There could be two ways to create a new poll:
++
+- Explain the user how to send a command with parameters (e.g. /newpoll question answer1 answer2). May be appealing for hardcore users but lacks modern day polish.
+- Guide the user through a step-by-step process. 'Please send me your question', 'Cool, now let's add the first answer option', 'Great. Keep adding answer options, then send /done when you're ready'.
+The last option is definitely more attractive. And if you use ForceReply in your bot's questions, it will receive the user's answers even if it only receives replies, commands and mentions — without any extra work for the user.
+
This object represents a chat photo.
+Field | +Type | +Description | +
---|---|---|
small_file_id | +String | +File identifier of small (160x160) chat photo. This file_id can be used only for photo download and only for as long as the photo is not changed. | +
small_file_unique_id | +String | +Unique file identifier of small (160x160) chat photo, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. | +
big_file_id | +String | +File identifier of big (640x640) chat photo. This file_id can be used only for photo download and only for as long as the photo is not changed. | +
big_file_unique_id | +String | +Unique file identifier of big (640x640) chat photo, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. | +
Represents an invite link for a chat.
+Field | +Type | +Description | +
---|---|---|
invite_link | +String | +The invite link. If the link was created by another chat administrator, then the second part of the link will be replaced with “…”. | +
creator | +User | +Creator of the link | +
is_primary | +Boolean | +True, if the link is primary | +
is_revoked | +Boolean | +True, if the link is revoked | +
expire_date | +Integer | +Optional. Point in time (Unix timestamp) when the link will expire or has been expired | +
member_limit | +Integer | +Optional. Maximum number of users that can be members of the chat simultaneously after joining the chat via this invite link; 1-99999 | +
This object contains information about one member of a chat.
+Field | +Type | +Description | +
---|---|---|
user | +User | +Information about the user | +
status | +String | +The member's status in the chat. Can be “creator”, “administrator”, “member”, “restricted”, “left” or “kicked” | +
custom_title | +String | +Optional. Owner and administrators only. Custom title for this user | +
is_anonymous | +Boolean | +Optional. Owner and administrators only. True, if the user's presence in the chat is hidden | +
can_be_edited | +Boolean | +Optional. Administrators only. True, if the bot is allowed to edit administrator privileges of that user | +
can_manage_chat | +Boolean | +Optional. Administrators only. True, if the administrator can access the chat event log, chat statistics, message statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege | +
can_post_messages | +Boolean | +Optional. Administrators only. True, if the administrator can post in the channel; channels only | +
can_edit_messages | +Boolean | +Optional. Administrators only. True, if the administrator can edit messages of other users and can pin messages; channels only | +
can_delete_messages | +Boolean | +Optional. Administrators only. True, if the administrator can delete messages of other users | +
can_manage_voice_chats | +Boolean | +Optional. Administrators only. True, if the administrator can manage voice chats | +
can_restrict_members | +Boolean | +Optional. Administrators only. True, if the administrator can restrict, ban or unban chat members | +
can_promote_members | +Boolean | +Optional. Administrators only. True, if the administrator can add new administrators with a subset of their own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by the user) | +
can_change_info | +Boolean | +Optional. Administrators and restricted only. True, if the user is allowed to change the chat title, photo and other settings | +
can_invite_users | +Boolean | +Optional. Administrators and restricted only. True, if the user is allowed to invite new users to the chat | +
can_pin_messages | +Boolean | +Optional. Administrators and restricted only. True, if the user is allowed to pin messages; groups and supergroups only | +
is_member | +Boolean | +Optional. Restricted only. True, if the user is a member of the chat at the moment of the request | +
can_send_messages | +Boolean | +Optional. Restricted only. True, if the user is allowed to send text messages, contacts, locations and venues | +
can_send_media_messages | +Boolean | +Optional. Restricted only. True, if the user is allowed to send audios, documents, photos, videos, video notes and voice notes | +
can_send_polls | +Boolean | +Optional. Restricted only. True, if the user is allowed to send polls | +
can_send_other_messages | +Boolean | +Optional. Restricted only. True, if the user is allowed to send animations, games, stickers and use inline bots | +
can_add_web_page_previews | +Boolean | +Optional. Restricted only. True, if the user is allowed to add web page previews to their messages | +
until_date | +Integer | +Optional. Restricted and kicked only. Date when restrictions will be lifted for this user; unix time | +
This object represents changes in the status of a chat member.
+Field | +Type | +Description | +
---|---|---|
chat | +Chat | +Chat the user belongs to | +
from | +User | +Performer of the action, which resulted in the change | +
date | +Integer | +Date the change was done in Unix time | +
old_chat_member | +ChatMember | +Previous information about the chat member | +
new_chat_member | +ChatMember | +New information about the chat member | +
invite_link | +ChatInviteLink | +Optional. Chat invite link, which was used by the user to join the chat; for joining by invite link events only. | +
Describes actions that a non-administrator user is allowed to take in a chat.
+Field | +Type | +Description | +
---|---|---|
can_send_messages | +Boolean | +Optional. True, if the user is allowed to send text messages, contacts, locations and venues | +
can_send_media_messages | +Boolean | +Optional. True, if the user is allowed to send audios, documents, photos, videos, video notes and voice notes, implies can_send_messages | +
can_send_polls | +Boolean | +Optional. True, if the user is allowed to send polls, implies can_send_messages | +
can_send_other_messages | +Boolean | +Optional. True, if the user is allowed to send animations, games, stickers and use inline bots, implies can_send_media_messages | +
can_add_web_page_previews | +Boolean | +Optional. True, if the user is allowed to add web page previews to their messages, implies can_send_media_messages | +
can_change_info | +Boolean | +Optional. True, if the user is allowed to change the chat title, photo and other settings. Ignored in public supergroups | +
can_invite_users | +Boolean | +Optional. True, if the user is allowed to invite new users to the chat | +
can_pin_messages | +Boolean | +Optional. True, if the user is allowed to pin messages. Ignored in public supergroups | +
Represents a location to which a chat is connected.
+Field | +Type | +Description | +
---|---|---|
location | +Location | +The location to which the supergroup is connected. Can't be a live location. | +
address | +String | +Location address; 1-64 characters, as defined by the chat owner | +
This object represents a bot command.
+Field | +Type | +Description | +
---|---|---|
command | +String | +Text of the command, 1-32 characters. Can contain only lowercase English letters, digits and underscores. | +
description | +String | +Description of the command, 3-256 characters. | +
Contains information about why a request was unsuccessful.
+Field | +Type | +Description | +
---|---|---|
migrate_to_chat_id | +Integer | +Optional. The group has been migrated to a supergroup with the specified identifier. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this identifier. | +
retry_after | +Integer | +Optional. In case of exceeding flood control, the number of seconds left to wait before the request can be repeated | +
This object represents the content of a media message to be sent. It should be one of
+ +Represents a photo to be sent.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be photo | +
media | +String | +File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More info on Sending Files » | +
caption | +String | +Optional. Caption of the photo to be sent, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional. Mode for parsing entities in the photo caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode | +
Represents a video to be sent.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be video | +
media | +String | +File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More info on Sending Files » | +
thumb | +InputFile or String | +Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More info on Sending Files » | +
caption | +String | +Optional. Caption of the video to be sent, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional. Mode for parsing entities in the video caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode | +
width | +Integer | +Optional. Video width | +
height | +Integer | +Optional. Video height | +
duration | +Integer | +Optional. Video duration | +
supports_streaming | +Boolean | +Optional. Pass True, if the uploaded video is suitable for streaming | +
Represents an animation file (GIF or H.264/MPEG-4 AVC video without sound) to be sent.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be animation | +
media | +String | +File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More info on Sending Files » | +
thumb | +InputFile or String | +Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More info on Sending Files » | +
caption | +String | +Optional. Caption of the animation to be sent, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional. Mode for parsing entities in the animation caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode | +
width | +Integer | +Optional. Animation width | +
height | +Integer | +Optional. Animation height | +
duration | +Integer | +Optional. Animation duration | +
Represents an audio file to be treated as music to be sent.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be audio | +
media | +String | +File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More info on Sending Files » | +
thumb | +InputFile or String | +Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More info on Sending Files » | +
caption | +String | +Optional. Caption of the audio to be sent, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional. Mode for parsing entities in the audio caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode | +
duration | +Integer | +Optional. Duration of the audio in seconds | +
performer | +String | +Optional. Performer of the audio | +
title | +String | +Optional. Title of the audio | +
Represents a general file to be sent.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be document | +
media | +String | +File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More info on Sending Files » | +
thumb | +InputFile or String | +Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More info on Sending Files » | +
caption | +String | +Optional. Caption of the document to be sent, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional. Mode for parsing entities in the document caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode | +
disable_content_type_detection | +Boolean | +Optional. Disables automatic server-side content type detection for files uploaded using multipart/form-data. Always true, if the document is sent as part of an album. | +
This object represents the contents of a file to be uploaded. Must be posted using multipart/form-data in the usual way that files are uploaded via the browser.
+There are three ways to send files (photos, stickers, audio, media, etc.):
+Sending by file_id
+Sending by URL
+Objects and methods used in the inline mode are described in the Inline mode section.
+++All methods in the Bot API are case-insensitive. We support GET and POST HTTP methods. Use either URL query string or application/json or application/x-www-form-urlencoded or multipart/form-data for passing parameters in Bot API requests.
+
On successful call, a JSON-object containing the result will be returned.
A simple method for testing your bot's auth token. Requires no parameters. Returns basic information about the bot in form of a User object.
+Use this method to log out from the cloud Bot API server before launching the bot locally. You must log out the bot before running it locally, otherwise there is no guarantee that the bot will receive updates. After a successful call, you can immediately log in on a local server, but will not be able to log in back to the cloud Bot API server for 10 minutes. Returns True on success. Requires no parameters.
+Use this method to close the bot instance before moving it from one local server to another. You need to delete the webhook before calling this method to ensure that the bot isn't launched again after server restart. The method will return error 429 in the first 10 minutes after the bot is launched. Returns True on success. Requires no parameters.
+Use this method to send text messages. On success, the sent Message is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
text | +String | +Yes | +Text of the message to be sent, 1-4096 characters after entities parsing | +
parse_mode | +String | +Optional | +Mode for parsing entities in the message text. See formatting options for more details. | +
entities | +Array of MessageEntity | +Optional | +List of special entities that appear in message text, which can be specified instead of parse_mode | +
disable_web_page_preview | +Boolean | +Optional | +Disables link previews for links in this message | +
disable_notification | +Boolean | +Optional | +Sends the message silently. Users will receive a notification with no sound. | +
reply_to_message_id | +Integer | +Optional | +If the message is a reply, ID of the original message | +
allow_sending_without_reply | +Boolean | +Optional | +Pass True, if the message should be sent even if the specified replied-to message is not found | +
reply_markup | +InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReply | +Optional | +Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. | +
The Bot API supports basic formatting for messages. You can use bold, italic, underlined and strikethrough text, as well as inline links and pre-formatted code in your bots' messages. Telegram clients will render them accordingly. You can use either markdown-style or HTML-style formatting.
+Note that Telegram clients will display an alert to the user before opening an inline link ('Open this link?' together with the full URL).
+Message entities can be nested, providing following restrictions are met:
- If two entities has common characters then one of them is fully contained inside another.
- bold, italic, underline and strikethrough entities can contain and to be contained in any other entities, except pre and code.
- All other entities can't contain each other.
Links tg://user?id=<user_id>
can be used to mention a user by their ID without using a username. Please note:
To use this mode, pass MarkdownV2 in the parse_mode field. Use the following syntax in your message:
+*bold \*text*
+_italic \*text_
+__underline__
+~strikethrough~
+*bold _italic bold ~italic bold strikethrough~ __underline italic bold___ bold*
+[inline URL](http://www.example.com/)
+[inline mention of a user](tg://user?id=123456789)
+`inline fixed-width code`
+```
+pre-formatted fixed-width code block
+```
+```python
+pre-formatted fixed-width code block written in the Python programming language
+```
+Please note:
+pre
and code
entities, all '`' and '\' characters must be escaped with a preceding '\' character.(...)
part of inline link definition, all ')' and '\' must be escaped with a preceding '\' character.italic
and underline
entities __
is always greadily treated from left to right as beginning or end of underline
entity, so instead of ___italic underline___
use ___italic underline_\r__
, where \r
is a character with code 13, which will be ignored.To use this mode, pass HTML in the parse_mode field. The following tags are currently supported:
+<b>bold</b>, <strong>bold</strong>
+<i>italic</i>, <em>italic</em>
+<u>underline</u>, <ins>underline</ins>
+<s>strikethrough</s>, <strike>strikethrough</strike>, <del>strikethrough</del>
+<b>bold <i>italic bold <s>italic bold strikethrough</s> <u>underline italic bold</u></i> bold</b>
+<a href="http://www.example.com/">inline URL</a>
+<a href="tg://user?id=123456789">inline mention of a user</a>
+<code>inline fixed-width code</code>
+<pre>pre-formatted fixed-width code block</pre>
+<pre><code class="language-python">pre-formatted fixed-width code block written in the Python programming language</code></pre>
+Please note:
+<
, >
and &
symbols that are not a part of a tag or an HTML entity must be replaced with the corresponding HTML entities (<
with <
, >
with >
and &
with &
).<
, >
, &
and "
.pre
and code
tags, to define programming language for pre
entity.code
tags.This is a legacy mode, retained for backward compatibility. To use this mode, pass Markdown in the parse_mode field. Use the following syntax in your message:
+*bold text*
+_italic text_
+[inline URL](http://www.example.com/)
+[inline mention of a user](tg://user?id=123456789)
+`inline fixed-width code`
+```
+pre-formatted fixed-width code block
+```
+```python
+pre-formatted fixed-width code block written in the Python programming language
+```
+Please note:
+_snake_\__case_
for italic snake_case
and *2*\**2=4*
for bold 2*2=4
.Use this method to forward messages of any kind. Service messages can't be forwarded. On success, the sent Message is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
from_chat_id | +Integer or String | +Yes | +Unique identifier for the chat where the original message was sent (or channel username in the format @channelusername ) |
+
disable_notification | +Boolean | +Optional | +Sends the message silently. Users will receive a notification with no sound. | +
message_id | +Integer | +Yes | +Message identifier in the chat specified in from_chat_id | +
Use this method to copy messages of any kind. Service messages and invoice messages can't be copied. The method is analogous to the method forwardMessage, but the copied message doesn't have a link to the original message. Returns the MessageId of the sent message on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
from_chat_id | +Integer or String | +Yes | +Unique identifier for the chat where the original message was sent (or channel username in the format @channelusername ) |
+
message_id | +Integer | +Yes | +Message identifier in the chat specified in from_chat_id | +
caption | +String | +Optional | +New caption for media, 0-1024 characters after entities parsing. If not specified, the original caption is kept | +
parse_mode | +String | +Optional | +Mode for parsing entities in the new caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional | +List of special entities that appear in the new caption, which can be specified instead of parse_mode | +
disable_notification | +Boolean | +Optional | +Sends the message silently. Users will receive a notification with no sound. | +
reply_to_message_id | +Integer | +Optional | +If the message is a reply, ID of the original message | +
allow_sending_without_reply | +Boolean | +Optional | +Pass True, if the message should be sent even if the specified replied-to message is not found | +
reply_markup | +InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReply | +Optional | +Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. | +
Use this method to send photos. On success, the sent Message is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
photo | +InputFile or String | +Yes | +Photo to send. Pass a file_id as String to send a photo that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a photo from the Internet, or upload a new photo using multipart/form-data. The photo must be at most 10 MB in size. The photo's width and height must not exceed 10000 in total. Width and height ratio must be at most 20. More info on Sending Files » | +
caption | +String | +Optional | +Photo caption (may also be used when resending photos by file_id), 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional | +Mode for parsing entities in the photo caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional | +List of special entities that appear in the caption, which can be specified instead of parse_mode | +
disable_notification | +Boolean | +Optional | +Sends the message silently. Users will receive a notification with no sound. | +
reply_to_message_id | +Integer | +Optional | +If the message is a reply, ID of the original message | +
allow_sending_without_reply | +Boolean | +Optional | +Pass True, if the message should be sent even if the specified replied-to message is not found | +
reply_markup | +InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReply | +Optional | +Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. | +
Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, this limit may be changed in the future.
+For sending voice messages, use the sendVoice method instead.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
audio | +InputFile or String | +Yes | +Audio file to send. Pass a file_id as String to send an audio file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get an audio file from the Internet, or upload a new one using multipart/form-data. More info on Sending Files » | +
caption | +String | +Optional | +Audio caption, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional | +Mode for parsing entities in the audio caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional | +List of special entities that appear in the caption, which can be specified instead of parse_mode | +
duration | +Integer | +Optional | +Duration of the audio in seconds | +
performer | +String | +Optional | +Performer | +
title | +String | +Optional | +Track name | +
thumb | +InputFile or String | +Optional | +Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More info on Sending Files » | +
disable_notification | +Boolean | +Optional | +Sends the message silently. Users will receive a notification with no sound. | +
reply_to_message_id | +Integer | +Optional | +If the message is a reply, ID of the original message | +
allow_sending_without_reply | +Boolean | +Optional | +Pass True, if the message should be sent even if the specified replied-to message is not found | +
reply_markup | +InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReply | +Optional | +Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. | +
Use this method to send general files. On success, the sent Message is returned. Bots can currently send files of any type of up to 50 MB in size, this limit may be changed in the future.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
document | +InputFile or String | +Yes | +File to send. Pass a file_id as String to send a file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. More info on Sending Files » | +
thumb | +InputFile or String | +Optional | +Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More info on Sending Files » | +
caption | +String | +Optional | +Document caption (may also be used when resending documents by file_id), 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional | +Mode for parsing entities in the document caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional | +List of special entities that appear in the caption, which can be specified instead of parse_mode | +
disable_content_type_detection | +Boolean | +Optional | +Disables automatic server-side content type detection for files uploaded using multipart/form-data | +
disable_notification | +Boolean | +Optional | +Sends the message silently. Users will receive a notification with no sound. | +
reply_to_message_id | +Integer | +Optional | +If the message is a reply, ID of the original message | +
allow_sending_without_reply | +Boolean | +Optional | +Pass True, if the message should be sent even if the specified replied-to message is not found | +
reply_markup | +InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReply | +Optional | +Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. | +
Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). On success, the sent Message is returned. Bots can currently send video files of up to 50 MB in size, this limit may be changed in the future.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
video | +InputFile or String | +Yes | +Video to send. Pass a file_id as String to send a video that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a video from the Internet, or upload a new video using multipart/form-data. More info on Sending Files » | +
duration | +Integer | +Optional | +Duration of sent video in seconds | +
width | +Integer | +Optional | +Video width | +
height | +Integer | +Optional | +Video height | +
thumb | +InputFile or String | +Optional | +Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More info on Sending Files » | +
caption | +String | +Optional | +Video caption (may also be used when resending videos by file_id), 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional | +Mode for parsing entities in the video caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional | +List of special entities that appear in the caption, which can be specified instead of parse_mode | +
supports_streaming | +Boolean | +Optional | +Pass True, if the uploaded video is suitable for streaming | +
disable_notification | +Boolean | +Optional | +Sends the message silently. Users will receive a notification with no sound. | +
reply_to_message_id | +Integer | +Optional | +If the message is a reply, ID of the original message | +
allow_sending_without_reply | +Boolean | +Optional | +Pass True, if the message should be sent even if the specified replied-to message is not found | +
reply_markup | +InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReply | +Optional | +Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. | +
Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
animation | +InputFile or String | +Yes | +Animation to send. Pass a file_id as String to send an animation that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get an animation from the Internet, or upload a new animation using multipart/form-data. More info on Sending Files » | +
duration | +Integer | +Optional | +Duration of sent animation in seconds | +
width | +Integer | +Optional | +Animation width | +
height | +Integer | +Optional | +Animation height | +
thumb | +InputFile or String | +Optional | +Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More info on Sending Files » | +
caption | +String | +Optional | +Animation caption (may also be used when resending animation by file_id), 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional | +Mode for parsing entities in the animation caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional | +List of special entities that appear in the caption, which can be specified instead of parse_mode | +
disable_notification | +Boolean | +Optional | +Sends the message silently. Users will receive a notification with no sound. | +
reply_to_message_id | +Integer | +Optional | +If the message is a reply, ID of the original message | +
allow_sending_without_reply | +Boolean | +Optional | +Pass True, if the message should be sent even if the specified replied-to message is not found | +
reply_markup | +InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReply | +Optional | +Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. | +
Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. For this to work, your audio must be in an .OGG file encoded with OPUS (other formats may be sent as Audio or Document). On success, the sent Message is returned. Bots can currently send voice messages of up to 50 MB in size, this limit may be changed in the future.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
voice | +InputFile or String | +Yes | +Audio file to send. Pass a file_id as String to send a file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. More info on Sending Files » | +
caption | +String | +Optional | +Voice message caption, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional | +Mode for parsing entities in the voice message caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional | +List of special entities that appear in the caption, which can be specified instead of parse_mode | +
duration | +Integer | +Optional | +Duration of the voice message in seconds | +
disable_notification | +Boolean | +Optional | +Sends the message silently. Users will receive a notification with no sound. | +
reply_to_message_id | +Integer | +Optional | +If the message is a reply, ID of the original message | +
allow_sending_without_reply | +Boolean | +Optional | +Pass True, if the message should be sent even if the specified replied-to message is not found | +
reply_markup | +InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReply | +Optional | +Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. | +
As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent Message is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
video_note | +InputFile or String | +Yes | +Video note to send. Pass a file_id as String to send a video note that exists on the Telegram servers (recommended) or upload a new video using multipart/form-data. More info on Sending Files ». Sending video notes by a URL is currently unsupported | +
duration | +Integer | +Optional | +Duration of sent video in seconds | +
length | +Integer | +Optional | +Video width and height, i.e. diameter of the video message | +
thumb | +InputFile or String | +Optional | +Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More info on Sending Files » | +
disable_notification | +Boolean | +Optional | +Sends the message silently. Users will receive a notification with no sound. | +
reply_to_message_id | +Integer | +Optional | +If the message is a reply, ID of the original message | +
allow_sending_without_reply | +Boolean | +Optional | +Pass True, if the message should be sent even if the specified replied-to message is not found | +
reply_markup | +InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReply | +Optional | +Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. | +
Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
media | +Array of InputMediaAudio, InputMediaDocument, InputMediaPhoto and InputMediaVideo | +Yes | +A JSON-serialized array describing messages to be sent, must include 2-10 items | +
disable_notification | +Boolean | +Optional | +Sends messages silently. Users will receive a notification with no sound. | +
reply_to_message_id | +Integer | +Optional | +If the messages are a reply, ID of the original message | +
allow_sending_without_reply | +Boolean | +Optional | +Pass True, if the message should be sent even if the specified replied-to message is not found | +
Use this method to send point on the map. On success, the sent Message is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
latitude | +Float number | +Yes | +Latitude of the location | +
longitude | +Float number | +Yes | +Longitude of the location | +
horizontal_accuracy | +Float number | +Optional | +The radius of uncertainty for the location, measured in meters; 0-1500 | +
live_period | +Integer | +Optional | +Period in seconds for which the location will be updated (see Live Locations, should be between 60 and 86400. | +
heading | +Integer | +Optional | +For live locations, a direction in which the user is moving, in degrees. Must be between 1 and 360 if specified. | +
proximity_alert_radius | +Integer | +Optional | +For live locations, a maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified. | +
disable_notification | +Boolean | +Optional | +Sends the message silently. Users will receive a notification with no sound. | +
reply_to_message_id | +Integer | +Optional | +If the message is a reply, ID of the original message | +
allow_sending_without_reply | +Boolean | +Optional | +Pass True, if the message should be sent even if the specified replied-to message is not found | +
reply_markup | +InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReply | +Optional | +Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. | +
Use this method to edit live location messages. A location can be edited until its live_period expires or editing is explicitly disabled by a call to stopMessageLiveLocation. On success, if the edited message is not an inline message, the edited Message is returned, otherwise True is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Optional | +Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
message_id | +Integer | +Optional | +Required if inline_message_id is not specified. Identifier of the message to edit | +
inline_message_id | +String | +Optional | +Required if chat_id and message_id are not specified. Identifier of the inline message | +
latitude | +Float number | +Yes | +Latitude of new location | +
longitude | +Float number | +Yes | +Longitude of new location | +
horizontal_accuracy | +Float number | +Optional | +The radius of uncertainty for the location, measured in meters; 0-1500 | +
heading | +Integer | +Optional | +Direction in which the user is moving, in degrees. Must be between 1 and 360 if specified. | +
proximity_alert_radius | +Integer | +Optional | +Maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified. | +
reply_markup | +InlineKeyboardMarkup | +Optional | +A JSON-serialized object for a new inline keyboard. | +
Use this method to stop updating a live location message before live_period expires. On success, if the message was sent by the bot, the sent Message is returned, otherwise True is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Optional | +Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
message_id | +Integer | +Optional | +Required if inline_message_id is not specified. Identifier of the message with live location to stop | +
inline_message_id | +String | +Optional | +Required if chat_id and message_id are not specified. Identifier of the inline message | +
reply_markup | +InlineKeyboardMarkup | +Optional | +A JSON-serialized object for a new inline keyboard. | +
Use this method to send information about a venue. On success, the sent Message is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
latitude | +Float number | +Yes | +Latitude of the venue | +
longitude | +Float number | +Yes | +Longitude of the venue | +
title | +String | +Yes | +Name of the venue | +
address | +String | +Yes | +Address of the venue | +
foursquare_id | +String | +Optional | +Foursquare identifier of the venue | +
foursquare_type | +String | +Optional | +Foursquare type of the venue, if known. (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.) | +
google_place_id | +String | +Optional | +Google Places identifier of the venue | +
google_place_type | +String | +Optional | +Google Places type of the venue. (See supported types.) | +
disable_notification | +Boolean | +Optional | +Sends the message silently. Users will receive a notification with no sound. | +
reply_to_message_id | +Integer | +Optional | +If the message is a reply, ID of the original message | +
allow_sending_without_reply | +Boolean | +Optional | +Pass True, if the message should be sent even if the specified replied-to message is not found | +
reply_markup | +InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReply | +Optional | +Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. | +
Use this method to send phone contacts. On success, the sent Message is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
phone_number | +String | +Yes | +Contact's phone number | +
first_name | +String | +Yes | +Contact's first name | +
last_name | +String | +Optional | +Contact's last name | +
vcard | +String | +Optional | +Additional data about the contact in the form of a vCard, 0-2048 bytes | +
disable_notification | +Boolean | +Optional | +Sends the message silently. Users will receive a notification with no sound. | +
reply_to_message_id | +Integer | +Optional | +If the message is a reply, ID of the original message | +
allow_sending_without_reply | +Boolean | +Optional | +Pass True, if the message should be sent even if the specified replied-to message is not found | +
reply_markup | +InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReply | +Optional | +Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove keyboard or to force a reply from the user. | +
Use this method to send a native poll. On success, the sent Message is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
question | +String | +Yes | +Poll question, 1-300 characters | +
options | +Array of String | +Yes | +A JSON-serialized list of answer options, 2-10 strings 1-100 characters each | +
is_anonymous | +Boolean | +Optional | +True, if the poll needs to be anonymous, defaults to True | +
type | +String | +Optional | +Poll type, “quiz” or “regular”, defaults to “regular” | +
allows_multiple_answers | +Boolean | +Optional | +True, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to False | +
correct_option_id | +Integer | +Optional | +0-based identifier of the correct answer option, required for polls in quiz mode | +
explanation | +String | +Optional | +Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters with at most 2 line feeds after entities parsing | +
explanation_parse_mode | +String | +Optional | +Mode for parsing entities in the explanation. See formatting options for more details. | +
explanation_entities | +Array of MessageEntity | +Optional | +List of special entities that appear in the poll explanation, which can be specified instead of parse_mode | +
open_period | +Integer | +Optional | +Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with close_date. | +
close_date | +Integer | +Optional | +Point in time (Unix timestamp) 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 open_period. | +
is_closed | +Boolean | +Optional | +Pass True, if the poll needs to be immediately closed. This can be useful for poll preview. | +
disable_notification | +Boolean | +Optional | +Sends the message silently. Users will receive a notification with no sound. | +
reply_to_message_id | +Integer | +Optional | +If the message is a reply, ID of the original message | +
allow_sending_without_reply | +Boolean | +Optional | +Pass True, if the message should be sent even if the specified replied-to message is not found | +
reply_markup | +InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReply | +Optional | +Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. | +
Use this method to send an animated emoji that will display a random value. On success, the sent Message is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
emoji | +String | +Optional | +Emoji on which the dice throw animation is based. Currently, must be one of “”, “”, “”, “”, “”, or “”. Dice can have values 1-6 for “”, “” and “”, values 1-5 for “” and “”, and values 1-64 for “”. Defaults to “” | +
disable_notification | +Boolean | +Optional | +Sends the message silently. Users will receive a notification with no sound. | +
reply_to_message_id | +Integer | +Optional | +If the message is a reply, ID of the original message | +
allow_sending_without_reply | +Boolean | +Optional | +Pass True, if the message should be sent even if the specified replied-to message is not found | +
reply_markup | +InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReply | +Optional | +Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. | +
Use this method when you need to tell the user that something is happening on the bot's side. The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status). Returns True on success.
+++Example: The ImageBot needs some time to process a request and upload the image. Instead of sending a text message along the lines of “Retrieving image, please wait…”, the bot may use sendChatAction with action = upload_photo. The user will see a “sending photo” status for the bot.
+
We only recommend using this method when a response from the bot will take a noticeable amount of time to arrive.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
action | +String | +Yes | +Type of action to broadcast. Choose one, depending on what the user is about to receive: typing for text messages, upload_photo for photos, record_video or upload_video for videos, record_voice or upload_voice for voice notes, upload_document for general files, find_location for location data, record_video_note or upload_video_note for video notes. | +
Use this method to get a list of profile pictures for a user. Returns a UserProfilePhotos object.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
user_id | +Integer | +Yes | +Unique identifier of the target user | +
offset | +Integer | +Optional | +Sequential number of the first photo to be returned. By default, all photos are returned. | +
limit | +Integer | +Optional | +Limits the number of photos to be retrieved. Values between 1-100 are accepted. Defaults to 100. | +
Use this method to get basic info about a file and prepare it for downloading. For the moment, bots can download files of up to 20MB in size. On success, a File object is returned. The file can then be downloaded via the link https://api.telegram.org/file/bot<token>/<file_path>
, where <file_path>
is taken from the response. It is guaranteed that the link will be valid for at least 1 hour. When the link expires, a new one can be requested by calling getFile again.
Parameter | +Type | +Required | +Description | +
---|---|---|---|
file_id | +String | +Yes | +File identifier to get info about | +
Note: This function may not preserve the original file name and MIME type. You should save the file's MIME type and name (if available) when the File object is received.
+Use this method to kick a user from a group, a supergroup or a channel. In the case of supergroups and channels, the user will not be able to return to the chat on their own using invite links, etc., unless unbanned first. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target group or username of the target supergroup or channel (in the format @channelusername ) |
+
user_id | +Integer | +Yes | +Unique identifier of the target user | +
until_date | +Integer | +Optional | +Date when the user will be unbanned, unix time. If user is banned for more than 366 days or less than 30 seconds from the current time they are considered to be banned forever. Applied for supergroups and channels only. | +
revoke_messages | +Boolean | +Optional | +Pass True to delete all messages from the chat for the user that is being removed. If False, the user will be able to see messages in the group that were sent before the user was removed. Always True for supergroups and channels. | +
Use this method to unban a previously kicked user in a supergroup or channel. The user will not return to the group or channel automatically, but will be able to join via link, etc. The bot must be an administrator for this to work. By default, this method guarantees that after the call the user is not a member of the chat, but will be able to join it. So if the user is a member of the chat they will also be removed from the chat. If you don't want this, use the parameter only_if_banned. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target group or username of the target supergroup or channel (in the format @username ) |
+
user_id | +Integer | +Yes | +Unique identifier of the target user | +
only_if_banned | +Boolean | +Optional | +Do nothing if the user is not banned | +
Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for this to work and must have the appropriate admin rights. Pass True for all permissions to lift restrictions from a user. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername ) |
+
user_id | +Integer | +Yes | +Unique identifier of the target user | +
permissions | +ChatPermissions | +Yes | +A JSON-serialized object for new user permissions | +
until_date | +Integer | +Optional | +Date when restrictions will be lifted for the user, unix time. If user is restricted for more than 366 days or less than 30 seconds from the current time, they are considered to be restricted forever | +
Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Pass False for all boolean parameters to demote a user. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
user_id | +Integer | +Yes | +Unique identifier of the target user | +
is_anonymous | +Boolean | +Optional | +Pass True, if the administrator's presence in the chat is hidden | +
can_manage_chat | +Boolean | +Optional | +Pass True, if the administrator can access the chat event log, chat statistics, message statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege | +
can_post_messages | +Boolean | +Optional | +Pass True, if the administrator can create channel posts, channels only | +
can_edit_messages | +Boolean | +Optional | +Pass True, if the administrator can edit messages of other users and can pin messages, channels only | +
can_delete_messages | +Boolean | +Optional | +Pass True, if the administrator can delete messages of other users | +
can_manage_voice_chats | +Boolean | +Optional | +Pass True, if the administrator can manage voice chats | +
can_restrict_members | +Boolean | +Optional | +Pass True, if the administrator can restrict, ban or unban chat members | +
can_promote_members | +Boolean | +Optional | +Pass True, if the administrator can add new administrators with a subset of their own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by him) | +
can_change_info | +Boolean | +Optional | +Pass True, if the administrator can change chat title, photo and other settings | +
can_invite_users | +Boolean | +Optional | +Pass True, if the administrator can invite new users to the chat | +
can_pin_messages | +Boolean | +Optional | +Pass True, if the administrator can pin messages, supergroups only | +
Use this method to set a custom title for an administrator in a supergroup promoted by the bot. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername ) |
+
user_id | +Integer | +Yes | +Unique identifier of the target user | +
custom_title | +String | +Yes | +New custom title for the administrator; 0-16 characters, emoji are not allowed | +
Use this method to set default chat permissions for all members. The bot must be an administrator in the group or a supergroup for this to work and must have the can_restrict_members admin rights. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername ) |
+
permissions | +ChatPermissions | +Yes | +New default chat permissions | +
Use this method to generate a new primary invite link for a chat; any previously generated primary link is revoked. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns the new invite link as String on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
++Note: Each administrator in a chat generates their own invite links. Bots can't use invite links generated by other administrators. If you want your bot to work with invite links, it will need to generate its own link using exportChatInviteLink or by calling the getChat method. If your bot needs to generate a new primary invite link replacing its previous one, use exportChatInviteLink again.
+
Use this method to create an additional invite link for a chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. The link can be revoked using the method revokeChatInviteLink. Returns the new invite link as ChatInviteLink object.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
expire_date | +Integer | +Optional | +Point in time (Unix timestamp) when the link will expire | +
member_limit | +Integer | +Optional | +Maximum number of users that can be members of the chat simultaneously after joining the chat via this invite link; 1-99999 | +
Use this method to edit a non-primary invite link created by the bot. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns the edited invite link as a ChatInviteLink object.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
invite_link | +String | +Yes | +The invite link to edit | +
expire_date | +Integer | +Optional | +Point in time (Unix timestamp) when the link will expire | +
member_limit | +Integer | +Optional | +Maximum number of users that can be members of the chat simultaneously after joining the chat via this invite link; 1-99999 | +
Use this method to revoke an invite link created by the bot. If the primary link is revoked, a new link is automatically generated. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns the revoked invite link as ChatInviteLink object.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier of the target chat or username of the target channel (in the format @channelusername ) |
+
invite_link | +String | +Yes | +The invite link to revoke | +
Use this method to set a new profile photo for the chat. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
photo | +InputFile | +Yes | +New chat photo, uploaded using multipart/form-data | +
Use this method to delete a chat photo. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
Use this method to change the title of a chat. Titles can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
title | +String | +Yes | +New chat title, 1-255 characters | +
Use this method to change the description of a group, a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
description | +String | +Optional | +New chat description, 0-255 characters | +
Use this method to add a message to the list of pinned messages in a chat. If the chat is not a private chat, the bot must be an administrator in the chat for this to work and must have the 'can_pin_messages' admin right in a supergroup or 'can_edit_messages' admin right in a channel. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
message_id | +Integer | +Yes | +Identifier of a message to pin | +
disable_notification | +Boolean | +Optional | +Pass True, if it is not necessary to send a notification to all chat members about the new pinned message. Notifications are always disabled in channels and private chats. | +
Use this method to remove a message from the list of pinned messages in a chat. If the chat is not a private chat, the bot must be an administrator in the chat for this to work and must have the 'can_pin_messages' admin right in a supergroup or 'can_edit_messages' admin right in a channel. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
message_id | +Integer | +Optional | +Identifier of a message to unpin. If not specified, the most recent pinned message (by sending date) will be unpinned. | +
Use this method to clear the list of pinned messages in a chat. If the chat is not a private chat, the bot must be an administrator in the chat for this to work and must have the 'can_pin_messages' admin right in a supergroup or 'can_edit_messages' admin right in a channel. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
Use this method for your bot to leave a group, supergroup or channel. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername ) |
+
Use this method to get up to date information about the chat (current name of the user for one-on-one conversations, current username of a user, group or channel, etc.). Returns a Chat object on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername ) |
+
Use this method to get a list of administrators in a chat. On success, returns an Array of ChatMember objects that contains information about all chat administrators except other bots. If the chat is a group or a supergroup and no administrators were appointed, only the creator will be returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername ) |
+
Use this method to get the number of members in a chat. Returns Int on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername ) |
+
Use this method to get information about a member of a chat. Returns a ChatMember object on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername ) |
+
user_id | +Integer | +Yes | +Unique identifier of the target user | +
Use this method to set a new group sticker set for a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Use the field can_set_sticker_set optionally returned in getChat requests to check if the bot can use this method. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername ) |
+
sticker_set_name | +String | +Yes | +Name of the sticker set to be set as the group sticker set | +
Use this method to delete a group sticker set from a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Use the field can_set_sticker_set optionally returned in getChat requests to check if the bot can use this method. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername ) |
+
Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. On success, True is returned.
+++Alternatively, the user can be redirected to the specified Game URL. For this option to work, you must first create a game for your bot via @Botfather and accept the terms. Otherwise, you may use links like
+t.me/your_bot?start=XXXX
that open your bot with a parameter.
Parameter | +Type | +Required | +Description | +
---|---|---|---|
callback_query_id | +String | +Yes | +Unique identifier for the query to be answered | +
text | +String | +Optional | +Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters | +
show_alert | +Boolean | +Optional | +If true, an alert will be shown by the client instead of a notification at the top of the chat screen. Defaults to false. | +
url | +String | +Optional | +URL that will be opened by the user's client. If you have created a Game and accepted the conditions via @Botfather, specify the URL that opens your game — note that this will only work if the query comes from a callback_game button. Otherwise, you may use links like t.me/your_bot?start=XXXX that open your bot with a parameter. |
+
cache_time | +Integer | +Optional | +The maximum amount of time in seconds that the result of the callback query may be cached client-side. Telegram apps will support caching starting in version 3.14. Defaults to 0. | +
Use this method to change the list of the bot's commands. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
commands | +Array of BotCommand | +Yes | +A JSON-serialized list of bot commands to be set as the list of the bot's commands. At most 100 commands can be specified. | +
Use this method to get the current list of the bot's commands. Requires no parameters. Returns Array of BotCommand on success.
+Methods and objects used in the inline mode are described in the Inline mode section.
+The following methods allow you to change an existing message in the message history instead of sending a new one with a result of an action. This is most useful for messages with inline keyboards using callback queries, but can also help reduce clutter in conversations with regular chat bots.
+Please note, that it is currently only possible to edit messages without reply_markup or with inline keyboards.
+Use this method to edit text and game messages. On success, if the edited message is not an inline message, the edited Message is returned, otherwise True is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Optional | +Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
message_id | +Integer | +Optional | +Required if inline_message_id is not specified. Identifier of the message to edit | +
inline_message_id | +String | +Optional | +Required if chat_id and message_id are not specified. Identifier of the inline message | +
text | +String | +Yes | +New text of the message, 1-4096 characters after entities parsing | +
parse_mode | +String | +Optional | +Mode for parsing entities in the message text. See formatting options for more details. | +
entities | +Array of MessageEntity | +Optional | +List of special entities that appear in message text, which can be specified instead of parse_mode | +
disable_web_page_preview | +Boolean | +Optional | +Disables link previews for links in this message | +
reply_markup | +InlineKeyboardMarkup | +Optional | +A JSON-serialized object for an inline keyboard. | +
Use this method to edit captions of messages. On success, if the edited message is not an inline message, the edited Message is returned, otherwise True is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Optional | +Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
message_id | +Integer | +Optional | +Required if inline_message_id is not specified. Identifier of the message to edit | +
inline_message_id | +String | +Optional | +Required if chat_id and message_id are not specified. Identifier of the inline message | +
caption | +String | +Optional | +New caption of the message, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional | +Mode for parsing entities in the message caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional | +List of special entities that appear in the caption, which can be specified instead of parse_mode | +
reply_markup | +InlineKeyboardMarkup | +Optional | +A JSON-serialized object for an inline keyboard. | +
Use this method to edit animation, audio, document, photo, or video messages. If a message is part of a message album, then it can be edited only to an audio for audio albums, only to a document for document albums and to a photo or a video otherwise. When an inline message is edited, a new file can't be uploaded. Use a previously uploaded file via its file_id or specify a URL. On success, if the edited message was sent by the bot, the edited Message is returned, otherwise True is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Optional | +Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
message_id | +Integer | +Optional | +Required if inline_message_id is not specified. Identifier of the message to edit | +
inline_message_id | +String | +Optional | +Required if chat_id and message_id are not specified. Identifier of the inline message | +
media | +InputMedia | +Yes | +A JSON-serialized object for a new media content of the message | +
reply_markup | +InlineKeyboardMarkup | +Optional | +A JSON-serialized object for a new inline keyboard. | +
Use this method to edit only the reply markup of messages. On success, if the edited message is not an inline message, the edited Message is returned, otherwise True is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Optional | +Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
message_id | +Integer | +Optional | +Required if inline_message_id is not specified. Identifier of the message to edit | +
inline_message_id | +String | +Optional | +Required if chat_id and message_id are not specified. Identifier of the inline message | +
reply_markup | +InlineKeyboardMarkup | +Optional | +A JSON-serialized object for an inline keyboard. | +
Use this method to stop a poll which was sent by the bot. On success, the stopped Poll with the final results is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
message_id | +Integer | +Yes | +Identifier of the original message with the poll | +
reply_markup | +InlineKeyboardMarkup | +Optional | +A JSON-serialized object for a new message inline keyboard. | +
Use this method to delete a message, including service messages, with the following limitations:
- A message can only be deleted if it was sent less than 48 hours ago.
- A dice message in a private chat can only be deleted if it was sent more than 24 hours ago.
- Bots can delete outgoing messages in private chats, groups, and supergroups.
- Bots can delete incoming messages in private chats.
- Bots granted can_post_messages permissions can delete outgoing messages in channels.
- If the bot is an administrator of a group, it can delete any message there.
- If the bot has can_delete_messages permission in a supergroup or a channel, it can delete any message there.
Returns True on success.
Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
message_id | +Integer | +Yes | +Identifier of the message to delete | +
The following methods and objects allow your bot to handle stickers and sticker sets.
+This object represents a sticker.
+Field | +Type | +Description | +
---|---|---|
file_id | +String | +Identifier for this file, which can be used to download or reuse the file | +
file_unique_id | +String | +Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. | +
width | +Integer | +Sticker width | +
height | +Integer | +Sticker height | +
is_animated | +Boolean | +True, if the sticker is animated | +
thumb | +PhotoSize | +Optional. Sticker thumbnail in the .WEBP or .JPG format | +
emoji | +String | +Optional. Emoji associated with the sticker | +
set_name | +String | +Optional. Name of the sticker set to which the sticker belongs | +
mask_position | +MaskPosition | +Optional. For mask stickers, the position where the mask should be placed | +
file_size | +Integer | +Optional. File size | +
This object represents a sticker set.
+Field | +Type | +Description | +
---|---|---|
name | +String | +Sticker set name | +
title | +String | +Sticker set title | +
is_animated | +Boolean | +True, if the sticker set contains animated stickers | +
contains_masks | +Boolean | +True, if the sticker set contains masks | +
stickers | +Array of Sticker | +List of all set stickers | +
thumb | +PhotoSize | +Optional. Sticker set thumbnail in the .WEBP or .TGS format | +
This object describes the position on faces where a mask should be placed by default.
+Field | +Type | +Description | +
---|---|---|
point | +String | +The part of the face relative to which the mask should be placed. One of “forehead”, “eyes”, “mouth”, or “chin”. | +
x_shift | +Float number | +Shift by X-axis measured in widths of the mask scaled to the face size, from left to right. For example, choosing -1.0 will place mask just to the left of the default mask position. | +
y_shift | +Float number | +Shift by Y-axis measured in heights of the mask scaled to the face size, from top to bottom. For example, 1.0 will place the mask just below the default mask position. | +
scale | +Float number | +Mask scaling coefficient. For example, 2.0 means double size. | +
Use this method to send static .WEBP or animated .TGS stickers. On success, the sent Message is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
sticker | +InputFile or String | +Yes | +Sticker to send. Pass a file_id as String to send a file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a .WEBP file from the Internet, or upload a new one using multipart/form-data. More info on Sending Files » | +
disable_notification | +Boolean | +Optional | +Sends the message silently. Users will receive a notification with no sound. | +
reply_to_message_id | +Integer | +Optional | +If the message is a reply, ID of the original message | +
allow_sending_without_reply | +Boolean | +Optional | +Pass True, if the message should be sent even if the specified replied-to message is not found | +
reply_markup | +InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReply | +Optional | +Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. | +
Use this method to get a sticker set. On success, a StickerSet object is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
name | +String | +Yes | +Name of the sticker set | +
Use this method to upload a .PNG file with a sticker for later use in createNewStickerSet and addStickerToSet methods (can be used multiple times). Returns the uploaded File on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
user_id | +Integer | +Yes | +User identifier of sticker file owner | +
png_sticker | +InputFile | +Yes | +PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. More info on Sending Files » | +
Use this method to create a new sticker set owned by a user. The bot will be able to edit the sticker set thus created. You must use exactly one of the fields png_sticker or tgs_sticker. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
user_id | +Integer | +Yes | +User identifier of created sticker set owner | +
name | +String | +Yes | +Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., animals). Can contain only english letters, digits and underscores. Must begin with a letter, can't contain consecutive underscores and must end in “_by_<bot username>”. <bot_username> is case insensitive. 1-64 characters. |
+
title | +String | +Yes | +Sticker set title, 1-64 characters | +
png_sticker | +InputFile or String | +Optional | +PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. Pass a file_id as a String to send a file that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. More info on Sending Files » | +
tgs_sticker | +InputFile | +Optional | +TGS animation with the sticker, uploaded using multipart/form-data. See https://core.telegram.org/animated_stickers#technical-requirements for technical requirements | +
emojis | +String | +Yes | +One or more emoji corresponding to the sticker | +
contains_masks | +Boolean | +Optional | +Pass True, if a set of mask stickers should be created | +
mask_position | +MaskPosition | +Optional | +A JSON-serialized object for position where the mask should be placed on faces | +
Use this method to add a new sticker to a set created by the bot. You must use exactly one of the fields png_sticker or tgs_sticker. Animated stickers can be added to animated sticker sets and only to them. Animated sticker sets can have up to 50 stickers. Static sticker sets can have up to 120 stickers. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
user_id | +Integer | +Yes | +User identifier of sticker set owner | +
name | +String | +Yes | +Sticker set name | +
png_sticker | +InputFile or String | +Optional | +PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. Pass a file_id as a String to send a file that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. More info on Sending Files » | +
tgs_sticker | +InputFile | +Optional | +TGS animation with the sticker, uploaded using multipart/form-data. See https://core.telegram.org/animated_stickers#technical-requirements for technical requirements | +
emojis | +String | +Yes | +One or more emoji corresponding to the sticker | +
mask_position | +MaskPosition | +Optional | +A JSON-serialized object for position where the mask should be placed on faces | +
Use this method to move a sticker in a set created by the bot to a specific position. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
sticker | +String | +Yes | +File identifier of the sticker | +
position | +Integer | +Yes | +New sticker position in the set, zero-based | +
Use this method to delete a sticker from a set created by the bot. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
sticker | +String | +Yes | +File identifier of the sticker | +
Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for animated sticker sets only. Returns True on success.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
name | +String | +Yes | +Sticker set name | +
user_id | +Integer | +Yes | +User identifier of the sticker set owner | +
thumb | +InputFile or String | +Optional | +A PNG image with the thumbnail, must be up to 128 kilobytes in size and have width and height exactly 100px, or a TGS animation with the thumbnail up to 32 kilobytes in size; see https://core.telegram.org/animated_stickers#technical-requirements for animated sticker technical requirements. Pass a file_id as a String to send a file that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. More info on Sending Files ». Animated sticker set thumbnail can't be uploaded via HTTP URL. | +
The following methods and objects allow your bot to work in inline mode.
Please see our Introduction to Inline bots for more details.
To enable this option, send the /setinline
command to @BotFather and provide the placeholder text that the user will see in the input field after typing your bot's name.
This object represents an incoming inline query. When the user sends an empty query, your bot could return some default or trending results.
+Field | +Type | +Description | +
---|---|---|
id | +String | +Unique identifier for this query | +
from | +User | +Sender | +
query | +String | +Text of the query (up to 256 characters) | +
offset | +String | +Offset of the results to be returned, can be controlled by the bot | +
chat_type | +String | +Optional. Type of the chat, from which the inline query was sent. Can be either “sender” for a private chat with the inline query sender, “private”, “group”, “supergroup”, or “channel”. The chat type should be always known for requests sent from official clients and most third-party clients, unless the request was sent from a secret chat | +
location | +Location | +Optional. Sender location, only for bots that request user location | +
Use this method to send answers to an inline query. On success, True is returned.
No more than 50 results per query are allowed.
Parameter | +Type | +Required | +Description | +
---|---|---|---|
inline_query_id | +String | +Yes | +Unique identifier for the answered query | +
results | +Array of InlineQueryResult | +Yes | +A JSON-serialized array of results for the inline query | +
cache_time | +Integer | +Optional | +The maximum amount of time in seconds that the result of the inline query may be cached on the server. Defaults to 300. | +
is_personal | +Boolean | +Optional | +Pass True, 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 | +
next_offset | +String | +Optional | +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. | +
switch_pm_text | +String | +Optional | +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 the parameter switch_pm_parameter | +
switch_pm_parameter | +String | +Optional | +Deep-linking parameter for the /start message sent to the bot when user presses the switch button. 1-64 characters, only A-Z , a-z , 0-9 , _ and - are allowed.Example: An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube account to adapt search results accordingly. To do this, it displays a 'Connect your YouTube account' button above the results, or even before showing any. The user presses the button, switches to a private chat with the bot and, in doing so, passes a start parameter that instructs the bot to return an oauth link. Once done, the bot can offer a switch_inline button so that the user can easily return to the chat where they wanted to use the bot's inline capabilities. |
+
This object represents one result of an inline query. Telegram clients currently support results of the following 20 types:
+Note: All URLs passed in inline query results will be available to end users and therefore must be assumed to be public.
+Represents a link to an article or web page.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be article | +
id | +String | +Unique identifier for this result, 1-64 Bytes | +
title | +String | +Title of the result | +
input_message_content | +InputMessageContent | +Content of the message to be sent | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message | +
url | +String | +Optional. URL of the result | +
hide_url | +Boolean | +Optional. Pass True, if you don't want the URL to be shown in the message | +
description | +String | +Optional. Short description of the result | +
thumb_url | +String | +Optional. Url of the thumbnail for the result | +
thumb_width | +Integer | +Optional. Thumbnail width | +
thumb_height | +Integer | +Optional. Thumbnail height | +
Represents a link to a photo. By default, this photo will be sent by the user with optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the photo.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be photo | +
id | +String | +Unique identifier for this result, 1-64 bytes | +
photo_url | +String | +A valid URL of the photo. Photo must be in jpeg format. Photo size must not exceed 5MB | +
thumb_url | +String | +URL of the thumbnail for the photo | +
photo_width | +Integer | +Optional. Width of the photo | +
photo_height | +Integer | +Optional. Height of the photo | +
title | +String | +Optional. Title for the result | +
description | +String | +Optional. Short description of the result | +
caption | +String | +Optional. Caption of the photo to be sent, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional. Mode for parsing entities in the photo caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message | +
input_message_content | +InputMessageContent | +Optional. Content of the message to be sent instead of the photo | +
Represents a link to an animated GIF file. By default, this animated GIF file will be sent by the user with optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the animation.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be gif | +
id | +String | +Unique identifier for this result, 1-64 bytes | +
gif_url | +String | +A valid URL for the GIF file. File size must not exceed 1MB | +
gif_width | +Integer | +Optional. Width of the GIF | +
gif_height | +Integer | +Optional. Height of the GIF | +
gif_duration | +Integer | +Optional. Duration of the GIF | +
thumb_url | +String | +URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result | +
thumb_mime_type | +String | +Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or “video/mp4”. Defaults to “image/jpeg” | +
title | +String | +Optional. Title for the result | +
caption | +String | +Optional. Caption of the GIF file to be sent, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional. Mode for parsing entities in the caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message | +
input_message_content | +InputMessageContent | +Optional. Content of the message to be sent instead of the GIF animation | +
Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). By default, this animated MPEG-4 file will be sent by the user with optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the animation.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be mpeg4_gif | +
id | +String | +Unique identifier for this result, 1-64 bytes | +
mpeg4_url | +String | +A valid URL for the MP4 file. File size must not exceed 1MB | +
mpeg4_width | +Integer | +Optional. Video width | +
mpeg4_height | +Integer | +Optional. Video height | +
mpeg4_duration | +Integer | +Optional. Video duration | +
thumb_url | +String | +URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result | +
thumb_mime_type | +String | +Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or “video/mp4”. Defaults to “image/jpeg” | +
title | +String | +Optional. Title for the result | +
caption | +String | +Optional. Caption of the MPEG-4 file to be sent, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional. Mode for parsing entities in the caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message | +
input_message_content | +InputMessageContent | +Optional. Content of the message to be sent instead of the video animation | +
Represents a link to a page containing an embedded video player or a video file. By default, this video file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the video.
+++If an InlineQueryResultVideo message contains an embedded video (e.g., YouTube), you must replace its content using input_message_content.
+
Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be video | +
id | +String | +Unique identifier for this result, 1-64 bytes | +
video_url | +String | +A valid URL for the embedded video player or video file | +
mime_type | +String | +Mime type of the content of video url, “text/html” or “video/mp4” | +
thumb_url | +String | +URL of the thumbnail (jpeg only) for the video | +
title | +String | +Title for the result | +
caption | +String | +Optional. Caption of the video to be sent, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional. Mode for parsing entities in the video caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode | +
video_width | +Integer | +Optional. Video width | +
video_height | +Integer | +Optional. Video height | +
video_duration | +Integer | +Optional. Video duration in seconds | +
description | +String | +Optional. Short description of the result | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message | +
input_message_content | +InputMessageContent | +Optional. Content of the message to be sent instead of the video. This field is required if InlineQueryResultVideo is used to send an HTML-page as a result (e.g., a YouTube video). | +
Represents a link to an MP3 audio file. By default, this audio file will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the audio.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be audio | +
id | +String | +Unique identifier for this result, 1-64 bytes | +
audio_url | +String | +A valid URL for the audio file | +
title | +String | +Title | +
caption | +String | +Optional. Caption, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional. Mode for parsing entities in the audio caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode | +
performer | +String | +Optional. Performer | +
audio_duration | +Integer | +Optional. Audio duration in seconds | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message | +
input_message_content | +InputMessageContent | +Optional. Content of the message to be sent instead of the audio | +
Note: This will only work in Telegram versions released after 9 April, 2016. Older clients will ignore them.
+Represents a link to a voice recording in an .OGG container encoded with OPUS. By default, this voice recording will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the the voice message.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be voice | +
id | +String | +Unique identifier for this result, 1-64 bytes | +
voice_url | +String | +A valid URL for the voice recording | +
title | +String | +Recording title | +
caption | +String | +Optional. Caption, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional. Mode for parsing entities in the voice message caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode | +
voice_duration | +Integer | +Optional. Recording duration in seconds | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message | +
input_message_content | +InputMessageContent | +Optional. Content of the message to be sent instead of the voice recording | +
Note: This will only work in Telegram versions released after 9 April, 2016. Older clients will ignore them.
+Represents a link to a file. By default, this file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the file. Currently, only .PDF and .ZIP files can be sent using this method.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be document | +
id | +String | +Unique identifier for this result, 1-64 bytes | +
title | +String | +Title for the result | +
caption | +String | +Optional. Caption of the document to be sent, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional. Mode for parsing entities in the document caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode | +
document_url | +String | +A valid URL for the file | +
mime_type | +String | +Mime type of the content of the file, either “application/pdf” or “application/zip” | +
description | +String | +Optional. Short description of the result | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message | +
input_message_content | +InputMessageContent | +Optional. Content of the message to be sent instead of the file | +
thumb_url | +String | +Optional. URL of the thumbnail (jpeg only) for the file | +
thumb_width | +Integer | +Optional. Thumbnail width | +
thumb_height | +Integer | +Optional. Thumbnail height | +
Note: This will only work in Telegram versions released after 9 April, 2016. Older clients will ignore them.
+Represents a location on a map. By default, the location will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the location.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be location | +
id | +String | +Unique identifier for this result, 1-64 Bytes | +
latitude | +Float number | +Location latitude in degrees | +
longitude | +Float number | +Location longitude in degrees | +
title | +String | +Location title | +
horizontal_accuracy | +Float number | +Optional. The radius of uncertainty for the location, measured in meters; 0-1500 | +
live_period | +Integer | +Optional. Period in seconds for which the location can be updated, should be between 60 and 86400. | +
heading | +Integer | +Optional. For live locations, a direction in which the user is moving, in degrees. Must be between 1 and 360 if specified. | +
proximity_alert_radius | +Integer | +Optional. For live locations, a maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified. | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message | +
input_message_content | +InputMessageContent | +Optional. Content of the message to be sent instead of the location | +
thumb_url | +String | +Optional. Url of the thumbnail for the result | +
thumb_width | +Integer | +Optional. Thumbnail width | +
thumb_height | +Integer | +Optional. Thumbnail height | +
Note: This will only work in Telegram versions released after 9 April, 2016. Older clients will ignore them.
+Represents a venue. By default, the venue will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the venue.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be venue | +
id | +String | +Unique identifier for this result, 1-64 Bytes | +
latitude | +Float | +Latitude of the venue location in degrees | +
longitude | +Float | +Longitude of the venue location in degrees | +
title | +String | +Title of the venue | +
address | +String | +Address of the venue | +
foursquare_id | +String | +Optional. Foursquare identifier of the venue if known | +
foursquare_type | +String | +Optional. Foursquare type of the venue, if known. (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.) | +
google_place_id | +String | +Optional. Google Places identifier of the venue | +
google_place_type | +String | +Optional. Google Places type of the venue. (See supported types.) | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message | +
input_message_content | +InputMessageContent | +Optional. Content of the message to be sent instead of the venue | +
thumb_url | +String | +Optional. Url of the thumbnail for the result | +
thumb_width | +Integer | +Optional. Thumbnail width | +
thumb_height | +Integer | +Optional. Thumbnail height | +
Note: This will only work in Telegram versions released after 9 April, 2016. Older clients will ignore them.
+Represents a contact with a phone number. By default, this contact will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the contact.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be contact | +
id | +String | +Unique identifier for this result, 1-64 Bytes | +
phone_number | +String | +Contact's phone number | +
first_name | +String | +Contact's first name | +
last_name | +String | +Optional. Contact's last name | +
vcard | +String | +Optional. Additional data about the contact in the form of a vCard, 0-2048 bytes | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message | +
input_message_content | +InputMessageContent | +Optional. Content of the message to be sent instead of the contact | +
thumb_url | +String | +Optional. Url of the thumbnail for the result | +
thumb_width | +Integer | +Optional. Thumbnail width | +
thumb_height | +Integer | +Optional. Thumbnail height | +
Note: This will only work in Telegram versions released after 9 April, 2016. Older clients will ignore them.
+Represents a Game.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be game | +
id | +String | +Unique identifier for this result, 1-64 bytes | +
game_short_name | +String | +Short name of the game | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message | +
Note: This will only work in Telegram versions released after October 1, 2016. Older clients will not display any inline results if a game result is among them.
+Represents a link to a photo stored on the Telegram servers. By default, this photo will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the photo.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be photo | +
id | +String | +Unique identifier for this result, 1-64 bytes | +
photo_file_id | +String | +A valid file identifier of the photo | +
title | +String | +Optional. Title for the result | +
description | +String | +Optional. Short description of the result | +
caption | +String | +Optional. Caption of the photo to be sent, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional. Mode for parsing entities in the photo caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message | +
input_message_content | +InputMessageContent | +Optional. Content of the message to be sent instead of the photo | +
Represents a link to an animated GIF file stored on the Telegram servers. By default, this animated GIF file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with specified content instead of the animation.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be gif | +
id | +String | +Unique identifier for this result, 1-64 bytes | +
gif_file_id | +String | +A valid file identifier for the GIF file | +
title | +String | +Optional. Title for the result | +
caption | +String | +Optional. Caption of the GIF file to be sent, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional. Mode for parsing entities in the caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message | +
input_message_content | +InputMessageContent | +Optional. Content of the message to be sent instead of the GIF animation | +
Represents a link to a video animation (H.264/MPEG-4 AVC video without sound) stored on the Telegram servers. By default, this animated MPEG-4 file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the animation.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be mpeg4_gif | +
id | +String | +Unique identifier for this result, 1-64 bytes | +
mpeg4_file_id | +String | +A valid file identifier for the MP4 file | +
title | +String | +Optional. Title for the result | +
caption | +String | +Optional. Caption of the MPEG-4 file to be sent, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional. Mode for parsing entities in the caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message | +
input_message_content | +InputMessageContent | +Optional. Content of the message to be sent instead of the video animation | +
Represents a link to a sticker stored on the Telegram servers. By default, this sticker will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the sticker.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be sticker | +
id | +String | +Unique identifier for this result, 1-64 bytes | +
sticker_file_id | +String | +A valid file identifier of the sticker | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message | +
input_message_content | +InputMessageContent | +Optional. Content of the message to be sent instead of the sticker | +
Note: This will only work in Telegram versions released after 9 April, 2016 for static stickers and after 06 July, 2019 for animated stickers. Older clients will ignore them.
+Represents a link to a file stored on the Telegram servers. By default, this file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the file.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be document | +
id | +String | +Unique identifier for this result, 1-64 bytes | +
title | +String | +Title for the result | +
document_file_id | +String | +A valid file identifier for the file | +
description | +String | +Optional. Short description of the result | +
caption | +String | +Optional. Caption of the document to be sent, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional. Mode for parsing entities in the document caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message | +
input_message_content | +InputMessageContent | +Optional. Content of the message to be sent instead of the file | +
Note: This will only work in Telegram versions released after 9 April, 2016. Older clients will ignore them.
+Represents a link to a video file stored on the Telegram servers. By default, this video file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the video.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be video | +
id | +String | +Unique identifier for this result, 1-64 bytes | +
video_file_id | +String | +A valid file identifier for the video file | +
title | +String | +Title for the result | +
description | +String | +Optional. Short description of the result | +
caption | +String | +Optional. Caption of the video to be sent, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional. Mode for parsing entities in the video caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message | +
input_message_content | +InputMessageContent | +Optional. Content of the message to be sent instead of the video | +
Represents a link to a voice message stored on the Telegram servers. By default, this voice message will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the voice message.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be voice | +
id | +String | +Unique identifier for this result, 1-64 bytes | +
voice_file_id | +String | +A valid file identifier for the voice message | +
title | +String | +Voice message title | +
caption | +String | +Optional. Caption, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional. Mode for parsing entities in the voice message caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message | +
input_message_content | +InputMessageContent | +Optional. Content of the message to be sent instead of the voice message | +
Note: This will only work in Telegram versions released after 9 April, 2016. Older clients will ignore them.
+Represents a link to an MP3 audio file stored on the Telegram servers. By default, this audio file will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the audio.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Type of the result, must be audio | +
id | +String | +Unique identifier for this result, 1-64 bytes | +
audio_file_id | +String | +A valid file identifier for the audio file | +
caption | +String | +Optional. Caption, 0-1024 characters after entities parsing | +
parse_mode | +String | +Optional. Mode for parsing entities in the audio caption. See formatting options for more details. | +
caption_entities | +Array of MessageEntity | +Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode | +
reply_markup | +InlineKeyboardMarkup | +Optional. Inline keyboard attached to the message | +
input_message_content | +InputMessageContent | +Optional. Content of the message to be sent instead of the audio | +
Note: This will only work in Telegram versions released after 9 April, 2016. Older clients will ignore them.
+This object represents the content of a message to be sent as a result of an inline query. Telegram clients currently support the following 5 types:
+Represents the content of a text message to be sent as the result of an inline query.
+Field | +Type | +Description | +
---|---|---|
message_text | +String | +Text of the message to be sent, 1-4096 characters | +
parse_mode | +String | +Optional. Mode for parsing entities in the message text. See formatting options for more details. | +
entities | +Array of MessageEntity | +Optional. List of special entities that appear in message text, which can be specified instead of parse_mode | +
disable_web_page_preview | +Boolean | +Optional. Disables link previews for links in the sent message | +
Represents the content of a location message to be sent as the result of an inline query.
+Field | +Type | +Description | +
---|---|---|
latitude | +Float | +Latitude of the location in degrees | +
longitude | +Float | +Longitude of the location in degrees | +
horizontal_accuracy | +Float number | +Optional. The radius of uncertainty for the location, measured in meters; 0-1500 | +
live_period | +Integer | +Optional. Period in seconds for which the location can be updated, should be between 60 and 86400. | +
heading | +Integer | +Optional. For live locations, a direction in which the user is moving, in degrees. Must be between 1 and 360 if specified. | +
proximity_alert_radius | +Integer | +Optional. For live locations, a maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified. | +
Represents the content of a venue message to be sent as the result of an inline query.
+Field | +Type | +Description | +
---|---|---|
latitude | +Float | +Latitude of the venue in degrees | +
longitude | +Float | +Longitude of the venue in degrees | +
title | +String | +Name of the venue | +
address | +String | +Address of the venue | +
foursquare_id | +String | +Optional. Foursquare identifier of the venue, if known | +
foursquare_type | +String | +Optional. Foursquare type of the venue, if known. (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.) | +
google_place_id | +String | +Optional. Google Places identifier of the venue | +
google_place_type | +String | +Optional. Google Places type of the venue. (See supported types.) | +
Represents the content of a contact message to be sent as the result of an inline query.
+Field | +Type | +Description | +
---|---|---|
phone_number | +String | +Contact's phone number | +
first_name | +String | +Contact's first name | +
last_name | +String | +Optional. Contact's last name | +
vcard | +String | +Optional. Additional data about the contact in the form of a vCard, 0-2048 bytes | +
Represents the content of an invoice message to be sent as the result of an inline query.
+Field | +Type | +Description | +
---|---|---|
title | +String | +Product name, 1-32 characters | +
description | +String | +Product description, 1-255 characters | +
payload | +String | +Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes. | +
provider_token | +String | +Payment provider token, obtained via Botfather | +
currency | +String | +Three-letter ISO 4217 currency code, see more on currencies | +
prices | +Array of LabeledPrice | +Price breakdown, a JSON-serialized list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) | +
max_tip_amount | +Integer | +Optional. The maximum accepted amount for tips in the smallest units of the currency (integer, not float/double). For example, for a maximum tip of US$ 1.45 pass max_tip_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). Defaults to 0 |
+
suggested_tip_amounts | +Array of Integer | +Optional. A JSON-serialized array of suggested amounts of tip 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. | +
provider_data | +String | +Optional. 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. | +
photo_url | +String | +Optional. 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. | +
photo_size | +Integer | +Optional. Photo size | +
photo_width | +Integer | +Optional. Photo width | +
photo_height | +Integer | +Optional. Photo height | +
need_name | +Boolean | +Optional. Pass True, if you require the user's full name to complete the order | +
need_phone_number | +Boolean | +Optional. Pass True, if you require the user's phone number to complete the order | +
need_email | +Boolean | +Optional. Pass True, if you require the user's email address to complete the order | +
need_shipping_address | +Boolean | +Optional. Pass True, if you require the user's shipping address to complete the order | +
send_phone_number_to_provider | +Boolean | +Optional. Pass True, if user's phone number should be sent to provider | +
send_email_to_provider | +Boolean | +Optional. Pass True, if user's email address should be sent to provider | +
is_flexible | +Boolean | +Optional. Pass True, if the final price depends on the shipping method | +
Represents a result of an inline query that was chosen by the user and sent to their chat partner.
+Field | +Type | +Description | +
---|---|---|
result_id | +String | +The unique identifier for the result that was chosen | +
from | +User | +The user that chose the result | +
location | +Location | +Optional. Sender location, only for bots that require user location | +
inline_message_id | +String | +Optional. 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. | +
query | +String | +The query that was used to obtain the result | +
Note: It is necessary to enable inline feedback via @Botfather in order to receive these objects in updates.
+Your bot can accept payments from Telegram users. Please see the introduction to payments for more details on the process and how to set up payments for your bot. Please note that users will need Telegram v.4.0 or higher to use payments (released on May 18, 2017).
+Use this method to send invoices. On success, the sent Message is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer or String | +Yes | +Unique identifier for the target chat or username of the target channel (in the format @channelusername ) |
+
title | +String | +Yes | +Product name, 1-32 characters | +
description | +String | +Yes | +Product description, 1-255 characters | +
payload | +String | +Yes | +Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes. | +
provider_token | +String | +Yes | +Payments provider token, obtained via Botfather | +
currency | +String | +Yes | +Three-letter ISO 4217 currency code, see more on currencies | +
prices | +Array of LabeledPrice | +Yes | +Price breakdown, a JSON-serialized list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) | +
max_tip_amount | +Integer | +Optional | +The maximum accepted amount for tips in the smallest units of the currency (integer, not float/double). For example, for a maximum tip of US$ 1.45 pass max_tip_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). Defaults to 0 |
+
suggested_tip_amounts | +Array of Integer | +Optional | +A JSON-serialized array 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. | +
start_parameter | +String | +Optional | +Unique deep-linking parameter. If left empty, forwarded copies of the sent message will have a Pay button, allowing multiple users to pay directly from the forwarded message, using the same invoice. If non-empty, forwarded copies of the sent message will have a URL button with a deep link to the bot (instead of a Pay button), with the value used as the start parameter | +
provider_data | +String | +Optional | +A JSON-serialized 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. | +
photo_url | +String | +Optional | +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. | +
photo_size | +Integer | +Optional | +Photo size | +
photo_width | +Integer | +Optional | +Photo width | +
photo_height | +Integer | +Optional | +Photo height | +
need_name | +Boolean | +Optional | +Pass True, if you require the user's full name to complete the order | +
need_phone_number | +Boolean | +Optional | +Pass True, if you require the user's phone number to complete the order | +
need_email | +Boolean | +Optional | +Pass True, if you require the user's email address to complete the order | +
need_shipping_address | +Boolean | +Optional | +Pass True, if you require the user's shipping address to complete the order | +
send_phone_number_to_provider | +Boolean | +Optional | +Pass True, if user's phone number should be sent to provider | +
send_email_to_provider | +Boolean | +Optional | +Pass True, if user's email address should be sent to provider | +
is_flexible | +Boolean | +Optional | +Pass True, if the final price depends on the shipping method | +
disable_notification | +Boolean | +Optional | +Sends the message silently. Users will receive a notification with no sound. | +
reply_to_message_id | +Integer | +Optional | +If the message is a reply, ID of the original message | +
allow_sending_without_reply | +Boolean | +Optional | +Pass True, if the message should be sent even if the specified replied-to message is not found | +
reply_markup | +InlineKeyboardMarkup | +Optional | +A JSON-serialized object for an inline keyboard. If empty, one 'Pay total price ' button will be shown. If not empty, the first button must be a Pay button. |
+
If you sent an invoice requesting a shipping address and the parameter is_flexible was specified, the Bot API will send an Update with a shipping_query field to the bot. Use this method to reply to shipping queries. On success, True is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
shipping_query_id | +String | +Yes | +Unique identifier for the query to be answered | +
ok | +Boolean | +Yes | +Specify True if delivery to the specified address is possible and False if there are any problems (for example, if delivery to the specified address is not possible) | +
shipping_options | +Array of ShippingOption | +Optional | +Required if ok is True. A JSON-serialized array of available shipping options. | +
error_message | +String | +Optional | +Required if ok is False. 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. | +
Once the user has confirmed their payment and shipping details, the Bot API sends the final confirmation in the form of an Update with the field pre_checkout_query. Use this method to respond to such pre-checkout queries. On success, True is returned. Note: The Bot API must receive an answer within 10 seconds after the pre-checkout query was sent.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
pre_checkout_query_id | +String | +Yes | +Unique identifier for the query to be answered | +
ok | +Boolean | +Yes | +Specify True if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order. Use False if there are any problems. | +
error_message | +String | +Optional | +Required if ok is False. 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. | +
This object represents a portion of the price for goods or services.
+Field | +Type | +Description | +
---|---|---|
label | +String | +Portion label | +
amount | +Integer | +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). |
+
This object contains basic information about an invoice.
+Field | +Type | +Description | +
---|---|---|
title | +String | +Product name | +
description | +String | +Product description | +
start_parameter | +String | +Unique bot deep-linking parameter that can be used to generate this invoice | +
currency | +String | +Three-letter ISO 4217 currency code | +
total_amount | +Integer | +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). |
+
This object represents a shipping address.
+Field | +Type | +Description | +
---|---|---|
country_code | +String | +ISO 3166-1 alpha-2 country code | +
state | +String | +State, if applicable | +
city | +String | +City | +
street_line1 | +String | +First line for the address | +
street_line2 | +String | +Second line for the address | +
post_code | +String | +Address post code | +
This object represents information about an order.
+Field | +Type | +Description | +
---|---|---|
name | +String | +Optional. User name | +
phone_number | +String | +Optional. User's phone number | +
String | +Optional. User email | +|
shipping_address | +ShippingAddress | +Optional. User shipping address | +
This object represents one shipping option.
+Field | +Type | +Description | +
---|---|---|
id | +String | +Shipping option identifier | +
title | +String | +Option title | +
prices | +Array of LabeledPrice | +List of price portions | +
This object contains basic information about a successful payment.
+Field | +Type | +Description | +
---|---|---|
currency | +String | +Three-letter ISO 4217 currency code | +
total_amount | +Integer | +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). |
+
invoice_payload | +String | +Bot specified invoice payload | +
shipping_option_id | +String | +Optional. Identifier of the shipping option chosen by the user | +
order_info | +OrderInfo | +Optional. Order info provided by the user | +
telegram_payment_charge_id | +String | +Telegram payment identifier | +
provider_payment_charge_id | +String | +Provider payment identifier | +
This object contains information about an incoming shipping query.
+Field | +Type | +Description | +
---|---|---|
id | +String | +Unique query identifier | +
from | +User | +User who sent the query | +
invoice_payload | +String | +Bot specified invoice payload | +
shipping_address | +ShippingAddress | +User specified shipping address | +
This object contains information about an incoming pre-checkout query.
+Field | +Type | +Description | +
---|---|---|
id | +String | +Unique query identifier | +
from | +User | +User who sent the query | +
currency | +String | +Three-letter ISO 4217 currency code | +
total_amount | +Integer | +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). |
+
invoice_payload | +String | +Bot specified invoice payload | +
shipping_option_id | +String | +Optional. Identifier of the shipping option chosen by the user | +
order_info | +OrderInfo | +Optional. Order info provided by the user | +
Telegram Passport is a unified authorization method for services that require personal identification. Users can upload their documents once, then instantly share their data with services that require real-world ID (finance, ICOs, etc.). Please see the manual for details.
+Contains information about Telegram Passport data shared with the bot by the user.
+Field | +Type | +Description | +
---|---|---|
data | +Array of EncryptedPassportElement | +Array with information about documents and other Telegram Passport elements that was shared with the bot | +
credentials | +EncryptedCredentials | +Encrypted credentials required to decrypt the data | +
This object represents a file uploaded to Telegram Passport. Currently all Telegram Passport files are in JPEG format when decrypted and don't exceed 10MB.
+Field | +Type | +Description | +
---|---|---|
file_id | +String | +Identifier for this file, which can be used to download or reuse the file | +
file_unique_id | +String | +Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. | +
file_size | +Integer | +File size | +
file_date | +Integer | +Unix time when the file was uploaded | +
Contains information about documents or other Telegram Passport elements shared with the bot by the user.
+Field | +Type | +Description | +
---|---|---|
type | +String | +Element type. One of “personal_details”, “passport”, “driver_license”, “identity_card”, “internal_passport”, “address”, “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration”, “phone_number”, “email”. | +
data | +String | +Optional. Base64-encoded encrypted Telegram Passport element data provided by the user, available for “personal_details”, “passport”, “driver_license”, “identity_card”, “internal_passport” and “address” types. Can be decrypted and verified using the accompanying EncryptedCredentials. | +
phone_number | +String | +Optional. User's verified phone number, available only for “phone_number” type | +
String | +Optional. User's verified email address, available only for “email” type | +|
files | +Array of PassportFile | +Optional. Array of encrypted files with documents provided by the user, available for “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration” and “temporary_registration” types. Files can be decrypted and verified using the accompanying EncryptedCredentials. | +
front_side | +PassportFile | +Optional. Encrypted file with the front side of the document, provided by the user. Available for “passport”, “driver_license”, “identity_card” and “internal_passport”. The file can be decrypted and verified using the accompanying EncryptedCredentials. | +
reverse_side | +PassportFile | +Optional. Encrypted file with the reverse side of the document, provided by the user. Available for “driver_license” and “identity_card”. The file can be decrypted and verified using the accompanying EncryptedCredentials. | +
selfie | +PassportFile | +Optional. Encrypted file with the selfie of the user holding a document, provided by the user; available for “passport”, “driver_license”, “identity_card” and “internal_passport”. The file can be decrypted and verified using the accompanying EncryptedCredentials. | +
translation | +Array of PassportFile | +Optional. Array of encrypted files with translated versions of documents provided by the user. Available if requested for “passport”, “driver_license”, “identity_card”, “internal_passport”, “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration” and “temporary_registration” types. Files can be decrypted and verified using the accompanying EncryptedCredentials. | +
hash | +String | +Base64-encoded element hash for using in PassportElementErrorUnspecified | +
Contains data required for decrypting and authenticating EncryptedPassportElement. See the Telegram Passport Documentation for a complete description of the data decryption and authentication processes.
+Field | +Type | +Description | +
---|---|---|
data | +String | +Base64-encoded encrypted JSON-serialized data with unique user's payload, data hashes and secrets required for EncryptedPassportElement decryption and authentication | +
hash | +String | +Base64-encoded data hash for data authentication | +
secret | +String | +Base64-encoded secret, encrypted with the bot's public RSA key, required for data decryption | +
Informs a user that some of the Telegram Passport elements they provided contains errors. The user will not be able to re-submit their Passport to you until the errors are fixed (the contents of the field for which you returned the error must change). Returns True on success.
+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.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
user_id | +Integer | +Yes | +User identifier | +
errors | +Array of PassportElementError | +Yes | +A JSON-serialized array describing the errors | +
This object represents an error in the Telegram Passport element which was submitted that should be resolved by the user. It should be one of:
+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.
+Field | +Type | +Description | +
---|---|---|
source | +String | +Error source, must be data | +
type | +String | +The section of the user's Telegram Passport which has the error, one of “personal_details”, “passport”, “driver_license”, “identity_card”, “internal_passport”, “address” | +
field_name | +String | +Name of the data field which has the error | +
data_hash | +String | +Base64-encoded data hash | +
message | +String | +Error message | +
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.
+Field | +Type | +Description | +
---|---|---|
source | +String | +Error source, must be front_side | +
type | +String | +The section of the user's Telegram Passport which has the issue, one of “passport”, “driver_license”, “identity_card”, “internal_passport” | +
file_hash | +String | +Base64-encoded hash of the file with the front side of the document | +
message | +String | +Error message | +
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.
+Field | +Type | +Description | +
---|---|---|
source | +String | +Error source, must be reverse_side | +
type | +String | +The section of the user's Telegram Passport which has the issue, one of “driver_license”, “identity_card” | +
file_hash | +String | +Base64-encoded hash of the file with the reverse side of the document | +
message | +String | +Error message | +
Represents an issue with the selfie with a document. The error is considered resolved when the file with the selfie changes.
+Field | +Type | +Description | +
---|---|---|
source | +String | +Error source, must be selfie | +
type | +String | +The section of the user's Telegram Passport which has the issue, one of “passport”, “driver_license”, “identity_card”, “internal_passport” | +
file_hash | +String | +Base64-encoded hash of the file with the selfie | +
message | +String | +Error message | +
Represents an issue with a document scan. The error is considered resolved when the file with the document scan changes.
+Field | +Type | +Description | +
---|---|---|
source | +String | +Error source, must be file | +
type | +String | +The section of the user's Telegram Passport which has the issue, one of “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration” | +
file_hash | +String | +Base64-encoded file hash | +
message | +String | +Error message | +
Represents an issue with a list of scans. The error is considered resolved when the list of files containing the scans changes.
+Field | +Type | +Description | +
---|---|---|
source | +String | +Error source, must be files | +
type | +String | +The section of the user's Telegram Passport which has the issue, one of “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration” | +
file_hashes | +Array of String | +List of base64-encoded file hashes | +
message | +String | +Error message | +
Represents an issue with one of the files that constitute the translation of a document. The error is considered resolved when the file changes.
+Field | +Type | +Description | +
---|---|---|
source | +String | +Error source, must be translation_file | +
type | +String | +Type of element of the user's Telegram Passport which has the issue, one of “passport”, “driver_license”, “identity_card”, “internal_passport”, “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration” | +
file_hash | +String | +Base64-encoded file hash | +
message | +String | +Error message | +
Represents an issue with the translated version of a document. The error is considered resolved when a file with the document translation change.
+Field | +Type | +Description | +
---|---|---|
source | +String | +Error source, must be translation_files | +
type | +String | +Type of element of the user's Telegram Passport which has the issue, one of “passport”, “driver_license”, “identity_card”, “internal_passport”, “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration” | +
file_hashes | +Array of String | +List of base64-encoded file hashes | +
message | +String | +Error message | +
Represents an issue in an unspecified place. The error is considered resolved when new data is added.
+Field | +Type | +Description | +
---|---|---|
source | +String | +Error source, must be unspecified | +
type | +String | +Type of element of the user's Telegram Passport which has the issue | +
element_hash | +String | +Base64-encoded element hash | +
message | +String | +Error message | +
Your bot can offer users HTML5 games to play solo or to compete against each other in groups and one-on-one chats. Create games via @BotFather using the /newgame command. Please note that this kind of power requires responsibility: you will need to accept the terms for each game that your bots will be offering.
+Use this method to send a game. On success, the sent Message is returned.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
chat_id | +Integer | +Yes | +Unique identifier for the target chat | +
game_short_name | +String | +Yes | +Short name of the game, serves as the unique identifier for the game. Set up your games via Botfather. | +
disable_notification | +Boolean | +Optional | +Sends the message silently. Users will receive a notification with no sound. | +
reply_to_message_id | +Integer | +Optional | +If the message is a reply, ID of the original message | +
allow_sending_without_reply | +Boolean | +Optional | +Pass True, if the message should be sent even if the specified replied-to message is not found | +
reply_markup | +InlineKeyboardMarkup | +Optional | +A JSON-serialized object for an inline keyboard. If empty, one 'Play game_title' button will be shown. If not empty, the first button must launch the game. | +
This object represents a game. Use BotFather to create and edit games, their short names will act as unique identifiers.
+Field | +Type | +Description | +
---|---|---|
title | +String | +Title of the game | +
description | +String | +Description of the game | +
photo | +Array of PhotoSize | +Photo that will be displayed in the game message in chats. | +
text | +String | +Optional. Brief description of the game or high scores included in the game message. Can be automatically edited to include current high scores for the game when the bot calls setGameScore, or manually edited using editMessageText. 0-4096 characters. | +
text_entities | +Array of MessageEntity | +Optional. Special entities that appear in text, such as usernames, URLs, bot commands, etc. | +
animation | +Animation | +Optional. Animation that will be displayed in the game message in chats. Upload via BotFather | +
A placeholder, currently holds no information. Use BotFather to set up your game.
+Use this method to set the score of the specified user in a game. On success, if the message was sent by the bot, returns the edited Message, otherwise returns True. Returns an error, if the new score is not greater than the user's current score in the chat and force is False.
+Parameter | +Type | +Required | +Description | +
---|---|---|---|
user_id | +Integer | +Yes | +User identifier | +
score | +Integer | +Yes | +New score, must be non-negative | +
force | +Boolean | +Optional | +Pass True, if the high score is allowed to decrease. This can be useful when fixing mistakes or banning cheaters | +
disable_edit_message | +Boolean | +Optional | +Pass True, if the game message should not be automatically edited to include the current scoreboard | +
chat_id | +Integer | +Optional | +Required if inline_message_id is not specified. Unique identifier for the target chat | +
message_id | +Integer | +Optional | +Required if inline_message_id is not specified. Identifier of the sent message | +
inline_message_id | +String | +Optional | +Required if chat_id and message_id are not specified. Identifier of the inline message | +
Use this method to get data for high score tables. Will return the score of the specified user and several of their neighbors in a game. On success, returns an Array of GameHighScore objects.
+++This method will currently return scores for the target user, plus two of their closest neighbors on each side. Will also return the top three users if the user and his neighbors are not among them. Please note that this behavior is subject to change.
+
Parameter | +Type | +Required | +Description | +
---|---|---|---|
user_id | +Integer | +Yes | +Target user id | +
chat_id | +Integer | +Optional | +Required if inline_message_id is not specified. Unique identifier for the target chat | +
message_id | +Integer | +Optional | +Required if inline_message_id is not specified. Identifier of the sent message | +
inline_message_id | +String | +Optional | +Required if chat_id and message_id are not specified. Identifier of the inline message | +
This object represents one row of the high scores table for a game.
+Field | +Type | +Description | +
---|---|---|
position | +Integer | +Position in high score table for the game | +
user | +User | +User | +
score | +Integer | +Score | +
And that's about all we've got for now.
If you've got any questions, please check out our Bot FAQ »
++If you are new to Telegram bots, we recommend checking out our Introduction to Bots first.
+
You may also find the Bot API Manual useful.
Creating Telegram bots is super-easy, but you will need at least some skills at computer programming. In order for a bot to work, set up a bot account with @BotFather, then connect it to your backend server via our API.
+Unfortunately, there are no out-of-the-box ways to create a working bot if you are not a developer. But we're sure you'll soon find plenty of bots created by other people to play with.
+Here are two sample bots, both written in PHP:
+++Many members of our community are building bots and publishing sources.
+
We're collecting them on this page »
Ping us on @BotSupport if you've built a bot and would like to share it with others.
+The bot API is still pretty young. There are many potential features to consider and implement. We'll be studying what people do with their bots for a while to see which directions will be most important for the platform.
+All bot developers are welcome to share ideas for our Bot API with our @BotSupport account.
+1. All bots, regardless of settings, will receive:
+2. Bot admins and bots with privacy mode disabled will receive all messages except messages sent by other bots.
+3. Bots with privacy mode enabled will receive:
+Note that each particular message can only be available to one privacy-enabled bot at a time, i.e., a reply to bot A containing an explicit command for bot B or sent via bot C will only be available to bot A. Replies have the highest priority.
+Bots talking to each other could potentially get stuck in unwelcome loops. To avoid this, we decided that bots will not be able to see messages from other bots regardless of mode.
+There are currently two ways of getting updates. You can either use long polling or Webhooks. Please note that it's not possible to get updates via long polling while an outgoing Webhook is set.
+The getUpdates method returns the earliest 100 unconfirmed updates. To confirm an update, use the offset parameter when calling getUpdates like this:
+offset = update_id of last processed update + 1
+All updates with update_id less than or equal to offset will be marked as confirmed on the server and will no longer be returned.
+If you've set up your webhook successfully, but are not getting any updates, please remember:
+++Please check out this new WEBHOOK GUIDE to learn all there is to know about webhooks!
+
Please take a look at this self-signed certificate guide we made just for you. If you've read it and still have a question, ping us on botsupport.
+If you'd like to make sure that the Webhook request comes from Telegram, we recommend using a secret path in the URL you give us, e.g. www.example.com/your_token. Since nobody else knows your bot's token, you can be pretty sure it's us.
+This is possible if you're using webhooks. The upside is that you need less requests, the downside — that in this case it's not possible to know that such a request was successful or get its result.
+Whenever you receive a webhook update, you have two options:
+1. Issue POST to https://api.telegram.org/bot<token>/method
+ + +2. Reply directly and give method as JSON payload in the reply
+ + +++You may also want to look at our sample HelloBot, it offers a PHP implementation of this.
+
Use the getFile method. Please note that this will only work with files of up to 20 MB in size.
+Bots can currently send files of any type of up to 50 MB in size, so yes, very large files won't work for now. Sorry. This limit may be changed in the future.
+Yes, file_ids can be treated as persistent.
+When sending messages inside a particular chat, avoid sending more than one message per second. We may allow short bursts that go over this limit, but eventually you'll begin receiving 429 errors.
+If you're sending bulk notifications to multiple users, the API will not allow more than 30 messages per second or so. Consider spreading out notifications over large intervals of 8—12 hours for best results.
+Also note that your bot will not be able to send more than 20 messages per minute to the same group.
+Unfortunately, at this moment we don't have methods for sending bulk messages, e.g. notifications. We may add something along these lines in the future.
+In order to avoid hitting our limits when sending out mass notifications, consider spreading them over longer intervals, e.g. 8-12 hours. The API will not allow bulk notifications to more than ~30 users per second, if you go over that, you'll start getting 429 errors.
+See also: How to avoid hitting limits?
+++If you've got questions that are not answered on this page, ping us at @BotSupport in Telegram.
+
We welcome any suggestions for the Bot Platform and API.
Bots can offer their users HTML5 games to play solo or to compete against each other in groups and one-on-one chats. Games are a new type of content on Telegram that your bot can send to users.
+ + +++This introduction is meant for bot developers. Click here, in case you'd like something more user-friendly.
+
Check out the @gamebot and @gamee bots for examples of what you can do using the new Gaming Platform.
To get started, send the /newgame
command to @BotFather.You will be prompted for a description text and a photo. You can also upload an optional GIF animation that demostrates your game to the users to make messages with the game more attractive (check out Lumberjack or Corsairs for examples).
Once the game is created, your bot can send it to chats as regular messages, or offer them via inline mode. The game message will always have an inline Play button.
+When this button is pressed, your bot gets a callback query that indicates the requested game. You provide the correct URL for this particular user and the app automatically opens the game in the in-app browser.
+If you send the game message without any buttons, it will automatically have a 'Play GameName' button. You can manually add multiple buttons to your game message. Please note that the first button in the first row must always be the one that launches the game. You can add more buttons: e.g., for a description of the rules, or a button that links to the game's official community.
+The message with your game will also display high scores for the current chat. When a new high score is set, a service message will be sent to the chat and the message with the current scoreboard will be updated. You can also request the necessary data for building in-game high score tables.
+ + +There are many way for users to spread your game virally if they like it. The interface will always have the standard system button for sharing the game in the top right corner:
+ + + + +You can also create an additional Share button inside your HTML page. Pressing this button will send the game to a desired chat along with the user's best score in the game.
+ + + +To add the sharing button, include this script at the end of the <body>
tag on your page:
<script src="https://telegram.org/js/games.js"></script>
+Then use the method TelegramGameProxy.shareScore()
to call the sharing option.
++Warning: Do not call this method without consent and direct action from the user.
+
Example:
+<button onclick="TelegramGameProxy.shareScore()">Share score</button>
+This library will only work when launched from inside Telegram, so please don't use it on ordinary web pages.
+If your URL is using a fragment identifier, please note that Telegram Apps could add certain service parameters to the fragment id. The names for such parameters will start in tg
(you can check the code that adds them here). Use the TelegramGameProxy.initParams
object if you need to read your own parameters from the fragment id.
Please make sure that your HTML5 page is responsive and works well on all Telegram apps and supported platforms. If you find it impossible to support certain conditions or platforms, don't leave your users hanging and at least provide a notification.
+++See the Bot API Manual for the relevant methods and objects.
+
Beyond sending commands in private messages or groups, users can interact with your bot via inline queries. If inline queries are enabled, users can call your bot by typing its username and a query in the text input field in any chat. The query is sent to your bot in an update. This way, people can request content from your bot in any of their chats, groups, or channels without sending any messages at all.
+ + + +To enable this option, send the /setinline
command to @BotFather and provide the placeholder text that the user will see in the input field after typing your bot’s name.
++See the Bot API Manual for the relevant methods and objects.
+
Inline bots support all types of content available in Telegram (20 in all). They are capable of sending stickers, videos, music, locations, documents and more.
+ + +Clients can display the results with vertical or horizontal scrolling, depending on the type of content:
+ + +As soon as the user taps on an item, it's immediately sent to the recipient, and the input field is cleared.
+Some inline bots can benefit from an initial setup process, like connecting them to an account on an external service (e.g., YouTube). We've added an easy way of switching between the private chat with a bot and whatever chat the user wants to share inline results in.
+ + +You can display a special ‘Switch to PM’ button above the inline results (or instead of them). This button will open a private chat with the bot and pass a parameter of your choosing, so that you can prompt the user for the relevant setup actions. Once done, you can use an inline keyboard with a switch_inline_query button to send the user back to the original chat.
+Sample bots
@youtube – Shows a ‘Sign in to YouTube’ button, then suggests personalized results.
+ ++
Inline bots can request location data from their users. Use the /setinlinegeo
command with @BotFather to enable this. Your bot will ask the user for permission to access their location whenever they send an inline request.
Sample bot
@foursquare – This bot will ask for permission to access the user's location, then provide geo-targeted results.
Messages sent with the help of your bot will show its username next to the sender's name.
+ + +When a user taps on the bot username in the message header, the mention is automatically inserted into the input field. Entering the @
symbol in the input field brings up a list of suggestions, featuring recently used inline bots.
To know which of the provided results your users are sending to their chat partners, send @Botfather the /setinlinefeedback
command. With this enabled, you will receive updates on the results chosen by your users.
Please note that this can create load issues for popular bots – you may receive more results than actual requests due to caching (see the cache_time parameter in answerInlineQuery). For these cases, we recommend adjusting the probability setting to receive 1/10, 1/100 or 1/1000 of the results.
+Here are some sample inline bots, in case you’re curious to see one in action. Try any of these:
@gif – GIF search
@vid – Video search
@pic – Yandex image search
@bing – Bing image search
@wiki – Wikipedia search
@imdb – IMDB search
@bold – Make bold, italic or fixed sys text
NEW
@youtube - Connect your account for personalized results
@music - Search and send classical music
@foursquare – Find and send venue addresses
@sticker – Find and send stickers based on emoji
++If you want to learn more about Telegram bots, start with our Introduction to Bots »
+
Check out the FAQ, if you have questions.
Many members of our community are building bots and libraries and publishing their source code. We collect these examples here. Ping us on BotSupport if you've built a bot and would like to share its code with others.
+Telegram Bot Api Base. Clear and simple Telegram Bot API.
https://github.com/tg-bot-api/bot-api-base
PHP Telegram API. A complete async capable Telegram Bot API implementation for PHP7.
https://github.com/unreal4u/telegram-api
PHP Telegram Bot. PHP Telegram Bot based on the official Telegram Bot API.
https://github.com/php-telegram-bot/core
Bot API PHP SDK. Telegram Bot API PHP SDK. Supports Laravel out of the box.
https://github.com/irazasyed/telegram-bot-sdk
TeleBot. Easy way to create Telegram bots in PHP. Rich Laravel support out of the box.
https://github.com/westacks/telebot
NovaGram. An Object-Oriented PHP library for Telegram Bots.
https://github.com/skrtdev/NovaGram
PHP Telegram Bot. Telegram bot as it should be.
https://github.com/formapro/telegram-bot-php
PHP Telegram Bot Api. Native PHP Wrapper for Telegram BOT API.
https://github.com/TelegramBot/Api
TuriBot. A simple way to communicate with Telegram APIs in PHP.
https://github.com/davtur19/TuriBot
TelegramBotApiBundle. A symfony wrapper bundle for Telegram Bot API.
https://github.com/borsaco/TelegramBotApiBundle
Telegraf. Modern Telegram Bot Framework for Node.js.
https://github.com/telegraf/telegraf
Node-Telegram-bot. Node.js module to interact with the official Telegram Bot API.
https://github.com/yagop/node-telegram-bot-api
Telebot. The easy way to write Telegram bots.
https://github.com/mullwar/telebot
telegram-bot-api. Node.js library for Telegram Bot API.
https://github.com/mast/telegram-bot-api
Slimbot. A fuss-free, thin wrapper around Telegram Bot API for Node.js. No frills.
https://github.com/edisonchee/slimbot
tgapi. Reactive and with full Flow coverage Telegram Bot API JS implementation.
https://github.com/bigslycat/tgapi
Botgram. Practical Telegram Bot microframework.
https://github.com/botgram/botgram
teloxide. An elegant Telegram bots framework for Rust.
https://github.com/teloxide/teloxide
Telebot. Write Telegram bots in Rust with Tokio and Futures.
https://github.com/bytesnake/telebot
carapax. A Telegram Bot API framework.
https://github.com/tg-rs/carapax
telegram-bot. Rust Library for creating a Telegram Bot.
https://github.com/telegram-rs/telegram-bot
python-telegram-bot. A wrapper you can't refuse.
https://github.com/python-telegram-bot/python-telegram-bot
pyTelegramBotAPI. Python Telegram Bot API.
https://github.com/eternnoir/pyTelegramBotAPI
AIOGram. A pretty simple and fully asynchronous library for Telegram Bot API written with asyncio and aiohttp.
https://github.com/aiogram/aiogram
Telegram::Bot. Ruby gem for building Telegram Bot with optional Rails integration.
https://github.com/telegram-bot-rb/telegram-bot
telegram-bot-ruby. Ruby wrapper for Telegram's Bot API.
https://github.com/atipugin/telegram-bot-ruby
TelegramBot. A charismatic Ruby client for Telegram's Bot API.
https://github.com/eljojo/telegram_bot
telegram-bot-swift. Telegram Bot SDK for Swift.
https://github.com/rapierorg/telegram-bot-swift
Telegrammer. Telegram Bot Framework written in Swift 5.1 with SwiftNIO network framework.
https://github.com/givip/Telegrammer
SwiftyBot. Swift + Vapor + Telegram.
https://github.com/FabrizioBrancati/SwiftyBot
TelegramBotAPI. Type-safe library for work with Telegram Bot API.
https://github.com/InsanusMokrassar/TelegramBotAPI
Kotlin Telegram Bot. A wrapper for the Telegram Bot API.
https://github.com/kotlin-telegram-bot/kotlin-telegram-bot
kt-telegram-bot. Telegram Bot API library for Kotlin language.
https://github.com/elbekD/kt-telegram-bot
Telegraff. Kotlin DSL for Telegram Bots.
https://github.com/ruslanys/telegraff
TelegramBots. A simple to use library to create Telegram Bots.
https://github.com/rubenlagus/TelegramBots
Java API. Telegram Bot API for Java.
https://github.com/pengrad/java-telegram-bot-api
go-telegram-bot-api. Golang bindings for the Telegram Bot API.
https://github.com/go-telegram-bot-api/telegram-bot-api
telebot. A Telegram bot framework in Go.
https://github.com/tucnak/telebot
C#. Telegram.bot. Library.
https://github.com/TelegramBots/Telegram.Bot
Elixir. Nadia. Telegram Bot API Wrapper.
https://github.com/zhyu/nadia
C++. tgbot-cpp. C++ library for Telegram Bot API.
https://github.com/reo7sp/tgbot-cpp
Dart. TeleDart. A Dart library interfacing with the latest Telegram Bot API.
https://github.com/DinoLeung/TeleDart
Lua. telegram-bot-lua. A feature-filled Telegram Bot API library.
https://github.com/wrxck/telegram-bot-lua
OCaml. TelegraML. An OCaml library for creating bots for Telegram.
https://github.com/nv-vn/TelegraML
Haskell. haskell-telegram-api High-level bindings to the Telegram Bot API based on servant library.
https://github.com/klappvisor/haskell-telegram-api
Scala. canoe. Functional Telegram Bot API for Scala.
https://github.com/augustjune/canoe
Scala. bot4s.telegram. 100% idiomatic Scala wrapper for the Telegram Bot API.
https://github.com/bot4s/telegram
Perl. Telegram Bot. A genuine Perl 6 client for the Telegram's Bot API.
https://github.com/GildedHonour/TelegramBot
++This sample PHP bot demonstrates the basics of the Telegram Bot API.
+
If you have questions, try our FAQ or check out this page for more examples.
<?php
+
+define('BOT_TOKEN', '12345678:replace-me-with-real-token');
+define('API_URL', 'https://api.telegram.org/bot'.BOT_TOKEN.'/');
+
+function apiRequestWebhook($method, $parameters) {
+ if (!is_string($method)) {
+ error_log("Method name must be a string\n");
+ return false;
+ }
+
+ if (!$parameters) {
+ $parameters = array();
+ } else if (!is_array($parameters)) {
+ error_log("Parameters must be an array\n");
+ return false;
+ }
+
+ $parameters["method"] = $method;
+
+ $payload = json_encode($parameters);
+ header('Content-Type: application/json');
+ header('Content-Length: '.strlen($payload));
+ echo $payload;
+
+ return true;
+}
+
+function exec_curl_request($handle) {
+ $response = curl_exec($handle);
+
+ if ($response === false) {
+ $errno = curl_errno($handle);
+ $error = curl_error($handle);
+ error_log("Curl returned error $errno: $error\n");
+ curl_close($handle);
+ return false;
+ }
+
+ $http_code = intval(curl_getinfo($handle, CURLINFO_HTTP_CODE));
+ curl_close($handle);
+
+ if ($http_code >= 500) {
+ // do not wat to DDOS server if something goes wrong
+ sleep(10);
+ return false;
+ } else if ($http_code != 200) {
+ $response = json_decode($response, true);
+ error_log("Request has failed with error {$response['error_code']}: {$response['description']}\n");
+ if ($http_code == 401) {
+ throw new Exception('Invalid access token provided');
+ }
+ return false;
+ } else {
+ $response = json_decode($response, true);
+ if (isset($response['description'])) {
+ error_log("Request was successful: {$response['description']}\n");
+ }
+ $response = $response['result'];
+ }
+
+ return $response;
+}
+
+function apiRequest($method, $parameters) {
+ if (!is_string($method)) {
+ error_log("Method name must be a string\n");
+ return false;
+ }
+
+ if (!$parameters) {
+ $parameters = array();
+ } else if (!is_array($parameters)) {
+ error_log("Parameters must be an array\n");
+ return false;
+ }
+
+ foreach ($parameters as $key => &$val) {
+ // encoding to JSON array parameters, for example reply_markup
+ if (!is_numeric($val) && !is_string($val)) {
+ $val = json_encode($val);
+ }
+ }
+ $url = API_URL.$method.'?'.http_build_query($parameters);
+
+ $handle = curl_init($url);
+ curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 5);
+ curl_setopt($handle, CURLOPT_TIMEOUT, 60);
+
+ return exec_curl_request($handle);
+}
+
+function apiRequestJson($method, $parameters) {
+ if (!is_string($method)) {
+ error_log("Method name must be a string\n");
+ return false;
+ }
+
+ if (!$parameters) {
+ $parameters = array();
+ } else if (!is_array($parameters)) {
+ error_log("Parameters must be an array\n");
+ return false;
+ }
+
+ $parameters["method"] = $method;
+
+ $handle = curl_init(API_URL);
+ curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 5);
+ curl_setopt($handle, CURLOPT_TIMEOUT, 60);
+ curl_setopt($handle, CURLOPT_POST, true);
+ curl_setopt($handle, CURLOPT_POSTFIELDS, json_encode($parameters));
+ curl_setopt($handle, CURLOPT_HTTPHEADER, array("Content-Type: application/json"));
+
+ return exec_curl_request($handle);
+}
+
+function processMessage($message) {
+ // process incoming message
+ $message_id = $message['message_id'];
+ $chat_id = $message['chat']['id'];
+ if (isset($message['text'])) {
+ // incoming text message
+ $text = $message['text'];
+
+ if (strpos($text, "/start") === 0) {
+ apiRequestJson("sendMessage", array('chat_id' => $chat_id, "text" => 'Hello', 'reply_markup' => array(
+ 'keyboard' => array(array('Hello', 'Hi')),
+ 'one_time_keyboard' => true,
+ 'resize_keyboard' => true)));
+ } else if ($text === "Hello" || $text === "Hi") {
+ apiRequest("sendMessage", array('chat_id' => $chat_id, "text" => 'Nice to meet you'));
+ } else if (strpos($text, "/stop") === 0) {
+ // stop now
+ } else {
+ apiRequestWebhook("sendMessage", array('chat_id' => $chat_id, "reply_to_message_id" => $message_id, "text" => 'Cool'));
+ }
+ } else {
+ apiRequest("sendMessage", array('chat_id' => $chat_id, "text" => 'I understand only text messages'));
+ }
+}
+
+
+define('WEBHOOK_URL', 'https://my-site.example.com/secret-path-for-webhooks/');
+
+if (php_sapi_name() == 'cli') {
+ // if run from console, set or delete webhook
+ apiRequest('setWebhook', array('url' => isset($argv[1]) && $argv[1] == 'delete' ? '' : WEBHOOK_URL));
+ exit;
+}
+
+
+$content = file_get_contents("php://input");
+$update = json_decode($content, true);
+
+if (!$update) {
+ // receive wrong update, must not happen
+ exit;
+}
+
+if (isset($update["message"])) {
+ processMessage($update["message"]);
+}
+Upload your certificate using the certificate parameter in the setWebhook method. The certificate supplied should be PEM encoded (ASCII BASE64), the pem file should only contain the public key (including BEGIN and END portions). When converting from a bundle format, please split the file to only include the public key.
+++Windows binaries for Openssl are available online
+
openssl req -newkey rsa:2048 -sha256 -nodes -keyout YOURPRIVATE.key -x509 -days 365 -out YOURPUBLIC.pem -subj "/C=US/ST=New York/L=Brooklyn/O=Example Brooklyn Company/CN=YOURDOMAIN.EXAMPLE"
YOURPUBLIC.pem has to be used as input for setting the self-signed webhook.
+You can inspect the generated certificate with:openssl x509 -text -noout -in YOURPUBLIC.pem
Converting from a previously generated DER:openssl x509 -inform der -in YOURDER.der -out YOURPEM.pem
Converting from a previously generated PKCS12:openssl pkcs12 -in YOURPKCS.p12 -out YOURPEM.pem
++More information: https://www.openssl.org/
+
Generate self-signed JKS:keytool -genkey -keyalg RSA -alias YOURDOMAIN.EXAMPLE -keystore YOURJKS.jks -storepass YOURPASSWORD -validity 360 -keysize 2048
Converting JKS to pkcs12 (intermediate step for conversion to PEM):keytool -importkeystore -srckeystore YOURJKS.jks -destkeystore YOURPKCS.p12 -srcstoretype jks -deststoretype pkcs12
Convert PKCS12 to PEM (requires openssl):openssl pkcs12 -in YOURPKCS.p12 -out YOURPEM.pem
++More information: https://docs.oracle.com
+
Creating a self-signed certificate using Windows native utilities is also possible, although OpenSSL binaries for Windows are available online.
+On the commandline:certreq -new TEMPLATE.txt RequestFileOut
TEMPLATE.txt example file:
+[NewRequest]
+
+; At least one value must be set in this section
+Subject = "CN=DOMAIN.EXAMPLE"
+KeyLength = 2048
+KeyAlgorithm = RSA
+HashAlgorithm = sha256
+;MachineKeySet = true
+RequestType = Cert
+UseExistingKeySet=false ;generates a new private key (for export)
+Exportable = true ;makes the private key exportable with the PFX
+A self-signed certificate will be generated and installed, to view the certificate:certutil -store -user my
To export in DER format (intermediate step for conversion to PEM)certutil -user -store -split my SERIALNUMBER YOURDER.crt
Converting to PEM (used for setting the webhook)certutil -encode YOURDER.crt YOURPEM.cer
To delete a certificate from your store:certutil -delstore -user my SERIALNUMBER
(from view)
To export in PFX(PKCS12) formatcertutil -exportpfx -user YOURDOMAIN.EXAMPLE YOURPKCS.pfx NoChain
++More information: https://technet.microsoft.com
+
Converting YOURPKCS.pfx to PEM including the private key is best done with OpenSSL:openssl pkcs12 -in YOURPKCS.pfx -out YOURPEM.cer
Remember that only the public key is needed as input for the self-signed webhook certificate parameter. certmgr.msc can also be used as a GUI to export the public part of self-signed certificates to PEM.
+We currently support two ways of processing bot updates, getUpdates and setWebhook. getUpdates is a pull mechanism, setwebhook is push. Although the concept of a webhook is fairly simple, the setup of the individual components has proven to be tricky for many. This guide provides some extra information for those of you brave enough to venture into the art of the webhook.
+There are some advantages of using a webhook over getUpdates. As soon as an update arrives, we’ll kindly deliver it to your bot for processing.
+This:
+1. Avoids your bot having to ask for updates frequently.
+2. Avoids the need for some kind of polling mechanism in your code.
+Other advantages may include saving some potential CPU cycles and an increase in response time, these things however depend heavily on the usage pattern of your bot.
+Setting a webhook means you supplying Telegram with a location in the form of a URL, on which your bot listens for updates. We need to be able to connect and post updates to that URL.
+To ensure that we can do that, there are some basic requirements:
+You'll need a server that:
+149.154.160.0/20
and 91.108.4.0/22
on port 443, 80, 88, or 8443. That’s almost all there’s to it.
If you decide to limit traffic to our specific range of addresses, keep an eye on this document whenever you seem to run into trouble. Our IP-range might change in the future.
Setting a webhook needs a URL for us to post to. For that you'll need a server with a domain name. If you don't have one, you'll need to obtain one first. Telegram currently doesn't offer hosting or domain name services. There are quite a few VPS/Web hosting providers around the internet, feel free to pick one to your liking.
If you're using a self-signed certificate, you may use the IP as a CN, instead of the domain name.
How do I get a server with a domain name?
A webhook needs an open port on your server. We currently support the following ports: 443, 80, 88 and 8443. Other ports are not supported and will not work. Make sure your bot is running on one of those supported ports, and that the bot is reachable via its public address.
+If you want to limit access to Telegram only, please allow traffic from 149.154.167.197-233 (starting July 2019 please use: 149.154.160.0/20 and 91.108.4.0/22).
+Whenever something stops working in the future, please check this document again as
+the range might expand or change.
+
+A webhook requires SSL/TLS encryption, no matter which port is used. It's not possible to use a plain-text HTTP webhook. You shouldn't want to either, for the sake of your bot and users.
SSL/TLS, why do I have to handle this for a webhook?
We support any SSL/TLS version TLS1.2 and up for your webhook. This means that SSLV2/3/TLS1.0/TSL1.1 are NOT supported, due to security issues associated with those older versions.
How do I check that I’m handling the right version?
The common name (CN) of your certificate (self-signed or verified) has to match the domain name where your bot is hosted. You may also use a subject alternative name (SAN), that matches the domain for your webhook. Server Name Indication (SNI)-routing is supported. If you're using a self-signed certificate, you may use the IP as a CN, instead of the domain name.
A certificate, where do I get one, and how?
A certificate can either be verified or self-signed. Setting a webhook with a self-signed certificate differs a little from setting a webhook with a verified certificate. Ensure you're using the correct setup for the type of certificate you've chosen for your webhook.
How do I set a webhook for either type?
Not all verified certificates are supported. Certificates are based on a network of trust and come in a chain. Trusting your verified certificate means we have to trust the provider of that certificate, the Certificate Authority (and hence its root certificate). Before you pick a certificate provider, Check this list to make sure that we actually trust their root certificate.
What if my root certificate isn’t on that list?
Ok, so you already had a certificate installed and just discovered it’s not on our list.
Start by ignoring it, and just try to set it. We occasionally add extra root certificates to keep up with popular demand, so the list isn't always exhaustive. Unlucky after all? We'll allow you to supply an unsupported root certificate when setting the webhook. This method is nearly identical to setting a self-signed certificate webhook. Instead of your self-signed certificate you'll be sending us the root certificate as inputFile.
Setting a verified webhook with an untrusted root
Some verified certificates require an intermediate certificate. In this construction the provider of your verified certificate has used their root certificate to sign an intermediate certificate. This intermediate certificate is then used to sign your verified certificate. You'll need to provide the intermediate certificate for us to be able to verify the chain of trust. CA's that use this type of chain supply an intermediate certificate.
Supplying an intermediate certificate
Since we know webhooks can be a tad overwhelming, we’re working on a little digital assistant that’ll try and help you with the most common problems, it's not nearly perfect, but you may try using @CanOfWormsBot to check if your chain of certificates is installed correctly before contacting support.
+We took the liberty of adding a set of example updates. They come in handy when testing your bot, no matter which method of getting updates you might be using.
+If by now you're looking for your fishing gear because we've mentioned ports and hooks or you're about to Google what kind of bait URL and TLS exactly are, this guide might not be completely for you. You’re quite likely still a brilliant bot programmer, don’t worry. Perhaps this whole webhook thing is just new to you, not all is lost. If you currently have a working getUpdates situation, it's a good idea to pick up this guide again on a rainy Sunday afternoon and take your time to read up on some subjects around the internet. This guide can only contain a finite amount of information after all.
+If you use a webhook, we have to deliver requests to your bot to a server we can reach. So yes, you need a server we can connect to. It can be anywhere in the galaxy, if you ensure we can reach the server by domain name (or at least via IP for a self-signed certificate), it will work just fine.
+There are quite a few ways to get this done, as a novice however it's likely that you're not directly jumping at the chance of crafting this from scratch. Actually, as a novice, we recommend you don't. It's likely to be a complex and long ride.
+If you got stuck here, make a choice:
+You use getUpdates at the moment and it works, keep it that way. Especially if you're running your bot from a nice machine that does well. There is nothing wrong with using getUpdates.
+Go with a hosted service and let a bunch of professionals worry about things like registering a domain, setting up DNS, a web server, securing it and so on.
+If you're going with a hosted service, make sure to look for a hosting provider that
+not only supports your code’s needs, for example: support for your PHP version,
+but one that also handles SSL and allows you to create/deploy certificates.
+So you have the hosting thing down and all is good so far, however, when you enter the address of your bot in your browser it seems unreachable.
+Explaining every firewall or web server solution in detail isn't possible for us, which we hope you understand. If you’re running a hosted solution, you’re more likely to have a nice UI where you configure these settings. Head to your configuration panel and check all of them. If you’re on a Linux based VPS with shell access, we have some tips for you:
+netstat –ln | grep portnumber
sudo lsof -i | grep process name
(your public IP)
, it can also listen on all addresses (*: or 0.0.0.0)
.netstat
and lsof
-commands mentioned above assist in checking this. If nothing shows up, it is time to check your configuration and fix it. Set the correct IP, make sure it’s listening on a supported port and fire away! Just use a Web Browser to check if you’re reachable. The problem can be in the configuration of your bot, your web server virtual host configuration, or the servers binding configuration.If you still can’t reach your address, check your firewall.sudo iptables –L
ORsudo ufw status verbose
(Ubuntu)
Gives you some insight in the current firewall settings.
If it looks like you’re blocking incoming traffic, let’s fix that.sudo iptables –A INPUT –p tcp –m tcp –dport portnumber -j ACCEPT
ORsudo ufw allow portnumber/tcp
Allows incoming traffic on all interfaces to the specified tcp port.sudo iptables –A INPUT –i interfacename –p tcp –m tcp –dport portnumber -j ACCEPT
ORsudo ufw allow in on interfacename to any port portnumber proto tcp
Allows incoming traffic to a specific interface and a specific port from everywhere.sudo ifconfig
Helps you find the interface with the public address you’re going to use.
If you use iptables, make sure to actually SAVE after changing the configuration.
+On a Debian based system the iptables-persistent package is be a good option.
+RHEL/CentOS offers a service iptables save -command.
+A quick online search for "YOUROPERATINGSYSTEM save iptables" also helps.
+sudo iptables –A INPUT –i interfacename –p tcp –m iprange –src-range 149.154.167.197-149.154.167.233 –dport portnumber -j ACCEPT
sudo ufw allow in on interfacename to any port portnumber proto tcp from 149.154.167.192/26
That’s all for our examples. More information on best practices for setting up your firewall, on whichever operating system you prefer for your bot, is best found on the internet.
+You’re already familiar with it in some form or another. Whenever you see that (nicely green) lock in your browser bar, you know it’s reasonably safe to assume that you’ve landed on the site you actually wanted to visit. If you see the green lock, that's SSL/TLS in action. If you want to learn more about how SSL/TLS works in general, it's best to search the internet.
+The main difference between getUpdates and a webhook is the way the connection takes place. getUpdates means you'll connect to our server, a webhook means we'll be connecting to your server instead. Connecting to your server has to be done secure, we have to know for sure it's you we're talking to after all. This means you'll have to handle all that server side encryption stuff, virtually presenting us with a green lock. If you use a web server for us to post to, you need to support SSL/TLS handling on the port/virtual host of your choice. An online search for “YOURWEBSERVER enable HTTPS” will help you.
+Not using a regular web server? Have a look at our example page, most examples there include code for handling SSL/TLS in a webhook setup.
+You just read up on the whole SSL/TLS stuff, figured out that it’s not all that bad to setup and we add some more requirements. Here are some tips to check if you’re indeed supporting at least TLS1.2.
+Several online services exist that allow you to check your certificate installation,
They give you an overview of your supported TLS versions/Cipher suites and other details. Search online for Symantec crypto report or Qualys ssl. Both supply tools to verify your setup.
Checking locally can also be done, in several ways, here are three options,
+Go simple:
Using Chrome as a browser? Open up the URL to your bot and inspect the certificate details. If you’re supporting TLS Chrome tells you so in the security overview tab. Other browsers are likely able to give you similar basic information.
Using curl:curl --tlsv1.2 -v -k https://yourbotdomain:yourbotport/
You can add --tlsv1.2
to force curl into using TLS1.2 when trying to connect. -k
is optional and used to check against a self-signed certificate. yourbotdomain
is the public hostname your webhook is running on. For local testing purposes you can also use the IP. yourbotport
is the port you’re using.
Using OpenSSLopenssl s_client -tls1_2 -connect yourbotdomain:yourbotport -servername yourbotdomain
You can add -tls1_2
to force OpenSSL into using TLS1.2 when trying to connect. yourbotdomain
is the public hostname your webhook is running on. For local testing purposes you can also use the IP. yourbotport
is the port you’re using. Note that https:// isn’t used for OpenSSL. -servername
is optional, and included here for some shared hosters, which use SNI to route traffic to the correct domain. When SNI is used you’ll notice that your server appears to be returning a certificate for a different domain than your own. Adding -servername yourbotdomain
ensures that SNI negotiation is done, and the correct certificate is returned.
Some additional configuration pointers
+SSLProtocol -all +TLSv1.2
ssl_protocols TLSv1.2;
-Dhttps.protocols=TLSv1.2 -Djdk.tls.client.protocols=TLSv1.2
-Djavax.net.debug=ssl,handshake,record
Other tools that may help in debugging issues:
+You need a certificate, pick on of these types;
+Using a verified certificate means you already have, or will obtain, a certificate backed by a trusted certificate authority (CA). There are many ways to acquire a verified certificate, paid or free. Two popular examples of free suppliers are StartSSL and Let’s Encrypt. You’re welcome to pick another. Just make sure first the supplier is likely to be supported.
Check this list before selecting a CA.
Once you’ve picked a CA and validated your identity with them, you can craft your certificate. This frequently starts by generating a CSR (Certificate Signing Request). Generating a CSR is done either through your host machine, or online via the tools provided by the CA.
Here is an example (PEM format output).
+openssl req -newkey rsa:2048 -keyout yourprivatekey.key -out yoursigningrequest.csr
----
+Generating a 2048 bit RSA private keywriting new private key to yourprivatekey.key
+Enter PEM pass phrase: enter a password for your key here
+Verifying - Enter PEM pass phrase: confirm the entered password
+-----
+You are about to be asked to enter information that will be incorporated
+into your certificate request.
+What you are about to enter is what is called a Distinguished Name or a DN.
+There are quite a few fields but you can leave some blank
+For some fields there will be a default value,If you enter '.',
+the field will be left blank.-----
+Country Name (2 letter code) [AU]:
+State or Province Name (full name) [Some-State]:
+Locality Name (eg, city) []:
+Organization Name (eg, company) [Internet Widgits Pty Ltd]:
+Organizational Unit Name (eg, section) []:
+Common Name (e.g. server FQDN or YOUR name) []: yourbotdomainname
+Email Address []:
+Please enter the following 'extra' attributes
+to be sent with your certificate request
+A challenge password []:
+An optional company name []:
+---
+Another example:
+keytool -genkey -alias yourbotdomainname -keyalg RSA -keystore yourkeystore.jks -keysize 2048
---
+Enter keystore password:
+Re-enter new password:
+What is your first and last name? [Unknown]: yourbotdomainname
+What is the name of your organizational unit? [Unknown]:
+What is the name of your organization? [Unknown]:
+What is the name of your City or Locality? [Unknown]:
+What is the name of your State or Province? [Unknown]:
+What is the two-letter country code for this unit? [Unknown]:
+Is CN=test.telegram.org, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct?
+[no]: yes
+Enter key password for yourbotdomainname
+(RETURN if same as keystore password):
+---
+This generates the initial keystore, from which you can then create a CSR like this:
+keytool -certreq -alias yourbotdomainname -keystore yourkeystore.jks -file yourbotdomainname.csr
---
+Enter keystore password:
+---
+To validate your certificate the Common Name (CN) has to match your webhook domain. Example, if you’re using https://www.example.com/example.php as a webhook address, the certificate CN has to be www.example.com.
So you need an exact match of the FQDN you’re setting for the webhook
There is an exception, if you’re using a SAN (Subject Alternative Name) the webhook address can either match the CN of your certificate, OR one of the SANs provided in the certificate. In most cases you’ll be using the CN.
+Create your CSR and supply the contents of the file to your CA. Most CA’s are kind enough to give you an example command of the input format they expect.
+cat yoursigningrequest.csr
or cat yourbotdomainname.csr
Lets you have a look at the CSR we just generated:
That doesn’t seem to informative, but we can deduce that the file is in PEM format (ASCII base64 encoded) and contains a certificate signing request. Luckily it is possible to look at the human readable contents of the CSR. Use the following commands to double check if all fields are set correctly.
+Using OpenSSLopenssl req -text -noout -verify -in yoursigningrequest.csr
Using Java keytoolkeytool -printcertreq -v -file yourbotdomainname.csr
Verify your CSR and supply it to your CA to get a certificate. We’ll use StartSSL as an example here. StartSSL allows you to set up to 5 names (SAN), Their intermediate certificate is also needed for a webhook to work, which makes for a nice complete example.
+Go to the certificates wizard, enter the required hostname(s) for your SSL certificate (this is the CN you’ve also set in the CSR and an optional SAN).
+ + +In the example above we’ve chosen to set a CN (test.telegram.org)
, but also a SAN (sanexample.telegram.org)
The CN given has to match the CN used for generating the CSR.
Set your CN (and optional SAN) and copy the contents of the yoursigningrequest.csr file.
Paste the contents, submit and you’re done.
+ + +Now you can download the created certificate directly. In the example used above you’ll receive a zip file with several PEM certificates. The root, intermediate and yourdomain certificate.
You need the intermediate
and yourdomain
to set a webhook with a StartSSL certificate.
You can inspect the set of certificates you’ve just downloaded.
+Here are some example commands:
+Using OpenSSL:openssl x509 -in yourdomain.crt -text -noout
Using Java keytool:keytool -printcert -v -yourdomain.crt
Using Windows:
StartSSL supplies certificates in PEM format with a .crt extension, on Windows you can view the contents of them with a quick double click. Extract the files or open the “Otherserver.zip” and double click each of the certificates for inspection. The details tab supplies you with extra information.
Make sure you have a correct CN in the Subject
-field of the yourdomain
-certificate. If you're using a SAN, make sure that it is listed in the Subject Alternative Name
-field.
With your fresh certificates at hand, you can now continue setting your webhook.
+Using a self-signed certificate means you’ll forfeit on the chain of trust backed by a CA. Instead you are the CA. For this to work, a slight difference in setup is required. Because Telegram will have no chain of trust to verify your certificate, you have to use the generated public certificate as an input file when setting the webhook. Keep in mind that the certificate file has to be uploaded as multipart/form data in PEM encoded (ASCII BASE64) format.
+First let’s generate some certificates:
+Using OpenSSL:openssl req -newkey rsa:2048 -sha256 -nodes -keyout YOURPRIVATE.key -x509 -days 365 -out YOURPUBLIC.pem -subj "/C=US/ST=New York/L=Brooklyn/O=Example Brooklyn Company/CN=YOURDOMAIN.EXAMPLE"
You’ll end up with 2 files, a private key and the public certificate file. Use YOURPUBLIC.PEM as input file for setting the webhook.
Using Java keytool:keytool -genkey -keyalg RSA -alias YOURDOMAIN.EXAMPLE -keystore YOURJKS.jks -storepass YOURPASSWORD -validity 360 -keysize 2048
What is your first and last name?
+[test.telegram.org]:
+What is the name of your organizational unit?
+[Unknown]:
+What is the name of your organization?
+[Unknown]:
+What is the name of your City or Locality?
+[Unknown]:
+What is the name of your State or Province?
+[Unknown]:
+What is the two-letter country code for this unit?
+[Unknown]:
+Is CN=test.telegram.org, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct?
+[no]: yes
+Once done you’ll need 2 more commands to export the public certificate file from the generated store (you’ll be using the store for your JVM and the PEM for setting the webhook)
+Convert the JKS to pkcs12 (intermediate step for conversion to PEM):keytool -importkeystore -srckeystore YOURJKS.jks -destkeystore YOURPKCS.p12 -srcstoretype jks -deststoretype pkcs12
Convert PKCS12 to PEM (requires OpenSSL)openssl pkcs12 -in YOURPKCS.p12 -out YOURPEM.pem -nokeys
Using Windows:
Creating a self-signed certificate using Windows native utilities is also possible, although OpenSSL binaries for Windows are available online.certreq -new TEMPLATE.txt RequestFileOut generates a CSR.
TEMPLATE.txt example file:
+[NewRequest]
+; At least one value must be set in this section
+Subject = "CN=DOMAIN.EXAMPLE"
+KeyLength = 2048
+KeyAlgorithm = RSA
+HashAlgorithm = sha256
+;MachineKeySet = true
+RequestType = Cert
+UseExistingKeySet=false ;generates a new private key (for export)
+Exportable = true ;makes the private key exportable with the PFX
+A self-signed certificate is generated and installed, to use the certificate for a self-signed webhook you'll have to export it in PEM format.
+Windows continued:
+You can have a look at the certificates in your store with:certutil -store -user my
To export the installed certificate in DER format (intermediate step for conversion to PEM):certutil -user -store -split my SERIALNUMBER YOURDER.der
Now you can convert the certificate to PEM:certutil -encode YOURDER.der YOURPEM.pem
Remember that only the public certificate is needed as input for the self-signed webhook certificate parameter.certmgr.msc
can also be used as a GUI to export the public part of self-signed certificate to PEM.
After following the above you'll end up with a nice self-signed certificate. You’ll still have to set the webhook, and handle SSL correctly.
+The setWebhook method is needed for both types. For a verified certificate with a trusted root CA, it’s enough to use the setWebhook method with just the URL parameter.
+curl -F "url=https://<YOURDOMAIN.EXAMPLE>/<WEBHOOKLOCATION>" https://api.telegram.org/bot<YOURTOKEN>/setWebhook
For a self-signed certificate an extra parameter is needed, certificate
, with the public certificate in PEM format as data.
curl -F "url=https://<YOURDOMAIN.EXAMPLE>/<WEBHOOKLOCATION>" -F "certificate=@<YOURCERTIFICATE>.pem" https://api.telegram.org/bot<YOURTOKEN>/setWebhook
The -F
means we’re using the multipart/form-data
-type to supply the certificate, the type of the certificate parameter is inputFile. Make sure that you’re supplying the correct type.
Both parameters for the setWebhook method are classed as optional. Calling the method with an empty URL parameter can be used to clear a previously set webhook.
+curl -F "url=" https://api.telegram.org/bot<YOURTOKEN>/setWebhook
Keep in mind that the URL parameter starts with https://
when setting a webhook. By default that means we’re knocking at your door on port 443. If you want to use another port (80,88 or 8443), you’ll have to specify the port in the URL parameter.
url=https://<YOURDOMAIN.EXAMPLE>:88/<WEBHOOKLOCATION>
If you already have a verified certificate and our servers don’t trust your root CA, we have an alternative way for you to set a webhook. Instead of using the setWebhook method without the certificate
parameter, you can use the self-signed method. Your CA's root certificate has to be used as an inputFile for the certificate
parameter.
curl -F "url=https://<YOURDOMAIN.EXAMPLE>" -F "certificate=@<YOURCAROOTCERTIFICATE>.pem" https://api.telegram.org/bot<YOURTOKEN>/setWebhook
You can use these commands to quickly convert a DER formatted root certificate to PEM:
+Using OpenSSL:openssl x509 -inform der -in root.cer -out root.pem
Using Java keytool:keytool -import -alias Root -keystore YOURKEYSTORE.JKS -trustcacerts -file ROOTCERT.CER
The root certificate needs to be imported in your keystore first:keytool -exportcert -alias Root -file <YOURROOTPEMFILE.PEM> -rfc -keystore YOURKEYSTORE.JKS
Once done, set your webhook with the root-pem-file and you’ll be good to go. If you need more pointers, have a look at the self-signed part of this guide.
+Once you’ve crafted your certificate, your CA might present you with a nice bundle. Most bundles contain a root certificate, your public certificate and sometimes an intermediate certificate. StartSSL is one of many CA’s that’ll supply such an intermediate beast. This certificate has to be supplied in the chain of certificates you’re presenting to us when we connect to your server. If an intermediate was used to sign your certificate but isn’t supplied to our servers, we won’t be able to verify the chain of trust and your webhook will not work.
+If your webhook isn’t working and you’re wondering if the chain is complete:
+Search online for Symantec crypto report or Qualys ssl.
+Both supply tools to verify your setup.
+Here’s an example of a complete chain, note that in this case 2 intermediate certificates have been supplied.
+ + +Even though your browser might not complain when visiting your page, an incomplete chain will not work for your webhook. If your chain is incomplete we have some tips to add them to your current setup:
+Apache:
Add the intermediate certificate to the end of the file configured in the SSLCertificateFile
directive of your virtual host configuration. If you’re using an older version than Apache 2.4.8, you may use the SSLCertificateChainFile
directive instead.
Nginx:
Add the intermediate certificate to the end of the file configured in the ssl_certificate_key
directive of your virtual host configuration.
A quick command for doing this correctly:cat your_domain_name.pem intermediate.pem >> bundle.pem
Make sure the order is correct, expect failure otherwise.
Java keytool:keytool -import -trustcacerts -alias intermediate -file intermediate.pem -keystore YOURKEYSTORE.jks
The end result of all this is a complete certificate chain, backed by either a root certificate we trust or, in the case of an untrusted root, a root certificate you're supplying to us. Make sure to verify your setup again after adding the intermediate, once done, you're good to go!
+Update examples
A set of example updates, which comes in handy for testing your bot.
Message with text using curl:
+curl --tlsv1.2 -v -k -X POST -H "Content-Type: application/json" -H "Cache-Control: no-cache" -d '{
+"update_id":10000,
+"message":{
+ "date":1441645532,
+ "chat":{
+ "last_name":"Test Lastname",
+ "id":1111111,
+ "first_name":"Test",
+ "username":"Test"
+ },
+ "message_id":1365,
+ "from":{
+ "last_name":"Test Lastname",
+ "id":1111111,
+ "first_name":"Test",
+ "username":"Test"
+ },
+ "text":"/start"
+}
+}' "https://YOUR.BOT.URL:YOURPORT/"
+--tlsv1.2
will force using TLS1.2.
Message with text using Postman:
+ +More examples in curl:
+curl -v -k -X POST -H "Content-Type: application/json" -H "Cache-Control: no-cache" -d '{
+"update_id":10000,
+"message":{
+ "date":1441645532,
+ "chat":{
+ "last_name":"Test Lastname",
+ "id":1111111,
+ "type": "private",
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "message_id":1365,
+ "from":{
+ "last_name":"Test Lastname",
+ "id":1111111,
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "text":"/start"
+}
+}' "https://YOUR.BOT.URL:YOURPORT/"
+curl -v -k -X POST -H "Content-Type: application/json" -H "Cache-Control: no-cache" -d '{
+"update_id":10000,
+"message":{
+ "date":1441645532,
+ "chat":{
+ "last_name":"Test Lastname",
+ "id":1111111,
+ "type": "private",
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "message_id":1365,
+ "from":{
+ "last_name":"Test Lastname",
+ "id":1111111,
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "forward_from": {
+ "last_name":"Forward Lastname",
+ "id": 222222,
+ "first_name":"Forward Firstname"
+ },
+ "forward_date":1441645550,
+ "text":"/start"
+}
+}' "https://YOUR.BOT.URL:YOURPORT/"
+curl -v -k -X POST -H "Content-Type: application/json" -H "Cache-Control: no-cache" -d '{
+"update_id":10000,
+"message":{
+ "date":1441645532,
+ "chat":{
+ "last_name":"Test Lastname",
+ "type": "private",
+ "id":1111111,
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "message_id":1365,
+ "from":{
+ "last_name":"Test Lastname",
+ "id":1111111,
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "forward_from": {
+ "id": -10000000000,
+ "type": "channel",
+ "title": "Test channel"
+ },
+ "forward_date":1441645550,
+ "text":"/start"
+}
+}' "https://YOUR.BOT.URL:YOURPORT/"
+curl -v -k -X POST -H "Content-Type: application/json" -H "Cache-Control: no-cache" -d '{
+"update_id":10000,
+"message":{
+ "date":1441645532,
+ "chat":{
+ "last_name":"Test Lastname",
+ "type": "private",
+ "id":1111111,
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "message_id":1365,
+ "from":{
+ "last_name":"Test Lastname",
+ "id":1111111,
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "text":"/start",
+ "reply_to_message":{
+ "date":1441645000,
+ "chat":{
+ "last_name":"Reply Lastname",
+ "type": "private",
+ "id":1111112,
+ "first_name":"Reply Firstname",
+ "username":"Testusername"
+ },
+ "message_id":1334,
+ "text":"Original"
+ }
+}
+}' "https://YOUR.BOT.URL:YOURPORT/"
+curl -v -k -X POST -H "Content-Type: application/json" -H "Cache-Control: no-cache" -d '{
+"update_id":10000,
+"edited_message":{
+ "date":1441645532,
+ "chat":{
+ "last_name":"Test Lastname",
+ "type": "private",
+ "id":1111111,
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "message_id":1365,
+ "from":{
+ "last_name":"Test Lastname",
+ "id":1111111,
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "text":"Edited text",
+ "edit_date": 1441646600
+}
+}' "https://YOUR.BOT.URL:YOURPORT/"
+curl -v -k -X POST -H "Content-Type: application/json" -H "Cache-Control: no-cache" -d '{
+"update_id":10000,
+"message":{
+ "date":1441645532,
+ "chat":{
+ "last_name":"Test Lastname",
+ "type": "private",
+ "id":1111111,
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "message_id":1365,
+ "from":{
+ "last_name":"Test Lastname",
+ "id":1111111,
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "text":"Bold and italics",
+ "entities": [
+ {
+ "type": "italic",
+ "offset": 9,
+ "length": 7
+ },
+ {
+ "type": "bold",
+ "offset": 0,
+ "length": 4
+ }
+ ]
+}
+}' "https://YOUR.BOT.URL:YOURPORT/"
+curl -v -k -X POST -H "Content-Type: application/json" -H "Cache-Control: no-cache" -d '{
+"update_id":10000,
+"message":{
+ "date":1441645532,
+ "chat":{
+ "last_name":"Test Lastname",
+ "type": "private",
+ "id":1111111,
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "message_id":1365,
+ "from":{
+ "last_name":"Test Lastname",
+ "id":1111111,
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "audio": {
+ "file_id": "AwADBAADbXXXXXXXXXXXGBdhD2l6_XX",
+ "duration": 243,
+ "mime_type": "audio/mpeg",
+ "file_size": 3897500,
+ "title": "Test music file"
+ }
+}
+}' "https://YOUR.BOT.URL:YOURPORT/"
+curl -v -k -X POST -H "Content-Type: application/json" -H "Cache-Control: no-cache" -d '{
+"update_id":10000,
+"message":{
+ "date":1441645532,
+ "chat":{
+ "last_name":"Test Lastname",
+ "type": "private",
+ "id":1111111,
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "message_id":1365,
+ "from":{
+ "last_name":"Test Lastname",
+ "id":1111111,
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "voice": {
+ "file_id": "AwADBAADbXXXXXXXXXXXGBdhD2l6_XX",
+ "duration": 5,
+ "mime_type": "audio/ogg",
+ "file_size": 23000
+ }
+}
+}' "https://YOUR.BOT.URL:YOURPORT/"
+curl -v -k -X POST -H "Content-Type: application/json" -H "Cache-Control: no-cache" -d '{
+"update_id":10000,
+"message":{
+ "date":1441645532,
+ "chat":{
+ "last_name":"Test Lastname",
+ "type": "private",
+ "id":1111111,
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "message_id":1365,
+ "from":{
+ "last_name":"Test Lastname",
+ "id":1111111,
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "document": {
+ "file_id": "AwADBAADbXXXXXXXXXXXGBdhD2l6_XX",
+ "file_name": "Testfile.pdf",
+ "mime_type": "application/pdf",
+ "file_size": 536392
+ }
+}
+}' "https://YOUR.BOT.URL:YOURPORT/"
+curl -v -k -X POST -H "Content-Type: application/json" -H "Cache-Control: no-cache" -d '{
+"update_id":10000,
+"inline_query":{
+ "id": 134567890097,
+ "from":{
+ "last_name":"Test Lastname",
+ "type": "private",
+ "id":1111111,
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "query": "inline query",
+ "offset": ""
+}
+}' "https://YOUR.BOT.URL:YOURPORT/"
+curl -v -k -X POST -H "Content-Type: application/json" -H "Cache-Control: no-cache" -d '{
+"update_id":10000,
+"chosen_inline_result":{
+ "result_id": "12",
+ "from":{
+ "last_name":"Test Lastname",
+ "type": "private",
+ "id":1111111,
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "query": "inline query",
+ "inline_message_id": "1234csdbsk4839"
+}
+}' "https://YOUR.BOT.URL:YOURPORT/"
+curl -v -k -X POST -H "Content-Type: application/json" -H "Cache-Control: no-cache" -d '{
+"update_id":10000,
+"callback_query":{
+ "id": "4382bfdwdsb323b2d9",
+ "from":{
+ "last_name":"Test Lastname",
+ "type": "private",
+ "id":1111111,
+ "first_name":"Test Firstname",
+ "username":"Testusername"
+ },
+ "data": "Data from button callback",
+ "inline_message_id": "1234csdbsk4839"
+}
+}' "https://YOUR.BOT.URL:YOURPORT/"
+That's all we have for now!Following the launch of version 4.2 of the official apps, Telegram client apps may be required to download popular files that were published in public channels with more than 100,000 members from secondary Content Delivery Network data centers. These CDN DCs are located in regions with significant Telegram traffic where we wouldn't want to place Telegram servers for various reasons.
+The CDN DCs are not a part of the Telegram cloud and should be considered enemy territory. For this reason, each file that is to be sent to these CDN DCs is encrypted with a unique key using AES-256-CTR encryption. The CDN can't access the data it stores because these keys are only accessible to the main MTProto server and to the authorized client.
+++See also: More about CDNs and governments in the Advanced FAQ
+
When a file from a public channel with ~100,000 members becomes popular in a particular region, the Telegram server may encrypt this file with a unique AES-256-CTR key and send it to a relevant CDN DC for storage.
+When a file is stored in a CDN DC close to the end user, the download speed will be much higher because the data needs to travel smaller distances and will likely avoid many bottlenecks that exist between regions.
+This is secure because CDN DCs are treated the same way as internet providers / random third parties:
+CDNs are very limited when it comes to communication: the master data center only uploads encrypted files for storage and will accept no data from the CDN. The client apps only download encrypted files and accept no other updates. The client apps obtain the keys necessary to decrypt the file from the main Telegram server and verify the integrity of the file by its hash, which means that the CDN may only supply the correct file – anything different will be immediately discarded by the client.
+CDN DCs do not store files on hard disks – only in memory. When a CDN server runs out of memory, a simple LRU algorithm is used to replace the least popular files with new ones.
+The API may return the upload.fileCdnRedirect constructor after an upload.getFile query. In this case, the client must request the required file from a CDN DC. The dc_id in the response is the id of the new CDN. The IP address for the connection will be available in help.getConfig, same as with the master DCs. The corresponding dcOption will have the flag cdn:flags.3?true.
+Once a successful connection to the CDN-dc_id is established, the client must generate an auth_key (after confirming that the public RSA MTProto key of the CDN DC matches one from the list returned in help.getCdnConfig). Then the client must perform an upload.getCdnFile for each offset. For files of an unknown size it is necessary to repeat the query until an empty reply is returned.
+upload.getCdnFile may return the upload.cdnFileReuploadNeeded constructor. In this case, the client needs to send an upload.reuploadCdnFile request to the DC that got the original upload.getFile request. Once upload.reuploadCdnFile is successfull, the app needs to request the file from the CDN DC again.
+The main DC for a file is the DC where its main copy is stored (not to be confused with the main DC of the user) – either userProfilePhoto.dc_id, chatPhoto.dc_id, photo.dc_id, or document.dc_id.
+In upload.fileCdnRedirect, the server sends a decryption key and IV for the file (the fields encryption_key:bytes
and encryption_iv:bytes
respectively).
Having received a portion of encrypted data from the CDN DC inside upload.cdnFile, the client must decrypt this data using AES-256-CTR
. For IV, it should use the value of encryption_iv
, modified in the following manner: for each offset
replace the last 4 bytes of the encryption_iv
with offset / 16
in big-endian. This allows to effectively decrypt a file and to use random access to a file's content (e.g., for streaming).
In order to confirm that the CDN DC passed an untampered file, clients must verify hashes for each downloaded part. upload.fileCdnRedirect, upload.reuploadCdnFile and upload.getCdnFileHashes contain FileHash constructors. Each of these constructors contains the SHA-256 hash of a part of the file that starts with offset
and takes limit
bytes.
Before saving each portion of the data received from the CDN DC into the file, the client must confirm that its hash matches the hash that was received from the master DC. If missing a hash for any file part, client developers must use the upload.getCdnFileHashes method to obtain the missing hash.
+fileHash#6242c773 offset:int limit:int hash:bytes = FileHash;
+
+upload.fileCdnRedirect#f18cda44 dc_id:int file_token:bytes encryption_key:bytes encryption_iv:bytes file_hashes:Vector<FileHash> = upload.File;
+
+upload.cdnFileReuploadNeeded#eea8e46e request_token:bytes = upload.CdnFile;
+upload.cdnFile#a99fca4f bytes:bytes = upload.CdnFile;
+
+
+dcOption#18b7a10d flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true id:int ip_address:string port:int secret:flags.10?bytes = DcOption;
+
+cdnPublicKey#c982eaba dc_id:int public_key:string = CdnPublicKey;
+
+
+cdnConfig#5725e40a public_keys:Vector<CdnPublicKey> = CdnConfig;
+
+---functions---
+
+// CDN DC
+upload.getCdnFile#2000bcc3 file_token:bytes offset:int limit:int = upload.CdnFile;
+
+// Master DC
+upload.reuploadCdnFile#9b2754a8 file_token:bytes request_token:bytes = Vector<FileHash>;
+upload.getCdnFileHashes#4da54231 file_token:bytes offset:int = Vector<FileHash>;
+
+help.getCdnConfig#52029342 = CdnConfig;
+offset
must be divisible by 4096 byteslimit
must be divisible by 4096 byteslimit
offset / (1024 * 1024) == (offset + limit - 1) / (1024 * 1024)
upload.getCdnFile | +FILE_TOKEN_INVALID | +The CDN DC did not accept the file_token (e.g., the token has expired). Continue downloading the file from the master DC using upload.getFile. |
+
upload.reuploadCdnFile | +FILE_TOKEN_INVALID | +The master DC did not accept the file_token (e.g., the token has expired). Continue downloading the file from the master DC using upload.getFile. |
+
upload.reuploadCdnFile | +REQUEST_TOKEN_INVALID | +The master DC did not accept the request_token from the CDN DC. Continue downloading the file from the master DC using upload.getFile. |
+
از نسخهٔ ۴.۲ تلگرام ما از CDN رمزنگاری شده برای ذخیره فایلهای کانال های عمومی با بیش از ۱۰۰٫۰۰۰ عضو پشتیبانی میکنیم. نقاط ذخیره سازی CDN در مناطقی در نظر گرفته شده اند که تلگرام ترافیک زیادی دارد ولی ما به دلایل مختلف تمایلی به قرارگیری سرورهای تلگرام در آن مناطق نداریم.
+++برای اطلاعات فنی در خصوص نحوه پیاده سازی، رمزنگاری و تایید اعتبار دادهها راهنمای CDN در این لینک را ببینید. همچنین برای توضیحات سیستم از طرف پاول دورُف به این پست مراجعه کنید.
+
ما از سرورهای توزیع شده خود برای افزایش سرعت دانلود در مناطق بدون سانسور که در آنها آزادی بیان تضمین شدهاست استفاده میکنیم. و حتی در آن مناطق نیز به آنها اعتماد نمیکنیم. اما وقتی تلگرام در مناطق دیگر به شدت محبوب میشود، فقط میتوانیم روی CDNها تکیه کنیم؛ که با آنها از نقطه نظر فنی مانند سرویسدهندههای اینترنت (ISP) رفتار میکنیم و آنها فقط دادههای رمزنگاری شدهای را که قادر به کشف رمز آن نیستند دریافت میکنند.
به لطف این فناوری، سرعت دانلود عکسها و فیلمهایی که به صورت عمومی منتشر میشوند در مناطقی نظیر ترکیه، اندونزی، عربستان سعودی، هند، ایران و عراق به مراتب افزایش مییابد، بدون اینکه کمترین خدشهای به امنیت وارد شود.
خیر. هر فایلی که به مراکز داده CDN ارسال میشود، با یک کلید منحصر به فرد با استفاده از تکنولوژی AES-256-CTR رمزگذاری میشود. CDN دسترسی به اطلاعاتی که ذخیره میکند ندارد، چرا که این کلیدها تنها در سرور MTProto (سرور رمزگذاری) اصلی و دستگاه کاربر قابل دسترسی هستند.
+خیر. اطلاعات و دادههای دانلود شده (دریافتی) از سمت نقاط ذخیره سازی CDN همیشه از طرف Telegram بوسیله رمزنگاری hash تایید و بررسی میشود: بدين ترتیب هکرها یا افراد متجاوز نمیتوانند هیچ فایلی را جایگزین کنند.
+خير. CDNها فقط كپى فايل هاى رمزنگارى شده را ذخيره مىكنند، فايلهاى اصلي در سرورهای تلگرام ذخيره مىشوند. كاربر تنها اعلان دريافت فايل را از طريق سرور تلگرام مىگيرد. اگر كه CDN فايل مورد نظر را به كاربر ارسال نكند، كاربر اين فايل را مستقيماً از سرور تلگرام دريافت خواهد كرد.
+خير. تمامى فايلهاى اصلي در سرورهای تلگرام ذخيره مىشوند. CDN ها فقط داده هاى رمزگذاري شده را دريافت مىكنند و قادر نيستند آن را رمزگشايى و يا هيچ گونه داده را جايگزين كنند. اگر مشكلى پيش آيد، فايل مورد نظر مستقيما از سرور هاى تلگرام به كاربران ارسال میشود. كاربران هميشه داده هایشان را دريافت مىكنند و هيچ كس نمىتواند مانع آنها بشود.
+بله. همه میتوانند روش ما برای پیادهسازی CDN را با بررسی کدهای Source Code برنامههای تلگرام و بررسی ترافیک داده بررسی کنند.
+خیر. نقاط ذخیره سازی CDN بخشی از سرور اَبری تلگرام نیستند. CDNها فقط برای ذخیرهسازی فایلهای پر طرفدار از کانالهای پر بازدید استفاده میشوند. اطلاعات خصوصی و شخصی هرگز به آنها راه پیدا نمیکنند.
+خیر. ما وارد هیچ توافق یا تفاهمی با دولتها در این مورد نشدهایم و CDNها بخشی از هیچ معاملهای نیستند. این به دلیل غیر منطقی بودن قوانین محلی نیست – ما هیچگاه سرورهایمان را به کشوری با سابقه سانسور اینترنتی انتقال نمیدهیم.
+تنها هدف استفاده از CDNها این است که کیفیت اتصال در مناطق با تقاضای بالا، که تلگرام نمیتواند سرورهای خود را به آنجا منتقل کند، با روشی ایمن، افزایش یابد.
+خیر. ما احتیاط ویژهای به خرج دادهایم تا مطمئن شویم هیچ کشوری از طریق نقاط ذخیره سازی CDN به قدرت نفوذ روی تلگرام دست نیابد:
- CDNها و نقاط آن به تلگرام تعلق نداشته و همه ریسک آن به گردن شرکت ثالثی که نقاط ذخیره سازی CDN در سراسر دنیا را برای ما تامین میکند خواهد بود.
- ما هیچ سرمایهگذاریای روی این CDNها انجام نمیدهیم و تنها بابت ترافیک مصرفی برای انتقال موارد ذخیره شده از خوشهٔ کامپیوترهای اصلی خودمان به کاربر نهایی، هزینه پرداخت میکنیم.
در نتیجه، اگر کشوری تصمیم به دستکاری CDN در منطقه خودش بگیرد، به چیزی جز پایین آوردن کیفیت اتصال شهروندان خودشان دست نمییابد – و تلگرام هیچ چیز با ارزشی را از دست نخواهد داد.
+++ترجمه فارسى اين پست از كانال رسمى پاول دورُف در تلگرام در زير آمده است:
+
همانطور که پیشتر قول داده بودم، جزئیات بیشتری درباره اینکه چطور نقاط ذخیره سازی CDN ثالث میتوانند سرعت دانلود محتوای عمومیِ پربازدید را در مناطقی که تلگرام تمایل به نصب سرورهای خود در آنجا ندارد، به روشی امن بالا ببرد، منتشر میکنم.
+همانطور که مشاهده میکنید، نقاط ذخیره سازی CDN هیچ ارتباطی با مساله انتقال سرورهای تلگرام یا رعایت قوانین غیر منطقی محلی ندارند. CDNها صرفا ابزاری جهت ارتقا کیفیت اتصال میلیونها کاربر، به روشی امن، هستند. ما با این نقاط CDN دقیقا همانطور رفتار میکنیم که با مراکز تامین کننده اینترنت (ISP) شما رفتار میکنیم. آنها صرفا مشتی دادهٔ رمزنگاری شده دریافت میکنند که قادر به کشف رمز آن نخواهند بود.
+خوبی تلگرام این است که وقتی بحث امنیت مطرح میشود، لازم نیست صرفا روی گفتههای من حساب کنید. هر کسی میتواند نحوه اجرای نقاط CDN در نسخه بروز شده تلگرام برای iOS و اندروید را بررسی کند. همچنین اگر تمایل داشتهباشید میتوانید نگاهی به اسناد ما در لینک فوق بیندازید، تا مطمئن شوید همه چیز امن است.
+ما با یک تامین کننده بینالمللی CDN همکاری میکنیم که با نقاط ذخیره سازی خود در سراسر دنیا به ما کمک میکند. اگر یک دولت محلی تصمیم به توقیف یک نقطه ذخیره سازی در مرزهای خود بگیرد، تلگرام چیزی از دست نخواهد داد؛ چرا که آن CDN جز اموال ما نیست و حتی در تئوری کوچکترین اطلاعات خصوصی تحت تاثیر این عمل قرار نخواهد گرفت. بنابراین به قوانین و دولتهای محلی وابستگی پیدا نمیکنیم و هیچ ریسک حقوقی یا مالی نخواهیم داشت، اما سرعت دانلود محتوای عمومی را بطور چشمگیری افزایش میدهیم.
+چنانچه، با وجود توضیحات فوق، باز هم برخی رسانهها از تیتر های جنجالی مانند «تلگرام سرورهایش را به کره شمالی انتقال داد» استفاده کردند، لطفا مارا با اطلاع رسانی در مورد حقیقت ماجرا که در لینکهای این پیام آمده است، یاری کنید. بابت مطالعه این مطلب از شما تشکر میکنم و مثل همیشه، در همین کانال، شما را از هر خبر بین المللی در مورد تلگرام مطلع خواهم کرد.
+Contains info on a confirmation code message sent via Telegram.
+You can force resending the message via SMS by invoking the method auth.sendSms.
+Constructor schema is available as of layer 50. Switch »
+Name | +Type | +Description | +
---|---|---|
phone_registered | +Bool | +The given number corresponds to a registered Telegram user | +
phone_code_hash | +string | +Message identifier | +
send_call_timeout | +int | +Delay in seconds before calling auth.sendCall | +
is_password | +Bool | +The sent code is a text password | +
Contents of an encrypted message.
+===8===
+decryptedMessage#1f814f1f random_id:long random_bytes:bytes message:string media:DecryptedMessageMedia = DecryptedMessage;
+
+===17===
+decryptedMessage#204d3878 random_id:long ttl:int message:string media:DecryptedMessageMedia = DecryptedMessage;
+
+===45===
+decryptedMessage#36b091de flags:# random_id:long ttl:int message:string media:flags.9?DecryptedMessageMedia entities:flags.7?Vector<MessageEntity> via_bot_name:flags.11?string reply_to_random_id:flags.3?long = DecryptedMessage;
+
+===73===
+decryptedMessage#91cc4674 flags:# no_webpage:flags.1?true silent:flags.5?true random_id:long ttl:int message:string media:flags.9?DecryptedMessageMedia entities:flags.7?Vector<MessageEntity> via_bot_name:flags.11?string reply_to_random_id:flags.3?long grouped_id:flags.17?long = DecryptedMessage;
+Name | +Type | +Description | +
---|---|---|
flags | +# | +Flags, see TL conditional fields (added in layer 45) | +
no_webpage | +flags.1?true | +Whether the webpage preview is disabled | +
silent | +flags.5?true | +Whether this is a silent message (no notification triggered) | +
random_id | +long | +Random message ID, assigned by the author of message. Must be equal to the ID passed to sending method. |
+
ttl | +int | +Message lifetime. Has higher priority than decryptedMessageActionSetMessageTTL. Parameter added in Layer 17. |
+
message | +string | +Message text | +
media | +flags.9?DecryptedMessageMedia | +Media content | +
entities | +flags.7?Vector<MessageEntity> | +Message entities for styled text (parameter added in layer 45) | +
via_bot_name | +flags.11?string | +Specifies the ID of the inline bot that generated the message (parameter added in layer 45) | +
reply_to_random_id | +flags.3?long | +Random message ID of the message this message replies to (parameter added in layer 45) | +
grouped_id | +flags.17?long | +Random group ID, assigned by the author of message. Multiple encrypted messages with a photo attached and with the same group ID indicate an album or grouped media (parameter added in layer 45) |
+
Setting of a message lifetime after reading.
+Upon receiving such message the client shall start deleting of all messages of an encrypted chat ttl_seconds seconds after the messages were read by user.
+Below you will find information on scheme changes. For more details on the use of layers, see Invoking API methods.
+How to create styled text with message entities
+How to transfer large data batches correctly.
Abort rekeying
+===20===
+decryptedMessageActionAbortKey#dd05ec6b exchange_id:long = DecryptedMessageAction;
+Name | +Type | +Description | +
---|---|---|
exchange_id | +long | +Exchange ID | +
Accept new key
+===20===
+decryptedMessageActionAcceptKey#6fe1735b exchange_id:long g_b:bytes key_fingerprint:long = DecryptedMessageAction;
+Name | +Type | +Description | +
---|---|---|
exchange_id | +long | +Exchange ID | +
g_b | +bytes | +B parameter, see rekeying process | +
key_fingerprint | +long | +Key fingerprint, see rekeying process | +
Commit new key, see rekeying process
+===20===
+decryptedMessageActionCommitKey#ec2e0b9b exchange_id:long key_fingerprint:long = DecryptedMessageAction;
+Name | +Type | +Description | +
---|---|---|
exchange_id | +long | +Exchange ID, see rekeying process | +
key_fingerprint | +long | +Key fingerprint, see rekeying process | +
Deleted messages.
+===8===
+decryptedMessageActionDeleteMessages#65614304 random_ids:Vector<long> = DecryptedMessageAction;
+Name | +Type | +Description | +
---|---|---|
random_ids | +Vector<long> | +List of deleted message IDs | +
The entire message history has been deleted.
+===8===
+decryptedMessageActionFlushHistory#6719e45c = DecryptedMessageAction;
+This constructor does not require any parameters.
+NOOP action
+===20===
+decryptedMessageActionNoop#a82fdd63 = DecryptedMessageAction;
+This constructor does not require any parameters.
+A notification stating the API layer that is used by the client. You should use your current layer and take notice of the layer used on the other side of a conversation when sending messages.
+===17===
+decryptedMessageActionNotifyLayer#f3048883 layer:int = DecryptedMessageAction;
+Name | +Type | +Description | +
---|---|---|
layer | +int | +Layer number, must be 17 or higher (this contructor was introduced in Layer 17). | +
Below you will find information on scheme changes. For more details on the use of layers, see Invoking API methods.
Messages marked as read.
+===8===
+decryptedMessageActionReadMessages#c4f40be random_ids:Vector<long> = DecryptedMessageAction;
+Name | +Type | +Description | +
---|---|---|
random_ids | +Vector<long> | +List of message IDs | +
Request rekeying, see rekeying process
+===20===
+decryptedMessageActionRequestKey#f3c9611b exchange_id:long g_a:bytes = DecryptedMessageAction;
+Name | +Type | +Description | +
---|---|---|
exchange_id | +long | +Exchange ID | +
g_a | +bytes | +g_a, see rekeying process | +
Request for the other party in a Secret Chat to automatically resend a contiguous range of previously sent messages, as explained in Sequence number is Secret Chats.
+===17===
+decryptedMessageActionResend#511110b0 start_seq_no:int end_seq_no:int = DecryptedMessageAction;
+Name | +Type | +Description | +
---|---|---|
start_seq_no | +int | +out_seq_no of the first message to be resent, with correct parity |
+
end_seq_no | +int | +out_seq_no of the last message to be resent, with same parity. |
+
A screenshot was taken.
+===8===
+decryptedMessageActionScreenshotMessages#8ac1f475 random_ids:Vector<long> = DecryptedMessageAction;
+Name | +Type | +Description | +
---|---|---|
random_ids | +Vector<long> | +List of affected message ids (that appeared on the screenshot) | +
Setting of a message lifetime after reading.
+Upon receiving such message the client shall start deleting of all messages of an encrypted chat ttl_seconds seconds after the messages were read by user.
+===8===
+decryptedMessageActionSetMessageTTL#a1733aec ttl_seconds:int = DecryptedMessageAction;
+Name | +Type | +Description | +
---|---|---|
ttl_seconds | +int | +Lifetime in seconds | +
User is preparing a message: typing, recording, uploading, etc.
+===17===
+decryptedMessageActionTyping#ccb27641 action:SendMessageAction = DecryptedMessageAction;
+Name | +Type | +Description | +
---|---|---|
action | +SendMessageAction | +Type of action | +
Sets the layer number for the contents of an encrypted message.
+===17===
+decryptedMessageLayer#1be31789 random_bytes:bytes layer:int in_seq_no:int out_seq_no:int message:DecryptedMessage = DecryptedMessageLayer;
+Name | +Type | +Description | +
---|---|---|
random_bytes | +bytes | +Set of random bytes to prevent content recognition in short encrypted messages. Clients are required to check that there are at least 15 random bytes included in each message. Messages with less than 15 random bytes must be ignored. Parameter moved here from decryptedMessage in Layer 17. |
+
layer | +int | +Layer number. Mimimal value - 17 (the layer in which the constructor was added). | +
in_seq_no | +int | +2x the number of messages in the sender's inbox (including deleted and service messages), incremented by 1 if current user was not the chat creator Parameter added in Layer 17. |
+
out_seq_no | +int | +2x the number of messages in the recipient's inbox (including deleted and service messages), incremented by 1 if current user was the chat creator Parameter added in Layer 17. |
+
message | +DecryptedMessage | +The content of message itself | +
Contents of an encrypted message.
+Below you will find information on scheme changes. For more details on the use of layers, see Invoking API methods.
Audio file attached to a secret chat message.
+===8===
+decryptedMessageMediaAudio#6080758f duration:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
+
+===17===
+decryptedMessageMediaAudio#57e0a9cb duration:int mime_type:string size:int key:bytes iv:bytes = DecryptedMessageMedia;
+Name | +Type | +Description | +
---|---|---|
duration | +int | +Audio duration in seconds | +
mime_type | +string | +MIME-type of the audio file Parameter added in Layer 13. |
+
size | +int | +File size | +
key | +bytes | +Key to decrypt the attached media file | +
iv | +bytes | +Initialization vector | +
Below you will find information on scheme changes. For more details on the use of layers, see Invoking API methods.
Contact attached to an encrypted message.
+===8===
+decryptedMessageMediaContact#588a0a97 phone_number:string first_name:string last_name:string user_id:int = DecryptedMessageMedia;
+Name | +Type | +Description | +
---|---|---|
phone_number | +string | +Phone number | +
first_name | +string | +Contact's first name | +
last_name | +string | +Contact's last name | +
user_id | +int | +Telegram User ID of signed-up contact | +
Document attached to a message in a secret chat.
+===8===
+decryptedMessageMediaDocument#b095434b thumb:bytes thumb_w:int thumb_h:int file_name:string mime_type:string size:int key:bytes iv:bytes = DecryptedMessageMedia;
+
+===45===
+decryptedMessageMediaDocument#7afe8ae2 thumb:bytes thumb_w:int thumb_h:int mime_type:string size:int key:bytes iv:bytes attributes:Vector<DocumentAttribute> caption:string = DecryptedMessageMedia;
+Name | +Type | +Description | +
---|---|---|
thumb | +bytes | +Thumbnail-file contents (JPEG-file, quality 55, set in a 90x90 square) | +
thumb_w | +int | +Thumbnail width | +
thumb_h | +int | +Thumbnail height | +
mime_type | +string | +File MIME-type | +
size | +int | +Document size | +
key | +bytes | +Key to decrypt the attached document file | +
iv | +bytes | +Initialization | +
attributes | +Vector<DocumentAttribute> | +Document attributes for media types | +
caption | +string | +Caption | +
Empty constructor, no media content.
+===8===
+decryptedMessageMediaEmpty#89f5c4a = DecryptedMessageMedia;
+This constructor does not require any parameters.
+Non-e2e documented forwarded from non-secret chat
+===23===
+decryptedMessageMediaExternalDocument#fa95b0dd id:long access_hash:long date:int mime_type:string size:int thumb:PhotoSize dc_id:int attributes:Vector<DocumentAttribute> = DecryptedMessageMedia;
+Name | +Type | +Description | +
---|---|---|
id | +long | +Document ID | +
access_hash | +long | +access hash | +
date | +int | +Date | +
mime_type | +string | +Mime type | +
size | +int | +Size | +
thumb | +PhotoSize | +Thumbnail | +
dc_id | +int | +DC ID | +
attributes | +Vector<DocumentAttribute> | +Attributes for media types | +
GeoPont attached to an encrypted message.
+===8===
+decryptedMessageMediaGeoPoint#35480a59 lat:double long:double = DecryptedMessageMedia;
+Name | +Type | +Description | +
---|---|---|
lat | +double | +Latitude of point | +
long | +double | +Longtitude of point | +
Photo attached to an encrypted message.
+===8===
+decryptedMessageMediaPhoto#32798a8c thumb:bytes thumb_w:int thumb_h:int w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
+
+===45===
+decryptedMessageMediaPhoto#f1fa8d78 thumb:bytes thumb_w:int thumb_h:int w:int h:int size:int key:bytes iv:bytes caption:string = DecryptedMessageMedia;
+Name | +Type | +Description | +
---|---|---|
thumb | +bytes | +Content of thumbnail file (JPEGfile, quality 55, set in a square 90x90) | +
thumb_w | +int | +Thumbnail width | +
thumb_h | +int | +Thumbnail height | +
w | +int | +Photo width | +
h | +int | +Photo height | +
size | +int | +Size of the photo in bytes | +
key | +bytes | +Key to decrypt an attached file with a full version | +
iv | +bytes | +Initialization vector | +
caption | +string | +Caption | +
Venue
+===45===
+decryptedMessageMediaVenue#8a0df56f lat:double long:double title:string address:string provider:string venue_id:string = DecryptedMessageMedia;
+Name | +Type | +Description | +
---|---|---|
lat | +double | +Latitude of venue | +
long | +double | +Longitude of venue | +
title | +string | +Venue name | +
address | +string | +Address | +
provider | +string | +Venue provider: currently only "foursquare" needs to be supported | +
venue_id | +string | +Venue ID in the provider's database | +
Video attached to an encrypted message.
+===8===
+decryptedMessageMediaVideo#4cee6ef3 thumb:bytes thumb_w:int thumb_h:int duration:int w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
+
+===17===
+decryptedMessageMediaVideo#524a415d thumb:bytes thumb_w:int thumb_h:int duration:int mime_type:string w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
+
+===45===
+decryptedMessageMediaVideo#970c8c0e thumb:bytes thumb_w:int thumb_h:int duration:int mime_type:string w:int h:int size:int key:bytes iv:bytes caption:string = DecryptedMessageMedia;
+Name | +Type | +Description | +
---|---|---|
thumb | +bytes | +Content of thumbnail file (JPEG file, quality 55, set in a square 90x90) | +
thumb_w | +int | +Thumbnail width | +
thumb_h | +int | +Thumbnail height | +
duration | +int | +Duration of video in seconds | +
mime_type | +string | +MIME-type of the video file Parameter added in Layer 17. |
+
w | +int | +Image width | +
h | +int | +Image height | +
size | +int | +File size | +
key | +bytes | +Key to decrypt the attached video file | +
iv | +bytes | +Initialization vector | +
caption | +string | +Caption | +
Below you will find information on scheme changes. For more details on the use of layers, see Invoking API methods.
Webpage preview
+===45===
+decryptedMessageMediaWebPage#e50511d8 url:string = DecryptedMessageMedia;
+Name | +Type | +Description | +
---|---|---|
url | +string | +URL of webpage | +
Contents of an encrypted service message.
+===8===
+decryptedMessageService#aa48327d random_id:long random_bytes:bytes action:DecryptedMessageAction = DecryptedMessage;
+
+===17===
+decryptedMessageService#73164160 random_id:long action:DecryptedMessageAction = DecryptedMessage;
+Name | +Type | +Description | +
---|---|---|
random_id | +long | +Random message ID, assigned by the message author. Must be equal to the ID passed to the sending method. |
+
action | +DecryptedMessageAction | +Action relevant to the service message | +
File location.
+Constructor schema is available as of layer 86. Switch »
+Name | +Type | +Description | +
---|---|---|
dc_id | +int | +Number of the data center holding the file | +
volume_id | +long | +Server volume | +
local_id | +int | +File ID | +
secret | +long | +Checksum to access the file | +
New document with a thumbnail.
+Constructor schema is available as of layer 22. Switch »
+Name | +Type | +Description | +
---|---|---|
file | +InputFile | +Document, saved in parts by using the methods upload.saveFilePart or upload.saveBigFilePart | +
thumb | +InputFile | +Thumbnail file, saved in parts by using upload.saveFilePart | +
file_name | +string | +File name with extension | +
mime_type | +string | +File MIME-type | +
Info on successfully sent message and on changes links.
+Constructor schema is available as of layer 24. Switch »
+Name | +Type | +Description | +
---|---|---|
id | +int | +Message ID | +
date | +int | +Date of sending | +
pts | +int | +New value of pts parameter of a current state | +
seq | +int | +New value of seq parameter of a current state | +
links | +Vector<contacts.Link> | +List of changes links | +
Message with auxiliary data, state data and the list of changed links.
+Constructor schema is available as of layer 24. Switch »
+Name | +Type | +Description | +
---|---|---|
message | +Message | +Message | +
chats | +Vector<Chat> | +List of chats mentioned in message | +
users | +Vector<User> | +List of users mentioned in message and chats | +
links | +Vector<contacts.Link> | +List of changed links | +
pts | +int | +Number of events occurred in text box | +
seq | +int | +Number of sent updates | +
Message with auxiliary data, state data and the list of changed links.
+Constructor schema is available as of layer 24. Switch »
+Name | +Type | +Description | +
---|---|---|
messages | +Vector<Message> | +List of messages | +
chats | +Vector<Chat> | +List of cats mentioned in messages | +
users | +Vector<User> | +List of users mentioned in messages and cahts | +
links | +Vector<contacts.Link> | +List of changed links | +
pts | +int | +Number of event occurred in a text box | +
seq | +int | +Number of sent updates | +
Validated user-provided info
+{scheme}
+Name | +Type | +Description | +
---|---|---|
flags | +# | +Flags, see TL conditional fields | +
id | +flags.0?string | +ID | +
shipping_options | +flags.1?Vector<ShippingOption> | +Shipping options | +
A participant has left, joined, was banned or admined in a channel or supergroup.
+Constructor schema is available as of layer 124. Switch »
+Name | +Type | +Description | +
---|---|---|
flags | +# | +Flags, see TL conditional fields | +
channel_id | +int | +Channel ID | +
date | +int | +Date of the event | +
user_id | +int | +User in question | +
prev_participant | +flags.0?ChannelParticipant | +Previous participant status | +
new_participant | +flags.1?ChannelParticipant | +New participant status | +
qts | +int | +PTS | +
How to subscribe to updates and handle them properly.
+How to handle channels, supergroups, groups, and what's the difference between them.
User was added to the blacklist (method contacts.block) or removed from the blacklist (method contacts.unblock).
+Constructor schema is available as of layer 119. Switch »
+Name | +Type | +Description | +
---|---|---|
user_id | +int | +User id | +
blocked | +Bool | +(boolTrue) if the the user is blocked | +
The constructor can be interpreted as a booleantrue
value.
Adds the user to the blacklist.
+Deletes the user from the blacklist.
A user that is not a contact of the current user.
+Constructor schema is available as of layer 18. Switch »
+Name | +Type | +Description | +
---|---|---|
id | +int | +User identifier | +
first_name | +string | +First name as supplied by the user | +
last_name | +string | +Last name as supplied by the user | +
access_hash | +long | +Checksum dependent on the user identifier | +
photo | +UserProfilePhoto | +Profile photo | +
status | +UserStatus | +Current status | +
username | +string | +Username Parameter added in Layer 18. |
+
A user that is not a contact of the current user, but whose phone number is available.
+Constructor schema is available as of layer 18. Switch »
+Name | +Type | +Description | +
---|---|---|
id | +int | +User identifier | +
first_name | +string | +First name the user supplied | +
last_name | +string | +Last name the user supplied | +
access_hash | +long | +Checksum dependent on the user identifier | +
phone | +string | +Phone number | +
photo | +UserProfilePhoto | +Profile photo | +
status | +UserStatus | +Current status | +
username | +string | +Username Parameter added in Layer 18. |
+
One-color background wallpaper (no image).
+Constructor schema is available as of layer 93. Switch »
+Name | +Type | +Description | +
---|---|---|
id | +int | +Identifier | +
title | +string | +Name | +
bg_color | +int | +Background color (RGB) | +
color | +int | +Basic background color (RGB) | +
« Back to Contest Announcement
+++The current round of the contest is over. Go to results »
+
In this contest you assume the role of a malicious entity in control of Telegram's servers. Your goal is to extract sensitive data (a secret email and password) from a conversation between two peers — Paul and Nick. They are represented by two virtual users that communicate via Secret Chats in Telegram.
+Paul and Nick are both using clients that perform all the checks from Telegram Security Guidelines and compare their key visualizations over an independent channel as soon as a new Secret Chat is established. If any of these checks fails, they stop accepting messages in that Secret Chat. You control the entire process by sending commands to the Telegram user @CryptoContest
, used as an interface for this contest. This enables contestants to try CPA, KPA, MITM and other kinds of active attacks and data tampering.
The protocol used by Paul and Nick to establish Secret Сhats and exchange messages is identical to the one used for Secret Chats in Telegram. Since we assume that the attacker is already in full control of the Telegram servers, basic MTProto encryption is bypassed altogether. In order to further simplify the task for contestants, we have removed irrelevant parameters, such as user_id and random_id.
+The following TL scheme is used to establish Secret Chats in this contest:
+contest.dhConfig#01e00a51 g:int p:64*[int] random:64*[int] = contest.DhConfig;
+contest.requestEncryption#3a73a74c g_a:64*[int] = contest.Message;
+contest.acceptEncryption#068e4342 g_b:64*[int] fingerprint:int = contest.Message;
+contest.encryptedMessage#11a6d4b1 id:long message:string = contest.Message;
+---functions---
+contest.getDhConfig#369ee1a6 = contest.DhConfig;
+For exchange of encrypted messages (see documentation), the up-to-date layer 17 scheme with sequence numbers is used, but with plain text message support only.
+Each plaintext message is first created as a layer 17 decryptedMessage, then embedded in a decryptedMessageLayer and encrypted as explained in the Secret Chat documentation. For the purpose of this contest, it is the result of this encryption (ciphertext) that is exchanged between the parties.
+Notice that sending messages in an actual Telegram Secret Chat involves further embedding of that ciphertext into an API call and an additional layer of MTProto encryption for client-server interaction. This step is omitted here, since we assume the attacker to be in control of the Telegram servers, not just of the communication lines between the clients and Telegram servers.
+To access the interface, find the Telegram user @CryptoContest
using the Global Search by username in any of the Telegram apps. This is a special bot we created for this contest. You can control communication between Paul and Nick by sending particularly formed text messages to this bot and processing automatically generated answers to these messages (you may find the unofficial Linux CLI convenient for mass automated queries).
You can create as many parallel Secret Chats between Paul and Nick as you like using the bot — each of them will have a separate session_id. All data is represented in hexadecimal format, with the exception of the session_id.
+Below, A stands for the creator of the Secret Chat, B stands for the second party, S — the Telegram Server.
+Each Secret Chat session in this contest is divided in two phases:
+ +In order to create a new Secret Chat, six messages need to be exchanged:
+Source | +Destination | +Message | +
---|---|---|
A | +S | +contest.getDhConfig | +
S | +A | +contest.DhConfig | +
A | +B | +contest.requestEncryption | +
B | +S | +contest.getDhConfig | +
S | +B | +contest.DhConfig | +
B | +A | +contest.acceptEncryption | +
To create a Secret Chat in this contest:
+@CryptoContest
in Telegram. You'll get the getDhConfig query, sent by A to the Server, and the answer that the server would normally send to A. You shall also receive the new session_id as the first 32-bit integer. All further messages related to this particular session (Secret Chat instance) must be prefixed with this session_id in decimal form.You will receive an error text as the result after any of these steps in case the participating clients perceive that something went wrong. This can happen if a security check is failed, or in the case that the first 128 bits of the SHA-1 of the newly created encryption key don‘t match on both parties’ clients when this stage is completed (this corresponds to Paul and Nick comparing the key visualizations for the Secret Chat in their Telegram apps).
+If you obtain such an error, the session is failed and can no longer be used. You'll have to start new session. Note that the time to complete this phase is limited. Each step should not take longer than one hour, otherwise the Secret Chat will get cancelled.
+Example:
+START
+15 a6e19e36 510ae00103000000c71caeb9c6b1c9048e6c522f70f13f73980d40238e3e21c14934d037563d930f48198a0aa7c14058229493d22530f4dbfa336f6e0ac925139543aed44cce7c3720fd51f69458705ac68cd4fe6b6b13abdc9746512969328454f18faf8c595f642477fe96bb2a941d5bcd1d4ac8cc49880708fa9b378e3c4f3a9060bee67cf9a4a4a695811051907e162753b56b0f6b410dba74d8a84b2a14b3144e0ef1284754fd17ed950d5965b4b9dd46582db1178d169c6bc465b0d6ff9ca3928fef5b9ae4e418fc15e83ebea0f87fa9ff5eed70050ded2849f47bf959d956850ce929851f0d8115f635b105ee2e4e15d04b2454bf6f4fadf034b10403119cd8e3b92fcc5b202d33053c2340fd84dd024e8012277e9c6442ad7cd09fe85955c13196e2d32861ad0d8f8139ce5870f1c3563fbff77428632897352abd91cd0a6497a0f64a33d87cd8b53470cf1bc6a052bba7d0121623e9611c0de83ffeb63b7d15831a70187093373cb20df5613bdfab12a54bbc6fff94598d95a8dcdb1374631b021e77c350261bca9ffc16c59b19d3041bee011a20b06fc9806d633b6b9cdd79cbb8b02fe8ef9dde29b6d31d80b030c69d67d6fc4a7edb33ffab532d085796cf3e7635fd42ee72ea24840082186fd40c3c45cf0acef886533d4de7468f88942a662d302928470aa8704529180a6aec2f877398efb91893cc9b549e5123d7269adfe7b6ee
+15 PASS
+15 4ca7733a1a7823b420111d8e86e3fe9a7cff9fc611ce339d6999fc3053973ef6c8276af841b53547fdebdcb057cbad16aff6178be3fb8747889937dec082c984227c974a19232b85ad85ef457521fcf17d5f697a17b7e62952306f0ed086deb1ebcff0c8a32787789fe7afaa4035c2d0e07c10db46c0df6930a1729d3607fb035154e90c02036318862c5a9537e87a55bc656e3fc53db08f41a07f834e4917ebaaace1214409ffb44c5a806a9cb4def209bfb8ab2e59f1cb6257e422f37dfab288170bdc5666e6a63d1b0447a7b935ad3bdac8d53f64278d433b45925c84dc60214473363d57a30e31324d9b3cc42fb56d375aac2d9d1af16331ad3a92b43a9d64e47813
+15 PASS
+15 a6e19e36 510ae00103000000c71caeb9c6b1c9048e6c522f70f13f73980d40238e3e21c14934d037563d930f48198a0aa7c14058229493d22530f4dbfa336f6e0ac925139543aed44cce7c3720fd51f69458705ac68cd4fe6b6b13abdc9746512969328454f18faf8c595f642477fe96bb2a941d5bcd1d4ac8cc49880708fa9b378e3c4f3a9060bee67cf9a4a4a695811051907e162753b56b0f6b410dba74d8a84b2a14b3144e0ef1284754fd17ed950d5965b4b9dd46582db1178d169c6bc465b0d6ff9ca3928fef5b9ae4e418fc15e83ebea0f87fa9ff5eed70050ded2849f47bf959d956850ce929851f0d8115f635b105ee2e4e15d04b2454bf6f4fadf034b10403119cd8e3b92fcc5b1ccd9c752428f0bca9ac9060bb85b8f90acb9374cd8d5a03110635f591a18f131cb7cc204407efec0687a8b77ba6c4e6732c35174e79e36aaa7fa6ab685257710e074065961ce1b16d21fed8a83cd95efcc4be7111cd33b5704fe759dfab21fc3e8aaa86d44609dc0b073354f8160c653f4fbde3ae7c28c87c3667e0797fac24b32e5c1a870cd898b2a9c517709bb0b8e4ee875ff857868eb56548e6dc993f198fd78c8a77cf997ed42a15f99a9b6265c7cf9bedc7580a11514047b881f717b233f3570ec21856bd2b9791e4c43b125e9260ac3fd48b9a10de5f9d5080e53d92d194adb796766684d905cca35e691fab0c76d6b5f49242f81eb92fcc8adc5a64
+15 ANSWER 510ae00103000000c71caeb9c6b1c9048e6c522f70f13f73980d40238e3e21c14934d037563d930f48198a0aa7c14058229493d22530f4dbfa336f6e0ac925139543aed44cce7c3720fd51f69458705ac68cd4fe6b6b13abdc9746512969328454f18faf8c595f642477fe96bb2a941d5bcd1d4ac8cc49880708fa9b378e3c4f3a9060bee67cf9a4a4a695811051907e162753b56b0f6b410dba74d8a84b2a14b3144e0ef1284754fd17ed950d5965b4b9dd46582db1178d169c6bc465b0d6ff9ca3928fef5b9ae4e418fc15e83ebea0f87fa9ff5eed70050ded2849f47bf959d956850ce929851f0d8115f635b105ee2e4e15d04b2454bf6f4fadf034b10403119cd8e3b92fcc5b1ccd9c752428f0bca9ac9060bb85b8f90acb9374cd8d5a03110635f591a18f131cb7cc204407efec0687a8b77ba6c4e6732c35174e79e36aaa7fa6ab685257710e074065961ce1b16d21fed8a83cd95efcc4be7111cd33b5704fe759dfab21fc3e8aaa86d44609dc0b073354f8160c653f4fbde3ae7c28c87c3667e0797fac24b32e5c1a870cd898b2a9c517709bb0b8e4ee875ff857868eb56548e6dc993f198fd78c8a77cf997ed42a15f99a9b6265c7cf9bedc7580a11514047b881f717b233f3570ec21856bd2b9791e4c43b125e9260ac3fd48b9a10de5f9d5080e53d92d194adb796766684d905cca35e691fab0c76d6b5f49242f81eb92fcc8ad00000
+15 42438e06bbb424bba5fd95122ec2f206c9b502f1f6d4e4fdbf74ed2c946ad60abaefd6fbd6a08e3ef418709d15bc557ef5e486a51d1e304f6c1e943faad948fde4e6273c0cad0df07068ad028fb01dc0fd7221aeed6ed5dc510dbe4824939036b0f3a45e740b40cef86a32f0b73b20234efc41d573f3e14efc08b3f65e9f7be52d5b930de52d41c7aadc4e0e85dfcf3bb1dd2e9cdf94fc236082879aea27207415cb846a5d5969e619040416a7f0f708f56a5b340a8fd0be1a26bfdc3de365a950532d363b427d6d905af7534af574ae8afd3f47658de5da3fa02dd818a31523122ff53dd31ffd7aa22e53cbf2da7772a1589e9a242f28f9cb1130f54553fcb355b3398fc877b80b3ef2cc3d
+15 PASS
+15 Ok
+Once the Secret Chat has been established, you can use the following queries to make Paul and Nick exchange text messages inside the Secret Chat:
+Example:
+15 ASK A
+15 b1d4a6119278722b0309a8c1fee80000c877b80b3ef2cc3dc92104de4322d8ae374fbf38758091fe4c86bafffa792f7eb37d8431cf8f868319c3af005791b7c55f788e260b8fa6a96b6808d0d448abfdb49913160c5355ef2d4e439a676055e42de6b26dd7d0e06e3fb48981208449658aff63fd8262ef0669f8bb242ade401e1190d2f54f3896ac17c1b796cbe185d5b0166649d5bac25e4626c08c78527458fc7877ee2add14a8e7b1f9b56651b8264284aa2fd28de55f96bcec8075dd43bbc69f6c05c2428795e51a081e3995e4ede72d190d55d0b30d8215bf4ed13fde7c8f578993050280ec4a940e910eb182bd335e52e2a699d9b5
+15 MSG B b1d4a6119278722b0309a8c1fee80000c877b80b3ef2cc3dc92104de4322d8ae374fbf38758091fe4c86bafffa792f7eb37d8431cf8f868319c3af005791b7c55f788e260b8fa6a96b6808d0d448abfdb49913160c5355ef2d4e439a676055e42de6b26dd7d0e06e3fb48981208449658aff63fd8262ef0669f8bb242ade401e1190d2f54f3896ac17c1b796cbe185d5b0166649d5bac25e4626c08c78527458fc7877ee2add14a8e7b1f9b56651b8264284aa2fd28de55f96bcec8075dd43bbc69f6c05c2428795e51a081e3995e4ede72d190d55d0b30d8215bf4ed13fde7c8f578993050280ec4a940e910eb182bd335e52e2a699d9b0
+15 Fail
+15 MSG B b1d4a6119278722b0309a8c1fee80000c877b80b3ef2cc3dc92104de4322d8ae374fbf38758091fe4c86bafffa792f7eb37d8431cf8f868319c3af005791b7c55f788e260b8fa6a96b6808d0d448abfdb49913160c5355ef2d4e439a676055e42de6b26dd7d0e06e3fb48981208449658aff63fd8262ef0669f8bb242ade401e1190d2f54f3896ac17c1b796cbe185d5b0166649d5bac25e4626c08c78527458fc7877ee2add14a8e7b1f9b56651b8264284aa2fd28de55f96bcec8075dd43bbc69f6c05c2428795e51a081e3995e4ede72d190d55d0b30d8215bf4ed13fde7c8f578993050280ec4a940e910eb182bd335e52e2a699d9b5
+15 Ok
+15 TXT B abac
+15 b1d4a61101771d42f62323e6fe680000c877b80b3ef2cc3df751e68b935b083a6f5c15ba8d95b94388fc34453a1e7b9b20222402b7698be5dd8a6ff69a5141b01ca2488b0dada8f2b0e47980218f48912168ddd2cebd3b61b1edf2f557c7ec44768595ce1cb42a01f7c14dd4e6e6e7601cb17ab0b6d5a274
+We are offering a $300,000 reward to the first person to break Telegram's encryption protocol in this contest.
+Your goal is to extract a secret email address from one of the random messages that are exchanged between Nick and Paul when you use the ASK command. It is guaranteed that at least one of the first ten generated messages within a session will contain the secret address. It is also guaranteed that apart from that, all messages will contain only dictionary English words, spaces, line breaks and punctuation marks.
+Once you have the address, you will need to send an email to it. That email must contain:
- The entire text of the message that contained the secret email.
- Session logs for the successful attempt with your user_id.
- A detailed explanation of the attack on the protocol.
- Your bank account details to receive the $300,000 prize.
To prove that the competition was fair, we will add a command that returns the keys used for a particular session by its session_id at the end of the contest. This will be done as soon as a winner is announced, or on February 4, 2015 in case no winner is announced to that date.
+We are also offering an independent $100,000 reward to the first person to make the bot accept a ciphertext message (i.e. the first person to send a message using MSG [A|B] bytes and receive the result ‘OK’), provided that that ciphertext deciphers to a plaintext that was never encrypted by the bot itself within this session.
+Should you succeed at this, kindly send an email to security@telegram.org and include the following:
- Session logs for the successful attempt with your user_id.
- A detailed explanation of the attack on the protocol.
- Your bank account details to receive the $100,000 prize.
++This contest has ended. Check out our new contest »
+
The goal of this contest is to solve a real-life problem. The question at hand is whether your internet-provider or another entity that intercepts your traffic would be able to decrypt your conversations over Telegram. We are inviting hackers and security experts to find ways of decrypting Telegram traffic. As a result we will either find a vulnerability in our encryption algorithm and fix it, or or get indirect evidence that decrypting our traffic is no easy task.
+If we have a winner in the current competition, we will start a new competition with an even larger prize. Of course, before launching a new competition we will have to fix the vulnerability that allowed the winner to decipher the traffic of Telegram.
+You have access to a detailed description of the encryption system we use, app source code, as well as complete traffic logs for the target ‘Paul’ (+79112317383) from the day he signed up for Telegram, updated in real time.
+You need to decipher the secret email address, that Paul sends daily in one of the messages to ‘Nick’ (+79218944725), and describe the successful attack in an email to that address.
+The structure of the traffic log is as follows:
+ Unixtime Length-in-bytes Direction (in/out) ServerIP:Port Hexdump.
+For your convenience, only high-level TCP stream bytes are shown, ignoring IP packet boundaries and omitting TCP/IP headers.
+No, just as in real life, Paul‘s messages to Nick can be different each time. The only thing that doesn’t change is the secret email address in his daily messages.
+Sure. The message may look like “Hey Nick, so here is the secret email address for the bounty hunters – {here goes the email}”.
+At this stage, it is possible to analyze the traffic and send modified packets to the server, therefore perfoming length extension attacks, replay attacks etc. In case nobody achieves the goal of the current contest (deciphering intercepted Telegram traffic) by March 2014, we are willing to facilitate the task and provide the contestants with tools for performing more complicated active attacks.
+If the winner prefers conventional money over bitcoin, we will be happy to transfer them 200,000 regular USD instead of BTC.
+The contests to crack Telegram's encrypted protocol are a permanent feature of our project. We will always be launching a new contest after the end of the previous one, and the amount of the prize money is likely to increase. So whenever you are the first person to find vulnerabilities in our encryption system, you will be able to claim a prize — even after the current competition is over.
+")[0],f=t.each;p.style.cssText="background-color:rgba(1,1,1,.5)",d.rgba=p.style.backgroundColor.indexOf("rgba")>-1,f(c,function(t,e){e.cache="_"+t,e.props.alpha={idx:3,type:"percent",def:1}}),h.fn=t.extend(h.prototype,{parse:function(n,a,r,l){if(n===e)return this._rgba=[null,null,null,null],this;(n.jquery||n.nodeType)&&(n=t(n).css(a),a=e);var u=this,d=t.type(n),p=this._rgba=[];return a!==e&&(n=[n,a,r,l],d="array"),"string"===d?this.parse(s(n)||o._default):"array"===d?(f(c.rgba.props,function(t,e){p[e.idx]=i(n[e.idx],e)}),this):"object"===d?(n instanceof h?f(c,function(t,e){n[e.cache]&&(u[e.cache]=n[e.cache].slice())}):f(c,function(e,s){var o=s.cache;f(s.props,function(t,e){if(!u[o]&&s.to){if("alpha"===t||null==n[t])return;u[o]=s.to(u._rgba)}u[o][e.idx]=i(n[t],e,!0)}),u[o]&&0>t.inArray(null,u[o].slice(0,3))&&(u[o][3]=1,s.from&&(u._rgba=s.from(u[o])))}),this):e},is:function(t){var i=h(t),s=!0,n=this;return f(c,function(t,o){var a,r=i[o.cache];return r&&(a=n[o.cache]||o.to&&o.to(n._rgba)||[],f(o.props,function(t,i){return null!=r[i.idx]?s=r[i.idx]===a[i.idx]:e})),s}),s},_space:function(){var t=[],e=this;return f(c,function(i,s){e[s.cache]&&t.push(i)}),t.pop()},transition:function(t,e){var s=h(t),n=s._space(),o=c[n],a=0===this.alpha()?h("transparent"):this,r=a[o.cache]||o.to(a._rgba),l=r.slice();return s=s[o.cache],f(o.props,function(t,n){var o=n.idx,a=r[o],h=s[o],c=u[n.type]||{};null!==h&&(null===a?l[o]=h:(c.mod&&(h-a>c.mod/2?a+=c.mod:a-h>c.mod/2&&(a-=c.mod)),l[o]=i((h-a)*e+a,n)))}),this[n](l)},blend:function(e){if(1===this._rgba[3])return this;var i=this._rgba.slice(),s=i.pop(),n=h(e)._rgba;return h(t.map(i,function(t,e){return(1-s)*n[e]+s*t}))},toRgbaString:function(){var e="rgba(",i=t.map(this._rgba,function(t,e){return null==t?e>2?1:0:t});return 1===i[3]&&(i.pop(),e="rgb("),e+i.join()+")"},toHslaString:function(){var e="hsla(",i=t.map(this.hsla(),function(t,e){return null==t&&(t=e>2?1:0),e&&3>e&&(t=Math.round(100*t)+"%"),t});return 1===i[3]&&(i.pop(),e="hsl("),e+i.join()+")"},toHexString:function(e){var i=this._rgba.slice(),s=i.pop();return e&&i.push(~~(255*s)),"#"+t.map(i,function(t){return t=(t||0).toString(16),1===t.length?"0"+t:t}).join("")},toString:function(){return 0===this._rgba[3]?"transparent":this.toRgbaString()}}),h.fn.parse.prototype=h.fn,c.hsla.to=function(t){if(null==t[0]||null==t[1]||null==t[2])return[null,null,null,t[3]];var e,i,s=t[0]/255,n=t[1]/255,o=t[2]/255,a=t[3],r=Math.max(s,n,o),l=Math.min(s,n,o),h=r-l,c=r+l,u=.5*c;return e=l===r?0:s===r?60*(n-o)/h+360:n===r?60*(o-s)/h+120:60*(s-n)/h+240,i=0===h?0:.5>=u?h/c:h/(2-c),[Math.round(e)%360,i,u,null==a?1:a]},c.hsla.from=function(t){if(null==t[0]||null==t[1]||null==t[2])return[null,null,null,t[3]];var e=t[0]/360,i=t[1],s=t[2],o=t[3],a=.5>=s?s*(1+i):s+i-s*i,r=2*s-a;return[Math.round(255*n(r,a,e+1/3)),Math.round(255*n(r,a,e)),Math.round(255*n(r,a,e-1/3)),o]},f(c,function(s,n){var o=n.props,a=n.cache,l=n.to,c=n.from;h.fn[s]=function(s){if(l&&!this[a]&&(this[a]=l(this._rgba)),s===e)return this[a].slice();var n,r=t.type(s),u="array"===r||"object"===r?s:arguments,d=this[a].slice();return f(o,function(t,e){var s=u["object"===r?t:e.idx];null==s&&(s=d[e.idx]),d[e.idx]=i(s,e)}),c?(n=h(c(d)),n[a]=d,n):h(d)},f(o,function(e,i){h.fn[e]||(h.fn[e]=function(n){var o,a=t.type(n),l="alpha"===e?this._hsla?"hsla":"rgba":s,h=this[l](),c=h[i.idx];return"undefined"===a?c:("function"===a&&(n=n.call(this,c),a=t.type(n)),null==n&&i.empty?this:("string"===a&&(o=r.exec(n),o&&(n=c+parseFloat(o[2])*("+"===o[1]?1:-1))),h[i.idx]=n,this[l](h)))})})}),h.hook=function(e){var i=e.split(" ");f(i,function(e,i){t.cssHooks[i]={set:function(e,n){var o,a,r="";if("transparent"!==n&&("string"!==t.type(n)||(o=s(n)))){if(n=h(o||n),!d.rgba&&1!==n._rgba[3]){for(a="backgroundColor"===i?e.parentNode:e;(""===r||"transparent"===r)&&a&&a.style;)try{r=t.css(a,"backgroundColor"),a=a.parentNode}catch(l){}n=n.blend(r&&"transparent"!==r?r:"_default")}n=n.toRgbaString()}try{e.style[i]=n}catch(l){}}},t.fx.step[i]=function(e){e.colorInit||(e.start=h(e.elem,i),e.end=h(e.end),e.colorInit=!0),t.cssHooks[i].set(e.elem,e.start.transition(e.end,e.pos))}})},h.hook(a),t.cssHooks.borderColor={expand:function(t){var e={};return f(["Top","Right","Bottom","Left"],function(i,s){e["border"+s+"Color"]=t}),e}},o=t.Color.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00",transparent:[null,null,null,0],_default:"#ffffff"}}(r),function(){function e(e){var i,s,n=e.ownerDocument.defaultView?e.ownerDocument.defaultView.getComputedStyle(e,null):e.currentStyle,o={};if(n&&n.length&&n[0]&&n[n[0]])for(s=n.length;s--;)i=n[s],"string"==typeof n[i]&&(o[t.camelCase(i)]=n[i]);else for(i in n)"string"==typeof n[i]&&(o[i]=n[i]);return o}function i(e,i){var s,o,a={};for(s in i)o=i[s],e[s]!==o&&(n[s]||(t.fx.step[s]||!isNaN(parseFloat(o)))&&(a[s]=o));return a}var s=["add","remove","toggle"],n={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};t.each(["borderLeftStyle","borderRightStyle","borderBottomStyle","borderTopStyle"],function(e,i){t.fx.step[i]=function(t){("none"!==t.end&&!t.setAttr||1===t.pos&&!t.setAttr)&&(r.style(t.elem,i,t.end),t.setAttr=!0)}}),t.fn.addBack||(t.fn.addBack=function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}),t.effects.animateClass=function(n,o,a,r){var l=t.speed(o,a,r);return this.queue(function(){var o,a=t(this),r=a.attr("class")||"",h=l.children?a.find("*").addBack():a;h=h.map(function(){var i=t(this);return{el:i,start:e(this)}}),o=function(){t.each(s,function(t,e){n[e]&&a[e+"Class"](n[e])})},o(),h=h.map(function(){return this.end=e(this.el[0]),this.diff=i(this.start,this.end),this}),a.attr("class",r),h=h.map(function(){var e=this,i=t.Deferred(),s=t.extend({},l,{queue:!1,complete:function(){i.resolve(e)}});return this.el.animate(this.diff,s),i.promise()}),t.when.apply(t,h.get()).done(function(){o(),t.each(arguments,function(){var e=this.el;t.each(this.diff,function(t){e.css(t,"")})}),l.complete.call(a[0])})})},t.fn.extend({addClass:function(e){return function(i,s,n,o){return s?t.effects.animateClass.call(this,{add:i},s,n,o):e.apply(this,arguments)}}(t.fn.addClass),removeClass:function(e){return function(i,s,n,o){return arguments.length>1?t.effects.animateClass.call(this,{remove:i},s,n,o):e.apply(this,arguments)}}(t.fn.removeClass),toggleClass:function(e){return function(i,s,n,o,a){return"boolean"==typeof s||void 0===s?n?t.effects.animateClass.call(this,s?{add:i}:{remove:i},n,o,a):e.apply(this,arguments):t.effects.animateClass.call(this,{toggle:i},s,n,o)}}(t.fn.toggleClass),switchClass:function(e,i,s,n,o){return t.effects.animateClass.call(this,{add:i,remove:e},s,n,o)}})}(),function(){function e(e,i,s,n){return t.isPlainObject(e)&&(i=e,e=e.effect),e={effect:e},null==i&&(i={}),t.isFunction(i)&&(n=i,s=null,i={}),("number"==typeof i||t.fx.speeds[i])&&(n=s,s=i,i={}),t.isFunction(s)&&(n=s,s=null),i&&t.extend(e,i),s=s||i.duration,e.duration=t.fx.off?0:"number"==typeof s?s:s in t.fx.speeds?t.fx.speeds[s]:t.fx.speeds._default,e.complete=n||i.complete,e}function i(e){return!e||"number"==typeof e||t.fx.speeds[e]?!0:"string"!=typeof e||t.effects.effect[e]?t.isFunction(e)?!0:"object"!=typeof e||e.effect?!1:!0:!0}t.extend(t.effects,{version:"1.11.4",save:function(t,e){for(var i=0;e.length>i;i++)null!==e[i]&&t.data(a+e[i],t[0].style[e[i]])},restore:function(t,e){var i,s;for(s=0;e.length>s;s++)null!==e[s]&&(i=t.data(a+e[s]),void 0===i&&(i=""),t.css(e[s],i))},setMode:function(t,e){return"toggle"===e&&(e=t.is(":hidden")?"show":"hide"),e},getBaseline:function(t,e){var i,s;switch(t[0]){case"top":i=0;break;case"middle":i=.5;break;case"bottom":i=1;break;default:i=t[0]/e.height}switch(t[1]){case"left":s=0;break;case"center":s=.5;break;case"right":s=1;break;default:s=t[1]/e.width}return{x:s,y:i}},createWrapper:function(e){if(e.parent().is(".ui-effects-wrapper"))return e.parent();var i={width:e.outerWidth(!0),height:e.outerHeight(!0),"float":e.css("float")},s=t("
").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),n={width:e.width(),height:e.height()},o=document.activeElement;try{o.id}catch(a){o=document.body}return e.wrap(s),(e[0]===o||t.contains(e[0],o))&&t(o).focus(),s=e.parent(),"static"===e.css("position")?(s.css({position:"relative"}),e.css({position:"relative"})):(t.extend(i,{position:e.css("position"),zIndex:e.css("z-index")}),t.each(["top","left","bottom","right"],function(t,s){i[s]=e.css(s),isNaN(parseInt(i[s],10))&&(i[s]="auto")}),e.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),e.css(n),s.css(i).show()},removeWrapper:function(e){var i=document.activeElement;return e.parent().is(".ui-effects-wrapper")&&(e.parent().replaceWith(e),(e[0]===i||t.contains(e[0],i))&&t(i).focus()),e},setTransition:function(e,i,s,n){return n=n||{},t.each(i,function(t,i){var o=e.cssUnit(i);o[0]>0&&(n[i]=o[0]*s+o[1])}),n}}),t.fn.extend({effect:function(){function i(e){function i(){t.isFunction(o)&&o.call(n[0]),t.isFunction(e)&&e()}var n=t(this),o=s.complete,r=s.mode;(n.is(":hidden")?"hide"===r:"show"===r)?(n[r](),i()):a.call(n[0],s,i)}var s=e.apply(this,arguments),n=s.mode,o=s.queue,a=t.effects.effect[s.effect]; +return t.fx.off||!a?n?this[n](s.duration,s.complete):this.each(function(){s.complete&&s.complete.call(this)}):o===!1?this.each(i):this.queue(o||"fx",i)},show:function(t){return function(s){if(i(s))return t.apply(this,arguments);var n=e.apply(this,arguments);return n.mode="show",this.effect.call(this,n)}}(t.fn.show),hide:function(t){return function(s){if(i(s))return t.apply(this,arguments);var n=e.apply(this,arguments);return n.mode="hide",this.effect.call(this,n)}}(t.fn.hide),toggle:function(t){return function(s){if(i(s)||"boolean"==typeof s)return t.apply(this,arguments);var n=e.apply(this,arguments);return n.mode="toggle",this.effect.call(this,n)}}(t.fn.toggle),cssUnit:function(e){var i=this.css(e),s=[];return t.each(["em","px","%","pt"],function(t,e){i.indexOf(e)>0&&(s=[parseFloat(i),e])}),s}})}(),function(){var e={};t.each(["Quad","Cubic","Quart","Quint","Expo"],function(t,i){e[i]=function(e){return Math.pow(e,t+2)}}),t.extend(e,{Sine:function(t){return 1-Math.cos(t*Math.PI/2)},Circ:function(t){return 1-Math.sqrt(1-t*t)},Elastic:function(t){return 0===t||1===t?t:-Math.pow(2,8*(t-1))*Math.sin((80*(t-1)-7.5)*Math.PI/15)},Back:function(t){return t*t*(3*t-2)},Bounce:function(t){for(var e,i=4;((e=Math.pow(2,--i))-1)/11>t;);return 1/Math.pow(4,3-i)-7.5625*Math.pow((3*e-2)/22-t,2)}}),t.each(e,function(e,i){t.easing["easeIn"+e]=i,t.easing["easeOut"+e]=function(t){return 1-i(1-t)},t.easing["easeInOut"+e]=function(t){return.5>t?i(2*t)/2:1-i(-2*t+2)/2}})}(),t.effects,t.effects.effect.slide=function(e,i){var s,n=t(this),o=["position","top","bottom","left","right","width","height"],a=t.effects.setMode(n,e.mode||"show"),r="show"===a,l=e.direction||"left",h="up"===l||"down"===l?"top":"left",c="up"===l||"left"===l,u={};t.effects.save(n,o),n.show(),s=e.distance||n["top"===h?"outerHeight":"outerWidth"](!0),t.effects.createWrapper(n).css({overflow:"hidden"}),r&&n.css(h,c?isNaN(s)?"-"+s:-s:s),u[h]=(r?c?"+=":"-=":c?"-=":"+=")+s,n.animate(u,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){"hide"===a&&n.hide(),t.effects.restore(n,o),t.effects.removeWrapper(n),i()}})}}); \ No newline at end of file diff --git a/data/core.telegram.org/js/telegram-passport2.js b/data/core.telegram.org/js/telegram-passport2.js new file mode 100644 index 0000000000..026006ade7 --- /dev/null +++ b/data/core.telegram.org/js/telegram-passport2.js @@ -0,0 +1,413 @@ +(function(window) { + if (!Array.isArray) { + Array.isArray = function(arg) { + return Object.prototype.toString.call(arg) === '[object Array]'; + }; + } + if (!Object.isSimpleObject) { + Object.isSimpleObject = function(arg) { + return arg != null && typeof arg === 'object'; + }; + } + function isFunction(func) { + return func && {}.toString.call(func) === '[object Function]'; + } + function getEl(elOrId) { + if (elOrId.nodeType) return elOrId; + return document.getElementById(elOrId); + } + function preventDefault(event) { + if (window.event) { + window.event.returnValue = false; + } + else if (event.preventDefault) { + event.preventDefault(); + } + else { + event.returnValue = false; + } + } + function addEvent(el, event, handler) { + var events = event.split(/\s+/); + for (var i = 0; i < events.length; i++) { + if (el.addEventListener) { + el.addEventListener(events[i], handler, false); + } else { + el.attachEvent('on' + events[i], handler); + } + } + } + + var UA = window.navigator.userAgent || ''; + var ScopeAliases = { + personal_details: 'pd', + passport: 'pp', + driver_license: 'dl', + identity_card: 'ic', + internal_passport: 'ip', + id_document: 'idd', + address: 'ad', + utility_bill: 'ub', + bank_statement: 'bs', + rental_agreement: 'ra', + passport_registration: 'pr', + temporary_registration: 'tr', + address_document: 'add', + phone_number: 'pn', + email: 'em', + }; + + function openDeepLink(url, tooltipToggle) { + var is_ios = /ios|iphone os|iphone|ipod|ipad/i.test(UA); + var is_firefox = /firefox/i.test(UA); + var use_iframe = (is_ios || is_firefox); + var use_once = (!is_ios && is_firefox); + var timeOpen; + var ttNeedHide = false; + var pageShown = true; + var onPageHide = function() { + pageShown = false; + }; + tooltipToggle = tooltipToggle || function(){}; + var onPageShow = function() { + pageShown = true; + if (ttNeedHide && (+(new Date) - timeOpen) > 5000) { + tooltipToggle(false); + } + }; + addEvent(window, 'pagehide', onPageHide); + addEvent(window, 'pageshow', onPageShow); + addEvent(window, 'blur', onPageHide); + addEvent(window, 'focus', onPageShow); + var showTooltip = function() { + tooltipToggle(true); + if (!pageShown) { + ttNeedHide = true; + } + }; + if (use_iframe) { + var iframeEl = document.createElement('iframe'); + iframeEl.style.position = 'absolute'; + iframeEl.style.left = '-10000px'; + iframeEl.style.top = '-10000px'; + document.body.appendChild(iframeEl); + if (iframeEl !== null) { + timeOpen = +(new Date); + iframeEl.src = url; + setTimeout(showTooltip, 2500); + } + if (!use_once) { + setTimeout(function() { + if (pageShown) { + timeOpen = +(new Date); + window.location = url; + } + }, 2000); + } + } + else { + setTimeout(function() { + timeOpen = +(new Date); + window.location = url; + }, 100); + setTimeout(showTooltip, 1500); + } + } + + function packScope(scope) { + if (scope.data) { + scope.d = scope.data; + delete scope.data; + } + if (!scope.d) { + throw new TypeError('scope data is required'); + } + if (!scope.v) { + throw new TypeError('scope version is required'); + } + for (var i = 0; i < scope.d.length; i++) { + scope.d[i] = packScopeField(scope.d[i]); + } + return JSON.stringify(scope); + } + function packScopeField(field) { + if (field.one_of) { + field._ = field.one_of; + delete field.one_of; + } else if (field.type) { + field._ = field.type; + delete field.type; + } + if (Array.isArray(field._)) { + for (var j = 0; j < field._.length; j++) { + field._[j] = packScopeField(field._[j]); + } + field = packScopeOpts(field); + } else if (field._) { + if (ScopeAliases[field._]) { + field._ = ScopeAliases[field._]; + } + field = packScopeOpts(field); + } else if (ScopeAliases[field]) { + field = ScopeAliases[field]; + } + return field; + } + function packScopeOpts(scope) { + if (scope.selfie) { + scope.s = 1; + delete scope.selfie; + } + if (scope.translation) { + scope.t = 1; + delete scope.translation; + } + if (scope.native_names) { + scope.n = 1; + delete scope.native_names; + } + return scope; + } + + function passportAuth(options, tooltipToggle) { + if (!options.bot_id) { + throw new Error('bot_id is required'); + } + if (!options.scope) { + throw new Error('scope is required'); + } + if (!Object.isSimpleObject(options.scope)) { + throw new TypeError('scope should be an object'); + } + if (!options.public_key) { + throw new Error('public_key is required'); + } + if (!options.nonce) { + throw new Error('nonce is required'); + } + if (options.payload) { + throw new Error('payload is deprecated, use nonce instead'); + } + var is_android = /android/i.test(UA); + var url = (is_android ? 'tg:' : 'tg://') + 'resolve?domain=telegrampassport' + + '&bot_id=' + encodeURIComponent(options.bot_id) + + '&scope=' + encodeURIComponent(packScope(options.scope)) + + '&public_key=' + encodeURIComponent(options.public_key) + + '&nonce=' + encodeURIComponent(options.nonce); + if (options.callback_url) { + url += '&callback_url=' + encodeURIComponent(options.callback_url); + } + url += '&payload=nonce'; // legacy for outdated apps + openDeepLink(url, tooltipToggle); + } + + function createAuthButton(contEl, authDataOrFunc, options) { + contEl = getEl(contEl); + if (!contEl) { + return false; + } + options = options || {}; + var btnText = options.text || 'Log In With Telegram'; + var btnRadius = parseInt(options.radius, 10) || 23; + var tooltipText = options.tooltip_text || 'Please install Telegram to use this option.'; + var tooltipForce = options.tooltip_force || false; + var tooltipPos = options.tooltip_position; + if (tooltipPos == 'top') { + var tooltipWrapClass = 'tooltip-top'; + } else if (tooltipPos == 'left') { + var tooltipWrapClass = 'tooltip-left'; + } else if (tooltipPos == 'right') { + var tooltipWrapClass = 'tooltip-right'; + } else { + var tooltipWrapClass = 'tooltip-bottom'; + } + var btnStyle = '' + + '.telegram-passport {' + + 'display: inline-block;' + + 'position: relative;' + + 'max-width: 100%;' + + '}' + + 'button.telegram-passport-button {' + + 'display: inline-block;' + + 'vertical-align: top;' + + 'font-family: "Lucida Grande", Arial, Helvetica, sans-serif;' + + 'font-size: 16px;' + + 'font-weight: 500;' + + 'line-height: 20px;' + + 'text-align: left;' + + 'border-radius: ' + btnRadius + 'px;' + + 'background-color: #54a9eb;' + + 'text-decoration: none;' + + 'padding: 12px 21px 14px;' + + 'margin: 0;' + + 'white-space: nowrap;' + + 'text-overflow: ellipsis;' + + 'overflow: hidden;' + + 'border: none;' + + 'color: #fff;' + + 'cursor: pointer;' + + 'max-width: 100%;' + + '}' + + 'button.telegram-passport-button:focus {' + + 'outline: none;' + + '}' + + '.telegram-passport-tooltip-wrap {' + + 'position: absolute;' + + 'pointer-events: none;' + + 'text-align: center;' + + 'z-index: 100;' + + '}' + + '.telegram-passport-tooltip-wrap.tooltip-bottom {' + + 'left: -500px;' + + 'right: -500px;' + + 'top: 100%;' + + '}' + + '.telegram-passport-tooltip-wrap.tooltip-top {' + + 'left: -500px;' + + 'right: -500px;' + + 'bottom: 100%;' + + '}' + + '.telegram-passport-tooltip-wrap.tooltip-left {' + + 'right: 100%;' + + 'top: 7px;' + + 'bottom: 7px;' + + '}' + + '.telegram-passport-tooltip-wrap.tooltip-right {' + + 'left: 100%;' + + 'top: 7px;' + + 'bottom: 7px;' + + '}' + + '.telegram-passport-tooltip {' + + 'position: relative;' + + 'font-family: "Lucida Grande", Arial, Helvetica, sans-serif;' + + 'background: #949494;' + + 'color: #fff;' + + 'white-space: nowrap;' + + 'border-radius: ' + btnRadius + 'px;' + + 'padding: 7px 16px;' + + 'display: inline-block;' + + 'font-size: 14px;' + + 'line-height: 18px;' + + 'text-decoration: none;' + + 'pointer-events: none;' + + 'visibility: hidden;' + + 'opacity: 0;' + + '}' + + '.telegram-passport-tooltip-wrap.tooltip-bottom .telegram-passport-tooltip {' + + 'margin-top: 32px;' + + '}' + + '.telegram-passport-tooltip-wrap.tooltip-top .telegram-passport-tooltip {' + + 'margin-bottom: 32px;' + + '}' + + '.telegram-passport-tooltip-wrap.tooltip-left .telegram-passport-tooltip {' + + 'margin-right: 32px;' + + '}' + + '.telegram-passport-tooltip-wrap.tooltip-right .telegram-passport-tooltip {' + + 'margin-left: 32px;' + + '}' + + '.telegram-passport-tooltip:hover {' + + 'color: #fff;' + + 'text-decoration: none;' + + '}' + + '.telegram-passport-tooltip-shown {' + + 'transition: all .2s ease;' + + 'pointer-events: auto;' + + 'visibility: visible;' + + 'opacity: 1;' + + '}' + + '.telegram-passport-tooltip-wrap.tooltip-bottom .telegram-passport-tooltip-shown {' + + 'margin-top: 16px;' + + '}' + + '.telegram-passport-tooltip-wrap.tooltip-top .telegram-passport-tooltip-shown {' + + 'margin-bottom: 16px;' + + '}' + + '.telegram-passport-tooltip-wrap.tooltip-left .telegram-passport-tooltip-shown {' + + 'margin-right: 16px;' + + '}' + + '.telegram-passport-tooltip-wrap.tooltip-right .telegram-passport-tooltip-shown {' + + 'margin-left: 16px;' + + '}' + + '.telegram-passport-button-icon {' + + 'display: inline-block;' + + 'vertical-align: top;' + + "background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADYAAAAsCAYAAAFZD3u2AAAAAXNSR0IArs4c6QAAB+FJREFUaAXVWllsVFUYPndm2qG0Q4EipaWspSJbQRYxDWIUHyBqAgYTjUsTNBqN+qAJLg8+ugTlQeITARNQNERcXlyesAxVhOKSSGhLVyi0dDqdznTWznL9/ts5t2fuNrfTEvFP/p5//89/z52z3TJmArIsOyWtDkKZyxxEgE+SkOBMm5/rxlsSxpPpTFPrEJEqKJ4S4PfOgBSMp1QvkqmMSJCryOto6HuUsFwDwbvkRQDZb0rcLEM2JFRklFP1zJaQ0wm1t+FEmjxzQPUcioyxbPcHcyzsMOhXzhNyaZ1gMAeyYVF+visgskp2CYY0CHNzNGD42GxfWaFVKY4u9D1HQ2NGlkYO2QibdJFIgB5I3/zRLx/xXlUCgM8BQyc7QkQ5YceOerBZTGnpJBr2DkXHOK9zguIcV/JW+2IrTlDu5QZie7bdL1/oCogiorupvzpIpzMpit7ji+h0EBTxTG9yLRkTRuLq75GrlFZXj7fNf4ocMplMjqHI6JxIQE40uGZANurPghgRjjb39YI/IspAv6zhx1lv+/ALR73XJuYHiHlWQ4fpFCLRazwZb6ccH4FKgD/xgFbtpJIh0ENAw5eFksSS6Zs0cCawxTQZHA6aOOWIfaOJGCVoxltuBZRImd5gdAH0ZtPMGsVVf5T1DMUUaUVZ0QyLiYl7vkoETYkz0NJkSa0ptPaH2WAooerXLPSwirJilbcisjM/o2k0DsMSrTE68dZfvcH3Q8JqRzYNK+Ywl9P056INQ/wXXJizSnEhb1ta5KJImX+MeAw+W1sziy2cbfkAuKva8qpIYJmMDM60Dn2ICX8/ree+USUviVljQ03G6ZhYYhWh/k8HktVxcd5kZEhv20BojIUTKSxhLLRv2+JyHgCPeznoTs5rWhom/VquMdKxX52/8YNOqBEg8TtADsc06lvPIvMuYBdw9y3JhsDzgBeBIvRPazJEPiRG19AHppwMAZ8EpjSBjdjqgpIh0lJgh1HEdCYTTiTTOtWkEsHbATyuiyIIWroDcsfNsCCZIG0lg/lLEy7GVCiaDNBv8XogZmwgy17TZPBYDxww8xTlv17xK1uckYi6SxPVnN5DydQZBFI3+K+Bj5AiHwwEE+n2gbCT7LYun83cRQpp6MbnRxeS7ITFj4ZWxsK0t93vxNFEib6tbi5zONQ+G3tkpbSeKRtrS6ussnMwwq4HaEUaBxuLJhnGUNlMImhh+ogIK0hl5DSdL3giKsRmIgr7CY+t1o8Cl0L4LXADV1L7T1+IDUeSqsjtcrCttXQwsg2VqEw5n6nJRFckrh1LpU+c6xy5R5TPKnGxDROri6gypfnLQQaGybgn/XY4XTXbzeoqyzhruxWTWW4mYKgma+kJ2k4gGDYJtPlGn4wk5ribG8eSGXb07DUW1myAuN6kPSjKLR8jGfJH2eGLqn53LSjFLkt3IFb1nBAfIcksH6PiJEkj3Jm3SFwDupTzdtu8yTIOtk4M5nI6NzY2LLqOXkezPT8v6gU6ItAKmTfZA3UVfdxJcjh3NzYs/JPz1CLhVjS7RFmWzhkvA72xyHvZt+mz5qvmpxC44bdJ6516ujWO9D+VorC1wAPAG0AtdN/2ZaHHxcA9wO+Algs09BzO3laFoVfVwP3AS7yHBbaP/WeFocPbgIeBwQI7b+bWQ0Xp7ianu1Jk9yDmXmAjcDsw78QPGw7pgeDYSOdguKIap91ldyjbGK4za5VZazJJzAKpchSxEgwV8AyQFoVCINHti2WuDUeV+4QlFSVsyTxbBVEu2vXNwlSdLGjEUAD50baWingUSPvoggG3jLGOwah7IBin9cdNF/2rqz1snsfeDYuQ+BAVRXzeEUMR82H3FJCKWA+cFsA3jVhbf7hE3IwWYzO6rsbDSt0FPW/qVzUKU87RORFQBK22VMATwEltd2GfF+LJTKK1f9QdiikXrerVFW166Q6saHLXU9p8X/KiSEGHl9fRfqy1mi5+NJ5KY2Sc0TH9/U5VuZutqCyl7ct0pNuMOBd5IBqxnCMDV0ylDUSTcnt/WEqkMhRGdxCtQzFVk7zTy9OfM2JRZKs+KowcfSB6Gkiv4b3ASQFdrdJxFPcehn4uHBvX4PdTXjL+ccPQqHDhbhT2veiuFiYKRRoFrwD/eBbVkwx4uuuQenDIMKlFCTOz2KlMCFY3CIph4X+6UFSt1j1vYVoH4k93d89wJDzj1+tGBpDRZfiqqjLbNxUmYeyIX0Fhn2oNCyqMgjRd9jdg6mnWBqSLii3LrO94tD5T4OlMWo7ClGlWjFNwYRTklzbfYUmWnhcD0ux3Izj+KaMev6mNS8qZY3pmPTENpz9AUW9zRmynVNjJkyedC+p3RGUmq1uEUDzNBkcnvtFQsmL8mwJdoC21vzUS+2hFz0dhPiODKRVGAZs6g3UsmWznwUeiSTYkXG1xObUYuEsP11e9ON/jOgx2lagrgD6Oop4188t7N2DmyOX315ZfcUoO9XXAtY8O0IFBR5F0J77YrK2cVdQMfjWQHiotLSGdgz2B5d3DlEeM+oAlQfK2DffglVzcN5Jg+OcWpWsIHmdOaee+hkVNVn2FPy1u7wHfANrp02k8lwetYtoJYuWv6n7+e6C0pNjV3zEU9eCGOc0kx77n7qs5phrYJFDkAph+Dtxh4kJPrRaF9Zrob38xiqwHngLSf3TQh3X6YGXr++q/M6e7wZMmuzIAAAAASUVORK5CYII=') no-repeat;" + + 'background-size: 27px 22px;' + + 'width: 27px;' + + 'height: 22px;' + + 'margin: 0 15px -2px -5px;' + + '}' + + '.telegram-passport-tooltip:before {' + + "content: '';" + + 'display: inline-block;' + + 'position: absolute;' + + '}' + + '.telegram-passport-tooltip-wrap.tooltip-bottom .telegram-passport-tooltip:before {' + + 'left: 50%;' + + 'top: 0;' + + 'border: 10px solid;' + + 'border-radius: 4px 0 0;' + + 'border-color: #949494 transparent transparent #949494;' + + 'transform: rotateZ(45deg) translate3d(-3px,5px,0);' + + 'transform-origin: 100% 0;' + + '}' + + '.telegram-passport-tooltip-wrap.tooltip-top .telegram-passport-tooltip:before {' + + 'left: 50%;' + + 'bottom: 0;' + + 'border: 10px solid;' + + 'border-radius: 0 0 4px;' + + 'border-color: transparent #949494 #949494 transparent;' + + 'transform: rotateZ(45deg) translate3d(-11px,9px,0);' + + 'transform-origin: 0 100%;' + + '}' + + '.telegram-passport-tooltip-wrap.tooltip-left .telegram-passport-tooltip:before {' + + 'top: 50%;' + + 'right: 0;' + + 'border: 8px solid;' + + 'border-radius: 0 2px 0 0;' + + 'border-color: #949494 #949494 transparent transparent;' + + 'transform: rotateZ(45deg) translate3d(4px,-3.5px,0);' + + 'transform-origin: 100% 0;' + + '}' + + '.telegram-passport-tooltip-wrap.tooltip-right .telegram-passport-tooltip:before {' + + 'top: 50%;' + + 'left: 0;' + + 'border: 8px solid;' + + 'border-radius: 0 0 0 2px;' + + 'border-color: transparent transparent #949494 #949494;' + + 'transform: rotateZ(45deg) translate3d(-15px,-7.5px,0);' + + 'transform-origin: 0 100%;' + + '}'; + var tooltipClass = tooltipForce ? 'telegram-passport-tooltip telegram-passport-tooltip-shown' : 'telegram-passport-tooltip'; + var btnHtml = '' + + '' + + '' + + ''; + var btnContEl = document.createElement('div'); + btnContEl.className = 'telegram-passport'; + btnContEl.innerHTML = btnHtml; + contEl.appendChild(btnContEl); + var buttonEl = btnContEl.getElementsByTagName('button')[0]; + var tooltipEl = btnContEl.getElementsByTagName('a')[0]; + addEvent(buttonEl, 'click', function(e) { + preventDefault(e); + var options = isFunction(authDataOrFunc) ? authDataOrFunc() : authDataOrFunc; + options = options || {}; + var tooltipToggle = function(show) { + if (show) { + tooltipEl.classList.add('telegram-passport-tooltip-shown'); + } else { + tooltipEl.classList.remove('telegram-passport-tooltip-shown'); + } + }; + passportAuth(options, !tooltipForce ? tooltipToggle : null); + }); + } + + if (!window.Telegram) { + window.Telegram = {}; + } + window.Telegram.Passport = { + auth: passportAuth, + createAuthButton: createAuthButton, + }; +})(window); diff --git a/data/core.telegram.org/method/auth.checkPhone b/data/core.telegram.org/method/auth.checkPhone new file mode 100644 index 0000000000..491df9a75e --- /dev/null +++ b/data/core.telegram.org/method/auth.checkPhone @@ -0,0 +1,176 @@ + + + + +Returns information on whether the passed phone number was registered.
+ Method schema is available as of layer 78. Switch »
+Name | +Type | +Description | +
---|---|---|
phone_number | +string | +Phone number in the international format | +
The method returns an auth.CheckedPhone type object with information on whether an account with such a phone number has already been registered, as well as whether invitations were sent to this number (using the auth.sendInvites method).
+(auth.checkPhone "79123413132")
+=
+(auth.checkedPhone
+ phone_registered:(boolFalse)
+ phone_invited:(boolFalse)
+)
+Code | +Type | +Description | +
---|---|---|
400 | +PHONE_NUMBER_BANNED | +The provided phone number is banned from telegram | +
400 | +PHONE_NUMBER_INVALID | +Invalid phone number | +
Makes a voice call to the passed phone number. A robot will repeat the confirmation code from a previously sent SMS message.
+{scheme}
+Name | +Type | +Description | +
---|---|---|
phone_number | +string | +Phone number in the international format | +
phone_code_hash | +string | +SMS-message ID | +
(auth.sendCall "79991234567" "2dc02d2cda9e615c84")
+=
+(boolTrue)
+
+03c51564 3939370b 33323139 37363534 63643212 32643230 39616463 35313665 00343863 64e1a61b
+=
+997275b5
+Code | +Type | +Description | +
---|---|---|
400 | +PHONE_NUMBER_INVALID | +Invalid phone number | +
400 | +PHONE_CODE_HASH_EMPTY | +phone_code_hash was not sent | +
400 | +PHONE_CODE_EXPIRED | +SMS expired | +
Saves information that the current user sent SMS-messages with invitations to its unregistered contacts.
+{scheme}
+Name | +Type | +Description | +
---|---|---|
phone_numbers | +Vector<string> | +List of phone numbers of message recipients in the international format | +
message | +string | +Message text | +
Code | +Type | +Description | +
---|---|---|
400 | +MESSAGE_EMPTY | +The provided message is empty | +
Forces sending an SMS message to the specified phone number. Use this method if auth.sentAppCode was returned as a response to auth.sendCode, but the user can't reach the device with Telegram.
+{scheme}
+Name | +Type | +Description | +
---|---|---|
phone_number | +string | +Phone number in international format | +
phone_code_hash | +string | +SMS-message ID | +
Code | +Type | +Description | +
---|---|---|
400 | +PHONE_NUMBER_INVALID | +Invalid phone number | +
400 | +PHONE_CODE_HASH_EMPTY | +phone_code_hash was not sent | +
400 | +PHONE_CODE_EXPIRED | +SMS expired | +
Forwards single messages.
+{scheme}
+Name | +Type | +Description | +
---|---|---|
peer | +InputPeer | +User or chat where a message will be forwarded | +
id | +int | +Forwarded message ID | +
random_id | +long | +Unique client message ID required to prevent message resending | +
Code | +Type | +Description | +
---|---|---|
400 | +CHAT_ID_INVALID | +The provided chat id is invalid | +
400 | +MESSAGE_ID_INVALID | +The provided message id is invalid | +
400 | +PEER_ID_INVALID | +The provided peer id is invalid | +
400 | +YOU_BLOCKED_USER | +You blocked this user | +
Get full message reaction list
+ Method schema is available as of layer 135. Switch »
+Name | +Type | +Description | +
---|---|---|
flags | +# | +Flags, see TL conditional fields | +
peer | +InputPeer | +Peer | +
id | +int | +Message ID | +
reaction | +flags.0?string | +Get only reactions of this type (UTF8 emoji) | +
offset | +flags.1?string | +Offset (typically taken from the next_offset field of the returned MessageReactionsList) |
+
limit | +int | +Maximum number of results to return, see pagination | +
List of message reactions
+How to fetch results from large lists of objects.
Search for GIFs
+ Method schema is available as of layer 114. Switch »
+Name | +Type | +Description | +
---|---|---|
q | +string | +Text query | +
offset | +int | +Offset for pagination » | +
Code | +Type | +Description | +
---|---|---|
400 | +SEARCH_QUERY_EMPTY | +The search query is empty | +
How to fetch results from large lists of objects.
Sends multiple messages to contacts.
+{scheme}
+Name | +Type | +Description | +
---|---|---|
contacts | +Vector<InputUser> | +List of user ID to whom a message will be sent | +
message | +string | +Message text | +
media | +InputMedia | +Message media-contents | +
Send reaction to message
+ Method schema is available as of layer 135. Switch »
+Name | +Type | +Description | +
---|---|---|
flags | +# | +Flags, see TL conditional fields | +
peer | +InputPeer | +Peer | +
msg_id | +int | +Message ID to react to | +
reaction | +flags.0?string | +Reaction (a UTF8 emoji) | +
Code | +Type | +Description | +
---|---|---|
400 | +MESSAGE_ID_INVALID | +The provided message id is invalid | +
400 | +REACTION_EMPTY | +Empty reaction provided | +
TL Language defines abstract data types in the spirit of a general theory of types (more accurately, Martin-Löf’s theories of dependent intuitionistic types) without specifying the values of these types should be represented in memory, when saved to disk, or transmitted over a network. In contrast, the article on binary serialization discusses the problem of effective serialization of values of abstract types. To this end, the concept of a concrete or serialized type has been defined as the sets of serializations of all possible values of the corresponding abstract type. In this case, the serializations take values in the set A of words in the alphabet A*, which consists of 2^32 characters -- 32-bit integers.
+In order to use a TL schema (e.g. “program”) in the TL language to describe the serialization of values of abstract types, we should explain how the concrete type [T] (subset [T] of A^) is associated with the abstract type T (defined in TL), and how the values of the abstract type T correspond to the values of the concrete type [T] (i.e. the elements of [T]*).
+Serialization is the process of constructing an element of [T] based on a value of the abstract type T. The reverse process is deserialization.
+Values of the abstract type T may be represented in a different way. Typically, some sort of trees or graphs are used in memory or, if desired, a set of nodes may be used, each of which contains a certain tag (“node type”) and several pointers to other nodes and/or values of built-in primitive types such as int
. However, for general discussions it is useful to write the values of abstract type T as a string, more specifically, an S-expression. Recall that an S-expression is either an atom (the value of a primitive type, for example, an integer or a string constant in quotation marks; or an identifier that corresponds to a built-in or defined function) or a space-delimited list of S-expressions ending in parentheses. In our case, we use S-expressions, the first element of which is a combinator identifier, while the remaining elements (the number of which depends on the combinator’s arity) are S-expressions representing elements of the chosen combinator’s fields (or parameters). Moreover, the type of the arguments’ S-expressions and the type of the S-expressions of the result (e.g. the associated expression) must match.
For example, for the schema
+pair x:int y:int = Pair;
+pnil = PairList;
+pcons hd:Pair tl:PairList = PairList;
+the following are examples of the abstract type PairList
, written as S-expressions:
(pnil)
+(pcons (pair 2 3) (pcons (pair 9 4) (pnil)))
+We usually write E : T (read "E of type T”) when we want to say that E is a value of type T. We assume there is a built-in type Type whose values are types. Thus, writing T : Type means that T is a type.
+For example, we can write:
+PairList : Type;
+(pcons (pair 2 3) (pcons (pair 9 4) (pnil))) : PairList;
+Converting an abstract value to a serialized value, generally speaking, is straightforward (and, if desired, can be defined by induction):
+It is the serialization of values n of the primitive type int
(as a single-symbol word in the alphabet A)
The serialization of a string constant (a value of the primitive type string) is a sequence of the 32-bit numbers defined in Binary serialization.
+The serialization of the S-expression (C E1 ... Er) : T
, where C
is a combinator with arity r with argument types T1, ..., Tr and result type T (e.g. C : T1->T2->...->Tr->T) is the concatenation of the combinator number C (a 32-bit number that unambiguously identifies the combinator, usually equal to the CRC-32 of the string of its TL description) and the serializations of the values E1 of type T1, E2 of type T2, ..., Er of type Tr.
If we use [T] to denote the concrete type corresponding to the abstract T, and [E] to denote an element of [T] corresponding to the value E of type T, then the last rule may be written as:
+Values of the built-in clothed types Int
and String
and serialized as if they were defined using int x:int = Int;
and string s:string = String;
, i.e. the serialization of integer constant or a string is preceded by number of the int
or string
combinator (constructor). In S-expressions, this may be written as (int 5)
or (string "Test")
.
However, what has been described above does not account for certain subtleties, such as the existence of naked types, or the difference between functions (active combinators whose application may be reduced, e.g. calculated) and constructors (passive combinators for which there are not and cannot be reduction rules). Furthermore, we have not explained how to handle polymorphic types and optional combinator parameters. We will attempt to explain this now.
+By dividing combinators into constructors and functions, we can introduce the following classes of expressions (values) of the abstract type T:
+Constant expressions: for the types int
and string
, these are all integer/string constants; for T, these are all expressions like (C E1 ... Er) : T, where the combinator C : T1->T2->...->Tr->T is a constructor, and Ei : Ti is constant expressions of types Ti. In other words, a constant expression is an S-expression consisting of only constructors and constant of primitive types.
Surface expressions are expressions that outwardly contain a functional combinator whose arguments, however, are constant expressions of the appropriate types. In other words, the functional combinator is resolved only at the outer level. (This is not entirely true; see the full explanation below).
+Functional expressions: These are expressions that may contain any combinators or constants at all levels.
+In practice, we most frequently need constant values (for storage and passing any data structures, in particular, responses to RPC queries) and surface expressions (for example, as RPC queries: then the functional combinator of the outer level is the name of the RPC function that we want to call, while its parameters are the arguments, which are constant values, for invoking the function). In some cases, arbitrary functional expressions are helpful (for example, it we want to remotely transmit the result of one RPC query to a different RPC query).
+We will use c(T) to denote a subtype of the abstract type T, whose values are constant expressions of type T. Clearly, c(T) possesses approximately the same constructors as T itself (with the types of all arguments Ti replaced by c(Ti), but it does not have functional combinators.
+Analogously, we will use f(T) to denote a subtype of T, whose values are surface expressions of type T. Clearly, the combinators of f(T) are essentially functional combinators of type T, but c() applies to the types of these combinators’ arguments: The combinator A : T1->...->Tr->T turns into A' : c(T1)->...->c(Tr)->f(T). (See the clarification of this rule below.)
+Thus, we have defined two “functionals” c : Type -> Type and f : Type -> Type, such that forall T : Type, c(T) :- T and forall T : Type, f(T) :- T (writing T :- T' means that T is contained in T', or that T is a subtype of T').
+We will assume that c and f are idempotent.
+From the perspective of abstract type theory, naked types (in contrast to built-in primitive types like int
and string
are unnecessary. However, they are extremely useful in practice.
Therefore, TL introduces the (partially defined) idempotent unary operator %
, which turns a standard functional (e.g. an expression of type ...->Type or simply Type) into a different standard functional of the same type. If T is a type, then from an abstract theoretical point of view, %T
is equivalent to c(T). In other words, the values of %T
are the constant values of T. If T is a k-arity standard expression, then T : S1 -> ... -> Sk -> Type, where each Si=Type or #, then by definition %T
is a k-arity standard expression with the same arity, which is defined by the equation (%T) a1 ... ak = % (T a1 ... ak).
When a constant value of type %T
is serialized, it is first serialized as a value of type T (assuming that T is not already a naked type itself). Then the first character of the serialization is discarded (e.g. the name of the enclosing combinator). Therefore, %T
is a only a valid type expression if there is not more than one constructor for %T
. The expression %T
, where T : S1 -> ... -> Sk -> Type, is valid, if for any choice of parameters a1 : S1, ... , ak : Sk, the type T a1 ... ak does not have more than one constructor. Using %
in other instances is incorrect.
If for every value of the parameters a1 : S1, ..., ak : Sk, there is exactly one constructor C for T a1 ... ak, then TL allows writing C a1 ... ak
instead of %T a1 ... ak
or %(T a1 .. ak)
. In other words, in certain situations the identifier C
is a synonym for %T
. This is only allowed in the context of a type (when specifying the type of a combinator’s field or result).
Moreover, it is assumed that %Int = int
and %String = string
.
!
modifierIn TL, the idempotent operator !
can modify any type, actually making surface values be allowed when its constant values are serialized. However, if T is a standard function like S1->..->Sr->Type, then !T is defined using the equation (!T) a1 ... ar = !(T a1 ... ar)
, for any a1:S1, ..., ar:Sr.
The !
operator is only allowed in a definition of the types of fields of functional combinators. It is usually used as a type prefix, for example:
set_timeout {X:Type} timeout:int f:!X = X;
+In this case, the set_timeout
“wrapper” is defined. It takes two explicit parameters: the integer timeout
and a surface expression of type X
. X : Type is itself an implicit parameter (it is not explicitly stated, rather it is inferred from the values of the other parameters and their types). A similar kind of wrapper may be helpful for modifying the action of RPC queries (which are surface expressions of various types). For example, suppose we have the function
factorial n:int = int;
+then we can wrap the RPC query (factorial 100)
as follows: (set_timeout 200 (factorial 100))
. This expression is still a surface value of type int
, which means it can be passed as an RPC query.
A consecutive pair of two computations is another example:
+pair {X Y : Type} x:X y:Y = Pair X Y; // constructor
+seq_pair {X Y : Type} x:!X y:!Y = Pair X Y; // functional wrapper for sequential computation
+par_pair {X Y : Type} x:!X y:!Y = Pair X Y; // functional wrapper for parallel computation
+Now the RPC query (seq_pair (factorial 2) (factorial 3)) : Pair int int
first calculates factorial 2, then factorial 3, and returns the pair (pair 2 6)
. In this case, the sequence of operations isn’t important, because they do not have side effects. It would have been just as well to use (par_pair (factorial 2) (factorial 3))
. However, this is not always the case.
We can also define an analogy to a “comma” operation:
+comma {X Y : Type} x:!X y:!Y = Y;
+For example, this operation could first calculate x
, then forget the result, calculate y
, and return y
.
Note that the semantics of the seq_pair
, par_pair
and comma
wrappers are indeed defined where they are implemented (like the semantics of all other functional combinators), not by their TL declaration.
In principle, polymorphic wrappers like set_timeout
can also be applied, for example, to “annotate” a RPC response’s constant values. For example, the server might return a response to a query together with the time it was calculated. However, a value of type !X must be constant, because that is what is expected as the enclosing expression’s value. In other words, set_timeout 239 E
is a constant/surface value of type X if and only if E is such itself.
$
modifierThe idempotent modifier $
permits the use of arbitrary functional values of an appropriate type in contexts where only constants or surface values are usually allowed. It recursively transforms all combinators for all of the types involved, canceling the action of %
and affixing $
to the parameter types and result of all combinators ($
is also added to the front of the transformed combinators). Moreover, built-in types are also transformed (in the final stage): $int = Int
and $string = String
.
This may be useful to create an RPC query that performs a “deep computation” of the expression passed to it:
+compute {X:Type} expr:$X = X;
+For example, now we can transmit the following as an RPC query:
+(compute ($factorial ($factorial (int 3)))) : int
+(Note that the three has become clothed; the combinator $factorial has type $int -> $int).
+This is very powerful tool. It does not have to be implemented in very simple versions of TL. $
is not encountered in currently used TL schemas.
In fact, at least in terms of its application to serialization, the TL language by default implies the c() modifier around all combinators’ parameter types and results, while !
and $
cancel it (more accurately, !
only cancels, and in some sense $
reverses the meaning). This is why there is no explicit c()
modifier in TL and why it is assumed that all functions only accept constant values and return constant results, unless otherwise specified.
You may think that some functional combinators may have a type such as partial_factorial n:int = $int;
and that the RPC query (partial_factorial 3)
might then unexpectedly return ($product (int 3) ($product (int 2) ($product (int 1) (int 1)))) : $int
...
It is probably more correct to think about the !
modifier as follows. All types initially include only constant values (and only constructors). The !
modifier makes a new type (it’s twin) out of each type. This new type has no inherent constructors. Functional combinators differ from constructors in that !
is implicitly added in front of their result’s type. After this, the (local or remote) process of calculating the expression can be represented using the polymorphic function eval : !X -> X
.
Formal declaration of TL combinators
+Main article: Formal description of TL. See also TL Language.
+Combinators in TL are declared as follows:
+combinator-decl ::= full-combinator-id { opt-args } { args } =
result-type ;
+full-combinator-id ::= lc-ident-full | _
+combinator-id ::= lc-ident-ns | _
+opt-args ::= {
var-ident { var-ident } : [excl-mark] type-expr }
+args ::= var-ident-opt :
[ conditional-arg-def ] [ !
] type-term
+args ::= [ var-ident-opt :
] [ multiplicity *
] [
{ args } ]
+args ::= (
var-ident-opt { var-ident-opt } :
[!
] type-term )
+args ::= [ !
] type-term
+multiplicity ::= nat-term
+var-ident-opt ::= var-ident | _
+conditional-arg-def ::= var-ident [ .
nat-const ] ?
+result-type ::= boxed-type-ident { subexpr }
+result-type ::= boxed-type-ident <
subexpr { ,
subexpr } >
We shall clarify what all this means.
+A combinator identifier is either an identifier starting with a lowercase Latin letter (lc-ident), or a namespace identifier (also lc-ident) followed by a period and another lc-ident. Therefore, cons
and lists.get
are valid combinator identifiers.
A combinator has a name, also known as a number (not to be confused with the designation) -- a 32-bit number that unambiguously determines it. It is either calculated automatically (see below) or it is explicitly assigned in the declaration. To do this, a hash mark (#
) and exactly 8 hexadecimal digits -- the combinator’s name -- are added to the identifier of the combinator being defined.
A combinator’s declaration begins with its identifier, to which its name (separated by a hash mark) may have been added.
+After the combinator identifier comes the main part of the declaration, which consists of declarations of fields (or variables), including an indication of their types.
+First come declarations of optional fields (of which there may be several or none at all). Then there are the declarations of the required fields (there may not be any of these either).
+Any identifier that begins with an uppercase or lowercase letter and which does not contain references to a namespace can be a field (variable) identifier. Using uc-ident for identifiers of variable types and lc-indent for other variables is good practice.
+Next a combinator declaration contains the equals sign (=
) and the result type (it may be composite or appearing for the first time). The result type may be polymorphic and/or dependent; any fields of the defined constructor’s fields of type Type
or #
may be returned (as subexpressions).
A combinator declaration is terminated with a semicolon.
+In what follows, a constructor’s fields, variables, and arguments mean the same thing.
+These have the form {
field_1 ... field_k :
type-expr }
, where field_i is a variable (field) identifier that is unique within the scope of the combinator declaration, and type-expr is a type shared by all of the fields.
If k>1, this entry is functionally equivalent to {
field_1 :
type-expr }
... {
field_k :
type-expr }
.
All optional fields must be explicitly named (using _
instead of field_i is not allowed).
Moreover, at present the names of all optional fields must share the combinator’s result type (possibly more than once) and themselves be of type #
(i,e., nat
) or Type
. Therefore, if the exact result type is known, it is possible to determine the values of all of the combinator’s implicit parameters (possibly obtaining a contradiction of the form 2=3
in doing so, which means that the combinator is not allowed in the context).
These may have the form (
field_1 ... field_k :
type-expr )
, similar to an optional field declaration, but with parentheses. This entry is equivalent to (
field_1 :
type-expr )
... (
field_k : type-expr )
, where the fields are defined one at a time.
The underscore sign (_
) can be used as names of one or more fields (field_i), indicating that the field is anonymous (the exact name is unimportant).
One field may be declared without outer parentheses, like this: field_id :
type-expr. Here, however, if type-expr is a complex type, parentheses may be necessary around type-expr (this is reflected in BNF).
Furthermore, one anonymous field may be declared using a type-expr entry, functionally equivalent to _
:
type-expr.
Required field declarations follow one after another, separated by spaces (by any number of whitespace symbols, to be more precise).
+The declared field’s type (type-expr) may use the declared combinator’s previously defined variables (fields) as subexpressions (i.e. parameter values). For example:
+nil {X:Type} = List X; +cons {X:Type} hd:X tl:(list X) = List X; +typed_list (X:Type) (l : list X) = TypedList;
+These may only exist among required parameters. They have the form [ field-id :
] [ multiplicity *
] [
args ]
, where args has the same format as the combinator’s declaration of (several) required fields, except that all of the enclosing combinator’s previously declared fields may be used in the argument types.
The name of a field of an enclosing combinator that receives a repetition as a value may be specified (field-id), or bypassed, which is equivalent to using the underscore sign as a field-id.
+The multiplicity field is an expression of the type #
(nat
), which can be a real constant, the name of a preceding field of type #
, or an expression in the form (
c +
v )
, where c is a real constant and v is the name of a field of type #
. The sense of the multiplicity field is to provide the length of the (repetition) vector, each element of which consists of values of the types enumerated in args.
The multiplicity field may be bypassed. In this case, the last preceding parameter of type #
from the enclosing combinator is used (it must be).
Functionally, the repetition field-id :
multiplicity *
[
args ]
is equivalent to the declaration of the single field (
field-id :
%Tuple
%AuxType
multiplicity )
, where aux_type
is an auxiliary type with a new name defined as aux_type *args* = AuxType
. If any of the enclosing type’s fields are used within args, they are added to the auxiliary constructor aux_type
and to its AuxType
result type as the first (optional) parameters.
If args consists of one anonymous field of type some-type, then some-type can be used directly instead of %AuxType
.
If during implementation the repetitions are rewritten as indicated above, it is logical to use instead of aux_type
and AuxType
, some identifiers that contain the name of the outer combinator being defined and the repetition’s index number inside its definition.
Example:
+matrix {m n : #} a : m* [ n* [ double ] ] = Matrix m n;
+is functionally equivalent to
+aux_type {n : #} (_ : %Tuple double n) = AuxType n;
+matrix {m : #} {n : #} (a : %Tuple %(AuxType n) m) = Matrix m n;
+Moreover, the built-in types Tuple
and Vector
could be defined as:
tnil {X : Type} = Tuple X 0;
+tcons {X : Type} {n : #} hd:X tl:%(Tuple X n) = Tuple X (S n);
+vector {X : Type} (n : #) (v : %(Tuple X n)) = Vector X;
+Actually, the following equivalent entry is considered the definition of Vector
(i.e. it is specifically this entry that is used to compute the name of the vector
constructor and its partial applications):
vector {t : Type} # [ t ] = Vector t;
+If we expand it using Tuple
, we obtain the previous definition exactly.
The construction
+args ::= var-ident-opt :
[ conditional-arg-def ] [ !
] type-term
+conditional-arg-def ::= var-ident [ .
nat-const ] ?
permits assigning fields which are only present if the value of a preceding mandatory or optional field of type #
is not null (or if its chosen bit is not zero if the special binary bit-selection operator .
is applied).
+Example:
user {fields:#} id:int first_name:(fields.0?string) last_name:(fields.1?string) friends:(fields.2?%(Vector int)) = User fields;
+get_users req_fields:# ids:%(Vector int) = Vector %(User req_fields)
Main article: TL Language.
+In certain cases, types may depend not only on other types (polymorphism), but also on the parameters of another type (dependent types). The TL language provides very limited support for this functionality: dependence is only allowed on a natural parameter whose type is designated using #
(alias nat
, but this is private -- TL doesn’t currently support this synonym). Values of type # are serialized as 32-bit signed numbers from 0 to 2^31-1.
Suppose we want to use induction to define the types “one integer”, “two integers”, and “three integers”. We could try to define them as follows:
+empty = Empty;
+single x:int = Single;
+pair x:int y:int = Pair;
+triple x:int y:int z:int = Triple;
+quadruple x:int y:int z:int t:int = Quadruple;
+...
+or as:
+empty = Empty;
+single x:int empty = Single;
+pair x:int y:single = Pair;
+triple x:int yz:pair = Triple;
+quadruple x:int yzt:triple = Quadruple;
+or as:
+tnil = Tuple0;
+tcons0 hd:int tl:Tuple0 = Tuple1;
+tcons1 hd:int tl:Tuple1 = Tuple2;
+tcons2 hd:int tl:Tuple2 = Tuple3;
+...
+tcons_n hd:int tl:Tuple_n = Tuple_(n+1)
+The first two variations lead to the same serialization. For example, (2 3 9):%triple
and (2 (3 9)):%triple
serialize as three 32-bit numbers: 2 3 9
. The last variation better emphasizes the inductive version of the definition, but it uses boxed types. This is good from a theoretical perspective, but it leads to “superfluous” constructor names in serialization.
Therefore, we will write %Type-Ident
to indicate the bare type that corresponds to the boxed type Type-Ident
with a single constructor. If this constructor is named constructor
, then according to the definition %Type-Ident
= %constructor
. Now we can write our definition like this:
tnil = Tuple0;
+tcons_n hd:int tl:%Tuple_n = Tuple_(n+1)
+If we now abstract n out of the name of the type name and make it like a parameter for a polymorphic (dependent, to be more exact) type, then something like the following can be written in a suitable functional language:
+NewType Tuple (n : #) :=
+| tnil = Tuple 0
+| tcons n:# hd:int tl:%(Tuple n) = Tuple (S n)
+EndType;
+In the TL language, it looks like this:
+tnil = Tuple 0;
+tcons {n:#} hd:int tl:%(Tuple n) = Tuple (S n);
+The function S : # -> #
and the constant O : #
(it is 0
) are the function for the next natural number (S n = n + 1
) and the constant null. Therefore, the type #
(alias nat
) behaves as if it were defined in TL using the constructors
O = nat;
+S nat = nat;
+or, using syntax more typical of other functional languages,
+NewType nat :=
+| O
+| S nat
+EndType;
+Types of all defined combinators:
+O : #
+S : # -> #
+Tuple : # -> Type
+tnil : Tuple 0
+tcons : forall n : #, int -> Tuple n -> Tuple (S n)
+or
+Tuple : forall n : #, Type;
+tcons : forall n : #, forall hd : int, forall tl : Tuple n, Tuple (S n)
+Note that in this case the constructor tnil
does not depend on the parameter n, while tcons
does.
In an analogous manner, it is possible to define a complete binary tree of height h with strings in the leaf nodes:
+tleaf value:string = BinTree 0;
+tnode {h:#} left:(BinTree h) right:(BinTree h) = BinTree (S h);
+Or a random tree whose leaf nodes are all a distance of h from the root and whose nodes are all labeled with integers:
+hleaf value:int = Tree 0;
+hnode {n:#} left:(Tree n) next:(Tree (S n)) = Tree (S n)
+hnil {n:#} = Tree (S n)
+Another version:
+hleaf' value:int = Tree' 0;
+hnode' {n:#} children:(list (Tree' n)) = Tree' (S n)
+Let us try to define a type Tuple X n
whose values are n-tuples of type X
values. In this way, Tuple
will be simultaneously polymorphic and dependent:
Tuple : Type -> # -> Type;
+In the familiar syntax of functional languages:
+NewType Tuple {X : Type} {n : #} :=
+| vnil : Tuple X 0
+| vcons {n:#} hd:X tl:%(Tuple X n) : Tuple X (S n)
+EndType
+or, in TL syntax,
+vnil {X:Type} = Tuple X 0;
+vcons {X:Type} {n:#} tl:(%Tuple X n) = Tuple X S n
+In the end we obtain terms for the following types:
+vnil : forall X : Type, Tuple X 0
+vcons : forall X : Type, forall n : #, X -> Tuple X n -> Tuple X (S n)
+or
+vnil : forall X : Type, Tuple X 0
+vcons : forall X : Type, forall n : #, forall hd : X, forall tl : Tuple X n, Tuple X (S n)
+The Tuple
we just defined differs from the built-in Vector
type. Specifically, the Vector
type formally depends on a single argument (a type), but our Tuple
depends on two (a type and a number):
Tuple : Type -> # -> Type;
+Vector : Type -> Type;
+The built-in Vector
could be defined in terms of our Tuple
using “summing across all n : #":
vector {X:Type} n:# v:(%Tuple X n) = Vector X;
+Nevertheless, our Tuple
has its advantages. For example, we can define data types such as:
matrix_10x10 a:(%Tuple (%Tuple double 10) 10) = Matrix_10x10;
+In any event, remember that during calculation of the matrix_10x10
combinator’s number, all parentheses must be removed and the CRC32 of the string matrix_10x10 a:%Tuple %Tuple double 10 10 = Matrix_10x10
must be computed.
Moreover, we can define arbitrarily-sized matrices:
+matrix {X:Type} m:# n:# a:(%Tuple (%Tuple X m) n) = Matrix X;
+In this case using vector would result in storing the length of a row (m) in each row, e.g. n times.
+Note that the serializations of values of type %Tuple X n
and vector X
(also known as %vector X
and %Vector X
) nearly match when n > 0: in both cases we obtain a single 32-bit number (equal to n-1 or n depending on the version) followed by the serializations of n objects of type X. (This is slightly untrue: values of type %Tuple X n
can only be serialized if n is a constant or value known from one of the preceding fields of the enclosing entry; but then this n won’t be serialized explicitly anywhere).
In view of the importance of the construction presented above, it is built into the TL language in the following manner. A substructure in the form of [ array-field-name ":" ] [ nat-ident "" ] "[" field-descr ... "]” may be used in the declaration of any combinator, where nat-ident* is the name of any previously encountered field of type # (if it is not explicitly indicated, the most recent is used). In abstract, this substructure is equivalent to:
+aux_type *field-descr* ... = AuxType;
+*current_constructor* ... [ *array-field-name* ":" ] (%Tuple aux_type *nat-ident*)
+For example, 10x10 matrices, vectors, and arbitrary matrices may be defined in the following way:
+matrix {X:Type} m:# n:# a:n*[ m*[ X ] ] = Matrix X;
+matrix_10x10 a:10*[ 10*[ double ]] = Matrix_10x10;
+vector {X:Type} # [ X ] = Vector X;
+We have already encountered the last version as a “definition” of the “built-in type” Vector
.
Of course, several fields, as complex as desired, may be within the repeating part. Furthermore, besides using n as a repeat counter, one may use expressions of the form (n+const) and (const+n), where const is a small nonnegative constant, which are shorthand for S (S ( ... (S n) ... )):
+repeat_np1 n:# a:(S n)*[ key:string value:string ] = Dictionary;
+To calculate the CRC32 these expressions are converted to expressions of the form (const+X)
without internal spaces. Additionally, the *
in this case is not set off by spaces on the left and right.
Serialization of dependent types and polymorphic types is not a fundamental challenge: we have combinators with non-zero arity with Type values. For example, the type Tuple double 10 : Type
serializes to 'Tuple' '%double' 10
. Note that at present in practice there is virtually no need to serialize types, whether dependent or not.
Optional combinator parameters in TL must possess the following properties:
+Optional parameters must be precisely ythe combinator’s first several arguments;
+The value of any optional parameter must be entirely determined by the combinator’s result type.
+For example, in cons {X:Type} hd:X tl:(list X) = list X
the parameter X
may be made optional, because it is located at the very beginning of the argument list and is unambiguously determined by the list X
result type. Similarly, in tcons {X:Type} {n:#} hd:X tl:(%Tuple X n) = Tuple X (S n)
the values of X and n are completely determined based on the Tuple X (S n)
result type, therefore they made be made optional parameters.
It usually makes sense to move all of a constructor’s arguments satisfying the second condition to the beginning of the list, arrange them in the order they appear in the result type’s parameters, and make them optional. Given such an approach, the full version of a constructor is rarely needed -- only when we want to transmit the value of the polymorphic or dependent type as a value of type Object. In all other cases, the type of the expected value from the context is already known, which means that all optional parameters can be recovered during decomposition.
+See also TL Language. +For the syntax of declaring combinators, see in article Formal declaration of TL combinators. +For the syntax of patterns, see in article Formal declaration of TL patterns.
+Comments are the same as in C/C++. They are removed by a lexical parser (for example, being replaced by a single space). Whitespace separates tokens. Except for string constants, tokens cannot contain spaces.
+Character classes:
+lc-letter ::= a
| b
| ... | z
+uc-letter ::= A
| B
| ... | Z
+digit ::= 0
| 1
| ... | 9
+hex-digit ::= digit | a
| b
| c
| d
| e
| f
+underscore ::= _
+letter ::= lc-letter | uc-letter
+ident-char ::= letter | digit | underscore
Simple identifiers and keywords:
+lc-ident ::= lc-letter { ident-char }
+uc-ident ::= uc-letter { ident-char }
+namespace-ident ::= lc-ident
+lc-ident-ns ::= [ namespace-ident .
] lc-ident
+uc-ident-ns ::= [ namespace-ident .
] uc-ident
+lc-ident-full ::= lc-ident-ns [ #
hex-digit *8 ]
Tokens:
+underscore ::= _
+colon ::= :
+semicolon ::= ;
+open-par ::= (
+close-par ::= )
+open-bracket ::= [
+close-bracket ::= ]
+open-brace ::= {
+close-brace ::= }
+triple-minus ::= ---
+nat-const ::= digit { digit }
+lc-ident-full
+lc-ident
+uc-ident-ns
+equals ::= =
+hash ::= #
+question-mark ::= ?
+percent ::= %
+plus ::= +
+langle ::= <
+rangle ::= >
+comma ::= ,
+dot ::= .
+asterisk ::= *
+excl-mark ::= !
+Final-kw ::= Final
+New-kw ::= New
+Empty-kw ::= Empty
Final
is a reserved keyword, e.g. a special token. Words like Type
are not keywords, rather they are identifiers with preset values.
Tokens consisting of one or more constant symbols shall be hereafter denoted using terms in quotation marks (for example, ---
replaces triple-minus).
Syntactically, a TL program consists of a stream of tokens (separated by spaces, which are ignored at this stage). General program structure:
+TL-program ::= constr-declarations { ---
functions
---
fun-declarations | ---
types
---
constr-declarations }
Here the constructor- and function declarations are nearly identical in their syntax (they are both combinators):
+constr-declarations ::= { declaration }
+fun-declarations ::= { declaration }
There are various declarations:
+declaration ::= combinator-decl | partial-app-decl | final-decl
+Before explaining how declarations of combinators, partial applications, and type finalization
are given, we will introduce additional syntactical categories:
The concept of an expression (expr) is important. There are type expressions (type-expr) and numeric expressions (nat-expr). However, they are defined the same way. Their correctness as type- or numeric expressions is checked when the type of the analyzed expression is checked.
+type-expr ::= expr
+nat-expr ::= expr
+expr ::= { subexpr }
+subexpr ::= term | nat-const +
subexpr | subexpr +
nat-const
+term ::= (
expr )
| type-ident | var-ident | nat-const | %
term | type-ident <
expr { ,
expr } >
+type-ident ::= boxed-type-ident | lc-ident-ns | #
+boxed-type-ident ::= uc-ident-ns
+var-ident ::= lc-ident | uc-ident
+type-term ::= term
+nat-term ::= term
Note that writing E = E_1 E_2 ... E_n
in the expression for expr means applying the function E_1 to the argument E_2, applying the result to E_3, etc. Specifically, E_1 E_2 E_3 = (E_1 E_2) E_3
. A solitary #
is included in type-ident, because it is actually the identifier for a built-in type (#
alias nat
).
The expression E<E_1,...,E_n>
is syntactic sugar for (E (E_1) ... (E_n))
, i.e. both expressions are transformed into the same internal representation.
combinator-decl ::= full-combinator-id { opt-args } { args } =
result-type ;
+full-combinator-id ::= lc-ident-full | _
+combinator-id ::= lc-ident-ns | _
+opt-args ::= {
var-ident { var-ident } : [excl-mark] type-expr }
+args ::= var-ident-opt :
[ conditional-def ] [ !
] type-term
+args ::= [ var-ident-opt :
] [ multiplicity *
] [
{ args } ]
+args ::= (
var-ident-opt { var-ident-opt } :
[!
] type-term )
+args ::= [ !
] type-term
+multiplicity ::= nat-term
+var-ident-opt ::= var-ident | _
+conditional-def ::= var-ident [ .
nat-const ] ?
+result-type ::= boxed-type-ident { subexpr }
+result-type ::= boxed-type-ident <
subexpr { ,
subexpr } >
See Formal declaration of TL combinators for a description of what exactly this means. Here we will only note that when declaring the type of a combinator’s next argument, only the names of previously arranged (more to the left) arguments of the same combinator may be used as variables, but when declaring the result type you can use all of its parameters (of type Type
and #
).
Note that the names of combinators declared in this way may be used in TL itself only as the corresponding bare types. The only combinators that appear in declarations are built-in: O : #
and S : # -> #
.
There are also “pseudo-declarations” that are allowed only to declare built-in types (such as int ? = Int;
):
builtin-combinator-decl ::= full-combinator-id ?
=
boxed-type-ident ;
partial-app-decl ::= partial-type-app-decl | partial-comb-app-decl
+partial-type-app-decl ::= boxed-type-ident subexpr { subexpr } ;
| boxed-type-ident <
expr { ,
expr } >
;
+partial-comb-app-decl ::= combinator-id subexpr { subexpr } ;
See Formal declaration of TL patterns.
+final-decl ::= New
boxed-type-ident ;
| Final
boxed-type-ident ;
| Empty
boxed-type-ident ;
This type of declaration means that there must not be any constructor for the indicated type: before the declaration for New
and after the declaration for Final
. The keyword Empty
enables both effects.
Nearly all predefined identifiers may be given using the following schema (usually located in common.tl
):
/////
+//
+// Common Types
+//
+/////
// Built-in types
+int ? = Int;
+long ? = Long;
+double ? = Double;
+string ? = String;
// Boolean emulation
+boolFalse = Bool;
+boolTrue = Bool;
// Boolean for diagonal queries
+boolStat statTrue:int statFalse:int statUnknown:int = BoolStat;
// Vector
+vector {t:Type} # [t] = Vector t;
+tuple {t:Type} {n:#} [t] = Tuple t n;
+vectorTotal {t:Type} total_count:int vector:%(Vector t) = VectorTotal t;
/////
+//
+// Result- (Maybe-) types
+//
+/////
resultFalse {t:Type} = Maybe t;
+resultTrue {t:Type} result:t = Maybe t;
pair {X:Type} {Y:Type} a:X b:Y = Pair X Y;
+map {X:Type} {Y:Type} key:X value:Y = Map X Y;
Empty False;
+true = True;
unit = Unit;
+Predefined identifier Type
: This type signifies the type of all types. It is usually used to specify the types of optional parameters in the constructors of polymorphic types. If strongly desired, it can be used in its own right, but this is very rarely needed in practice.
Identifier #
: This type is used to specify a special type of nonnegative integers in the range from 0 to 2^31-1; its main purpose is the same as that of Type
. There are two built-in constructors: O
: # and S
: # -> # (“null” and “next number”, respectively), which work as if #
was defined using the schema
O = #;
+S # = #;
Identifier Tuple
: Type -> # -> Type denotes a set of the specified number of values of the indicated type. In other words, Tuple X n means “a set of n values of type X".
The typeBool
, with two constructors boolTrue
and boolFalse
, is used to transmit Boolean values.
The constructor-less type False
may be used instead of undeclared or invalid types in the construction of a TL schema, because any attempt to (de)serialize a value of type False
will produce an error. Usage Example:
user {flags:#} id:flags.0?string first_name:flags.1?string last_name:flags.2?string reserved3:flags.3?False reserved4:flags.4?False = User flags;
+user_present {flags:#} info:%(User flags) = UserInfo flags;
+user_absent {flags:#} = UserInfo flags;
+getUser flags:# id:int = !UserInfo flags;
In the future, bits 3 and 4 in the flags
field may be used to transmit new fields after changing the names and types of the reserved3
and reserved4
fields. This will change the user
constructor’s number, but this isn’t too important, since the User flags
type is only used as a bare type. Transmitting bits 3 or 4 in the flags
field in a getUser
query before these fields have actually been defined will lead to an error in the (de)serialization of the request.
True
with a single null constructor true
plays a role similar to the void type in C/C++. It is especially useful as a bare type %True
, alias true
, because its serialization has zero length. For example, the first_name:flags.1?string
constructor used above is in fact shorthand for (the as-yet unsupported) alternative-type general constructor first_name:(flags.1?string:true)
.When directly used in a conditional field it may simply indicate the presence (absence) of a certain parameter with void type. +If the conditional field exists, the associated parameter will not be populated; the conditional field simply exists and the existance value can be used to perform certain operations, example:
+user {flags:#} id:flags.0?string first_name:flags.1?string last_name:flags.2?string bot:flags.3?true reserved4:flags.4?False = User flags;
+If bit 3 of the flags
parameter isn't set, the user is a normal user.
+If bit 3 of the flags
parameter is set, this indicates that the specified user is a bot: however, during deserialization, the bot
parameter must not be assigned any value, since true
is actually a void
type.
Unit
with a single null constructor Unit
is similar to the previous type.TL Language makes it possible to declare a combinator’s first few parameters optional. Subsequently, they are almost never required to be explicitly stated. This is directly related to another important property of TL: polymorphism.
+All optional parameters are typically required to be part of the result type (at least once).
+A (sub)expression may be serialized/deserialized in one of two ways:
+The result type is known (for example, we’re parsing the response to a previously sent RPC query and therefore know the value of some type is expected). In this case, the result type may be used to determine the values of the combinator’s implicit parameters.
+The result type is not known. It is determined as a result of (de)serialization (for example, we are serializing an RPC query). In this case, it is necessary to explicitly specify (and serialize) all of the combinator’s optional parameters by using the full version of the combinator.
+We will assume that the functional combinator only differs from the constructor in that before its result type the !
modifier is implicitly added, and the (remote or local) computation of a functional expression may be presented as the execution of some polymorphic function eval : !X -> X
.
Moreover, the unknown result type is obviously usually (but not always) bound to the serialization of an expression whose type has been modified by !
.
We can formulate the following rules. Let there be some constructor
+C {a1:T1} ... {am:Tm} b1:U1 ... bn:Un = T;
+Some of its arguments or its result may be marked with the !
modifier (We consider a functional combinator to be a constructor whose result type has been modified by an implicit !
).
The following conditions must hold:
+Each type T1, ..., Tm, U1, ..., Un, and T may depend on parameters of type Type
or #
, which have been declared to the left of the use of the given type.
The types of implicit parameters T1, ..., Tm may not be modified by !
.
Only implicit parameters of type Type
or #
are allowed. In other words, Ti is either a Type
or a #
. (Unlike the previous rule, this rule may be relaxed in the future.)
Each implicit parameter a1, ..., am must be used at least once -- either within one of the Ui types that have a !
, or in the result type T, if it does not have an explicit or implicit !
.
If an implicit parameter ai is not used in the result type, or if the result type has a !
, then its first (leftmost) usage must be within a type Uj that is modified by !
.
The idea is this: we assume that during (de)serialization of a value of a type modified by !
that we do not know this type in advance and we will find out what it is only based on the result of the (de)serialization; Conversely, we assume that during (de)serialization of a value of a type that is not modified by !
that we know this type in advance. In this case, complying with the rules stated above lets us always compute the values of all implicit parameters -- either from the (previously known) result type or from the type of one of the bj:!Uj
parameters (which was obtained during the serialization process).
In fact, !
denotes the direction in which type information is flowing. By default, the result type is the source of information about types (and their parameters), while argument types are the recipients of that information. The use of !
reverses the direction of information flow, making the result type the recipient and the argument type the source of type information.
See also Binary serialization and abstract TL types and Polymorphism in TL.
partial-app-decl ::= partial-type-app-decl | partial-comb-app-decl
+partial-type-app-decl ::= boxed-type-ident subexpr { subexpr } ;
| boxed-type-ident <
expr { ,
expr } >
;
+partial-comb-app-decl ::= combinator-id subexpr { subexpr } ;
According to the original design, templates must be used to replace concrete values in the first few or all of the optional arguments of a constructor or polymorphic type. For example, if Tuple int 10
is used frequently, you can declare a template for it, which will cause the appropriate constructors to be generated automatically. When using such a constructor there may be nowhere to pass an int
type or the tuple size 10
. Similarly, it was originally planned to declared templates for Vector int
, Vector string
, etc. in order to generate constructors for each vector type being used type. These constructors would make it possible during deserialization to determine what kind of array is being transmitted.
Templates are not used now. Instead, the same universal constructors (for example, vector {t:Type} [t] = Vector t
) are used with the values of the optional parameters being inferred from the type of the result (if we already know from the schema that in this location there must be a Vector int
during deserialization, we understand that we will see the universal vector
constructor in which t is equal to int
).
This approach is better in that it is not necessary to define Vector SomeType
templates in advance for all possible types in order to generate their own constructors for each of these cases. Nevertheless, there is a drawback. If someone wants to transmit the serialization of a value of the clothed type Vector int
as a serialization of a value of type Object
, a problem arises during serialization: after seeing the universal vector
constructor and then reading the vector length, we cannot determine what type of values should be expected next.
In theory, this problem can be solved by using the full form of the constructor (@vector
) corresponding to vector
(it is automatically defined and is different in that all of the optional parameters become required), or by defining
object X:Type X = TypedObject
+and passing the object type explicitly. Type serialization is required in both cases.
It should be noted that in the TL schema of the overwhelming majority of API calls the use of polymorphic types is restricted to the Vector type. Nevertheless, having a view of the big picture is still helpful.
+For example, let us consider the IntList, which is defined as follows:
+int_cons hd:int tl:IntList = IntList;
+int_nil = IntList;
+The “int_cons” and “int_nil” constructors as well as the “IntList” type itself are expressions of the following types (writing A : X means that A is an expression of type X):
+IntList : Type;
+int_cons : int -> IntList -> IntList;
+int_nil : IntList;
+The keyword Type is used to denote the type of all types. Note that Type is not Object (Object is the type of all terms). +Here is alternative syntax that could be used in some other functional programming language (but not in TL):
+NewType IntList :=
+| int_cons hd:int tl:IntList
+| int_nil
+EndType
+TL supports the following version (curly brackets indicate optional fields, see below):
+cons {X:Type} hd:X tl:(List X) = List X;
+nil {X:Type} = List X
+Here is an alternative formulation in other functional languages with dependent types:
+NewType List {X:Type} :=
+| cons {X:Type} hd:X tl:(List X)
+| nil {X:Type}
+EndType
+In any event, these variations are equivalent to one another from the point of view of the formal theory of types and lead to the definition of the following terms:
+List : Type -> Type;
+cons : forall (X:Type), X -> List X -> List X;
+nil : forall (X:Type), X -> List X;
+In each case, remember that writing “A -> B” is shorthand for “forall (x : A), B” for any variable x not entering into A and B. For example, the “cons” type could be written as follows:
+cons : forall (X:Type), forall (hd : X), forall (tl : List X), List X
+or more compactly:
+cons : forall (X : Type) (hd : X) (tl : List X), List X
+See Calculus of constructions. Examples of functional languages with dependent types, which support similar constructions are Coq and Agda.
+In this case, the entry after a universal quantifier proves to be more content-related than that after an arrow, because the name of a variable bound by the quantifier is used to transmit the name of the corresponding field in the constructor, even if this variable is not used anywhere as it pertains to the expression under the quantifier. Structurally, all of these entries of the “cons” type are equivalent.
+As we can see, to serialize a value of type List X, which has been obtained by applying the combinator “cons X:Type hd:X tl:(List X) = List X”, we need to:
+In the first step, the natural question is which string exactly will be used to calculate the CRC32. It is proposed to take "cons X:Type hd:X tl:List X = List X
” without the terminating semicolon and without any parentheses (closed type expressions are unambiguously reconstructed based on their construction’s prefix).
In the last step, we recursively resolve the very same problem of serializing a value of type List X; we will consider it resolved based on the assumption of induction in the construction of the value being serialized. We will similarly consider the third step understandable (induction in the construction of the value being serialized).
+We still need to describe how to transmit (serialize) types, e.g. values of type Type. Types in TL schemas currently appear only as constructors’ optional parameters and are therefore never serialized explicitly. Rather, their values are inferred from the previously known type of the value being serialized.
+For completeness we will describe how it would be possible to serialize types (values of type Type). However, keep in mind that for now this information is not useful. See Type serialization.
+It was stated above that any subset of (the first few) parameters of any constructor can be identified as optional (by enclosing their declarations in curly brackets), but this is not actually entirely accurate. First, these optional parameters can only be of type Type
or #
(natural numbers). Second, optional parameters must share the return value’s type, otherwise their value cannot be determined.
Note that @'''constr-id''' means the constructor’s “full form” (in which all optional parameters become required), while '''constr-id'’ denotes its abbreviated form (without the optional arguments). If there are no optional arguments, then these two forms are the same. Constructors’ full forms are never used at present.
+There is a small problem: if we want to serialize the value of the bare type ‘%pair string int’ or ‘%pair string Y’ (which in TL is usually denoted simply as “pair”, though the form “%Pair” is preferable), we cannot simultaneously use both the full constructor @pair and the partial pair, because the constructor’s name will not be serialized. Therefore, we must differentiate the bare types %@pair (type X, type Y, value x:X, and value y:Y are serialized) and %pair (only x:X and y:Y are serialized; types X and Y are known from the context). In practice, we nearly almost always need the bare type %pair, and this is precisely what “pair” means in the type’s context in TL. Therefore,
+record name:string map:(List (pair int string)) = Record;
+will be serialized approximately like we want it to be (the serialization of list elements will consist of the serialization of int and the serialization of string, without any additional headers, types, or combinator names).
+Incidentally, when calculating the “record” combinator’s name 'record' in the example given above, the CRC32 of record name:string map:List pair int string = Record
will be computed.
Also note that a more precise description of this type would be
+record name:string map:(List %(Pair int string)) = Record
If necessary, a TL schema can be serialized in binary form. Here, this serialization format is defined by a TL schema (usually stored in the file tl.tl
). This can be useful, for example, to make it possible to write a parser one time for converting a TL schema from text form (stored in the file something.tl
) to binary form (stored in the file something.tlo
). All other programs (for example, auto-generators of TL-(de)serializers for various programming languages) only need to know how to read .tlo
files, which only requires generating an automatic deserializer according to the schema presented below.
First, a fragment of the file common.tl
with certain required built-in types:
/////
+//
+// Common Types (source file common.tl, only necessary definitions included)
+//
+/////
+
+// Built-in types
+int ? = Int;
+long ? = Long;
+double ? = Double;
+string ? = String;
+
+// Boolean emulation
+boolFalse = Bool;
+boolTrue = Bool;
+
+// Vector
+vector {t:Type} # [t] = Vector t;
+tuple {t:Type} {n:#} [t] = Tuple t n;
+vectorTotal {t:Type} total_count:int vector:%(Vector t) = VectorTotal t;
+
+Empty False;
+true = True;
+Next, properly, comes tl.tl
itself. Note that the declaration for a fairly complex data type required only twenty lines in TL. This demonstrates the expressiveness and compactness of the TL language.
/////
+//
+// Serialized binary TL-schema in TL format, source file tl.tl
+//
+/////
+tls.schema_v2 version:int date:int types_num:# types:types_num*[tls.Type]
+
+ constructor_num:# constructors:constructor_num*[tls.Combinator]
+ functions_num:# functions:functions_num*[tls.Combinator] = tls.Schema;
+tls.type name:int id:string constructors_num:int flags:int arity:int params_type:long = tls.Type;
+
+tls.combinator name:int id:string type_name:int left:tls.CombinatorLeft right:tls.CombinatorRight = tls.Combinator;
+tls.combinatorLeftBuiltin = tls.CombinatorLeft;
+tls.combinatorLeft args_num:# args:args_num*[tls.Arg] = tls.CombinatorLeft;
+tls.combinatorRight value:tls.TypeExpr = tls.CombinatorRight;
+
+tls.arg id:string flags:# var_num:flags.1?int exist_var_num:flags.2?int exist_var_bit:flags.2?int type:tls.TypeExpr = tls.Arg;
+
+tls.exprType _:tls.TypeExpr = tls.Expr;
+
+tls.exprNat _:tls.NatExpr = tls.Expr;
+tls.natConst value:int = tls.NatExpr;
+
+tls.natVar dif:int var_num:int = tls.NatExpr;
+tls.typeVar var_num:int flags:int = tls.TypeExpr;
+
+tls.array multiplicity:tls.NatExpr args_num:# args:args_num*[tls.Arg] = tls.TypeExpr;
+tls.typeExpr name:int flags:int children_num:# children:children_num*[tls.Expr] = tls.TypeExpr;
+Schema serialization (version 2) always begins with the index number of the tls.schema_v2
constructor for tls.Schema
.
+Because the CRC32 of the string
tls.schema_v2 version:int date:int types_num:# types:types_num*[ tls.Type ] constructor_num:# constructors:constructor_num*[ tls.Combinator ] functions_num:# functions:functions_num*[ tls.Combinator ] = tls.Schema
+is 0x3a2f9be2, this constant is in fact the magic number for tlo files in the current version’s format.
+If the format is extended in the future (for example, if TL’s additional features are supported), then a tls.schema_v3
constructor with a different number will appear.
If one adds declarations for the used built-in types (like int ? = Int;
) from the file common.tl
before tl.tl
and serialize the resulting schema, the following binary data is obtained (tl.tlo
):
0000: 3a2f9be2 00000000 51fec698 00000015 12eb4386 70659eff 00002301 00000000
+0020: 00000000 00000000 00000000 00000000 12eb4386 250be282 6f6f4204 0000006c
+0040: 00000002 02000010 00000000 00000000 00000000 12eb4386 2210c154 756f4406
+0060: 00656c62 00000001 02000000 00000000 00000000 00000000 12eb4386 00000000
+0080: 6c614605 00006573 00000000 00000401 00000000 00000000 00000000 12eb4386
+00a0: a8509bda 746e4903 00000001 02000001 00000000 00000000 00000000 12eb4386
+00c0: 22076cba 6e6f4c04 00000067 00000001 02000001 00000000 00000000 00000000
+00e0: 12eb4386 b5286e24 72745306 00676e69 00000001 02000001 00000000 00000000
+0100: 00000000 12eb4386 3fedd339 75725404 00000065 00000001 02000000 00000000
+0120: 00000000 00000000 12eb4386 9770768a 70755405 0000656c 00000001 02000000
+0140: 00000002 00000002 00000000 12eb4386 2cecf817 70795404 00000065 00000000
+0160: 00000000 00000000 00000000 00000000 12eb4386 1cb5c415 63655606 00726f74
+0180: 00000001 02000008 00000001 00000000 00000000 12eb4386 10133f47 6365560b
+01a0: 54726f74 6c61746f 00000001 02000000 00000001 00000000 00000000 12eb4386
+01c0: 29dfe61b 736c7407 6772412e 00000001 02000000 00000000 00000000 00000000
+01e0: 12eb4386 5c0a1ed5 736c740e 6d6f432e 616e6962 00726f74 00000001 02000000
+0200: 00000000 00000000 00000000 12eb4386 8133d9ba 736c7412 6d6f432e 616e6962
+0220: 4c726f74 00746665 00000002 02000010 00000000 00000000 00000000 12eb4386
+0240: 2c064372 736c7413 6d6f432e 616e6962 52726f74 74686769 00000001 02000000
+0260: 00000000 00000000 00000000 12eb4386 307d41a0 736c7408 7078452e 00000072
+0280: 00000002 02000010 00000000 00000000 00000000 12eb4386 c2635441 736c740b
+02a0: 74614e2e 72707845 00000002 02000010 00000000 00000000 00000000 12eb4386
+02c0: 3a2f9be2 736c740a 6863532e 00616d65 00000001 02000000 00000000 00000000
+02e0: 00000000 12eb4386 12eb4386 736c7408 7079542e 00000065 00000001 02000000
+0300: 00000000 00000000 00000000 12eb4386 193fd378 736c740c 7079542e 70784565
+0320: 00000072 00000003 02000010 00000000 00000000 00000000 00000018 5c0a1ed5
+0340: bc799737 6f6f6209 6c61466c 00006573 250be282 4c12c6d9 00000000 2c064372
+0360: c1863d08 250be282 00000000 00000000 5c0a1ed5 997275b5 6f6f6208 7572546c
+0380: 00000065 250be282 4c12c6d9 00000000 2c064372 c1863d08 250be282 00000000
+03a0: 00000000 5c0a1ed5 2210c154 756f6406 00656c62 2210c154 cd211f63 2c064372
+03c0: c1863d08 2210c154 00000000 00000000 5c0a1ed5 a8509bda 746e6903 a8509bda
+03e0: cd211f63 2c064372 c1863d08 a8509bda 00000000 00000000 5c0a1ed5 22076cba
+0400: 6e6f6c04 00000067 22076cba cd211f63 2c064372 c1863d08 22076cba 00000000
+0420: 00000000 5c0a1ed5 b5286e24 72747306 00676e69 b5286e24 cd211f63 2c064372
+0440: c1863d08 b5286e24 00000000 00000000 5c0a1ed5 3fedd339 75727404 00000065
+0460: 3fedd339 4c12c6d9 00000000 2c064372 c1863d08 3fedd339 00000000 00000000
+0480: 5c0a1ed5 9770768a 70757405 0000656c 9770768a 4c12c6d9 00000003 29dfe61b
+04a0: 00007401 00020005 00000000 c1863d08 2cecf817 00000000 00000000 29dfe61b
+04c0: 00006e01 00020005 00000001 c1863d08 70659eff 00000000 00000000 29dfe61b
+04e0: 00000000 00000000 d9fb20de 4e8a14f0 00000000 00000001 00000001 29dfe61b
+0500: 00000000 00000000 0142ceae 00000000 00000000 2c064372 c1863d08 9770768a
+0520: 00000000 00000002 ecc9da78 0142ceae 00000000 00000000 dcb49bd8 4e8a14f0
+0540: 00000000 00000001 5c0a1ed5 1cb5c415 63657606 00726f74 1cb5c415 4c12c6d9
+0560: 00000003 29dfe61b 00007401 00020005 00000000 c1863d08 2cecf817 00000000
+0580: 00000000 29dfe61b 00000000 00000004 00000001 c1863d08 70659eff 00000000
+05a0: 00000000 29dfe61b 00000000 00000000 d9fb20de 4e8a14f0 00000000 00000001
+05c0: 00000001 29dfe61b 00000000 00000000 0142ceae 00000000 00000000 2c064372
+05e0: c1863d08 1cb5c415 00000000 00000001 ecc9da78 0142ceae 00000000 00000000
+0600: 5c0a1ed5 10133f47 6365760b 54726f74 6c61746f 10133f47 4c12c6d9 00000003
+0620: 29dfe61b 00007401 00020005 00000000 c1863d08 2cecf817 00000000 00000000
+0640: 29dfe61b 746f740b 635f6c61 746e756f 00000000 c1863d08 a8509bda 00000001
+0660: 00000000 29dfe61b 63657606 00726f74 00000000 c1863d08 1cb5c415 00000001
+0680: 00000001 ecc9da78 0142ceae 00000000 00000000 2c064372 c1863d08 10133f47
+06a0: 00000000 00000001 ecc9da78 0142ceae 00000000 00000000 5c0a1ed5 29dfe61b
+06c0: 736c7407 6772612e 29dfe61b 4c12c6d9 00000006 29dfe61b 00646902 00000000
+06e0: c1863d08 b5286e24 00000001 00000000 29dfe61b 616c6605 00007367 00000004
+0700: 00000000 c1863d08 70659eff 00000000 00000000 29dfe61b 72617607 6d756e5f
+0720: 00000002 00000000 00000001 c1863d08 a8509bda 00000001 00000000 29dfe61b
+0740: 6978650d 765f7473 6e5f7261 00006d75 00000002 00000000 00000002 c1863d08
+0760: a8509bda 00000001 00000000 29dfe61b 6978650d 765f7473 625f7261 00007469
+0780: 00000002 00000000 00000002 c1863d08 a8509bda 00000001 00000000 29dfe61b
+07a0: 70797404 00000065 00000000 c1863d08 193fd378 00000000 00000000 2c064372
+07c0: c1863d08 29dfe61b 00000000 00000000 5c0a1ed5 5c0a1ed5 736c740e 6d6f632e
+07e0: 616e6962 00726f74 5c0a1ed5 4c12c6d9 00000005 29dfe61b 6d616e04 00000065
+0800: 00000000 c1863d08 a8509bda 00000001 00000000 29dfe61b 00646902 00000000
+0820: c1863d08 b5286e24 00000001 00000000 29dfe61b 70797409 616e5f65 0000656d
+0840: 00000000 c1863d08 a8509bda 00000001 00000000 29dfe61b 66656c04 00000074
+0860: 00000000 c1863d08 8133d9ba 00000000 00000000 29dfe61b 67697205 00007468
+0880: 00000000 c1863d08 2c064372 00000000 00000000 2c064372 c1863d08 5c0a1ed5
+08a0: 00000000 00000000 5c0a1ed5 cd211f63 736c7419 6d6f632e 616e6962 4c726f74
+08c0: 42746665 746c6975 00006e69 8133d9ba 4c12c6d9 00000000 2c064372 c1863d08
+08e0: 8133d9ba 00000000 00000000 5c0a1ed5 4c12c6d9 736c7412 6d6f632e 616e6962
+0900: 4c726f74 00746665 8133d9ba 4c12c6d9 00000002 29dfe61b 67726108 756e5f73
+0920: 0000006d 00000004 00000000 c1863d08 70659eff 00000000 00000000 29dfe61b
+0940: 67726104 00000073 00000000 d9fb20de 4e8a14f0 00000000 00000000 00000001
+0960: 29dfe61b 00000000 00000000 c1863d08 29dfe61b 00000000 00000000 2c064372
+0980: c1863d08 8133d9ba 00000000 00000000 5c0a1ed5 2c064372 736c7413 6d6f632e
+09a0: 616e6962 52726f74 74686769 2c064372 4c12c6d9 00000001 29dfe61b 6c617605
+09c0: 00006575 00000000 c1863d08 193fd378 00000000 00000000 2c064372 c1863d08
+09e0: 2c064372 00000000 00000000 5c0a1ed5 ecc9da78 736c740c 7078652e 70795472
+0a00: 00000065 307d41a0 4c12c6d9 00000001 29dfe61b 00000000 00000000 c1863d08
+0a20: 193fd378 00000000 00000000 2c064372 c1863d08 307d41a0 00000000 00000000
+0a40: 5c0a1ed5 dcb49bd8 736c740b 7078652e 74614e72 307d41a0 4c12c6d9 00000001
+0a60: 29dfe61b 00000000 00000000 c1863d08 c2635441 00000000 00000000 2c064372
+0a80: c1863d08 307d41a0 00000000 00000000 5c0a1ed5 8ce940b1 736c740c 74616e2e
+0aa0: 736e6f43 00000074 c2635441 4c12c6d9 00000001 29dfe61b 6c617605 00006575
+0ac0: 00000000 c1863d08 a8509bda 00000001 00000000 2c064372 c1863d08 c2635441
+0ae0: 00000000 00000000 5c0a1ed5 4e8a14f0 736c740a 74616e2e 00726156 c2635441
+0b00: 4c12c6d9 00000002 29dfe61b 66696403 00000000 c1863d08 a8509bda 00000001
+0b20: 00000000 29dfe61b 72617607 6d756e5f 00000000 c1863d08 a8509bda 00000001
+0b40: 00000000 2c064372 c1863d08 c2635441 00000000 00000000 5c0a1ed5 3a2f9be2
+0b60: 736c740d 6863732e 5f616d65 00003276 3a2f9be2 4c12c6d9 00000008 29dfe61b
+0b80: 72657607 6e6f6973 00000000 c1863d08 a8509bda 00000001 00000000 29dfe61b
+0ba0: 74616404 00000065 00000000 c1863d08 a8509bda 00000001 00000000 29dfe61b
+0bc0: 70797409 6e5f7365 00006d75 00000004 00000000 c1863d08 70659eff 00000000
+0be0: 00000000 29dfe61b 70797405 00007365 00000000 d9fb20de 4e8a14f0 00000000
+0c00: 00000000 00000001 29dfe61b 00000000 00000000 c1863d08 12eb4386 00000000
+0c20: 00000000 29dfe61b 6e6f630f 75727473 726f7463 6d756e5f 00000004 00000001
+0c40: c1863d08 70659eff 00000000 00000000 29dfe61b 6e6f630c 75727473 726f7463
+0c60: 00000073 00000000 d9fb20de 4e8a14f0 00000000 00000001 00000001 29dfe61b
+0c80: 00000000 00000000 c1863d08 5c0a1ed5 00000000 00000000 29dfe61b 6e75660d
+0ca0: 6f697463 6e5f736e 00006d75 00000004 00000002 c1863d08 70659eff 00000000
+0cc0: 00000000 29dfe61b 6e756609 6f697463 0000736e 00000000 d9fb20de 4e8a14f0
+0ce0: 00000000 00000002 00000001 29dfe61b 00000000 00000000 c1863d08 5c0a1ed5
+0d00: 00000000 00000000 2c064372 c1863d08 3a2f9be2 00000000 00000000 5c0a1ed5
+0d20: 12eb4386 736c7408 7079742e 00000065 12eb4386 4c12c6d9 00000006 29dfe61b
+0d40: 6d616e04 00000065 00000000 c1863d08 a8509bda 00000001 00000000 29dfe61b
+0d60: 00646902 00000000 c1863d08 b5286e24 00000001 00000000 29dfe61b 6e6f6310
+0d80: 75727473 726f7463 756e5f73 0000006d 00000000 c1863d08 a8509bda 00000001
+0da0: 00000000 29dfe61b 616c6605 00007367 00000000 c1863d08 a8509bda 00000001
+0dc0: 00000000 29dfe61b 69726105 00007974 00000000 c1863d08 a8509bda 00000001
+0de0: 00000000 29dfe61b 7261700b 5f736d61 65707974 00000000 c1863d08 22076cba
+0e00: 00000001 00000000 2c064372 c1863d08 12eb4386 00000000 00000000 5c0a1ed5
+0e20: 0142ceae 736c740b 7079742e 72615665 193fd378 4c12c6d9 00000002 29dfe61b
+0e40: 72617607 6d756e5f 00000000 c1863d08 a8509bda 00000001 00000000 29dfe61b
+0e60: 616c6605 00007367 00000000 c1863d08 a8509bda 00000001 00000000 2c064372
+0e80: c1863d08 193fd378 00000000 00000000 5c0a1ed5 d9fb20de 736c7409 7272612e
+0ea0: 00007961 193fd378 4c12c6d9 00000003 29dfe61b 6c756d0c 6c706974 74696369
+0ec0: 00000079 00000000 c1863d08 c2635441 00000000 00000000 29dfe61b 67726108
+0ee0: 756e5f73 0000006d 00000004 00000000 c1863d08 70659eff 00000000 00000000
+0f00: 29dfe61b 67726104 00000073 00000000 d9fb20de 4e8a14f0 00000000 00000000
+0f20: 00000001 29dfe61b 00000000 00000000 c1863d08 29dfe61b 00000000 00000000
+0f40: 2c064372 c1863d08 193fd378 00000000 00000000 5c0a1ed5 c1863d08 736c740c
+0f60: 7079742e 70784565 00000072 193fd378 4c12c6d9 00000004 29dfe61b 6d616e04
+0f80: 00000065 00000000 c1863d08 a8509bda 00000001 00000000 29dfe61b 616c6605
+0fa0: 00007367 00000000 c1863d08 a8509bda 00000001 00000000 29dfe61b 6968630c
+0fc0: 6572646c 756e5f6e 0000006d 00000004 00000000 c1863d08 70659eff 00000000
+0fe0: 00000000 29dfe61b 69686308 6572646c 0000006e 00000000 d9fb20de 4e8a14f0
+1000: 00000000 00000000 00000001 29dfe61b 00000000 00000000 c1863d08 307d41a0
+1020: 00000000 00000000 2c064372 c1863d08 193fd378 00000000 00000000 00000000
See Polymorphism in TL and TL Language.
+It remains to describe how types, e.g. values of type Type, are transmitted (serialized). In general, there is nothing unexpected going on here: we have type constructors of various arities (for example, List is an arity-1 constructor, but IntList is a 0-arity constructor); and if we know that a 32-bit “name” is assigned to each type constructor, there are no further questions -- values of type Type are serialized exactly like values of any other recursive type with a defined set of constructors of differing arity.
+How can a 32-bit “name” be assigned to a type (a type constructor, to be more exact) such as List or IntList? +It is proposed to use the sum of the names of all of its constructors, plus the CRC32 of the string with the designation of the type's name and all of its parameters such as “IntList = Type” or “List X:Type = Type”. This way, the List constructor’s “name” is the sum of the CRC32s of the three strings "List X:Type = Type", "cons X:Type hd:X tl:List X = List X", and "nil X:Type = List X". +For “bare” types (which, formally speaking, are subtypes of the corresponding “boxed” type), the situation is somewhat more complicated; the logical negation of the corresponding constructor’s name is used. For built-in bareand boxed types (for example, int and Int), a pseudo-declaration is used (for example, int ? = Int").
+!
modifier has not been explained.*TL (Type Language) serves to describe the used system of types, constructors, and existing functions. In fact, the combinator description format presented in Binary Data Serialization is used.
+See also:
+ +Advanced topics:
+A TL program usually consists of two sections separated by keyword ---functions---
. The first section consists of declarations of built-in types and aggregate types (i.e. their constructors). The second section consists of the declared functions, i.e. functional combinators.
Actually, both the first and second sections consist of combinator declarations, each of which ends with a semicolon. However, the first section contains only constructors, while the second section only involves functions. Each combinator is declared using a “combinator declaration” in the format explained above. However, the combinator number and field names may be explicitly assigned.
+If additional type declarations are required after functions have been declared, the keyword (section divider) ---types---
is used. Furthermore, a functional combinator may be declared in the type section if its result type begins with an exclamation point (in fact, when the function section is interpreted, this exclamation point is added automatically).
To explicitly define 32-bit names of combinators, a hash mark (#) is added immediately after the combinator’s name, followed by 8 hexadecimal digits.
+Composite constructions like <namespace_identifier>.<constructor_identifier>
and <namespace_identifier>.<Type_identifier>
can be used as constructor- or type identifiers. The portion of the identifier to the left of the period is called the namespace. Moreover, the rule about a first uppercase letter in type identifiers and lowercase letter in constructor identifiers applies to the part of the construction after the period. For example, auth.Message
would be a type, while auth.std_message
would be a constructor.
Namespaces do not require a special declaration.
+Comments are the same as in C++.
+// built-in types
+int#a8509bda ? = Int;
+long ? = Long;
+double ? = Double;
+string ? = String;
+null = Null;
+
+vector {t:Type} # [ t ] = Vector t;
+coupleInt {alpha:Type} int alpha = CoupleInt<alpha>;
+coupleStr {gamma:Type} string gamma = CoupleStr gamma;
+/* The name of the type variable is irrelevant: "gamma" could be replaced with "alpha";
+ However, the combinator number will depend on the specific choice. */
+
+intHash {alpha:Type} vector<coupleInt<alpha>> = IntHash<alpha>;
+strHash {alpha:Type} (vector (coupleStr alpha)) = StrHash alpha;
+intSortedHash {alpha:Type} intHash<alpha> = IntSortedHash<alpha>;
+strSortedHash {alpha:Type} (strHash alpha) = StrSortedHash alpha;
+
+// custom types
+pair x:Object y:Object = Pair;
+triple x:Object y:Object z:Object = Triple;
+
+user#d23c81a3 id:int first_name:string last_name:string = User;
+no_user#c67599d1 id:int = User;
+group id:int title:string last_name:string = Group;
+no_group = Group;
+
+---functions---
+
+// Maybe some built-in arithmetic functions; inverse quotes make "identifiers" out of arbitrary non-alphanumeric strings
+`+` Int Int = Int;
+`-` Int Int = Int;
+`+` Double Double = Double;
+// ...
+
+// API functions (aka RPC functions)
+getUser#b0f732d5 int = User;
+getUsers#2d84d5f5 (Vector int) = Vector User;
+In this case, the user
constructor has been explicitly assigned a number (0xd23c81a3); In fact, this was not necessary, since this value is the CRC32 of the string "user id:int first_name:string last_name:string = User"
, which would have been used by default.
Special constructors are not required for Vector int, Vector User, Vector Object, etc. -- the same universal constructor can be used everywhere:
+vector#1cb5c415 {t:Type} # [ t ] = Vector t;
+Note that when the getUsers (Vector int) = Vector User;
constructor number is calculated, the CRC32 of the string "getUsers Vector int = Vector User” is computed (from which all parentheses have been removed).
Notation T0<T1,T2,...,Tn>
is syntactic sugar for (T0 (T1) (T2) ... (Tn))
. For example, Vector<User>
and (Vector User)
are entirely interchangeable.
Suppose we want to call getUsers([2,3,4])
. This query will be serialized into a sequence of 32-bit integers as follows:
0x2d84d5f5 0x1cb5c415 0x3 0x2 0x3 0x4
+Please note that TL serialization yields sequences of 32-bit integers. When it has to be embedded into a byte stream, for example a network packet, each 32-bit integer is represented by four bytes in little-endian order. In this way the above query corresponds to the following byte stream:
+F5 D5 84 2D 15 C4 B5 1C 03 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
+The response might look something like this:
+0x1cb5c415 0x3 0xd23c81a3 0x2 0x74655005 0x00007265 0x72615006 0x72656b 0xc67599d1 0x3 0xd23c81a3 0x4 0x686f4a04 0x6e 0x656f4403
+This roughly corresponds to
+[{"id":2,"first_name":"Peter", "last_name":"Parker"},{},{"id":4,"first_name":"John","last_name":"Doe"}]
+Note that in both cases the same universal constructor vector#1cb5c415
is used: in the request to serialize the value of type Vector int
, and in the serialization of the value of type Vector User
in the response. There is no ambiguity because in both cases the type of the value being (de)serialized is known before its (de)serialization begins. For example, after receiving the query, the server sees that the first part is 0x2d84d5f5
, which corresponds to the combinator getUsers#2d84d5f5 (Vector int) = Vector User
. Thus, it is understood that what follows will be a value of type Vector int
. After receiving the response to this query, the client knows that it must receive a value of type Vector User
and it deserializes the response accordingly.
The query format is described using Binary Data Serialization and the TL Language. All large numbers are transmitted as strings containing the required sequence of bytes in big endian order. Hash functions, such as SHA1, return strings (of 20 bytes) which can also be interpreted as big endian numbers. Small numbers (int
, long
, int128
, int256
) are normally little endian; however, if they are part of SHA1, the bytes are not rearranged. This way, if long
x
is the 64 lower-order bits of SHA1 of string s
, then the final 8 bytes of 20-byte string SHA1(s)
are taken and interpreted as a 64-bit integer.
Prior to sending off unencrypted messages (required in this instance to generate an authorization key), the client must undergo (p,q) authorization as follows.
+Client sends query to server
+req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;
+or (deprecated)
+req_pq#60469778 nonce:int128 = ResPQ;
+The value of nonce is selected randomly by the client (random number) and identifies the client within this communication. Following this step, it is known to all.
+Server sends response of the form
+resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector long = ResPQ;
+Here, string pq is a representation of a natural number (in binary big endian format). This number is the product of two different odd prime numbers. Normally, pq is less than or equal to 2^63-1. The value of server_nonce is selected randomly by the server; following this step, it is known to all.
+server_public_key_fingerprints
is a list of public RSA key fingerprints (64 lower-order bits of SHA1 (server_public_key); the public key is represented as a bare type rsa_public_key n:string e:string = RSAPublicKey
, where, as usual, n and е are numbers in big endian format serialized as strings of bytes, following which SHA1 is computed) received by the server. Because of compatibility issues with older clients, only one public key fingerprint is returned as a result to deprecated req_pq
query; an answer to req_pq_multi
may contain more than one fingerprint.
All subsequent messages contain the pair (nonce, server_nonce) both in the plain-text, and the encrypted portions which makes it possible to identify a “temporary session” — one run of the key generation protocol described on this page that uses the same (nonce, server_nonce) pair. An intruder could not create a parallel session with the server with the same parameters and reuse parts of server- or client-encrypted messages for its own purposes in such a parallel session, because a different server_nonce would be selected by the server for any new “temporary session”.
+This starts a round of Diffie-Hellman key exchanges.
+Client sends query to server
+req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long encrypted_data:string = Server_DH_Params
+Here, encrypted_data is obtained as follows:
+new_nonce := another (good) random number generated by the client; after this query, it is known to both client and server;
+data := a serialization of
+p_q_inner_data#83c95aec pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 = P_Q_inner_data
+or of
+p_q_inner_data_temp#3c6a84d4 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 expires_in:int = P_Q_inner_data;
+data_with_hash := SHA1(data) + data + (any random bytes); such that the length equals 255 bytes;
+encrypted_data := RSA (data_with_hash, server_public_key); a 255-byte long number (big endian) is raised to the requisite power over the requisite modulus, and the result is stored as a 256-byte number.
+Someone might intercept the query and replace it with their own, independently decomposing pq into factors instead of the client. The only field that it makes sense to modify is new_nonce which would be the one an intruder would have to re-generate (because an intruder cannot decrypt the encrypted data sent by the client). Since all subsequent messages are encrypted using new_nonce or contain new_nonce_hash, they will not be processed by the client (an intruder would not be able to make it look as though they had been generated by the server because they would not contain new_nonce). Therefore, this intercept will only result in the intruder’s completing the authorization key generation protocol in place of the client and creating a new key (that has nothing to do with the client); however, the same effect could be achieved simply by creating a new key in one's own name.
+An alternative form of inner data (p_q_inner_data_temp
) is used to create temporary keys, that are only stored in the server RAM and are discarded after at most expires_in
seconds. The server is free to discard its copy earlier. In all other respects the temporary key generation protocol is the same. After a temporary key is created, the client usually binds it to its principal authorisation key by means of the auth.bindTempAuthKey method, and uses it for all client-server communication until it expires; then a new temporary key is generated. Thus Perfect Forward Secrecy (PFS) in client-server communication is achieved. Read more about PFS »
Server responds in one of two ways:
+server_DH_params_fail#79cb045d nonce:int128 server_nonce:int128 new_nonce_hash:int128 = Server_DH_Params; +server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params;
+Here, encrypted_answer is obtained as follows:
+new_nonce_hash := 128 lower-order bits of SHA1 (new_nonce);
+answer := serialization
+ server_DH_inner_data#b5890dba nonce:int128 server_nonce:int128 g:int dh_prime:string g_a:string server_time:int = Server_DH_inner_data;
+answer_with_hash := SHA1(answer) + answer + (0-15 random bytes); such that the length be divisible by 16;
+tmp_aes_key := SHA1(new_nonce + server_nonce) + substr (SHA1(server_nonce + new_nonce), 0, 12);
+tmp_aes_iv := substr (SHA1(server_nonce + new_nonce), 12, 8) + SHA1(new_nonce + new_nonce) + substr (new_nonce, 0, 4);
+encrypted_answer := AES256_ige_encrypt (answer_with_hash, tmp_aes_key, tmp_aes_iv); here, tmp_aes_key is a 256-bit key, and tmp_aes_iv is a 256-bit initialization vector. The same as in all the other instances that use AES encryption, the encrypted data is padded with random bytes to a length divisible by 16 immediately prior to encryption.
+Following this step, new_nonce is still known to client and server only. The client is certain that it is the server that responded and that the response was generated specifically in response to client query req_DH_params, since the response data are encrypted using new_nonce.
+Client is expected to check whether p = dh_prime is a safe 2048-bit prime (meaning that both p and (p-1)/2 are prime, and that 2^2047 < p < 2^2048), and that g generates a cyclic subgroup of prime order (p-1)/2, i.e. is a quadratic residue mod p. Since g is always equal to 2, 3, 4, 5, 6 or 7, this is easily done using quadratic reciprocity law, yielding a simple condition on p mod 4g -- namely, p mod 8 = 7 for g = 2; p mod 3 = 2 for g = 3; no extra condition for g = 4; p mod 5 = 1 or 4 for g = 5; p mod 24 = 19 or 23 for g = 6; and p mod 7 = 3, 5 or 6 for g = 7. After g and p have been checked by the client, it makes sense to cache the result, so as not to repeat lengthy computations in future.
+If the verification takes too long time (which is the case for older mobile devices), one might initially run only 15 Miller--Rabin iterations for verifying primeness of p and (p - 1)/2 with error probability not exceeding one billionth, and do more iterations later in the background.
+Another optimization is to embed into the client application code a small table with some known "good" couples (g,p) (or just known safe primes p, since the condition on g is easily verified during execution), checked during code generation phase, so as to avoid doing such verification during runtime altogether. Server changes these values rarely, thus one usually has to put the current value of server's dh_prime into such a table. For example, current value of dh_prime equals (in big-endian byte order)
+C7 1C AE B9 C6 B1 C9 04 8E 6C 52 2F 70 F1 3F 73 98 0D 40 23 8E 3E 21 C1 49 34 D0 37 56 3D 93 0F 48 19 8A 0A A7 C1 40 58 22 94 93 D2 25 30 F4 DB FA 33 6F 6E 0A C9 25 13 95 43 AE D4 4C CE 7C 37 20 FD 51 F6 94 58 70 5A C6 8C D4 FE 6B 6B 13 AB DC 97 46 51 29 69 32 84 54 F1 8F AF 8C 59 5F 64 24 77 FE 96 BB 2A 94 1D 5B CD 1D 4A C8 CC 49 88 07 08 FA 9B 37 8E 3C 4F 3A 90 60 BE E6 7C F9 A4 A4 A6 95 81 10 51 90 7E 16 27 53 B5 6B 0F 6B 41 0D BA 74 D8 A8 4B 2A 14 B3 14 4E 0E F1 28 47 54 FD 17 ED 95 0D 59 65 B4 B9 DD 46 58 2D B1 17 8D 16 9C 6B C4 65 B0 D6 FF 9C A3 92 8F EF 5B 9A E4 E4 18 FC 15 E8 3E BE A0 F8 7F A9 FF 5E ED 70 05 0D ED 28 49 F4 7B F9 59 D9 56 85 0C E9 29 85 1F 0D 81 15 F6 35 B1 05 EE 2E 4E 15 D0 4B 24 54 BF 6F 4F AD F0 34 B1 04 03 11 9C D8 E3 B9 2F CC 5B
+Client computes random 2048-bit number b (using a sufficient amount of entropy) and sends the server a message
+set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:string = Set_client_DH_params_answer;
+Here, encrypted_data is obtained thus:
+g_b := pow(g, b) mod dh_prime;
+data := serialization
+ client_DH_inner_data#6643b654 nonce:int128 server_nonce:int128 retry_id:long g_b:string = Client_DH_Inner_Data
+data_with_hash := SHA1(data) + data + (0-15 random bytes); such that length be divisible by 16;
+encrypted_data := AES256_ige_encrypt (data_with_hash, tmp_aes_key, tmp_aes_iv);
+The retry_id field is equal to zero at the time of the first attempt; otherwise, it is equal to auth_key_aux_hash from the previous failed attempt (see Item 9).
+Thereafter, auth_key equals pow(g, {ab}) mod dh_prime
; on the server, it is computed as pow(g_b, a) mod dh_prime
, and on the client as (g_a)^b mod dh_prime
.
auth_key_hash is computed := 64 lower-order bits of SHA1 (auth_key). The server checks whether there already is another key with the same auth_key_hash and responds in one of the following ways.
+Server responds in one of three ways:
+dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer; +dh_gen_retry#46dc1fb9 nonce:int128 server_nonce:int128 new_nonce_hash2:int128 = Set_client_DH_params_answer; +dh_gen_fail#a69dae02 nonce:int128 server_nonce:int128 new_nonce_hash3:int128 = Set_client_DH_params_answer;
+In the other case, the client goes to Item 6) generating a new b.
+In the first case, the client and the server have negotiated auth_key, following which they forget all other temporary data, and the client creates another encrypted session using auth_key. At the same time, server_salt is initially set to substr(new_nonce, 0, 8) XOR substr(server_nonce, 0, 8)
. If required, the client stores the difference between server_time received in 5) and its local time, to be able always to have a good approximation of server time which is required to generate correct message identifiers.
IMPORTANT: Apart from the conditions on the Diffie-Hellman prime dh_prime and generator g, both sides are to check that g, g_a and g_b are greater than 1 and less than dh_prime - 1. We recommend checking that g_a and g_b are between 2^{2048-64} and dh_prime - 2^{2048-64} as well.
+If the client fails to receive any response to its query from the server within a certain time interval, it may simply re-send the query. If the server has already sent a response to this query (exactly the same request and not just similar: all the parameters during the repeat request must take on the same values) but it did not get to the client, the server will simply re-send the same response. The server remembers the response for up to 10 minutes after having received the query in 1). If the server has already forgotten the response or the requisite temporary data, the client will have to start from the beginning.
+The server may consider that if the client has already sent in the next query using the data from the previous server response to the specific client, the response is known to have been received by the client and may be forgotten by the server.
+An example of a complete list of queries required to generate an authorization key is shown on a separate page.
++As of version 4.6, major Telegram clients are using MTProto 2.0. +MTProto v.1.0 is deprecated and is currently being phased out.
+
This article describes the basic layer of the MTProto protocol version 2.0 (Cloud chats, server-client encryption). The principal differences from version 1.0 (described here for reference) are as follows:
+See also: MTProto 2.0: Secret Chats, end-to-end encryption
+Before a message (or a multipart message) is transmitted over a network using a transport protocol, it is encrypted in a certain way, and an external header is added at the top of the message that consists of a 64-bit key identifier auth_key_id (that uniquely identifies an authorization key for the server as well as the user) and a 128-bit message key msg_key.
+The authorization key auth_key combined with the message key msg_key define an actual 256-bit key aes_key and a 256-bit initialization vector aes_iv, which are used to encrypt the message using AES-256 encryption in infinite garble extension (IGE) mode. Note that the initial part of the message to be encrypted contains variable data (session, message ID, sequence number, server salt) that obviously influences the message key (and thus the AES key and iv). In MTProto 2.0, the message key is defined as the 128 middle bits of the SHA-256 of the message body (including session, message ID, padding, etc.) prepended by 32 bytes taken from the authorization key. In the older MTProto 1.0, the message key was computed as the lower 128 bits of SHA-1 of the message body, excluding the padding bytes.
+Multipart messages are encrypted as a single message.
+ +++Got questions about this setup? — Check out the Advanced FAQ!
+
Each plaintext message to be encrypted in MTProto always contains the following data to be checked upon decryption in order to make the system robust against known problems with the components:
+Telegram's End-to-end encrypted Secret Chats are using an additional layer of encryption on top of the described above. See Secret Chats, End-to-End encryption for details.
+A 2048-bit key shared by the client device and the server, created upon user registration directly on the client device by exchanging Diffie-Hellman keys, and never transmitted over a network. Each authorization key is user-specific. There is nothing that prevents a user from having several keys (that correspond to “permanent sessions” on different devices), and some of these may be locked forever in the event the device is lost. See also Creating an Authorization Key.
+A 2048-bit RSA key used by the server digitally to sign its own messages while registration is underway and the authorization key is being generated. The application has a built-in public server key which can be used to verify a signature but cannot be used to sign messages. A private server key is stored on the server and changed very infrequently.
+The 64 lower-order bits of the SHA1 hash of the authorization key are used to indicate which particular key was used to encrypt a message. Keys must be uniquely defined by the 64 lower-order bits of their SHA1, and in the event of a collision, an authorization key is regenerated. A zero key identifier means that encryption is not used which is permissible for a limited set of message types used during registration to generate an authorization key in a Diffie-Hellman exchange. For MTProto 2.0, SHA1 is still used here, because auth_key_id should identify the authorization key used independently of the protocol version.
+A (random) 64-bit number generated by the client to distinguish between individual sessions (for example, between different instances of the application, created with the same authorization key). The session in conjunction with the key identifier corresponds to an application instance. The server can maintain session state. Under no circumstances can a message meant for one session be sent into a different session. The server may unilaterally forget any client sessions; clients should be able to handle this.
+A (random) 64-bit number periodically (say, every 24 hours) changed (separately for each session) at the request of the server. All subsequent messages must contain the new salt (although, messages with the old salt are still accepted for a further 300 seconds). Required to protect against replay attacks and certain tricks associated with adjusting the client clock to a moment in the distant future.
+A (time-dependent) 64-bit number used uniquely to identify a message within a session. Client message identifiers are divisible by 4, server message identifiers modulo 4 yield 1 if the message is a response to a client message, and 3 otherwise. Client message identifiers must increase monotonically (within a single session), the same as server message identifiers, and must approximately equal unixtime*2^32. This way, a message identifier points to the approximate moment in time the message was created. A message is rejected over 300 seconds after it is created or 30 seconds before it is created (this is needed to protect from replay attacks). In this situation, it must be re-sent with a different identifier (or placed in a container with a higher identifier). The identifier of a message container must be strictly greater than those of its nested messages.
+Important: to counter replay-attacks the lower 32 bits of msg_id passed by the client must not be empty and must present a fractional part of the time point when the message was created.
+A message requiring an explicit acknowledgment. These include all the user and many service messages, virtually all with the exception of containers and acknowledgments.
+A 32-bit number equal to twice the number of “content-related” messages (those requiring acknowledgment, and in particular those that are not containers) created by the sender prior to this message and subsequently incremented by one if the current message is a content-related message. A container is always generated after its entire contents; therefore, its sequence number is greater than or equal to the sequence numbers of the messages contained in it.
+In MTProto 2.0, the middle 128 bits of the SHA-256 hash of the message to be encrypted (including the internal header and the padding bytes for MTProto 2.0), prepended by a 32-byte fragment of the authorization key.
+In MTProto 1.0, message key was defined differently, as the lower 128 bits of the SHA-1 hash of the message to be encrypted, with padding bytes excluded from the computation of the hash. Authorization key was not involved in this computation.
+A header (16 bytes) added before a message or a container before it is all encrypted together. Consists of the server salt (64 bits) and the session (64 bits).
+A header (24 bytes) added before an encrypted message or a container. Consists of the key identifier auth_key_id (64 bits) and the message key msg_key (128 bits).
+External header + encrypted message or container.
+The 2048-bit authorization key (auth_key) and the 128-bit message key (msg_key) are used to compute a 256-bit AES key (aes_key) and a 256-bit initialization vector (aes_iv) which are subsequently used to encrypt the part of the message to be encrypted (i. e. everything with the exception of the external header that is added later) with AES-256 in infinite garble extension (IGE) mode.
+For MTProto 2.0, the algorithm for computing aes_key and aes_iv from auth_key and msg_key is as follows.
+where x = 0 for messages from client to server and x = 8 for those from server to client.
+For the obsolete MTProto 1.0, msg_key, aes_key, and aes_iv were computed differently (see this document for reference).
+The lower-order 1024 bits of auth_key are not involved in the computation. They may (together with the remaining bits or separately) be used on the client device to encrypt the local copy of the data received from the server. The 512 lower-order bits of auth_key are not stored on the server; therefore, if the client device uses them to encrypt local data and the user loses the key or the password, data decryption of local data is impossible (even if data from the server could be obtained).
+In MTProto 1.0, when AES was used to encrypt a block of data of a length not divisible by 16 bytes, the data was padded with 0 to 15 random padding bytes random_padding to a length divisible by 16 bytes prior to encryption. In MTProto 2.0, this padding is taken into account when computing msg_key
. Note that MTProto 2.0 requires from 12 to 1024 bytes of padding, still subject to the condition that the resulting message length be divisible by 16 bytes.
A client may either use only MTProto 2.0 or only MTProto 1.0 in the same TCP connection. The server detects the protocol used by the first message received from the client, and then uses the same encryption for its messages, and expects the client to use the same encryption henceforth. We recommend using MTProto 2.0; MTProto 1.0 is deprecated and supported for backward compatibility only.
+When an encrypted message is received, it must be checked that msg_key is in fact equal to the 128 middle bits of the SHA-256 of the decrypted data with a 32-byte fragment of auth_key prepended to it, and that msg_id has even parity for messages from client to server, and odd parity for messages from server to client.
+In addition, the identifiers (msg_id) of the last N messages received from the other side must be stored, and if a message comes in with msg_id lower than all or equal to any of the stored values, the message is to be ignored. Otherwise, the new message msg_id is added to the set, and, if the number of stored msg_id values is greater than N, the oldest (i. e. the lowest) is forgotten.
+On top of this, msg_id values that belong over 30 seconds in the future or over 300 seconds in the past are to be ignored. This is especially important for the server. The client would also find this useful (to protect from a replay attack), but only if it is certain of its time (for example, if its time has been synchronized with that of the server).
+Certain client-to-server service messages containing data sent by the client to the server (for example, msg_id of a recent client query) may, nonetheless, be processed on the client even if the time appears to be “incorrect”. This is especially true of messages to change server_salt and notifications of invalid client time. See Mobile Protocol: Service Messages.
+It may be suggested to users concerned with security that they password protect the authorization key in approximately the same way as in ssh. This can be accomplished by prepending the value of cryptographic hash function, such as SHA-256, of the key to the front of the key, following which the entire string is encrypted using AES in CBC mode and a key equal to the user’s (text) password. When the user inputs the password, the stored protected password is decrypted and verified by checking the SHA-256 value. From the user’s standpoint, this is practically the same as using an application or a website password.
+Special plain-text messages may be used to create an authorization key as well as to perform a time synchronization. They begin with auth_key_id = 0 (64 bits) which means that there is no auth_key. This is followed directly by the message body in serialized format without internal or external headers. A message identifier (64 bits) and body length in bytes (32 bytes) are added before the message body.
+Only a very limited number of messages of special types can be transmitted as plain text.
+auth_key_id int64 |
+msg_key int128 |
+encrypted_data bytes |
+
Contains the cypher text for the following data:
+salt int64 |
+session_id int64 |
+message_id int64 |
+seq_no int32 |
+message_data_length int32 |
+message_data bytes |
+padding12..1024 bytes |
+
auth_key_id = 0 int64 |
+message_id int64 |
+message_data_length int32 |
+message_data bytes |
+
MTProto 2.0 uses 12..1024 padding bytes, instead of the 0..15 used in MTProto 1.0
+An authorization key is normally created once for every user during the application installation process immediately prior to registration. Registration itself, in actuality, occurs after the authorization key is created. However, a user may be prompted to complete the registration form while the authorization key is being generated in the background. Intervals between user key strokes may be used as a source of entropy in the generation of high-quality random numbers required for the creation of an authorization key.
+See Creating an Authorization Key.
+During the creation of the authorization key, the client obtains its server salt (to be used with the new key for all communication in the near future). The client then creates an encrypted session using the newly generated key, and subsequent communication occurs within that session (including the transmission of the user's registration information and phone number validation) unless the client creates a new session. The client is free to create new or additional sessions at any time by choosing a new random session_id.
++This document describes MTProto v.1.0, its status is DEPRECATED. +For information on encryption used in up-to-date Telegram clients, kindly see this document.
+
Prior to a message (or a multipart message) being transmitted over a network using a transport protocol, it is encrypted in a certain way, and an external header is added at the top of the message which is: a 64-bit key identifier (that uniquely identifies an authorization key for the server as well as the user) and a 128-bit message key.
+A user key together with the message key define an actual 256-bit key and a 256-bit initialization vector, which is what encrypts the message using AES-256 encryption with infinite garble extension (IGE). Note that the initial part of the message to be encrypted contains variable data (session, message ID, sequence number, server salt) that obviously influences the message key (and thus the AES key and iv). The message key is defined as the 128 lower-order bits of the SHA1 of the message body (including session, message ID, etc.) Multipart messages are encrypted as a single message.
+ +a 2048-bit key shared by the client device and the server, created upon user registration directly on the client device be exchanging Diffie-Hellman keys, and never transmitted over a network. Each authorization key is user-specific. There is nothing that prevents a user from having several keys (that correspond to “permanent sessions” on different devices), and some of these may be locked forever in the event the device is lost. See also Creating an Authorization Key.
+a 2048-bit RSA key used by the server digitally to sign its own messages while registration is underway and the authorization key is being generated. The application has a built-in public server key which can be used to verify a signature but cannot be used to sign messages. A private server key is stored on the server and changed very infrequently.
+The 64 lower-order bits of the SHA1 hash of the authorization key are used to indicate which particular key was used to encrypt a message. Keys must be uniquely defined by the 64 lower-order bits of their SHA1, and in the event of a collision, an authorization key is regenerated. A zero key identifier means that encryption is not used which is permissible for a limited set of message types used during registration to generate an authorization key based on a Diffie-Hellman exchange.
+a (random) 64-bit number generated by the client to distinguish between individual sessions (for example, between different instances of the application, created with the same authorization key). The session in conjunction with the key identifier corresponds to an application instance. The server can maintain session state. Under no circumstances can a message meant for one session be sent into a different session. The server may unilaterally forget any client sessions; clients should be able to handle this.
+a (random) 64-bit number periodically (say, every 24 hours) changed (separately for each session) at the request of the server. All subsequent messages must contain the new salt (although, messages with the old salt are still accepted for a further 300 seconds). Required to protect against replay attacks and certain tricks associated with adjusting the client clock to a moment in the distant future.
+a (time-dependent) 64-bit number used uniquely to identify a message within a session. Client message identifiers are divisible by 4, server message identifiers modulo 4 yield 1 if the message is a response to a client message, and 3 otherwise. Client message identifiers must increase monotonically (within a single session), the same as server message identifiers, and must approximately equal unixtime*2^32. This way, a message identifier points to the approximate moment in time the message was created. A message is rejected over 300 seconds after it is created or 30 seconds before it is created (this is needed to protect from replay attacks). In this situation, it must be re-sent with a different identifier (or placed in a container with a higher identifier). The identifier of a message container must be strictly greater than those of its nested messages.
+Important: to counter replay-attacks the lower 32 bits of msg_id passed by the client must not be empty and must present a fractional part of the time point when the message was created. At some point in the nearest future the server will start ignoring messages, in which the lower 32 bits of msg_id contain too many zeroes.
+A message requiring an explicit acknowledgment. These include all the user and many service messages, virtually all with the exception of containers and acknowledgments.
+a 32-bit number equal to twice the number of “content-related” messages (those requiring acknowledgment, and in particular those that are not containers) created by the sender prior to this message and subsequently incremented by one if the current message is a content-related message. A container is always generated after its entire contents; therefore, its sequence number is greater than or equal to the sequence numbers of the messages contained in it.
+The lower-order 128 bits of the SHA1 hash of the part of the message to be encrypted (including the internal header and excluding the alignment bytes).
+A header (16 bytes) added before a message or a container before it is all encrypted together. Consists of the server salt (64 bits) and the session (64 bits).
+A header (24 bytes) added before an encrypted message or a container. Consists of a key identifier (64 bits) and a message key (128 bits).
+External header + encrypted message or container.
+The 2048-bit authorization key (auth_key) and the 128-bit message key (msg_key) are used to compute a 256-bit AES key (aes_key) and a 256-bit initialization vector (aes_iv) which are subsequently used to encrypt the part of the message to be encrypted (i. e. everything with the exception of the external header which is added later) with AES-256 in infinite garble extension (IGE) mode.
+The algorithm for computing aes_key and aes_iv from auth_key and msg_key is as follows:
+where x = 0 for messages from client to server and x = 8 for those from server to client.
+The lower-order 1024 bits of auth_key are not involved in the computation. They may (together with the remaining bits or separately) be used on the client device to encrypt the local copy of the data received from the server. The 512 lower-order bits of auth_key are not stored on the server; therefore, if the client device uses them to encrypt local data and the user loses the key or the password, data decryption of local data is impossible (even if data from the server could be obtained).
+When AES is used to encrypt a block of data of a length not divisible by 16 bytes, the data is padded with random bytes to the smallest length divisible by 16 bytes immediately prior to being encrypted.
+When an encrypted message is received, it must be checked that msg_key is in fact equal to the 128 lower-order bits of the SHA1 hash of the previously encrypted portion, and that msg_id has even parity for messages from client to server, and odd parity for messages from server to client.
+In addition, the identifiers (msg_id) of the last N messages received from the other side must be stored, and if a message comes in with msg_id lower than all or equal to any of the stored values, the message is to be ignored. Otherwise, the new message msg_id is added to the set, and, if the number of stored msg_id values is greater than N, the oldest (i. e. the lowest) is forgotten.
+In addition, msg_id values that belong over 30 seconds in the future or over 300 seconds in the past are to be ignored. This is especially important for the server. The client would also find this useful (to protect from a replay attack), but only if it is certain of its time (for example, if its time has been synchronized with that of the server).
+Certain client-to-server service messages containing data sent by the client to the server (for example, msg_id of a recent client query) may, nonetheless, be processed on the client even if the time appears to be “incorrect”. This is especially true of messages to change server_salt and notifications of invalid client time. See Mobile Protocol: Service Messages.
+It may be suggested to users concerned with security that they password protect the authorization key in approximately the same way as in ssh. This is accomplished by adding the SHA1 of the key to the front of the key, following which the entire string is encrypted using AES in CBC mode and a key equal to the user’s (text) password. When the user inputs the password, the stored protected password is decrypted and verified by being compared with SHA1. From the user’s standpoint, this is practically the same as using an application or a website password.
+Special plain-text messages may be used to create an authorization key as well as to perform a time synchronization. They begin with auth_key_id = 0 (64 bits) which means that there is no auth_key. This is followed directly by the message body in serialized format without internal or external headers. A message identifier (64 bits) and body length in bytes (32 bytes) are added before the message body.
+Only a very limited number of messages of special types can be transmitted as plain text.
+auth_key_id int64 |
+msg_key int128 |
+encrypted_data bytes |
+
Contains the cypher text for the following data:
+salt int64 |
+session_id int64 |
+message_id int64 |
+seq_no int32 |
+message_data_length int32 |
+message_data bytes |
+padding 0..15 bytes |
+
auth_key_id = 0 int64 |
+message_id int64 |
+message_data_length int32 |
+message_data bytes |
+
An authorization key is normally created once for every user during the application installation process immediately prior to registration. Registration itself, in actuality, occurs after the authorization key is created. However, a user may be prompted to complete the registration form while the authorization key is being generated in the background. Intervals between user key strokes may be used as a source of entropy in the generation of high-quality random numbers required for the creation of an authorization key.
+See Creating an Authorization Key.
+During the creation of the authorization key, the client obtains its server salt (to be used with the new key for all communication in the near future). The client then creates an encrypted session using the newly generated key, and subsequent communication occurs within that session (including the transmission of the user's registration information and phone number validation) unless the client creates a new session. The client is free to create new or additional sessions at any time by choosing a new random session_id.
Here's a list of MTProto transport protocols (see the ISO/OSI recap for a full explanation):
+ +The server recognizes these different protocols (and distinguishes them from HTTP, too) by the header. +Additionally, the following transport features can be used:
+ +Example implementations for these protocols can be seen in tdlib and MadelineProto.
+The lightest protocol available.
+Payload structure:
++-+----...----+
+|l| payload |
++-+----...----+
+OR
+
++-+---+----...----+
+|h|len| payload +
++-+---+----...----+
+Before sending anything into the underlying socket (see transports), the client must first send 0xef
as the first byte (the server will not send 0xef
as the first byte in the first reply).
+Then, payloads are wrapped in the following envelope:
0x01..0x7e
.If the packet length divided by four is bigger than or equal to 127 (>= 0x7f
), the following envelope must be used, instead:
0x7f
In case 4-byte data alignment is needed, an intermediate version of the original protocol may be used.
+Payload structure:
++----+----...----+
++len.+ payload +
++----+----...----+
+Before sending anything into the underlying socket (see transports), the client must first send 0xeeeeeeee
as the first int (four bytes, the server will not send 0xeeeeeeee
as the first int in the first reply).
+Then, payloads are wrapped in the following envelope:
Padded version of the intermediate protocol, to use with obfuscation enabled to bypass ISP blocks.
+Before sending anything into the underlying socket (see transports), the client must first send 0xdddddddd
as the first int (four bytes, the server will not send 0xdddddddd
as the first int in the first reply).
+Then, payloads are wrapped in the following envelope:
+----+----...----+----...----+
+|tlen| payload | padding |
++----+----...----+----...----+
+Envelope description:
+0-15
The basic MTProto transport protocol
+Payload structure:
++----+----+----...----+----+
+|len.|seq.| payload |crc.|
++----+----+----...----+----+
+Envelope description:
+Additionally, the following transport features can be used:
+These MTProto transport protocols have support for quick acknowledgment. +In this case, the client sets the highest-order length bit in the query packet, and the server responds with a special 4 bytes as a separate packet. +They are the 32 higher-order bits of SHA256 of the encrypted portion of the packet prepended by 32 bytes from the authorization key (the same hash as computed for verifying the message key), with the most significant bit set to make clear that this is not the length of a regular server response packet; if the abridged version is used, bswap is applied to these four bytes.
+In the event of a transport error (missing auth key, transport flood, etc.), the server may send a packet with a signed little-endian number of 4 bytes, whose absolute value contains the error code (the error itself is actually negative).
+For example, error Code 403 corresponds to situations where the corresponding HTTP error would have been returned by the HTTP protocol.
+Error 404 (auth key not found) is returned when the specified auth key ID cannot be found by the DC.
+Error 429 (transport flood) is returned when too many transport connections are established to the same IP in a too short lapse of time, or if any of the container/service message limits are reached.
+Transport obfuscation is required to use the websocket transports.
+Transport obfuscation to prevent ISP blocks is implemented using the following protocol, situated under the MTProto transport in the ISO/OSI stack, see the recap; this means that the payload is first wrapped in the MTProto transport envelope (all transports are supported), and then obfuscated:
+Prior to establishing the connection (and eventually sending the protocol header of a specific MTProto transport), a 64-byte (512-bit) random initialization payload is generated.
+During the generation process, special care must be taken in order to avoid a situation where that the first int (first four bytes) of the random string are equal to one of the known protocol identifiers (see above).
+In particular, the first four bytes must not be equal to 0xdddddddd
(padded intermediate), 0xeeeeeeee
(intermediate), POST
, GET
, HEAD
, or any of the HTTP methods that are accepted by the MTProto servers.
+The first byte must also not be equal to 0xef
(abridged).
+Bytes 4-8
must also not be equal to 0x00000000
, since that would indicate use of the full transport with the initial TCP sequence number (0).
The protocol identifier, if present, must be inserted in the initialization payload at byte offset 56
: if its length is less than 4, it must be padded using the protocol identifier itself, to make its length 4 (0xef
=> 0xefefefef
): the standalone protocol identifier must be not be sent aftwerwards.
This protocol is also (but not exclusively) used when connecting to MTProxies: only in this case, the DC ID in a specially encoded form must also be inserted in the initialization payload at offset 60
.
+The encoding simply consist of the DC ID in two-byte signed little-endian form; 10000
has to be added to the DC ID to connect to the test servers; it has to be made negative if the DC we're connecting to is a media (not CDN) DC.
Next, a secondary initialization payload is generated by reversing the primary intialization payload.
+Two keys are extracted from both initialization payloads, using bytes at offsets 8-40
: the key extracted from the primary payload is used as encryption key, the key extracted from the secondary payload is used as decryption key.
Two IVs are extracted from both initialization payloads, using bytes at offsets 40-56
: the IV extracted from the primary payload is used as encryption IV, the IV extracted from the secondary payload is used as decryption IV.
Only if using MTProxy, the secret is used to provide connection with the MTProxy server. +The secret is a 16-byte string, usually distributed in its hexadecimal form along with the MTProxy host and port.
+Often, a 17-byte version of the secret can be found: this simply indicates that the client should use a specific MTProto transport (based on the first byte, usually it's 0xdd
, to indicate that the padded intermediate protocol should be used 0xdddddddd
; however, clients should default to the padded intermediate transport whenever an additional byte in the secret is encountered).
The extracted encryption and decryption keys must be concatenated with the secret (the first byte of which should be ignored if it's the 17-byte version), and the SHA256 hash of such string should be used as encryption/decryption key.
+The obtained encryption and decryption key/IV pairs must then be used with AES-256-CTR to encrypt and decrypt all outgoing and incoming payloads.
+The first thing that must be encrypted using the encryption key is the initialization payload itself.
+Then bytes 56-64
of the encrypted initialization payload are substituted in the original initialization payload: this is the part that contains the constant MTProto transport protocol identifier and the DC ID (only for MTProxies).
The final initialization payload must then be sent in the socket as first 64 bytes after the TCP handshake.
+Example pseudocode for the generation of an MTProxy connection payload (media DC 4) using the obfuscated padded intermediate transport. +Warning: do not use the specified proxy secret in any MTProxy exposed on the internet.
+protocol := 0xdddddddd
+dc := 0xfcff
+
+while 1:
+ init := (56 random bytes) + protocol + dc + (2 random bytes)
+
+ if init[0] == 0xef:
+ continue
+
+ first_int := substr(init, 0, 4)
+ if first_int == 0x44414548 || first_int == 0x54534f50 || first_int == 0x20544547 || first_int == 0x4954504f || first_int == 0x02010316 || first_int == 0xdddddddd || first_int == 0xeeeeeeee:
+ continue
+
+ second_int := substr(init, 0, 4)
+ if second_int == 0x00000000:
+ continue
+
+ break
+
+initRev := strrev(init)
+
+encryptKey := substr(init, 8, 32)
+encryptIV := substr(init, 40, 16)
+
+decryptKey := substr(initRev, 8, 32)
+decryptIV := substr(initRev, 40, 16)
+
+secret := substr(0xdd99999999999999999999999999999999, 1, 16)
+
+encryptKey = SHA256(encryptKey + secret)
+decryptKey = SHA256(decryptKey + secret)
+
+encryptedInit := CTR(encryptKey, encryptIV, init)
+
+finalInit := substr(init, 0, 56) + substr(encryptedInit, 56, 8)
+
+write(finalInit)
In the examples below, the transport headers are omitted:
+++If a payload (packet) needs to be transmitted from server to client or from client to server, it is encapsulated as follows: 4 bytes are added at the front (to include the length, the sequence number, and CRC32; always divisible by 4) and 4 bytes with the packet sequence number for this TCP connection (the first packet sent is numbered 0, the next one 1, etc.), and 4 CRC32 bytes at the end (length, sequence number, and payload together).
+There is an abridged version of the same protocol: if the client sends
+0xef
as the first byte (important: only prior to the very first data packet), then packet length is encoded by a single byte (0x01-0x7e
= data length divided by 4; or0x7f
followed by 3 bytes (little endian) divided into 4) followed by the data themselves (sequence number and CRC32 not added). In this case, server responses have the same form (although the server does not send0xef
as the first byte).
Detailed documentation on creating authorization keys is available here ».
+req_pq#60469778 nonce:int128 = ResPQ
+Parameter | +Offset, Length in bytes | +Value | +Description | +
---|---|---|---|
auth_key_id | +0, 8 | +0 |
+Since message is in plain text | +
message_id | +8, 8 | +51e57ac42770964a |
+Exact unixtime * 2^32 | +
message_length | +16, 4 | +20 |
+Message body length | +
%(req_pq) | +20, 4 | +60469778 |
+req_pq constructor number from TL schema | +
nonce | +24, 16 | +3E0549828CCA27E966B301A48FECE2FC |
+Random number | +
The header is 20 bytes long, the message body is 20 bytes long, and the entire message is 40 bytes in length.
+0000 | 00 00 00 00 00 00 00 00 4A 96 70 27 C4 7A E5 51
+0010 | 14 00 00 00 78 97 46 60 3E 05 49 82 8C CA 27 E9
+0020 | 66 B3 01 A4 8F EC E2 FC
+0000 | 00 00 00 00 00 00 00 00 01 C8 83 1E C9 7A E5 51
+0010 | 40 00 00 00 63 24 16 05 3E 05 49 82 8C CA 27 E9
+0020 | 66 B3 01 A4 8F EC E2 FC A5 CF 4D 33 F4 A1 1E A8
+0030 | 77 BA 4A A5 73 90 73 30 08 17 ED 48 94 1A 08 F9
+0040 | 81 00 00 00 15 C4 B5 1C 01 00 00 00 21 6B E8 6C
+0050 | 02 2B B4 C3
+resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector long = ResPQ
+Parameter | +Offset, Length in bytes | +Value | +Description | +
---|---|---|---|
auth_key_id | +0, 8 | +0 |
+Since message is in plain text | +
message_id | +8, 8 | +51E57AC91E83C801 |
+Server message ID | +
message_length | +16, 4 | +64 |
+Message body length | +
%(resPQ) | +20, 4 | +05162463 |
+resPQ constructor number from TL schema | +
nonce | +24, 16 | +3E0549828CCA27E966B301A48FECE2FC |
+Value generated by client in Step 1 | +
server_nonce | +40, 16 | +A5CF4D33F4A11EA877BA4AA573907330 |
+Server-generated random number | +
pq | +56, 12 | +17ED48941A08F981 |
+Single-byte prefix denoting length, an 8-byte string, and three bytes of padding | +
%(Vector long) | +68, 4 | +1cb5c415 |
+Vector long constructor number from TL schema | +
count | +72, 4 | +1 |
+Number of elements in key fingerprint list | +
fingerprints[] | +76, 8 | +c3b42b026ce86b21 |
+64 lower-order bits of SHA1 (server_public_key) | +
The server_public_key public key has been selected whose fingerprint corresponds to the only one received from the server: c3b42b026ce86b21
.
p = 494C553B
+q = 53911073
+p_q_inner_data#83c95aec pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 = P_Q_inner_data
+Parameter | +Offset, Length in bytes | +Value | +Description | +
---|---|---|---|
%(p_q_inner_data) | +0, 4 | +83c95aec |
+p_q_inner_data constructor number from TL schema | +
pq | +4, 12 | +17ED48941A08F981 |
+Single-byte prefix denoting length, 8-byte string, and three bytes of padding | +
p | +16, 8 | +494C553B |
+First prime cofactor: single-byte prefix denoting length, 4-byte string, and three bytes of padding | +
q | +24, 8 | +53911073 |
+Second prime cofactor: single-byte prefix denoting length, 4-byte string, and three bytes of padding | +
nonce | +32, 16 | +3E0549828CCA27E966B301A48FECE2FC |
+Value generated by client in Step 1 | +
server_nonce | +48, 16 | +A5CF4D33F4A11EA877BA4AA573907330 |
+Value received from server in Step 2 | +
new_nonce | +64, 32 | +311C85DB234AA2640AFC4A76A735CF5B 1F0FD68BD17FA181E1229AD867CC024D |
+Client-generated random number | +
The serialization of P_Q_inner_data produces some string data. This is followed by encrypted_data:
+SHA1 (data) = DB761C27718A2305044F71F2AD951629D78B2449
+RSA (data_with_hash, server_public_key) = 7BB0100A523161904D9C69FA04BC60DECFC5DD74B99995C768EB60D8716E2109BAF2D4601DAB6B09610DC11067BB89021E09471FCFA52DBD0F23204AD8CA8B012BF40A112F44695AB6C266955386114EF5211E6372227ADBD34995D3E0E5FF02EC63A43F9926878962F7C570E6A6E78BF8366AF917A5272675C46064BE62E3E202EFA8B1ADFB1C32A898C2987BE27B5F31D57C9BB963ABCB734B16F652CEDB4293CBB7C878A3A3FFAC9DBEA9DF7C67BC9E9508E111C78FC46E057F5C65ADE381D91FEE430A6B576A99BDF8551FDB1BE2B57069B1A45730618F27427E8A04720B4971EF4A9215983D68F2830C3EAA6E40385562F970D38A05C9F1246DC33438E6
+The length of the final string was 256 bytes.
+req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long encrypted_data:string = Server_DH_Params
+Parameter | +Offset, Length in bytes | +Value | +Description | +
---|---|---|---|
auth_key_id | +0, 8 | +0 |
+Since message is in plain text | +
message_id | +8, 8 | +51e57ac917717a27 |
+Exact unixtime * 2^32 | +
message_length | +16, 4 | +320 |
+Message body length | +
%(req_DH_params) | +20, 4 | +d712e4be |
+req_DH_params constructor number from TL schema | +
nonce | +24, 16 | +3E0549828CCA27E966B301A48FECE2FC |
+Value generated by client in Step 1 | +
server_nonce | +40, 16 | +A5CF4D33F4A11EA877BA4AA573907330 |
+Value received from server in Step 2 | +
p | +56, 8 | +494C553B |
+First prime cofactor: single-byte prefix denoting length, 4-byte string, and three bytes of padding | +
q | +64, 8 | +53911073 |
+Second prime cofactor: single-byte prefix denoting length, 4-byte string, and three bytes of padding | +
public_key_fingerprint | +72, 8 | +c3b42b026ce86b21 |
+Fingerprint of public key used | +
encrypted_data | +80, 260 | +See above | +See “Generation of encrypted_data" | +
0000 | 00 00 00 00 00 00 00 00 27 7A 71 17 C9 7A E5 51
+0010 | 40 01 00 00 BE E4 12 D7 3E 05 49 82 8C CA 27 E9
+0020 | 66 B3 01 A4 8F EC E2 FC A5 CF 4D 33 F4 A1 1E A8
+0030 | 77 BA 4A A5 73 90 73 30 04 49 4C 55 3B 00 00 00
+0040 | 04 53 91 10 73 00 00 00 21 6B E8 6C 02 2B B4 C3
+0050 | FE 00 01 00 7B B0 10 0A 52 31 61 90 4D 9C 69 FA
+0060 | 04 BC 60 DE CF C5 DD 74 B9 99 95 C7 68 EB 60 D8
+0070 | 71 6E 21 09 BA F2 D4 60 1D AB 6B 09 61 0D C1 10
+0080 | 67 BB 89 02 1E 09 47 1F CF A5 2D BD 0F 23 20 4A
+0090 | D8 CA 8B 01 2B F4 0A 11 2F 44 69 5A B6 C2 66 95
+00A0 | 53 86 11 4E F5 21 1E 63 72 22 7A DB D3 49 95 D3
+00B0 | E0 E5 FF 02 EC 63 A4 3F 99 26 87 89 62 F7 C5 70
+00C0 | E6 A6 E7 8B F8 36 6A F9 17 A5 27 26 75 C4 60 64
+00D0 | BE 62 E3 E2 02 EF A8 B1 AD FB 1C 32 A8 98 C2 98
+00E0 | 7B E2 7B 5F 31 D5 7C 9B B9 63 AB CB 73 4B 16 F6
+00F0 | 52 CE DB 42 93 CB B7 C8 78 A3 A3 FF AC 9D BE A9
+0100 | DF 7C 67 BC 9E 95 08 E1 11 C7 8F C4 6E 05 7F 5C
+0110 | 65 AD E3 81 D9 1F EE 43 0A 6B 57 6A 99 BD F8 55
+0120 | 1F DB 1B E2 B5 70 69 B1 A4 57 30 61 8F 27 42 7E
+0130 | 8A 04 72 0B 49 71 EF 4A 92 15 98 3D 68 F2 83 0C
+0140 | 3E AA 6E 40 38 55 62 F9 70 D3 8A 05 C9 F1 24 6D
+0150 | C3 34 38 E6
+0000 | 00 00 00 00 00 00 00 00 01 54 43 36 CB 7A E5 51
+0010 | 78 02 00 00 5C 07 E8 D0 3E 05 49 82 8C CA 27 E9
+0020 | 66 B3 01 A4 8F EC E2 FC A5 CF 4D 33 F4 A1 1E A8
+0030 | 77 BA 4A A5 73 90 73 30 FE 50 02 00 28 A9 2F E2
+0040 | 01 73 B3 47 A8 BB 32 4B 5F AB 26 67 C9 A8 BB CE
+0050 | 64 68 D5 B5 09 A4 CB DD C1 86 24 0A C9 12 CF 70
+0060 | 06 AF 89 26 DE 60 6A 2E 74 C0 49 3C AA 57 74 1E
+0070 | 6C 82 45 1F 54 D3 E0 68 F5 CC C4 9B 44 44 12 4B
+0080 | 96 66 FF B4 05 AA B5 64 A3 D0 1E 67 F6 E9 12 86
+0090 | 7C 8D 20 D9 88 27 07 DC 33 0B 17 B4 E0 DD 57 CB
+00A0 | 53 BF AA FA 9E F5 BE 76 AE 6C 1B 9B 6C 51 E2 D6
+00B0 | 50 2A 47 C8 83 09 5C 46 C8 1E 3B E2 5F 62 42 7B
+00C0 | 58 54 88 BB 3B F2 39 21 3B F4 8E B8 FE 34 C9 A0
+00D0 | 26 CC 84 13 93 40 43 97 4D B0 35 56 63 30 38 39
+00E0 | 2C EC B5 1F 94 82 4E 14 0B 98 63 77 30 A4 BE 79
+00F0 | A8 F9 DA FA 39 BA E8 1E 10 95 84 9E A4 C8 34 67
+0100 | C9 2A 3A 17 D9 97 81 7C 8A 7A C6 1C 3F F4 14 DA
+0110 | 37 B7 D6 6E 94 9C 0A EC 85 8F 04 82 24 21 0F CC
+0120 | 61 F1 1C 3A 91 0B 43 1C CB D1 04 CC CC 8D C6 D2
+0130 | 9D 4A 5D 13 3B E6 39 A4 C3 2B BF F1 53 E6 3A CA
+0140 | 3A C5 2F 2E 47 09 B8 AE 01 84 4B 14 2C 1E E8 9D
+0150 | 07 5D 64 F6 9A 39 9F EB 04 E6 56 FE 36 75 A6 F8
+0160 | F4 12 07 8F 3D 0B 58 DA 15 31 1C 1A 9F 8E 53 B3
+0170 | CD 6B B5 57 2C 29 49 04 B7 26 D0 BE 33 7E 2E 21
+0180 | 97 7D A2 6D D6 E3 32 70 25 1C 2C A2 9D FC C7 02
+0190 | 27 F0 75 5F 84 CF DA 9A C4 B8 DD 5F 84 F1 D1 EB
+01A0 | 36 BA 45 CD DC 70 44 4D 8C 21 3E 4B D8 F6 3B 8A
+01B0 | B9 5A 2D 0B 41 80 DC 91 28 3D C0 63 AC FB 92 D6
+01C0 | A4 E4 07 CD E7 C8 C6 96 89 F7 7A 00 74 41 D4 A6
+01D0 | A8 38 4B 66 65 02 D9 B7 7F C6 8B 5B 43 CC 60 7E
+01E0 | 60 A1 46 22 3E 11 0F CB 43 BC 3C 94 2E F9 81 93
+01F0 | 0C DC 4A 1D 31 0C 0B 64 D5 E5 5D 30 8D 86 32 51
+0200 | AB 90 50 2C 3E 46 CC 59 9E 88 6A 92 7C DA 96 3B
+0210 | 9E B1 6C E6 26 03 B6 85 29 EE 98 F9 F5 20 64 19
+0220 | E0 3F B4 58 EC 4B D9 45 4A A8 F6 BA 77 75 73 CC
+0230 | 54 B3 28 89 5B 1D F2 5E AD 9F B4 CD 51 98 EE 02
+0240 | 2B 2B 81 F3 88 D2 81 D5 E5 BC 58 01 07 CA 01 A5
+0250 | 06 65 C3 2B 55 27 15 F3 35 FD 76 26 4F AD 00 DD
+0260 | D5 AE 45 B9 48 32 AC 79 CE 7C 51 1D 19 4B C4 2B
+0270 | 70 EF A8 50 BB 15 C2 01 2C 52 15 CA BF E9 7C E6
+0280 | 6B 8D 87 34 D0 EE 75 9A 63 8A F0 13
+server_DH_params_fail#79cb045d nonce:int128 server_nonce:int128 new_nonce_hash:int128 = Server_DH_Params;
+server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params;
+Parameter | +Offset, Length in bytes | +Value | +Description | +
---|---|---|---|
auth_key_id | +0, 8 | +0 |
+Since message is in plain text | +
message_id | +8, 8 | +51E57ACB36435401 |
+Exact unixtime * 2^32 | +
message_length | +16, 4 | +632 |
+Message body length | +
%(server_DH_params_ok) | +20, 4 | +d0e8075c |
+server_DH_params_ok constructor number from TL schema | +
nonce | +24, 16 | +3E0549828CCA27E966B301A48FECE2FC |
+Value generated by client in Step 1 | +
server_nonce | +40, 16 | +A5CF4D33F4A11EA877BA4AA573907330 |
+Value received from server in Step 2 | +
encrypted_answer | +56, 596 | +See below | +See “Decomposition of encrypted_answer" | +
encrypted_answer = 28A92FE20173B347A8BB324B5FAB2667C9A8BBCE6468D5B509A4CBDDC186240AC912CF7006AF8926DE606A2E74C0493CAA57741E6C82451F54D3E068F5CCC49B4444124B9666FFB405AAB564A3D01E67F6E912867C8D20D9882707DC330B17B4E0DD57CB53BFAAFA9EF5BE76AE6C1B9B6C51E2D6502A47C883095C46C81E3BE25F62427B585488BB3BF239213BF48EB8FE34C9A026CC8413934043974DB03556633038392CECB51F94824E140B98637730A4BE79A8F9DAFA39BAE81E1095849EA4C83467C92A3A17D997817C8A7AC61C3FF414DA37B7D66E949C0AEC858F048224210FCC61F11C3A910B431CCBD104CCCC8DC6D29D4A5D133BE639A4C32BBFF153E63ACA3AC52F2E4709B8AE01844B142C1EE89D075D64F69A399FEB04E656FE3675A6F8F412078F3D0B58DA15311C1A9F8E53B3CD6BB5572C294904B726D0BE337E2E21977DA26DD6E33270251C2CA29DFCC70227F0755F84CFDA9AC4B8DD5F84F1D1EB36BA45CDDC70444D8C213E4BD8F63B8AB95A2D0B4180DC91283DC063ACFB92D6A4E407CDE7C8C69689F77A007441D4A6A8384B666502D9B77FC68B5B43CC607E60A146223E110FCB43BC3C942EF981930CDC4A1D310C0B64D5E55D308D863251AB90502C3E46CC599E886A927CDA963B9EB16CE62603B68529EE98F9F5206419E03FB458EC4BD9454AA8F6BA777573CC54B328895B1DF25EAD9FB4CD5198EE022B2B81F388D281D5E5BC580107CA01A50665C32B552715F335FD76264FAD00DDD5AE45B94832AC79CE7C511D194BC42B70EFA850BB15C2012C5215CABFE97CE66B8D8734D0EE759A638AF013
+tmp_aes_key = F011280887C7BB01DF0FC4E17830E0B91FBB8BE4B2267CB985AE25F33B527253
+tmp_aes_iv = 3212D579EE35452ED23E0D0C92841AA7D31B2E9BDEF2151E80D15860311C85DB
+answer = BA0D89B53E0549828CCA27E966B301A48FECE2FCA5CF4D33F4A11EA877BA4AA57390733002000000FE000100C71CAEB9C6B1C9048E6C522F70F13F73980D40238E3E21C14934D037563D930F48198A0AA7C14058229493D22530F4DBFA336F6E0AC925139543AED44CCE7C3720FD51F69458705AC68CD4FE6B6B13ABDC9746512969328454F18FAF8C595F642477FE96BB2A941D5BCD1D4AC8CC49880708FA9B378E3C4F3A9060BEE67CF9A4A4A695811051907E162753B56B0F6B410DBA74D8A84B2A14B3144E0EF1284754FD17ED950D5965B4B9DD46582DB1178D169C6BC465B0D6FF9CA3928FEF5B9AE4E418FC15E83EBEA0F87FA9FF5EED70050DED2849F47BF959D956850CE929851F0D8115F635B105EE2E4E15D04B2454BF6F4FADF034B10403119CD8E3B92FCC5BFE000100262AABA621CC4DF587DC94CF8252258C0B9337DFB47545A49CDD5C9B8EAE7236C6CADC40B24E88590F1CC2CC762EBF1CF11DCC0B393CAAD6CEE4EE5848001C73ACBB1D127E4CB93072AA3D1C8151B6FB6AA6124B7CD782EAF981BDCFCE9D7A00E423BD9D194E8AF78EF6501F415522E44522281C79D906DDB79C72E9C63D83FB2A940FF779DFB5F2FD786FB4AD71C9F08CF48758E534E9815F634F1E3A80A5E1C2AF210C5AB762755AD4B2126DFA61A77FA9DA967D65DFD0AFB5CDF26C4D4E1A88B180F4E0D0B45BA1484F95CB2712B50BF3F5968D9D55C99C0FB9FB67BFF56D7D4481B634514FBA3488C4CDA2FC0659990E8E868B28632875A9AA703BCDCE8FCB7AE551
+server_DH_inner_data#b5890dba nonce:int128 server_nonce:int128 g:int dh_prime:string g_a:string server_time:int = Server_DH_inner_data;
+Parameter | +Offset, Length in bytes | +Value | +Description | +
---|---|---|---|
%(server_DH_inner_data) | +0, 4 | +b5890dba |
+server_DH_inner_data constructor number from TL schema | +
nonce | +4, 16 | +3E0549828CCA27E966B301A48FECE2FC |
+Value generated by client in Step 1 | +
server_nonce | +20, 16 | +A5CF4D33F4A11EA877BA4AA573907330 |
+Value received from server in Step 2 | +
g | +36, 4 | +2 |
+Value received from server in Step 2 | +
dh_prime | +40, 260 | +C71CAEB9C6B1C9048E6C522F70F13F73 980D40238E3E21C14934D037563D930F 48198A0AA7C14058229493D22530F4DB FA336F6E0AC925139543AED44CCE7C37 20FD51F69458705AC68CD4FE6B6B13AB DC9746512969328454F18FAF8C595F64 2477FE96BB2A941D5BCD1D4AC8CC4988 0708FA9B378E3C4F3A9060BEE67CF9A4 A4A695811051907E162753B56B0F6B41 0DBA74D8A84B2A14B3144E0EF1284754 FD17ED950D5965B4B9DD46582DB1178D 169C6BC465B0D6FF9CA3928FEF5B9AE4 E418FC15E83EBEA0F87FA9FF5EED7005 0DED2849F47BF959D956850CE929851F 0D8115F635B105EE2E4E15D04B2454BF 6F4FADF034B10403119CD8E3B92FCC5B |
+|
g_a | +300, 260 | +262AABA621CC4DF587DC94CF8252258C 0B9337DFB47545A49CDD5C9B8EAE7236 C6CADC40B24E88590F1CC2CC762EBF1C F11DCC0B393CAAD6CEE4EE5848001C73 ACBB1D127E4CB93072AA3D1C8151B6FB 6AA6124B7CD782EAF981BDCFCE9D7A00 E423BD9D194E8AF78EF6501F415522E4 4522281C79D906DDB79C72E9C63D83FB 2A940FF779DFB5F2FD786FB4AD71C9F0 8CF48758E534E9815F634F1E3A80A5E1 C2AF210C5AB762755AD4B2126DFA61A7 7FA9DA967D65DFD0AFB5CDF26C4D4E1A 88B180F4E0D0B45BA1484F95CB2712B5 0BF3F5968D9D55C99C0FB9FB67BFF56D 7D4481B634514FBA3488C4CDA2FC0659 990E8E868B28632875A9AA703BCDCE8F |
+|
server_time | +560, 4 | +1373993675 |
+Server time | +
b = 6F620AFA575C9233EB4C014110A7BCAF49464F798A18A0981FEA1E05E8DA67D9681E0FD6DF0EDF0272AE3492451A84502F2EFC0DA18741A5FB80BD82296919A70FAA6D07CBBBCA2037EA7D3E327B61D585ED3373EE0553A91CBD29B01FA9A89D479CA53D57BDE3A76FBD922A923A0A38B922C1D0701F53FF52D7EA9217080163A64901E766EB6A0F20BC391B64B9D1DD2CD13A7D0C946A3A7DF8CEC9E2236446F646C42CFE2B60A2A8D776E56C8D7519B08B88ED0970E10D12A8C9E355D765F2B7BBB7B4CA9360083435523CB0D57D2B106FD14F94B4EEE79D8AC131CA56AD389C84FE279716F8124A543337FB9EA3D988EC5FA63D90A4BA3970E7A39E5C0DE5
+client_DH_inner_data#6643b654 nonce:int128 server_nonce:int128 retry_id:long g_b:string = Client_DH_Inner_Data
+Parameter | +Offset, Length in bytes | +Value | +Description | +
---|---|---|---|
%(client_DH_inner_data) | +0, 4 | +6643b654 |
+client_DH_inner_data constructor number from TL schema | +
nonce | +4, 16 | +3E0549828CCA27E966B301A48FECE2FC |
+Value generated by client in Step 1 | +
server_nonce | +20, 16 | +A5CF4D33F4A11EA877BA4AA573907330 |
+Value received from server in Step 2 | +
retry_id | +36, 8 | +0 |
+First attempt | +
g_b | +44, 260 | +73700E7BFC7AEEC828EB8E0DCC04D09A 0DD56A1B4B35F72F0B55FCE7DB7EBB72 D7C33C5D4AA59E1C74D09B01AE536B31 8CFED436AFDB15FE9EB4C70D7F0CB14E 46DBBDE9053A64304361EB358A9BB32E 9D5C2843FE87248B89C3F066A7D5876D 61657ACC52B0D81CD683B2A0FA93E8AD AB20377877F3BC3369BBF57B10F5B589 E65A9C27490F30A0C70FFCFD3453F5B3 79C1B9727A573CFFDCA8D23C721B135B 92E529B1CDD2F7ABD4F34DAC4BE1EEAF 60993DDE8ED45890E4F47C26F2C0B2E0 37BB502739C8824F2A99E2B1E7E41658 3417CC79A8807A4BDAC6A5E9805D4F61 86C37D66F6988C9F9C752896F3D34D25 529263FAF2670A09B2A59CE35264511F |
+g^b mod dh_prime |
+
The serialization of Client_DH_Inner_Data produces some string data. This is followed by encrypted_data:
+data_with_hash := SHA1(data) + data + (0-15 random bytes); such that the length be divisible by 16;
+AES256_ige_encrypt (data_with_hash, tmp_aes_key, tmp_aes_iv) = 928A4957D0463B525C1CC48AABAA030A256BE5C746792C84CA4C5A0DF60AC799048D98A38A8480EDCF082214DFC79DCB9EE34E206513E2B3BC1504CFE6C9ADA46BF9A03CA74F192EAF8C278454ADABC795A566615462D31817382984039505F71CB33A41E2527A4B1AC05107872FED8E3ABCEE1518AE965B0ED3AED7F67479155BDA8E4C286B64CDF123EC748CF289B1DB02D1907B562DF462D8582BA6F0A3022DC2D3504D69D1BA48B677E3A830BFAFD67584C8AA24E1344A8904E305F9587C92EF964F0083F50F61EAB4A393EAA33C9270294AEDC7732891D4EA1599F52311D74469D2112F4EDF3F342E93C8E87E812DC3989BAECFE6740A46077524C75093F5A5405736DE8937BB6E42C9A0DCF22CA53227D462BCCC2CFE94B6FE86AB7FBFA395021F66661AF7C0024CA2986CA03F3476905407D1EA9C010B763258DB1AA2CC7826D91334EFC1FDC665B67FE45ED0
+The length of the final string was 336 bytes.
+set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:string = Set_client_DH_params_answer;
+Parameter | +Offset, Length in bytes | +Value | +Description | +
---|---|---|---|
auth_key_id | +0, 8 | +0 |
+Since message is in plain text | +
message_id | +8, 8 | +51e57acd2aa32c6d |
+Exact unixtime * 2^32 | +
message_length | +16, 4 | +376 |
+Message body length | +
%(set_client_DH_params) | +20, 4 | +f5045f1f |
+set_client_DH_params constructor number from TL schema | +
nonce | +24, 16 | +3E0549828CCA27E966B301A48FECE2FC |
+Value generated by client in Step 1 | +
server_nonce | +40, 16 | +A5CF4D33F4A11EA877BA4AA573907330 |
+Value received from server in Step 2 | +
encrypted_data | +56, 340 | +See above | +See “Generation of encrypted_data" | +
0000 | 00 00 00 00 00 00 00 00 6D 2C A3 2A CD 7A E5 51
+0010 | 78 01 00 00 1F 5F 04 F5 3E 05 49 82 8C CA 27 E9
+0020 | 66 B3 01 A4 8F EC E2 FC A5 CF 4D 33 F4 A1 1E A8
+0030 | 77 BA 4A A5 73 90 73 30 FE 50 01 00 92 8A 49 57
+0040 | D0 46 3B 52 5C 1C C4 8A AB AA 03 0A 25 6B E5 C7
+0050 | 46 79 2C 84 CA 4C 5A 0D F6 0A C7 99 04 8D 98 A3
+0060 | 8A 84 80 ED CF 08 22 14 DF C7 9D CB 9E E3 4E 20
+0070 | 65 13 E2 B3 BC 15 04 CF E6 C9 AD A4 6B F9 A0 3C
+0080 | A7 4F 19 2E AF 8C 27 84 54 AD AB C7 95 A5 66 61
+0090 | 54 62 D3 18 17 38 29 84 03 95 05 F7 1C B3 3A 41
+00A0 | E2 52 7A 4B 1A C0 51 07 87 2F ED 8E 3A BC EE 15
+00B0 | 18 AE 96 5B 0E D3 AE D7 F6 74 79 15 5B DA 8E 4C
+00C0 | 28 6B 64 CD F1 23 EC 74 8C F2 89 B1 DB 02 D1 90
+00D0 | 7B 56 2D F4 62 D8 58 2B A6 F0 A3 02 2D C2 D3 50
+00E0 | 4D 69 D1 BA 48 B6 77 E3 A8 30 BF AF D6 75 84 C8
+00F0 | AA 24 E1 34 4A 89 04 E3 05 F9 58 7C 92 EF 96 4F
+0100 | 00 83 F5 0F 61 EA B4 A3 93 EA A3 3C 92 70 29 4A
+0110 | ED C7 73 28 91 D4 EA 15 99 F5 23 11 D7 44 69 D2
+0120 | 11 2F 4E DF 3F 34 2E 93 C8 E8 7E 81 2D C3 98 9B
+0130 | AE CF E6 74 0A 46 07 75 24 C7 50 93 F5 A5 40 57
+0140 | 36 DE 89 37 BB 6E 42 C9 A0 DC F2 2C A5 32 27 D4
+0150 | 62 BC CC 2C FE 94 B6 FE 86 AB 7F BF A3 95 02 1F
+0160 | 66 66 1A F7 C0 02 4C A2 98 6C A0 3F 34 76 90 54
+0170 | 07 D1 EA 9C 01 0B 76 32 58 DB 1A A2 CC 78 26 D9
+0180 | 13 34 EF C1 FD C6 65 B6 7F E4 5E D0
+g^{ab} mod dh_prime
:auth_key = AB96E207C631300986F30EF97DF55E179E63C112675F0CE502EE76D74BBEE6CBD1E95772818881E9F2FF54BD52C258787474F6A7BEA61EABE49D1D01D55F64FC07BC31685716EC8FB46FEACF9502E42CFD6B9F45A08E90AA5C2B5933AC767CBE1CD50D8E64F89727CA4A1A5D32C0DB80A9FCDBDDD4F8D5A1E774198F1A4299F927C484FEEC395F29647E43C3243986F93609E23538C21871DF50E00070B3B6A8FA9BC15628E8B43FF977409A61CEEC5A21CF7DFB5A4CC28F5257BC30CD8F2FB92FBF21E28924065F50E0BBD5E11A420300E2C136B80E9826C6C5609B5371B7850AA628323B6422F3A94F6DFDE4C3DC1EA60F7E11EE63122B3F39CBD1A8430157
+The key is unique.
+0000 | 00 00 00 00 00 00 00 00 01 30 AA C5 CE 7A E5 51
+0010 | 34 00 00 00 34 F7 CB 3B 3E 05 49 82 8C CA 27 E9
+0020 | 66 B3 01 A4 8F EC E2 FC A5 CF 4D 33 F4 A1 1E A8
+0030 | 77 BA 4A A5 73 90 73 30 CC EB C0 21 72 66 E1 ED
+0040 | EC 7F B0 A0 EE D6 C2 20
+Set_client_DH_params_answer decomposition using the following formula:
+dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer;
+dh_gen_retry#46dc1fb9 nonce:int128 server_nonce:int128 new_nonce_hash2:int128 = Set_client_DH_params_answer;
+dh_gen_fail#a69dae02 nonce:int128 server_nonce:int128 new_nonce_hash3:int128 = Set_client_DH_params_answer;
+Parameter | +Offset, Length in bytes | +Value | +Description | +
---|---|---|---|
%(dh_gen_ok) | +0, 4 | +3bcbf734 |
+dh_gen_ok constructor number from TL schema | +
nonce | +4, 16 | +3E0549828CCA27E966B301A48FECE2FC |
+Value generated by client in Step 1 | +
server_nonce | +20, 16 | +A5CF4D33F4A11EA877BA4AA573907330 |
+Value received from server in Step 2 | +
new_nonce_hash1 | +36, 16 | +CCEBC0217266E1EDEC7FB0A0EED6C220 |
+
See also:
+While MTProto is designed to be a reasonably fast and secure protocol, its advantages can be easily negated by careless implementation. We collected some security guidelines for client software developers on this page. All Telegram clients are required to comply.
+++Note that as of version 4.6, major Telegram clients are using MTProto 2.0. +MTProto v.1.0 is deprecated and is currently being phased out.
+
We use DH key exchange in two cases:
+ +In both cases, there are some verifications to be done whenever DH is used:
+Client is expected to check whether p = dh_prime is a safe 2048-bit prime (meaning that both p and (p-1)/2 are prime, and that 2^2047 < p < 2^2048), and that g generates a cyclic subgroup of prime order (p-1)/2, i.e. is a quadratic residue mod p. Since g is always equal to 2, 3, 4, 5, 6 or 7, this is easily done using quadratic reciprocity law, yielding a simple condition on p mod 4g -- namely, p mod 8 = 7 for g = 2; p mod 3 = 2 for g = 3; no extra condition for g = 4; p mod 5 = 1 or 4 for g = 5; p mod 24 = 19 or 23 for g = 6; and p mod 7 = 3, 5 or 6 for g = 7. After g and p have been checked by the client, it makes sense to cache the result, so as not to repeat lengthy computations in future.
+If the verification takes too long (which is the case for older mobile devices), one might initially run only 15 Miller--Rabin iterations (use parameter 30 in Java) for verifying primeness of p and (p - 1)/2 with error probability not exceeding one billionth, and do more iterations in the background later.
+Another way to optimize this is to embed into the client application code a small table with some known "good" couples (g,p) (or just known safe primes p, since the condition on g is easily verified during execution), checked during code generation phase, so as to avoid doing such verification during runtime altogether. The server rarely changes these values, thus one usually needs to put the current value of server's dh_prime into such a table. For example, the current value of dh_prime equals (in big-endian byte order)
+C7 1C AE B9 C6 B1 C9 04 8E 6C 52 2F 70 F1 3F 73 98 0D 40 23 8E 3E 21 C1 49 34 D0 37 56 3D 93 0F 48 19 8A 0A A7 C1 40 58 22 94 93 D2 25 30 F4 DB FA 33 6F 6E 0A C9 25 13 95 43 AE D4 4C CE 7C 37 20 FD 51 F6 94 58 70 5A C6 8C D4 FE 6B 6B 13 AB DC 97 46 51 29 69 32 84 54 F1 8F AF 8C 59 5F 64 24 77 FE 96 BB 2A 94 1D 5B CD 1D 4A C8 CC 49 88 07 08 FA 9B 37 8E 3C 4F 3A 90 60 BE E6 7C F9 A4 A4 A6 95 81 10 51 90 7E 16 27 53 B5 6B 0F 6B 41 0D BA 74 D8 A8 4B 2A 14 B3 14 4E 0E F1 28 47 54 FD 17 ED 95 0D 59 65 B4 B9 DD 46 58 2D B1 17 8D 16 9C 6B C4 65 B0 D6 FF 9C A3 92 8F EF 5B 9A E4 E4 18 FC 15 E8 3E BE A0 F8 7F A9 FF 5E ED 70 05 0D ED 28 49 F4 7B F9 59 D9 56 85 0C E9 29 85 1F 0D 81 15 F6 35 B1 05 EE 2E 4E 15 D0 4B 24 54 BF 6F 4F AD F0 34 B1 04 03 11 9C D8 E3 B9 2F CC 5B
+Apart from the conditions on the Diffie-Hellman prime dh_prime and generator g, both sides are to check that g, g_a and g_b are greater than 1 and less than dh_prime - 1. We recommend checking that g_a and g_b are between 2^{2048-64} and dh_prime - 2^{2048-64} as well.
+Once the client receives a server_DH_params_ok
answer in step 5) of the Authorization Key generation protocol and decrypts it obtaining answer_with_hash
, it MUST check that
answer_with_hash := SHA1(answer) + answer + (0-15 random bytes)
+In other words, the first 20 bytes of answer_with_hash
must be equal to SHA1 of the remainder of the decrypted message without the padding random bytes.
When the client receives and/or decrypts server messages during creation of Authorization Key, and these messages contain some nonce fields already known to the client from messages previously obtained during the same run of the protocol, the client is to check that these fields indeed contain the values previosly known.
+a
and b
Client must use a cryptographically secure PRNG to generate secret exponents a
or b
for DH key exchange. For secret chats, the client might request some entropy (random bytes) from the server while invoking messages.getDhConfig and feed these random bytes into its PRNG (for example, by PRNG_seed
if OpenSSL library is used), but never using these "random" bytes by themselves or replacing by them the local PRNG seed. One should mix bytes received from server into local PRNG seed.
Some important checks are to be done while sending and especially receiving encrypted MTProto messages.
+msg_key
is used not only to compute the AES key and IV to decrypt the received message. After decryption, the client MUST check that msg_key
is indeed equal to SHA256 of the plaintext obtained as the result of decryption (including the final 12...1024 padding bytes), prepended with 32 bytes taken from the auth_key
, as explained in MTProto 2.0 Description.
If an error is encountered before this check could be performed, the client must perform the msg_key
check anyway before returning any result. Note that the response to any error encountered before the msg_key
check must be the same as the response to a failed msg_key
check.
The client must check that the length of the message or container obtained from the decrypted message (computed from its length
field) does not exceed the total size of the plaintext, and that the difference (i.e. the length of the random padding) lies in the range from 12 to 1024 bytes.
The length should be always divisible by 4 and non-negative. On no account the client is to access data past the end of the decryption buffer containing the plaintext message.
+The client is to check that the session_id
field in the decrypted message indeed equals to that of an active session created by the client.
The client must check that msg_id
has even parity for messages from client to server, and odd parity for messages from server to client.
In addition, the identifiers (msg_id) of the last N messages received from the other side must be stored, and if a message comes in with an msg_id lower than all or equal to any of the stored values, that message is to be ignored. Otherwise, the new message msg_id is added to the set, and, if the number of stored msg_id values is greater than N, the oldest (i. e. the lowest) is discarded.
+In addition, msg_id values that belong over 30 seconds in the future or over 300 seconds in the past are to be ignored (recall that msg_id
approximately equals unixtime * 2^32). This is especially important for the server. The client would also find this useful (to protect from a replay attack), but only if it is certain of its time (for example, if its time has been synchronized with that of the server).
Certain client-to-server service messages containing data sent by the client to the server (for example, msg_id
of a recent client query) may, nonetheless, be processed on the client even if the time appears to be “incorrect”. This is especially true of messages to change server_salt and notifications about invalid time on the client. See Mobile Protocol: Service Messages.
If one of the checks listed above fails, the client is to completely discard the message obtained from server. We also recommend closing and reestablishing the TCP connection to the server, then retrying the operation or the whole key generation protocol.
+No information from incorrect messages can be used. Even if the application throws an exception and dies, this is much better than continuing with invalid data.
+Notice that invalid messages will infrequently appear during normal work even if no malicious tampering is being done. This is due to network transmission errors. We recommend ignoring the invalid message and closing the TCP connection, then creating a new TCP connection to the server and retrying the original query.
++The previous version of security recommendations relevant for MTProto 1.0 clients is available here.
+
++This document describes security recommendations for clients using MTProto 1.0, its status is DEPRECATED. +For up-to-date security recommendations, kindly see this document.
+
See also:
+While MTProto is designed to be a reasonably fast and secure protocol, its advantages can be easily negated by careless implementation. We collected some security guidelines for client software developers on this page. All Telegram clients are required to comply with these.
+We use DH key exchange in two cases:
+ +In both cases, there are some verifications to be done whenever DH is used:
+Client is expected to check whether p = dh_prime is a safe 2048-bit prime (meaning that both p and (p-1)/2 are prime, and that 2^2047 < p < 2^2048), and that g generates a cyclic subgroup of prime order (p-1)/2, i.e. is a quadratic residue mod p. Since g is always equal to 2, 3, 4, 5, 6 or 7, this is easily done using quadratic reciprocity law, yielding a simple condition on p mod 4g -- namely, p mod 8 = 7 for g = 2; p mod 3 = 2 for g = 3; no extra condition for g = 4; p mod 5 = 1 or 4 for g = 5; p mod 24 = 19 or 23 for g = 6; and p mod 7 = 3, 5 or 6 for g = 7. After g and p have been checked by the client, it makes sense to cache the result, so as not to repeat lengthy computations in future.
+If the verification takes too long (which is the case for older mobile devices), one might initially run only 15 Miller--Rabin iterations (use parameter 30 in Java) for verifying primeness of p and (p - 1)/2 with error probability not exceeding one billionth, and do more iterations in the background later.
+Another way to optimize this is to embed into the client application code a small table with some known "good" couples (g,p) (or just known safe primes p, since the condition on g is easily verified during execution), checked during code generation phase, so as to avoid doing such verification during runtime altogether. The server rarely changes these values, thus one usually needs to put the current value of server's dh_prime into such a table. For example, the current value of dh_prime equals (in big-endian byte order)
+C7 1C AE B9 C6 B1 C9 04 8E 6C 52 2F 70 F1 3F 73 98 0D 40 23 8E 3E 21 C1 49 34 D0 37 56 3D 93 0F 48 19 8A 0A A7 C1 40 58 22 94 93 D2 25 30 F4 DB FA 33 6F 6E 0A C9 25 13 95 43 AE D4 4C CE 7C 37 20 FD 51 F6 94 58 70 5A C6 8C D4 FE 6B 6B 13 AB DC 97 46 51 29 69 32 84 54 F1 8F AF 8C 59 5F 64 24 77 FE 96 BB 2A 94 1D 5B CD 1D 4A C8 CC 49 88 07 08 FA 9B 37 8E 3C 4F 3A 90 60 BE E6 7C F9 A4 A4 A6 95 81 10 51 90 7E 16 27 53 B5 6B 0F 6B 41 0D BA 74 D8 A8 4B 2A 14 B3 14 4E 0E F1 28 47 54 FD 17 ED 95 0D 59 65 B4 B9 DD 46 58 2D B1 17 8D 16 9C 6B C4 65 B0 D6 FF 9C A3 92 8F EF 5B 9A E4 E4 18 FC 15 E8 3E BE A0 F8 7F A9 FF 5E ED 70 05 0D ED 28 49 F4 7B F9 59 D9 56 85 0C E9 29 85 1F 0D 81 15 F6 35 B1 05 EE 2E 4E 15 D0 4B 24 54 BF 6F 4F AD F0 34 B1 04 03 11 9C D8 E3 B9 2F CC 5B
+Apart from the conditions on the Diffie-Hellman prime dh_prime and generator g, both sides are to check that g, g_a and g_b are greater than 1 and less than dh_prime - 1. We recommend checking that g_a and g_b are between 2^{2048-64} and dh_prime - 2^{2048-64} as well.
+Once the client receives a server_DH_params_ok
answer in step 5) of the Authorization Key generation protocol and decrypts it obtaining answer_with_hash
, it MUST check that
answer_with_hash := SHA1(answer) + answer + (0-15 random bytes)
+In other words, the first 20 bytes of answer_with_hash
must be equal to SHA1 of the remainder of the decrypted message without the padding random bytes.
When the client receives and/or decrypts server messages during creation of Authorization Key, and these messages contain some nonce fields already known to the client from messages previously obtained during the same run of the protocol, the client is to check that these fields indeed contain the values previosly known.
+a
and b
Client must use a cryptographically secure PRNG to generate secret exponents a
or b
for DH key exchange. For secret chats, the client might request some entropy (random bytes) from the server while invoking messages.getDhConfig and feed these random bytes into its PRNG (for example, by PRNG_seed
if OpenSSL library is used), but never using these "random" bytes by themselves or replacing by them the local PRNG seed. One should mix bytes received from server into local PRNG seed.
Some important checks are to be done while sending and especially receiving encrypted MTProto messages.
+msg_key
is used not only to compute the AES key and IV to decrypt the received message. After decryption, the client MUST check that msg_key
is indeed equal to SHA1 of the plaintext obtained as the result of decryption (without the final padding bytes).
If an error is encountered before this check could be performed, the client must perform the msg-key
check anyway before returning any result. Note that the response to any error encountered before the msg_key
check must be the same as the response to a failed msg_key
check.
The client is to check that the length of the message or container obtained from the decrypted message (computed from its length
field) does not exceed the total size of the plaintext, and that the difference is not more than 15 bytes. Apart from this, knowing the total length is important for the previous verification.
The length should be always divisible by 4 and non-negative. On no account the client is to access data past the end of the decryption buffer containing the plaintext message.
+The client is to check that the session_id
field in the decrypted message indeed equals to that of an active session created by the client.
The client must check that msg_id
has even parity for messages from client to server, and odd parity for messages from server to client.
In addition, the identifiers (msg_id) of the last N messages received from the other side must be stored, and if a message comes in with an msg_id lower than all or equal to any of the stored values, that message is to be ignored. Otherwise, the new message msg_id is added to the set, and, if the number of stored msg_id values is greater than N, the oldest (i. e. the lowest) is discarded.
+In addition, msg_id values that belong over 30 seconds in the future or over 300 seconds in the past are to be ignored (recall that msg_id
approximately equals unixtime * 2^32). This is especially important for the server. The client would also find this useful (to protect from a replay attack), but only if it is certain of its time (for example, if its time has been synchronized with that of the server).
Certain client-to-server service messages containing data sent by the client to the server (for example, msg_id
of a recent client query) may, nonetheless, be processed on the client even if the time appears to be “incorrect”. This is especially true of messages to change server_salt and notifications about invalid time on the client. See Mobile Protocol: Service Messages.
If one of the checks listed above fails, the client is to completely discard the message obtained from server. We also recommend closing and reestablishing the TCP connection to the server, then retrying the operation or the whole key generation protocol.
+No information from incorrect messages can be used. Even if the application throws an exception and dies, this is much better than continuing with invalid data.
+Notice that invalid messages will infrequently appear during normal work even if no malicious tampering is being done. This is due to network transmission errors. We recommend ignoring the invalid message and closing the TCP connection, then creating a new TCP connection to the server and retrying the original query.
MTProto operation requires that elementary and composite data types as well as queries to which such data types are passed as arguments or by which they are returned, be transmitted in binary format (i. e. serialized) . +The TL language is used to describe the data types to be serialized.
+For our purposes, we can identify a type with the set of its (serialized) values understood as strings (finite sequences) of 32-bit numbers (transmitted in little endian order).
+Therefore:
+Combinator is a function that takes arguments of certain types and returns a value of some other type. We normally look at combinators whose argument and result types are data types (rather than functional types).
+Arity (of combinator) is a non-negative integer, the number of combinator arguments.
+Combinator identifier is an identifier beginning with a lowercase Roman letter that uniquely identifies a combinator.
+Combinator number or combinator name is a 32-bit number (i.e., an element of A) that uniquely identifies a combinator. Most often, it is CRC32 of the string containing the combinator description without the final semicolon, and with one space between contiguous lexemes. This always falls in the range from 0x01000000 to 0xffffff00. The highest 256 values are reserved for the so-called temporal-logic combinators used to transmit functions. We frequently denote as combinator the combinator name with single quotes: ‘combinator’.
+Combinator description is a string of format combinator_name type_arg_1 ... type_arg_N = type_res;
where N
stands for the arity of the combinator, type_arg_i
is the type of the i-th argument (or rather, a string with the combinator name), and type_res
is the combinator value type.
Constructor is a combinator that cannot be computed (reduced). This is used to represent composite data types. For example, combinator ‘int_tree’ with description int_tree IntTree int IntTree = IntTree
, alongside combinator empty_tree = IntTree
, may be used to define a composite data type called “IntTree” that takes on values in the form of binary trees with integers as nodes.
Function (functional combinator) is a combinator which may be computed (reduced) on condition that the requisite number of arguments of requisite types are provided. The result of the computation is an expression consisting of constructors and base type values only.
+Normal form is an expression consisting only of constructors and base type values; that which is normally the result of computing a function.
+Type identifier is an identifier that normally starts with a capital letter in Roman script and uniquely identifies the type.
+Type number or type name is a 32-bit number that uniquely identifies a type; it normally is the sum of the CRC32 values of the descriptions of the type constructors.
+Description of (composite) Type T is a collection of the descriptions of all constructors that take on Type T values. This is normally written as text with each string containing the description of a single constructor. Here is a description of Type ‘IntTree’, for example:
+int_tree IntTree int IntTree = IntTree; +empty_tree = IntTree;
+Polymorphic type is a type whose description contains parameters (type variables) in lieu of actual types; approximately, what would be a template in C++. Here is a description of Type List alpha
where List
is a polymorphic type of arity 1 (i. e., dependent on a single argument), and alpha
is a type variable which appears as the constructor’s optional parameter (in curly braces):
cons {alpha:Type} alpha (List alpha) = List alpha; +nil {alpha:Type} = List alpha;
+Value of (composite) Type T is any sequence from A* in the format constr_num arg1 ... argN
, where constr_num is the index number of some Constructor C which takes on values of Type T, and arg_i is a value of Type T_i which is the type of the i-th argument to Constructor C. For example, let Combinator int_tree have the index number 17, whereas Combinator empty_tree has the index number 239. Then, the value of Type IntTree
is, for example, 17 17 239 1 239 2 239
which is more conveniently written as 'int_tree' 'int_tree' 'empty_tree' 1 'empty_tree' 2 ‘empty_tree’
. From the standpoint of a high-level language, this is int_tree (int_tree (empty_tree) 1 (empty_tree)) 2 (empty_tree): IntTree
.
Schema is a collection of all the (composite) data type descriptions. This is used to define some agreed-to system of types.
+X
is a boxed type with no more than a single constructor, then %X
refers to the corresponding bare type. The values of a bare type are identical with the set of number sequences obtained by dropping the first number (i. e., the external constructor index number) from the set of values of the corresponding boxed type (which is the result type of the selected constructor), starting with the selected constructor index number. For example, 3 4
is a value of the int_couple
bare type, defined using int_couple int int = IntCouple
. The corresponding boxed type is IntCouple
; if 404 is the constructor index number for int_couple
, then 404 3 4
is the value for the IntCouple
boxed type which corresponds to the value of the bare type int_couple
(also known as %int_couple
and %IntCouple
; the latter form is conceptually preferable but longer).Conceptually, only boxed types should be used everywhere. However, for speed and compactness, bare types have to be used (for instance, an array of 10,000 bare int values is 40,000 bytes long, whereas boxed Int values take up twice as much space; therefore, when transmitting a large array of integer identifiers, say, it is more efficient to use the Vector int
type rather than Vector Int
). In addition, all base types (int, long, double, string) are bare.
If a boxed type is polymorphic of type arity r, this is also true of any derived bare type. In other words, if one were to define intCouple {alpha:Type} int alpha = IntCouple alpha
, then, thereafter, intCouple as an identifier would also be a polymorphic type of arity 1 in combinator (and consequently, in constructor and type) descriptions. The notations intCouple X
, %(IntCouple X)
, and %IntCouple X
are equivalent.
Base types exist both as bare (int, long, double, string) and as boxed (Int, Long, Double, String) versions. Their constructor identifiers coincide with the names of the relevant bare types. Their pseudodescriptions have the following appearance:
+int ? = Int;
+long ? = Long;
+double ? = Double;
+string ? = String;
+Consequently, the int
constructor index number, for example, is the CRC32 of the string "int ? = Int"
.
The values of bare type int
are exactly all the single-element sequences, i. e. numbers between -2^31 and 2^31-1 represent themselves in this case. Values of type long
are two-element sequences that are 64-bit signed numbers (little endian again). Values of type double
, again, are two-element sequences containing 64-bit real numbers in a standard double format. And finally, the values of type string
look differently depending on the length L of the string being serialized:
The Object
pseudotype is a “type” which can take on values that belong to any boxed type in the schema. This helps quickly define such types as list of random items without using polymorphic types. It is best not to abuse this capability since it results in the use of dynamic typing. Nonetheless, it is hard to imagine the data structures that we know from PHP and JSON without using the Object pseudotype.
It is recommended to use TypedObject
instead whenever possible:
object X:Type value:X = TypedObject;
+The Vector t polymorphic pseudotype is a “type” whose value is a sequence of values of any type t, either boxed or bare.
+vector {t:Type} # [ t ] = Vector t;
+Serialization always uses the same constructor “vector” (const 0x1cb5c415 = crc32("vector t:Type # [ t ] = Vector t”) that is not dependent on the specific value of the variable of type t. The value of the Vector t type is the index number of the relevant constructor number followed by N, the number of elements in the vector, and then by N values of type t. The value of the optional parameter t is not involved in the serialization since it is derived from the result type (always known prior to deserialization).
+Polymorphic pseudotypes IntHash t and StrHash t are associative arrays mapping integer and string keys to values of type t. They are, in fact, vectors containing bare 2-tuples (int, t) or (string, t):
+coupleInt {t:Type} int t = CoupleInt t;
+intHash {t:Type} (vector %(CoupleInt t)) = IntHash t;
+coupleStr {t:Type} string t = CoupleStr t;
+strHash {t:Type} (vector %(CoupleStr t)) = StrHash t;
+The percentage sign, in this case, means that a bare type that corresponds to the boxed type in parentheses is taken; the boxed type in question must have no more than a single constructor, whatever the values of the parameters.
+The keys may be sorted or be in some other order (as in PHP arrays). For associative arrays with sorted keys, the IntSortedHash or StrSortedHash alias is used:
+intSortedHash {t:Type} (intHash t) = IntSortedHash t;
+strSortedHash {t:Type} (strHash t) = StrSortedHash t;
+The constructor of a polymorphic type does not depend on the specific types to which the polymorphic type is applied. When it is computed, optional parameters (normally containing type variables and placed in curly braces) cease to be optional (the curly braces are removed), and, in addition to that, all parenthesis are also removed. Therefore,
+vector {t:Type} # [ t ] = Vector t;
+corresponds to the constructor number crc32("vector t:Type # [ t ] = Vector t") = 0x1cb5c415. During (de)serialization, the specific values of the optional variable t are derived from the result type (i. e. the object being serialized or deserialized) that is always known, and are never serialized explicitly.
+Previously, it had to be known which specific variable types each polymorphic type will apply to. To accomplish this, the type system used strings of the form
+polymorphic_type_name type_1 ... type_N;
+For example,
+Vector int;
+Vector string;
+Vector Object;
+Now they are ignored.
+See also polymorphism in TL.
+In this case, the Object pseudotype permits using Vector Object to store lists of anything (the values of any boxed types). Since bare types are efficient when short, in practice it is unlikely that cases more complex than the ones cited above will be required.
+Let us say that we need to represent users as triplets containing one integer (user ID) and two strings (first and last names). The requisite data structure is the triplet int, string, string which may be declared as follows:
+user int string string = User;
+On the other hand, a group may be described by a similar triplet consisting of a group ID, its name, and description:
+group int string string = Group;
+For the difference between User and Group to be clear, it is convenient to assign names to some or all of the fields:
+user id:int first_name:string last_name:string = User;
+group id:int title:string description:string = Group;
+If the User type needs to be extended at a later time by having records with some additional field added to it, it could be accomplished as follows:
+userv2 id:int unread_messages:int first_name:string last_name:string in_groups:vector int = User;
+Aside from other things, this approach helps define correct mappings between fields that belong to different constructors of the same type, convert between them as well as convert type values into an associative array with string keys (field names, if defined, are natural choices for such keys).
+See TL Language
A response to an RPC query is normally wrapped as follows:
+rpc_result#f35c6d01 req_msg_id:long result:Object = RpcResult;
+Here req_msg_id is the identifier of the message sent by the other party and containing an RPC query. This way, the recipient knows that the result is a response to the specific RPC query in question. +At the same time, this response serves as acknowledgment of the other party’s receipt of the req_msg_id message.
+Note that the response to an RPC query must also be acknowledged. Most frequently, this coincides with the transmission of the next message (which may have a container attached to it carrying a service message with the acknowledgment).
+The result field returned in response to any RPC query may also contain an error message in the following format:
+rpc_error#2144ca19 error_code:int error_message:string = RpcError;
+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 TCP 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 msg_id prior to receiving the response; the only thing it knows is the req_msg_id. i. e. the msg_id of the relevant RPC query. Therefore, a special query is used:
+rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;
+The response to this query returns as one of the following messages wrapped in rpc_result and requiring an acknowledgment:
+rpc_answer_unknown#5e2ad36e = RpcDropAnswer;
+rpc_answer_dropped_running#cd78e586 = RpcDropAnswer;
+rpc_answer_dropped#a43ad8b7 msg_id:long seq_no:int bytes:int = RpcDropAnswer;
+The first version of the response is used if the server remembers nothing of the incoming req_msg_id (if it has already been responded to, for example). The second version is used if the 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. The final version means that the RPC response was removed from the server’s outgoing queue, and its msg_id, seq_no, and length in bytes are transmitted to the client.
+Note that rpc_answer_dropped_running and rpc_answer_dropped serve as acknowledgments of the server’s receipt of the original query (the same one, the response to which we wish to forget). In addition, same as for any RPC queries, any response to rpc_drop_answer is an acknowledgment for rpc_drop_answer itself.
+As an alternative to using rpc_drop_answer, a new session may be created after the connection is reset and the old session is removed through destroy_session.
+See Mobile Protocol: Service Messages about Messages
+The client may at any time request from the server several (between 1 and 64) future server salts together with their validity periods. Having stored them in persistent memory, the client may use them to send messages in the future even if it changes sessions (a server salt is attached to the authorization key rather than being session-specific).
+get_future_salts#b921bd04 num:int = FutureSalts;
+future_salt#0949d9dc valid_since:int valid_until:int salt:long = FutureSalt;
+future_salts#ae500895 req_msg_id:long now:int salts:vector<future_salt> = FutureSalts;
+The client must check to see that the response’s req_msg_id in fact coincides with msg_id of the query for get_future_salts. The server returns a maximum of num future server salts (may return fewer). The response serves as the acknowledgment of the query and does not require an acknowledgment itself.
+ping#7abe77ec ping_id:long = Pong;
+A response is usually returned to the same connection:
+pong#347773c5 msg_id:long ping_id:long = Pong;
+These messages do not require acknowledgments. A pong is transmitted only in response to a ping while a ping can be initiated by either side.
+ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong;
+Works like ping. In addition, after this is received, the server starts a timer which will close the current connection disconnect_delay 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.
+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 auth_key_id). The result of this being applied to the current session is undefined.
+destroy_session#e7512126 session_id:long = DestroySessionRes;
+destroy_session_ok#e22045fc session_id:long = DestroySessionRes;
+destroy_session_none#62d350c9 session_id:long = DestroySessionRes;
+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.
+new_session_created#9ec20908 first_msg_id:long unique_id:long server_salt:long = NewSession
+The unique_id parameter is generated by the server every time a session is (re-)created.
+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 long poll notifications received from the server (the user may have failed to receive notifications during some period of time).
+Notice that the server may unilaterally destroy (close) any existing client sessions with all pending messages and notifications, without sending any notifications. This happens, for example, if the session is inactive for a long time, and the server runs out of memory. If the client at some point decides to send new messages to the server using the old session, already forgotten by the server, such a "new session created" notification will be generated. The client is expected to handle such situations gracefully.
+Containers are messages containing several other messages. Used for the ability to transmit several RPC queries and/or service messages at the same time, using HTTP or even TCP or UDP protocol. A container may only be accepted or rejected by the other party as a whole.
+A simple container carries several messages as follows:
+msg_container#73f1f8dc messages:vector message = MessageContainer;
+Here message refers to any message together with its length and msg_id:
+message msg_id:long seqno:int bytes:int body:Object = Message;
+bytes
is the number of bytes in the body serialization.
+All messages in a container must have msg_id lower than that of the container itself. A container does not require an acknowledgment and may not carry other simple containers. When messages are re-sent, they may be combined into a container in a different manner or sent individually.
MTProto container payloads have a limit of 2^15
bytes or 1020
messages, whichever limit is reached first.
+Clients should group acknowledgments, state requests and message resend requests into three separate msgs_ack
, msgs_state_req
, msg_resend_req
service messages with at most 8192 IDs each; these three messages (+http_wait) will not be included in the 1020
message limit.
Empty containers are also allowed. They are used by the server, for example, to respond to an HTTP request when the timeout specified in http_wait expires, and there are no messages to transmit.
+ +In some situations, an old message with a msg_id that is no longer valid needs to be re-sent. Then, it is wrapped in a copy container:
+msg_copy#e06046b2 orig_message:Message = MessageCopy;
+Once received, the message is processed as if the wrapper were not there. However, if it is known for certain that the message orig_message.msg_id was received, then the new message is not processed (while at the same time, it and orig_message.msg_id are acknowledged). The value of orig_message.msg_id must be lower than the container’s msg_id.
+This is not used at this time, because an old message can be wrapped in a simple container with the same result.
+Used to replace any other object (or rather, a serialization thereof) with its archived (gzipped) representation:
+gzip_packed#3072cfa1 packed_data:string = Object;
+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.
+The following special service query not requiring an acknowledgement (which must be transmitted only through an HTTP connection) is used to enable the server to send messages in the future to the client using HTTP protocol:
+http_wait#9299359f max_delay:int wait_after:int max_wait:int = HttpWait;
+When such a message (or a container carrying such a message) is received, the server either waits max_delay
milliseconds, whereupon it forwards all the messages that it is holding on to the client if there is at least one message queued in session (if needed, by placing them into a container to which acknowledgments may also be added); or else waits no more than max_wait
milliseconds until such a message is available. If a message never appears, an empty container is transmitted.
The max_delay
parameter denotes the maximum number of milliseconds that has elapsed between the first message for this session and the transmission of an HTTP response. The wait_after
parameter works as follows: after the receipt of the latest message for a particular session, the server waits another wait_after
milliseconds in case there are more messages. If there are no additional messages, the result is transmitted (a container with all the messages). If more messages appear, the wait_after
timer is reset.
At the same time, the max_delay
parameter has higher priority than wait_after
, and max_wait
has higher priority than max_delay
.
This message does not require a response or an acknowledgement. If the container transmitted over HTTP carries several such messages, the behavior is undefined (in fact, the latest parameter will be used).
+If no http_wait
is present in container, default values max_delay=0
(milliseconds), wait_after=0
(milliseconds), and max_wait=25000
(milliseconds) are used.
If the client’s ping of the server takes a long time, it may make sense to set max_delay
to a value that is comparable in magnitude to ping time.
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 following service message (not requiring an acknowledgment):
+msgs_ack#62d6b459 msg_ids:Vector<long> = MsgsAck;
+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.
+Max 8192 IDs are allowed per constructor.
+In certain cases, a server may notify a client that its incoming message was ignored for whatever reason. Note that such a notification cannot be generated unless a message is correctly decoded by the server.
+bad_msg_notification#a7eff811 bad_msg_id:long bad_msg_seqno:int error_code:int = BadMsgNotification;
+bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int error_code:int new_server_salt:long = BadMsgNotification;
+Here, error_code can also take on the following values:
+The intention is that error_code values are grouped (error_code >> 4): for example, the codes 0x40 - 0x4f correspond to errors in container decomposition.
+Notifications of an ignored message do not require acknowledgment (i.e., are irrelevant).
+Important: if server_salt has changed on the server or if client time is incorrect, any query will result in a notification in the above format. The client must check that it has, in fact, recently sent a message with the specified msg_id, and if that is the case, update its time correction value (the difference between the client’s and the server’s clocks) and the server salt based on msg_id and the server_salt notification, so as to use these to (re)send future messages. In the meantime, the original message (the one that caused the error message to be returned) must also be re-sent with a better msg_id and/or server_salt.
+In addition, the client can update the server_salt value used to send messages to the server, based on the values of RPC responses or containers carrying an RPC response, provided that this RPC response is actually a match for the query sent recently. (If there is doubt, it is best not to update since there is risk of a replay attack).
+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:
+msgs_state_req#da69fb52 msg_ids:Vector long = MsgsStateReq;
+Max 8192 IDs are allowed per constructor.
+The response to the query contains the following information:
+msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo;
+Here, info
is a string that contains exactly one byte of message status for each message from the incoming msg_ids list:
This response does not require an acknowledgment. It is an acknowledgment of the relevant msgs_state_req, in and of itself.
+Note that if it turns out suddenly that the other party does not have a message that looks like it has been sent to it, the message can simply be re-sent. Even if the other party should receive two copies of the message at the same time, the duplicate will be ignored. (If too much time has passed, and the original msg_id is not longer valid, the message is to be wrapped in msg_copy).
+Either party may voluntarily inform the other party of the status of the messages transmitted by the other party.
+msgs_all_info#8cc0d131 msg_ids:Vector long info:string = MsgsAllInfo
+All message codes known to this party are enumerated, with the exception of those for which the +128 and the +16 flags are set. However, if the +32 flag is set but not +64, then the message status will still be communicated.
+This message does not require an acknowledgment.
+Normally used by the server to respond to the receipt of a duplicate msg_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.
+msg_detailed_info#276d3ec6 msg_id:long answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
+msg_new_detailed_info#809db6df answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
+The second version is used to notify of messages that were created on the server not in response to an RPC query (such as notifications of new messages) and were transmitted to the client some time ago, but not acknowledged.
+Currently, status
is always zero. This may change in future.
This message does not require an acknowledgment.
+msg_resend_req#7d861a08 msg_ids:Vector long = MsgResendReq;
+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 msg_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.
+Max 8192 IDs are allowed per constructor.
msg_resend_ans_req#8610baeb msg_ids:Vector long = MsgResendReq;
+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.
++This document describes MTProto v1.0, its status is DEPRECATED.
+
For information on encryption used in up-to-date Telegram clients, kindly see this document.
This page deals with the basic layer of MTProto encryption used for Cloud chats (server-client encryption). See also:
+ +The protocol is designed for access to a server API from applications running on mobile devices. It must be emphasized that a web browser is not such an application.
+The protocol is subdivided into three virtually independent components:
+++Got questions about this setup? — Check out the Advanced FAQ!
+
Each plaintext message to be encrypted in MTProto always contains the following data to be checked upon decryption in order to make the system robust against known problems with the components:
+See additional comments on our use of IGE, SHA-1 and message authentication.
+Telegram's End-to-end encrypted Secret Chats are using an additional layer of encryption on top of the described above. See Secret Chats, End-to-End encryption for details.
+From the standpoint of the high-level component, the client and the server exchange messages inside a session. The session is attached to the client device (the application, to be more exact) rather than a specific http/https/tcp connection. In addition, each session is attached to a user key ID by which authorization is actually accomplished.
+Several connections to a server may be open; messages may be sent in either direction through any of the connections (a response to a query is not necessarily returned through the same connection that carried the original query, although most often, that is the case; however, in no case can a message be returned through a connection belonging to a different session). When the UDP protocol is used, a response might be returned by a different IP address than the one to which the query had been sent.
+There are several types of messages:
+From the standpoint of lower level protocols, a message is a binary data stream aligned along a 4 or 16-byte boundary. The first several fields in the message are fixed and are used by the cryptographic/authorization system.
+Each message, either individual or inside a container, consists of a message identifier (64 bits, see below), a message sequence number within a session (32 bits), the length (of the message body in bytes; 32 bits), and a body (any size which is a multiple of 4 bytes). In addition, when a container or a single message is sent, an internal header is added at the top (see below), then the entire message is encrypted, and an external header is placed at the top of the message (a 64-bit key identifier and a 128-bit message key).
+A message body normally consists of a 32-bit message type followed by type-dependent parameters. In particular, each RPC function has a corresponding message type. For more detail, see Binary Data Serialization, Mobile Protocol: Service Messages.
+All numbers are written as little endian. However, very large numbers (2048-bit) used in RSA and DH are written in the big endian format because that is what the OpenSSL library does.
+Prior to a message (or a multipart message) being transmitted over a network using a transport protocol, it is encrypted in a certain way, and an external header is added at the top of the message which is: a 64-bit key identifier (that uniquely identifies an authorization key for the server as well as the user) and a 128-bit message key. A user key together with the message key defines an actual 256-bit key which is what encrypts the message using AES-256 encryption. Note that the initial part of the message to be encrypted contains variable data (session, message ID, sequence number, server salt) that obviously influences the message key (and thus the AES key and iv). The message key is defined as the 128 lower-order bits of the SHA1 of the message body (including session, message ID, etc.). Multipart messages are encrypted as a single message.
+For a technical specification, see Mobile Protocol: Detailed Description
The first thing a client application must do is create an authorization key which is normally generated when it is first run and almost never changes.
The protocol’s principal drawback is that an intruder passively intercepting messages and then somehow appropriating the authorization key (for example, by stealing a device) will be able to decrypt all the intercepted messages post factum. This probably is not too much of a problem (by stealing a device, one could also gain access to all the information cached on the device without decrypting anything); however, the following steps could be taken to overcome this weakness:
+If client time diverges widely from server time, a server may start ignoring client messages, or vice versa, because of an invalid message identifier (which is closely related to creation time). Under these circumstances, the server will send the client a special message containing the correct time and a certain 128-bit salt (either explicitly provided by the client in a special RPC synchronization request or equal to the key of the latest message received from the client during the current session). This message could be the first one in a container that includes other messages (if the time discrepancy is significant but does not as yet result in the client’s messages being ignored).
+Having received such a message or a container holding it, the client first performs a time synchronization (in effect, simply storing the difference between the server’s time and its own to be able to compute the “correct” time in the future) and then verifies that the message identifiers for correctness.
+Where a correction has been neglected, the client will have to generate a new session to assure the monotonicity of message identifiers.
+Enables the delivery of encrypted containers together with the external header (hereinafter, Payload) from client to server and back. There are three types of transport:
+We shall examine the first two types.
+Implemented over HTTP/1.1 (with keepalive) running over the traditional TCP Port 80. HTTPS is not used; the above encryption method is used instead.
+An HTTP connection is attached to a session (or rather, to session + key identifier) specified in the most recent user query received; normally, the session is the same in all queries, but crafty HTTP proxies may corrupt that. A server may not return a message into an HTTP connection unless it belongs to the same session, and unless it is the server’s turn (an HTTP request had been received from the client to which a response has not been sent yet).
+The overall arrangement is as follows. The client opens one or more keepalive HTTP connections to the server. If one or more messages need to be sent, they are made into a payload which is followed by a POST request to the URL/api to which the payload is transmitted as data. In addition, Content-Length
, Keepalive
, and Host
are valid HTTP headers.
Having received the query, the server may either wait a little while (if the query requires a response following a short timeout) or immediately return a dummy response (only acknowledging the receipt of the container). In any case, the response may contain any number of messages. The server may at the same time send out any other messages it might be holding for the session.
+In addition, there exists a special long poll RPC query (valid for HTTP connections only) which transmits maximum timeout T. If the server has messages for the session, they are returned immediately; otherwise, a wait state is entered until such time as the server has a message for the client or T seconds have elapsed. If no events occur in the span of T seconds, a dummy response is returned (special message).
+If a server needs to send a message to a client, it checks for an HTTP connection that belongs to the required session and is in the “answering an HTTP request” state (including long poll) whereupon the message is added to the response container for the connection and sent to the user. In a typical case, there is some additional wait time (50 milliseconds) against the eventuality that the server will soon have more messages for the session.
+If no suitable HTTP connection is available, the messages are placed in the current session’s send queue. However, they find their way there anyway until receipt is explicitly or indirectly confirmed by the client. For the HTTP protocol, sending the next query into the same HTTP connection is regarded as an implicit acknowledgment (not any more, the HTTP protocol also requires that explicit acknowledgments be sent); in other cases, the client must return an explicit acknowledgment within a reasonable time (it can be added to a container for the following request).
+Important: if the acknowledgment fails to arrive on time, the message can be resent (possibly, in a different container). The parties must autonomously be ready for this and must store the identifiers of the most recent messages received (and ignore such duplicates rather than repeat actions). In order not to have the identifiers stored forever, there exist special garbage collection messages that take advantage of message identifier monotonicity.
+If the send queue overflows or if messages stay in the queue for over 10 minutes, the server forgets them (or sends them to swap, no genius required). This may happen even faster, if the server is running out of buffer space (for example, because of serious network issues resulting in a large number of connections becoming severed).
+This is very similar to the HTTP transport. May also be implemented over Port 80 (to penetrate all firewalls) and even use the same server IP addresses. In this situation, the server understands whether HTTP or TCP protocol must be used for the connection, based on the first four incoming bytes (for HTTP, it is POST).
+When a TCP connection is created, it is assigned to the session (and the authorization key) transmitted in the first user message, and subsequently used exclusively for this session (multiplexing arrangements are not allowed).
+If a payload (packet) needs to be transmitted from server to client or from client to server, it is encapsulated as follows: 4 length bytes are added at the front (to include the length, the sequence number, and CRC32; always divisible by 4) and 4 bytes with the packet sequence number within this TCP connection (the first packet sent is numbered 0, the next one 1, etc.), and 4 CRC32 bytes at the end (length, sequence number, and payload together).
+There is an abridged version of the same protocol: if the client sends 0xef
as the first byte (important: only prior to the very first data packet), then packet length is encoded by a single byte (0x01..0x7e
= data length divided by 4; or 0x7f
followed by 3 length bytes (little endian) divided by 4) followed by the data themselves (sequence number and CRC32 not added). In this case, server responses look the same (the server does not send 0xef
as the first byte).
In case 4-byte data alignment is needed, an intermediate version of the original protocol may be used: if the client sends 0xeeeeeeee
as the first int (four bytes), then packet length is encoded always by four bytes as in the original version, but the sequence number and CRC32 are omitted, thus decreasing total packet size by 8 bytes.
The full, the intermediate and the abridged versions of the protocol have support for quick acknowledgment. In this case, the client sets the highest-order length bit in the query packet, and the server responds with a special 4 bytes as a separate packet. They are the 32 higher-order SHA1 bits of the encrypted portion of the packet with the most significant bit set to make clear that this is not the length of a regular server response packet; if the abridged version is used, bswap is applied to these four bytes.
+There are no implicit acknowledgments for the TCP transport: all messages must be acknowledged explicitly. Most frequently, acknowledgments are placed in a container with the next query or response if it is transmitted in short order. For example, this is almost always the case for client messages containing RPC queries: the acknowledgment normally arrives with the RPC response.
+In the event of an error, the server may send a packet whose payload consists of 4 bytes as the error code. For example, Error Code 403 corresponds to situations where the corresponding HTTP error would have been returned by the HTTP protocol.
+Telegram Passport is a unified authorization method for services that require personal identification. Users can upload their documents once, then instantly share their data with services that require real-world ID (finance, ICOs, etc.). Telegram doesn't have access to the users' personal information thanks to end-to-end encryption.
+From the perspective of a service that requires real-world ID, the process looks like this:
+Check out this example to see Telegram Passport in action.
+++To learn more about Telegram Passport from the perspective of a user, please see this blog post and the technical MTProto documentation. +See this page if you're interested in encryption algorithms used on Telegram's side.
+
Telegram Passport 1.1 (blog post)
+To integrate Telegram Passport into your login or verification flow, you need a working Telegram bot (see this page for information on how to get one).
+To request data from Telegram Passport users, your bot will need to generate a pair of encryption keys.
+First, use a console to generate a private key:
+openssl genrsa 2048 > private.key
+WARNING: Keep your private key SECRET!
+Then use the console to print the corresponding public key:
+openssl rsa -in private.key -pubout
+Use the /setpublickey command with @BotFather to connect this public key with your bot.
+Add a link to your Privacy Policy by using the /setprivacypolicy command. Users will see this link when offered to authorize you to access their data.
+To request information stored in a Telegram Passport, use one of these SDKs:
+ +Use the following parameters to request information with the SDK:
+Parameters | +Type | +Required | +
bot_id | +Integer | +Yes | +
scope | +PassportScope | +Yes | +
public_key | +String | +Yes | +
nonce | +String | +Yes | +
This object represents the data to be requested.
+Field | +Type | +Description | +
data | +Array of PassportScopeElement | +List of requested elements, each type may be used only once in the entire array of PassportScopeElement objects | +
v | +Integer | +Scope version, must be 1 | +
This object represents a requested element, should be one of:
+This object represents several elements one of which must be provided.
+Field | +Type | +Description | +
one_of | +Array of PassportScopeElementOne | +List of elements one of which must be provided; must contain either several of “passport”, “driver_license”, “identity_card”, “internal_passport” or several of “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration” | +
selfie | +Boolean | +Optional. Use this parameter if you want to request a selfie with the document from this list that the user chooses to upload. | +
translation | +Boolean | +Optional. Use this parameter if you want to request a translation of the document from this list that the user chooses to upload. Note: We suggest to only request translations after you have received a valid document that requires one. | +
This object represents one particular element that must be provided. If no options are needed, String can be used instead of this object to specify the type of the element.
+Field | +Type | +Description | +
type | +String | +Element type. One of "personal_details", "passport", "driver_license", "identity_card", "internal_passport", "address", "utility_bill", "bank_statement", "rental_agreement", "passport_registration", "temporary_registration", "phone_number", "email" | +
selfie | +Boolean | +Optional. Use this parameter if you want to request a selfie with the document as well. Available for "passport", "driver_license", "identity_card" and "internal_passport" | +
translation | +Boolean | +Optional. Use this parameter if you want to request a translation of the document as well. Available for "passport", "driver_license", "identity_card", "internal_passport", "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration". Note: We suggest to only request translations after you have received a valid document that requires one. | +
native_names | +Boolean | +Optional. Use this parameter to request the first, last and middle name of the user in the language of the user's country of residence. Available for "personal_details" | +
You can also use the special type "id_document" as an alias for one of "passport", "driver_license", "identity_card" and the special type "address_document" as an alias for one of "utility_bill", "bank_statement", "rental_agreement".
+So {"type":"id_document",selfie:true}
is equal to {"one_of":["passport","driver_license","identity_card"],selfie:true}
.
Your bot can request personal details, one or several types of identity document, residential address, one or several types of proof of address document, a phone number, or an email address. You can also request optional selfies with the document and certified English translations of the document. +This is just a list of data types that can be requested, and the encrypted objects that will contain such data.
+++Note: We suggest to only request English translations after you have received a valid document that requires one.
+
Name | Key | Type | Description |
---|---|---|---|
personal_details | data | PersonalDetails | Personal Details |
passport | data | IdDocumentData | Passport |
front_side | PassportFile | ||
selfie | Optional. PassportFile | ||
translation | Optional. Array of PassportFile | ||
internal_passport | data | IdDocumentData | Internal Passport |
front_side | PassportFile | ||
selfie | Optional. PassportFile | ||
translation | Optional. Array of PassportFile | ||
driver_license | data | IdDocumentData | Driver License |
front_side | PassportFile | ||
reverse_side | PassportFile | ||
selfie | Optional. PassportFile | ||
translation | Optional. Array of PassportFile | ||
identity_card | data | IdDocumentData | Identity Card |
front_side | PassportFile | ||
reverse_side | PassportFile | ||
selfie | Optional. PassportFile | ||
translation | Optional. Array of PassportFile | ||
address | data | ResidentialAddress | Address |
utility_bill | files | Array of PassportFile | Utility Bill |
translation | Optional. Array of PassportFile | ||
bank_statement | files | Array of PassportFile | Bank Statement |
translation | Optional. Array of PassportFile | ||
rental_agreement | files | Array of PassportFile | Rental Agreement |
translation | Optional. Array of PassportFile | ||
passport_registration | files | Array of PassportFile | Registration Page in the Internal Passport |
translation | Optional. Array of PassportFile | ||
temporary_registration | files | Array of PassportFile | Temporary Registration |
translation | Optional. Array of PassportFile | ||
phone_number | String | Phone number | |
String |
This object represents personal details.
+Field | +Type | +Description | +
first_name | +String | +First Name | +
last_name | +String | +Last Name | +
middle_name | +String | +Optional. Middle Name | +
birth_date | +String | +Date of birth in DD.MM.YYYY format | +
gender | +String | +Gender, male or female | +
country_code | +String | +Citizenship (ISO 3166-1 alpha-2 country code) | +
residence_country_code | +String | +Country of residence (ISO 3166-1 alpha-2 country code) | +
first_name_native | +String | +First Name in the language of the user's country of residence | +
last_name_native | +String | +Last Name in the language of the user's country of residence | +
middle_name_native | +String | +Optional. Middle Name in the language of the user's country of residence | +
This object represents a residential address.
+Field | +Type | +Description | +
street_line1 | +String | +First line for the address | +
street_line2 | +String | +Optional. Second line for the address | +
city | +String | +City | +
state | +String | +Optional. State | +
country_code | +String | +ISO 3166-1 alpha-2 country code | +
post_code | +String | +Address post code | +
This object represents the data of an identity document.
+Field | +Type | +Description | +
document_no | +String | +Document number | +
expiry_date | +String | +Optional. Date of expiry, in DD.MM.YYYY format | +
This object represents a PassportFile related to a document. The file is up to 10MB in size and in the .jpg format.
+When the user confirms your request by pressing the 'Authorize' button, the Bot API sends an Update with the field passport_data to the bot that contains encrypted Telegram Passport data.
+++Note that all base64-encoded fields should be decoded before use.
+
To decrypt the received data, first, decrypt the credentials contained in EncryptedCredentials.
+Decrypt the credentials secret ( secret field in EncryptedCredentials) using your private key (set OAEP padding option, e.g. OPENSSL_PKCS1_OAEP_PADDING
in PHP)
Use this secret and the credentials hash ( hash field in EncryptedCredentials) to calculate credentials_key and credentials_iv as described below:
+ credentials_secret_hash = SHA512( credentials_secret + credentials_hash )
+ credentials_key = slice( credentials_secret_hash, 0, 32 )
+ credentials_iv = slice( credentials_secret_hash, 32, 16 )
+Decrypt the credentials data ( data field in EncryptedCredentials) by AES256-CBC using these credentials_key and credentials_iv. IMPORTANT: At this step, make sure that the credentials hash is equal to SHA256( credentials_data )
Credentials data is padded with 32 to 255 random padding bytes to make its length divisible by 16 bytes. The first byte contains the length of this padding (including this byte). Remove the padding to get the data.
+++Note that all hashes represent as raw binary data, not hexits
+
Credentials is a JSON-serialized object.
+Field | +Type | +Description | +
secure_data | +SecureData | +Credentials for encrypted data | +
nonce | +String | +Bot-specified nonce | +
IMPORTANT: Make sure that the nonce is the same as was passed in the request.
+This object represents the credentials required to decrypt encrypted data. All fields are optional and depend on fields that were requested.
+Field | +Type | +Description | +
personal_details | +SecureValue | +Optional. Credentials for encrypted personal details | +
passport | +SecureValue | +Optional. Credentials for encrypted passport | +
internal_passport | +SecureValue | +Optional. Credentials for encrypted internal passport | +
driver_license | +SecureValue | +Optional. Credentials for encrypted driver license | +
identity_card | +SecureValue | +Optional. Credentials for encrypted ID card | +
address | +SecureValue | +Optional. Credentials for encrypted residential address | +
utility_bill | +SecureValue | +Optional. Credentials for encrypted utility bill | +
bank_statement | +SecureValue | +Optional. Credentials for encrypted bank statement | +
rental_agreement | +SecureValue | +Optional. Credentials for encrypted rental agreement | +
passport_registration | +SecureValue | +Optional. Credentials for encrypted registration from internal passport | +
temporary_registration | +SecureValue | +Optional. Credentials for encrypted temporary registration | +
This object represents the credentials required to decrypt encrypted values. All fields are optional and depend on the type of fields that were requested.
+Field | +Type | +Description | +
data | +DataCredentials | +Optional. Credentials for encrypted Telegram Passport data. Available for "personal_details", "passport", "driver_license", "identity_card", "internal_passport" and "address" types. | +
front_side | +FileCredentials | +Optional. Credentials for an encrypted document's front side. Available for "passport", "driver_license", "identity_card" and "internal_passport". | +
reverse_side | +FileCredentials | +Optional. Credentials for an encrypted document's reverse side. Available for "driver_license" and "identity_card". | +
selfie | +FileCredentials | +Optional. Credentials for an encrypted selfie of the user with a document. Available for "passport", "driver_license", "identity_card" and "internal_passport". | +
translation | +Array of FileCredentials | +Optional. Credentials for an encrypted translation of the document. Available for "passport", "driver_license", "identity_card", "internal_passport", "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration". | +
files | +Array of FileCredentials | +Optional. Credentials for encrypted files. Available for "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. | +
These credentials can be used to decrypt encrypted data from the data field in EncryptedPassportElement.
+Field | +Type | +Description | +
data_hash | +String | +Checksum of encrypted data | +
secret | +String | +Secret of encrypted data | +
To decrypt data, use the corresponding secret and data_hash from DataCredentials as described below:
+ data_secret_hash = SHA512( data_secret + data_hash )
+ data_key = slice( data_secret_hash, 0, 32 )
+ data_iv = slice( data_secret_hash, 32, 16 )
+Use AES256-CBC with this data_key and data_iv to decrypt the data (the data field in EncryptedPassportElement). IMPORTANT: At this step, make sure that data_hash from the credentials is equal to SHA256( data )
.
The data is padded with 32 to 255 random padding bytes to make its length divisible by 16 bytes. The first byte contains the length of the padding (including this byte). Remove padding to get the data.
+The data is a JSON-serialized object of one of the following types: PersonalDetails, IdDocumentData, ResidentialAddress, depending on type.
+These credentials can be used to decrypt encrypted files from the front_side, reverse_side, selfie, files and translation fields in EncryptedPassportElement.
+Field | +Type | +Description | +
file_hash | +String | +Checksum of encrypted file | +
secret | +String | +Secret of encrypted file | +
To decrypt the file, use the corresponding secret and file_hash from FileCredentials as described below:
+ file_secret_hash = SHA512( file_secret + file_hash )
+ file_key = slice( file_secret_hash, 0, 32 )
+ file_iv = slice( file_secret_hash, 32, 16 )
+Download the encrypted file using the getFile method.
+Use AES256-CBC with this file_key and file_iv to decrypt the content of the file. IMPORTANT: At this step, make sure that file_hash from the credentials is equal to SHA256( file_content )
.
The content of the file is padded with 32 to 255 random padding bytes to make its length divisible by 16 bytes. The first byte contains the length of the padding (including that byte). Remove padding to get the file content.
+If the data you received contains errors, the bot can use the setPassportDataErrors method to inform the user and request information again. The user will not be able to resend the data, until all errors are fixed.
Telegram Passport data is stored encrypted End-to-End which means that the Telegram server does not have access to the data and only functions as a storage for encrypted data it can't decipher. Encryption and decryption are handled exclusively by the Telegram clients, which are open source.
+To encrypt each particular element of Telegram Passport, the client generates a random secret. The secret is a 32-byte number with the modulo 255 sum of bytes equal to 239. This secret is in turn encrypted with the passport_secret that is generated when the user creates their Telegram Passport. passport_secret is encrypted with the user's password and is stored encrypted in the Telegram Cloud.
+The passport secret is one of the secret parameters used to encrypt the data uploaded by the user to the Telegram Cloud.
+When first setting up Telegram Passport it must be created, encrypted and uploaded as described in Passport Secret Encryption.
+When using Telegram Passport normally, it must be downloaded and decrypted for use as described in Passport Secret Decryption.
+The passport secret must also be downloaded, re-encrypted and re-uploaded as described in Passport Secret Encryption if a new, more secure encryption algorithm is defined in a newer version of Telegram or the 2FA password is updated.
+First of all, server-side passport parameters are fetched, scheme:
+account.password#ad2641f8 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo srp_B:flags.2?bytes srp_id:flags.2?long hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes = account.Password;
+
+securePasswordKdfAlgoUnknown#4a8537 = SecurePasswordKdfAlgo;
+securePasswordKdfAlgoPBKDF2HMACSHA512iter100000#bbf2dda0 salt:bytes = SecurePasswordKdfAlgo;
+securePasswordKdfAlgoSHA512#86471d92 salt:bytes = SecurePasswordKdfAlgo;
+
+
+---functions---
+
+account.getPassword#548a30f5 = account.Password;
+When Telegram Passport is first used, the client generates a passport_secret (a 32-byte number with the modulo 255 sum of bytes equal to 239), using a part of server-generated random secure_random from account.password as an additional source of entropy for OpenSSL (when re-encrypting the passport_secret with a more secure algorithm or after a 2FA password change, the previous passport_secret is used, instead). +Then passport_secret is then encrypted using the user's password and hashed using the scheme and parameters specified in the new_algo field of account.password.
+The server should always return a securePasswordKdfAlgoPBKDF2HMACSHA512iter100000
constructor in the new_algo
field.
+If securePasswordKdfAlgoUnknown
is returned, the remotely stored secret is encrypted using a new algorithm, not supported by the current client: the user should update their app.
The other constructors may be used only when decrypting old passport parameters generated by a legacy client; in this case, the passport secret should be re-encrypted and updated using new_algo
.
First of all, a fingerprint of the secret is calculated ( passport_secret_fingerprint ):
+passport_secret_fingerprint = long( slice( SHA256( passport_secret ), 0, 8 ) )
+Next the user's 2FA plaintext password
is hashed using the specified algorithm.
securePasswordKdfAlgoPBKDF2HMACSHA512iter100000
+To make the password hashes stored on the server more resilient to brute-force attacks while maintaining practical speeds on the range of devices popular among Telegram users, PBKDF2-HMAC-SHA512 with 100000 iterations is used:
+server_passport_salt = new_algo.salt
+client_passport_salt = random_bytes(32)
+passport_secret_salt = server_passport_salt + client_passport_salt
+password_hash = PBKDF2( password, passport_secret_salt, HMACSHA512, 100000)
+The secret_key
and iv
parameters are extracted from the generated password_hash
secret_key = slice( password_hash, 0, 32 )
+iv = slice( password_hash, 32, 16 )
+The passport_secret generated previously is encrypted using AES256-CBC with the key secret_key and iv:
+encrypted_passport_secret = AES256_CBC_ENC(passport_secret, secret_key, iv)
+The encrypted_passport_secret is stored on the server together with the passport_secret_salt and the fingerprint of the secret passport_secret_fingerprint: +Scheme:
+boolFalse#bc799737 = Bool;
+boolTrue#997275b5 = Bool;
+
+inputCheckPasswordSRP#d27ff082 srp_id:long A:bytes M1:bytes = InputCheckPasswordSRP;
+
+securePasswordKdfAlgoPBKDF2HMACSHA512iter100000#bbf2dda0 salt:bytes = SecurePasswordKdfAlgo;
+
+secureSecretSettings#1527bcac secure_algo:SecurePasswordKdfAlgo secure_secret:bytes secure_secret_id:long = SecureSecretSettings;
+
+account.passwordInputSettings#c23727c9 flags:# new_algo:flags.0?PasswordKdfAlgo new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_settings:flags.2?SecureSecretSettings = account.PasswordInputSettings;
+
+---functions---
+
+account.updatePasswordSettings#a59b102f password:InputCheckPasswordSRP new_settings:account.PasswordInputSettings = Bool;
+The client calls account.updatePasswordSettings.
+password
parameter is generated using the user's 2FA password as per updating the 2FA password.new_settings
should contain only the new_secure_setting
parameter with the secureSecretSettings.The secureSecretSettings constructor is generated thusly:
+secure_algo
is set to the new_algo
field of account.password, used to generate the encrypted passport secret: salt
is substituted with passport_secret_salt
secure_secret
is set to the new encrypted_passport_secret
secure_secret_id
is set to the new passport_secret_fingerprint
Subsequently, the client receives the encrypted passport_secret from the server and decrypts it after the user enters their password ».
+In case the password is changed or a more secure algorithm is introduced in an update of the API, the client re-encrypts the passport_secret using the new password. +If the password is disabled, all Telegram Passport data is lost.
+Scheme:
+securePasswordKdfAlgoUnknown#4a8537 = SecurePasswordKdfAlgo;
+securePasswordKdfAlgoPBKDF2HMACSHA512iter100000#bbf2dda0 salt:bytes = SecurePasswordKdfAlgo;
+securePasswordKdfAlgoSHA512#86471d92 salt:bytes = SecurePasswordKdfAlgo;
+
+secureSecretSettings#1527bcac secure_algo:SecurePasswordKdfAlgo secure_secret:bytes secure_secret_id:long = SecureSecretSettings;
+
+account.passwordSettings#9a5c33e5 flags:# email:flags.0?string secure_settings:flags.1?SecureSecretSettings = account.PasswordSettings;
+
+---functions---
+
+account.getPasswordSettings#9cd4eaf9 password:InputCheckPasswordSRP = account.PasswordSettings;
+The client requests the user's 2FA password and generates the SRP paramaters to be passed to account.getPasswordSettings.
+If the password is correct, an account.passwordSettings constructor with secureSecretSettings is returned.
+encrypted_passport_secret
, passport_secret_fingerprint
parameters are extracted from the secureSecretSettings constructor:
encrypted_passport_secret = secureSecretSettings.secure_secret
+passport_secret_fingerprint = secureSecretSettings.secure_id
+The combined passport_secret_salt
is extracted from the SecurePasswordKdfAlgo.
passport_secret_salt = SecurePasswordKdfAlgo.salt
+Similar to passport secret encryption, the following process is used to decrypt and verify the encrypted_passport_secret
:
The user's 2FA plaintext password
is hashed using the specified algorithm.
securePasswordKdfAlgoPBKDF2HMACSHA512iter100000 +To make the password hashes stored on the server more resilient to brute-force attacks while maintaining practical speeds on the range of devices popular among Telegram users, PBKDF2-HMAC-SHA512 with 100000 iterations is used:
+password_hash = PBKDF2( password, passport_secret_salt, HMACSHA512, 100000)
+securePasswordKdfAlgoSHA512 +This mode can only be found in hashes generated by legacy clients: hashes generated with this mode must be re-encrypted and updated using securePasswordKdfAlgoPBKDF2HMACSHA512iter100000 as described in Passport Secret Encryption.
+password_hash = SHA512( passport_secret_salt + password + passport_secret_salt )
+The secret_key
and iv
parameters are extracted from the generated password_hash
secret_key = slice( password_hash, 0, 32 )
+iv = slice( password_hash, 32, 16 )
+The encrypted_passport_secret is decrypted using AES256-CBC with the key secret_key and iv:
+passport_secret = AES256_CBC_DEC(encrypted_passport_secret, secret_key, iv)
+The passport_secret is verified by generating and checking the fingerprint:
+my_passport_secret_fingerprint = long( slice( SHA256( passport_secret ), 0, 8 ) )
+The client must verify that passport_secret_fingerprint is indeed equal to my_passport_secret_fingerprint.
+The passport_secret can now be used to decrypt encrypted passport data stored on telegram servers:
+To encrypt Telegram Passport data, the client generates a data_secret (a 32-byte number with the modulo 255 sum of bytes equal to 239). The the data is encrypted according to the following scheme:
+Data is padded to a length that is divisible by 16 bytes. To achieve this, 32 to 255 bytes are added at the beginning, where the first byte always holds the number of added bytes and the rest are random.
+We calculate the hash from this data data_hash:
+data_hash = SHA256( data_bytes )
+The encryption key data_key is calculated:
+data_secret_hash = SHA512( data_secret + data_hash )
+data_key = slice( data_secret_hash, 0, 32 )
+iv = slice( data_secret_hash, 32, 16 )
+Data is encrypted using AES256-CBC with the key data_key and iv:
+encrypted_data = AES256_CBC_ENC(data, data_key, iv)
+secret_key, the key for encrypting the data_secret, is calculated:
+secret_hash = SHA512( passport_secret + data_hash )
+secret_key = slice( secret_hash, 0, 32 )
+iv = slice( secret_hash, 32, 16 )
+data_secret is encrypted using AES256-CBC with the key secret_key and iv:
+encrypted_data_secret = AES256_CBC_ENC(data_secret, secret_key, iv)
+encrypted_data together with the encrypted_data_secret and data_hash are saved on the server:
+Scheme:
+inputSecureFileUploaded#3334b0f0 id:long parts:int md5_checksum:string file_hash:bytes secret:bytes = InputSecureFile;
+inputSecureFile#5367e5be id:long access_hash:long = InputSecureFile;
+
+secureValueTypePersonalDetails#9d2a81e3 = SecureValueType;
+secureValueTypePassport#3dac6a00 = SecureValueType;
+secureValueTypeDriverLicense#6e425c4 = SecureValueType;
+secureValueTypeIdentityCard#a0d0744b = SecureValueType;
+secureValueTypeInternalPassport#99a48f23 = SecureValueType;
+secureValueTypeAddress#cbe31e26 = SecureValueType;
+secureValueTypeUtilityBill#fc36954e = SecureValueType;
+secureValueTypeBankStatement#89137c0d = SecureValueType;
+secureValueTypeRentalAgreement#8b883488 = SecureValueType;
+secureValueTypePassportRegistration#99e3806a = SecureValueType;
+secureValueTypeTemporaryRegistration#ea02ec33 = SecureValueType;
+secureValueTypePhone#b320aadb = SecureValueType;
+secureValueTypeEmail#8e3ca7ee = SecureValueType;
+
+securePlainPhone#7d6099dd phone:string = SecurePlainData;
+securePlainEmail#21ec5a5f email:string = SecurePlainData;
+
+secureData#8aeabec3 data:bytes data_hash:bytes secret:bytes = SecureData;
+
+inputSecureValue#db21d0a7 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?InputSecureFile reverse_side:flags.2?InputSecureFile selfie:flags.3?InputSecureFile translation:flags.6?Vector<InputSecureFile> files:flags.4?Vector<InputSecureFile> plain_data:flags.5?SecurePlainData = InputSecureValue;
+
+---functions---
+
+account.saveSecureValue#899fe31d value:InputSecureValue secure_secret_id:long = SecureValue;
+account.saveSecureValue must be used to save an encrypted passport value of a certain type.
+The secure_secret_id
parameter must be set to the passport_secret_fingerprint
of the passport_secret
used to encrypt the data_secret
.
The inputSecureValue constructor contains info about passport data of a certain type
, identified by the chosen SecureValueType constructor.
Depending on the chosen type, encrypted data will have to be stored into a secureData constructor, uploaded as an InputSecureFile, or in the case of email addresses and phone numbers, verified and provided in a SecurePlainData constructor. +For more info on each mode, and when each one should be used, read on.
+secureData#8aeabec3 data:bytes data_hash:bytes secret:bytes = SecureData;
+data
is an encrypted and padded (see Encryption) JSON-serialized object of one of the following types: PersonalDetails, IdDocumentData, ResidentialAddress, depending on the chosen type.
+Data must be in JSON format and not TL, as it has to be passed directly to the service using E2E encryption, without the bot API middleman to convert TL objects.data_hash
is the data_hashsecret
is the encrypted_data_secretData is an encrypted and padded JSON-serialized object of one of the specified JSON types, depending on the chosen type.
+Chosen type | +JSON object | +
---|---|
secureValueTypePersonalDetails | +PersonalDetails | +
secureValueTypePassport | +IdDocumentData | +
secureValueTypeDriverLicense | +IdDocumentData | +
secureValueTypeIdentityCard | +IdDocumentData | +
secureValueTypeInternalPassport | +IdDocumentData | +
secureValueTypeAddress | +ResidentialAddress | +
inputSecureFileUploaded#3334b0f0 id:long parts:int md5_checksum:string file_hash:bytes secret:bytes = InputSecureFile;
+inputSecureFile#5367e5be id:long access_hash:long = InputSecureFile;
+
+---functions---
+
+upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool;
+Files (JPG format, max. 10MB) are encrypted and padded (see Encryption), and then uploaded chunk by chunk as described in files », except that instead of generating an inputFile, an inputSecureFile should be generated, instead.
+md5_checksum
is to be set to the MD5 hash of the encrypted file, for a server-side integrity check.file_hash
field should be set to the data_hash of the data.secret
field is the encrypted_data_secret.securePlainPhone#7d6099dd phone:string = SecurePlainData;
+securePlainEmail#21ec5a5f email:string = SecurePlainData;
+
+---functions---
+
+account.sendVerifyPhoneCode#a5a356f9 phone_number:string settings:CodeSettings = auth.SentCode;
+account.verifyPhone#4dd3a7f6 phone_number:string phone_code_hash:string phone_code:string = Bool;
+account.sendVerifyEmailCode#7011509f email:string = account.SentEmailCode;
+account.verifyEmail#ecba39db email:string code:string = Bool;
+
+auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode;
+auth.cancelCode#1f040578 phone_number:string phone_code_hash:string = Bool;
+The email/phone is passed in plaintext using the respective SecurePlainData constructor. +To verify a phone number or email and use it in Telegram Passport, use the appropriate methods:
+The flow is similar to the one used for logging in:
+account.sendVerify*Code
methodaccount.verify*
methodFor more info, see the authorization docs.
+inputSecureFileUploaded#3334b0f0 id:long parts:int md5_checksum:string file_hash:bytes secret:bytes = InputSecureFile;
+inputSecureFile#5367e5be id:long access_hash:long = InputSecureFile;
+
+secureValueTypePersonalDetails#9d2a81e3 = SecureValueType;
+secureValueTypePassport#3dac6a00 = SecureValueType;
+secureValueTypeDriverLicense#6e425c4 = SecureValueType;
+secureValueTypeIdentityCard#a0d0744b = SecureValueType;
+secureValueTypeInternalPassport#99a48f23 = SecureValueType;
+secureValueTypeAddress#cbe31e26 = SecureValueType;
+secureValueTypeUtilityBill#fc36954e = SecureValueType;
+secureValueTypeBankStatement#89137c0d = SecureValueType;
+secureValueTypeRentalAgreement#8b883488 = SecureValueType;
+secureValueTypePassportRegistration#99e3806a = SecureValueType;
+secureValueTypeTemporaryRegistration#ea02ec33 = SecureValueType;
+secureValueTypePhone#b320aadb = SecureValueType;
+secureValueTypeEmail#8e3ca7ee = SecureValueType;
+
+securePlainPhone#7d6099dd phone:string = SecurePlainData;
+securePlainEmail#21ec5a5f email:string = SecurePlainData;
+
+secureData#8aeabec3 data:bytes data_hash:bytes secret:bytes = SecureData;
+
+inputSecureValue#db21d0a7 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?InputSecureFile reverse_side:flags.2?InputSecureFile selfie:flags.3?InputSecureFile translation:flags.6?Vector<InputSecureFile> files:flags.4?Vector<InputSecureFile> plain_data:flags.5?SecurePlainData = InputSecureValue;
+The scheme for the inputSecureValue constructor defines the constructor to use for each field.
+Name | +Type | +Description | +
---|---|---|
type | +SecureValueType | +Secure passport value type | +
data | +flags.0?SecureData | +Encrypted Telegram Passport element data | +
front_side | +flags.1?InputSecureFile | +Encrypted passport file with the front side of the document | +
reverse_side | +flags.2?InputSecureFile | +Encrypted passport file with the reverse side of the document | +
selfie | +flags.3?InputSecureFile | +Encrypted passport file with a selfie of the user holding the document | +
translation | +flags.6?Vector<InputSecureFile> | +Array of encrypted passport files with translated versions of the provided documents | +
files | +flags.4?Vector<InputSecureFile> | +Array of encrypted passport files with photos the of the documents | +
plain_data | +flags.5?SecurePlainData | +Plaintext verified passport data | +
Here's a list of possible SecureValueTypes, and the parameters that can be set/requested when using each type.
+Type | +Allowed fields | +
---|---|
secureValueTypeEmail | +plain_data |
+
secureValueTypePhone | +plain_data |
+
secureValueTypePersonalDetails | +data |
+
secureValueTypePassport | +data , front_side , selfie , translation |
+
secureValueTypeDriverLicense | +data , front_side , reverse_side , selfie , translation |
+
secureValueTypeIdentityCard | +data , front_side , reverse_side , selfie , translation |
+
secureValueTypeInternalPassport | +data , front_side , selfie , translation |
+
secureValueTypeAddress | +data |
+
secureValueTypeUtilityBill | +files , translation |
+
secureValueTypeBankStatement | +files , translation |
+
secureValueTypeRentalAgreement | +files , translation |
+
secureValueTypePassportRegistration | +files , translation |
+
secureValueTypeTemporaryRegistration | +files , translation |
+
---functions---
+
+account.getAllSecureValues#b288bc7d = Vector<SecureValue>;
+account.getSecureValue#73665bc2 types:Vector<SecureValueType> = Vector<SecureValue>;
+account.deleteSecureValue#b880bc4b types:Vector<SecureValueType> = Bool;
+The above methods can be used to fetch or remove encrypted Telegram Passport files stored in the Telegram Cloud by document type.
+When a service requests data, it passes a nonce to the client. The nonce is a cryptographically secure unique identifier which allows the service to identify a request when receiving data as well as confirm the integrity of the data. The Telegram server doesn't have access to this nonce.
+Once the user authorizes the Telegram Passport data transfer, the client forms the credentials (Credentials JSON object). Credentials contain the data_hash and data_secret from each element of Telegram Passport to which the user has allowed access. In addition to this, the credentials will always contain the nonce that the client received from the service at the initiation of the request.
+Credentials are then passed to the service through the Bot API in encrypted form. To encrypt the credentials, the client generates a credentials_secret (a 32-byte number with the modulo 255 sum of bytes equal to 239). Then the credentials are encrypted according to the following scheme:
+Credentials are padded to a length which is divisible by 16 bytes. To achieve this, 32 to 255 bytes are added at the beginning, where the first byte always holds the number of added bytes and the rest are random.
+A hash of the padded credentials credentials_hash is calculated:
+credentials_hash = SHA256( credentials )
+The encryption key credentials_key is calculated:
+credentials_secret_hash = SHA512( credentials_secret + credentials_hash )
+credentials_key = slice( credentials_secret_hash, 0, 32 )
+iv = slice( credentials_secret_hash, 32, 16 )
+Credentials are encrypted using AES256-CBC with the key credentials_key and iv.
+encrypted_credentials = AES256-CBC-ENC(credentials, credentials_key, iv)
+credentials_secret is encrypted with the public RSA-key of the service with OPENSSL_PKCS1_OAEP_PADDING.
+encrypted_credentials_secret = RSA-ENC(credentials_secret, key, OPENSSL_PKCS1_OAEP_PADDING)
+The encrypted credentials are passed to the service via the MTProto API together with the encrypred credentials_secret and credentials_hash. Along with the credentials, the service receives from the Telegram Cloud the data it requested in encrypted form. See Submitting the Passport Form and PassportData:
+secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted;
+data
is the encrypted_credentials
hash
is the credentials_hash
secret
is the encrypted_credentials_secret
Then the service decrypts the data as described here.
If you'd like to test Telegram Passport, use this page to request data. If you'd like to check out a real-life implementation, please see this blog post for examples of services that have integrated Telegram Passport.
++
+ + ++Note that the passport demo bot will only store data for 1 hour and then discard it. After logging in here, you can use the logout button on this page to reset your session.
+
The Android SDK helps you easily integrate Telegram Passport requests into your Android-based apps. Check out our GitHub repository to see samples using this SDK.
+Telegram Passport SDK is available from the Maven repository. +Add this line to the dependencies section in your build.gradle:
+compile 'org.telegram:passport:1.1'
+and sync your project.
+Download the library, unzip it and copy the library project to the root of your project directory (the one with settings.gradle and gradle.properties). Then, make the following changes to your Gradle scripts.
+In settings.gradle, add ':telegrampassport'
to includes:
include ':app', ':telegrampassport'
+In the build.gradle file for your app, add this line to the dependencies section:
+compile ':telegrampassport'
+and sync your project.
+The SDK provides the "Log in with Telegram" button which we recommend using for a consistent user experience across different apps. You can either add it from your Java code:
+TelegramLoginButton telegramButton;
+// ...
+telegramButton=new TelegramLoginButton(this);
+// Optionally you can change the roundness of the button corners
+// to better fit your design.
+telegramButton.setCornerRoundness(1f);
+viewGroupOfSomeSort.addView(telegramButton, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+Or from XML:
+ <org.telegram.passport.TelegramLoginButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:cornerRoundness="0.5"/>
+The button doesn't do anything by itself; you need to set an OnClickListener on it to start the authorization flow (replace the comments with actual parameters):
+private static final int TG_PASSPORT_RESULT=352; // this can be any integer less than 0xFFFF
+// ...
+telegramButton.setOnClickListener(new View.OnClickListener(){
+@Override
+public void onClick(View view){
+ TelegramPassport.AuthRequest req=new TelegramPassport.AuthRequest();
+ req.botID=/* your bot ID here */;
+ req.publicKey=/* your bot public key here */;
+ req.nonce=/* a unique nonce to pass to the bot server */;
+ // Request either a passport or an ID card with selfie, a driver license, personal details with
+ // name as it appears in the documents, address with any address document, and a phone number.
+ // You could also pass a raw JSON object here if that's what works better for you
+ // (for example, if you already get it from your server in the correct format).
+ req.scope=new PassportScope(
+ new PassportScopeElementOneOfSeveral(PassportScope.PASSPORT, PassportScope.IDENTITY_CARD).withSelfie(),
+ new PassportScopeElementOne(PassportScope.PERSONAL_DETAILS).withNativeNames(),
+ PassportScope.DRIVER_LICENSE,
+ PassportScope.ADDRESS,
+ PassportScope.ADDRESS_DOCUMENT,
+ PassportScope.PHONE_NUMBER
+ );
+ TelegramPassport.request(MyActivity.this, req, TG_PASSPORT_RESULT);
+}});
+If you need more control over the process, the TelegramPassport
class contains several more methods:
getAuthIntent(AuthParams)
returns an Intent
for you to use in startActivityForResult
if you need to do that in some special way. Be sure to check that an app is present that can handle this intent before starting it by using PackageManager
or intent.resolveActivity
.showAppInstallAlert(Activity)
shows an alert that the user needs to install Telegram in order to continue. This is intended to be used together with the previous method for the cases when the app isn't installed.The result is delivered via the onActivityResult
method in your activity with the request code you passed to TelegramPassport.request
. Currently, the only meaningful parameter is resultCode
, which is RESULT_OK
if the authorization was successful and RESULT_CANCELED
otherwise.
TGPassportKit helps you easily integrate Telegram Passport requests into your iOS & macOS apps. Check out our GitHub repository to see samples using this SDK.
+To install TGPassportKit via Cocoapods add the following to your Podfile:
+target 'MyApp' do
+ pod 'TGPassportKit'
+end
+then run pod install
in your project root directory.
Add the following line to your Cartfile:
+github "telegrammessenger/TGPassportKit"
+then run carthage update
, and you will get the latest version of TGPassportKit in your Carthage folder.
Configure your Info.plist by right-clicking it in Project Navigator, choosing Open As > Source Code and adding this snippet:
+Replace {bot_id}
with your value
<key>CFBundleURLTypes</key>
+<array>
+ <dict>
+ <key>CFBundleURLSchemes</key>
+ <array>
+ <string>tgbot{bot_id}</string>
+ </array>
+ </dict>
+</array>
+<key>LSApplicationQueriesSchemes</key>
+<array>
+ <string>tg</string>
+</array>
+Add this code to your UIApplicationDelegate
implementation
#import <TGPassportKit/TGPAppDelegate.h>
+
+- (BOOL)application:(UIApplication *)application
+ openURL:(NSURL *)url
+ options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
+ BOOL handledByPassportKit = [[TGPAppDelegate sharedDelegate] application:application
+ openURL:url
+ options:options];
+
+ return YES;
+}
+If you support iOS 9 and below, also add this method:
+- (BOOL)application:(UIApplication *)application
+ openURL:(NSURL *)url
+ sourceApplication:(nullable NSString *)sourceApplication
+ annotation:(id)annotation {
+ BOOL handledByPassportKit = [[TGPAppDelegate sharedDelegate] application:application
+ openURL:url
+ sourceApplication:sourceApplication
+ annotation:annotation];
+
+ return YES;
+}
+To add the Telegram Passport button, add the following code to your view controller:
+Replace {bot_id}
, {bot_public_key}
and {request_nonce}
with your values
#import <TGPassportKit/TGPButton.h>
+
+@interface ViewController <TGPButtonDelegate>
+
+@end
+
+@implementation ViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ TGPButton *button = [[TGPButton alloc] init];
+ button.botConfig = [[TGPBotConfig alloc] initWithBotId:{bot_id}
+ publicKey:@"{bot_public_key}"];
+ button.scope = [[TGPScope alloc] initWithJSONString:@"{\"data\":[\"id_document\",\"address_document\",\"phone_number\"],\"v\":1}"];
+// You can also construct a scope using provided data type classes like this:
+// button.scope = [[TGPScope alloc] initWithTypes:@[[[TGPPersonalDetails alloc] init], [[TGPIdentityDocument alloc] initWithType:TGPIdentityDocumentTypePassport selfie:true translation:true]]];
+ button.nonce = @"{request_nonce}";
+ button.delegate = self;
+ [self.view addSubview:button];
+}
+
+- (void)passportButton:(TGPButton *)passportButton
+ didCompleteWithResult:(TGPRequestResult)result
+ error:(NSError *)error {
+ switch (result) {
+ case TGPRequestResultSucceed:
+ NSLog(@"Succeed");
+ break;
+
+ case TGPRequestResultCancelled:
+ NSLog(@"Cancelled");
+ break;
+
+ default:
+ NSLog(@"Failed");
+ break;
+ }
+}
+
+@end
+If you want to design a custom UI and behavior, you can invoke a Passport request like this:
+Replace {bot_id}
, {bot_public_key}
and {request_nonce}
with your values
#import <TGPassportKit/TGPRequest.h>
+
+- (void)performPassportRequest
+{
+ TGPBotConfig *botConfig = [[TGPBotConfig alloc] initWithBotId:{bot_id}
+ publicKey:@"{bot_public_key}"];
+ TGPRequest *request = [[TGPRequest alloc] initWithBotConfig:botConfig];
+ [request performWithScope:[[TGPScope alloc] initWithJSONString:@"{\"data\":[\"id_document\",\"phone_number\"],\"v\":1}"]
+ payload:@"{request_nonce}"
+ completionHandler:^(TGPRequestResult result, NSError * _Nullable error) {
+ switch (result) {
+ case TGPRequestResultSucceed:
+ NSLog(@"Succeed");
+ break;
+
+ case TGPRequestResultCancelled:
+ NSLog(@"Cancelled");
+ break;
+
+ default:
+ NSLog(@"Failed");
+ break;
+ }
+ }];
+}
The Javascript SDK helps you easily integrate Telegram Passport requests into your website. Check out our GitHub repository to see samples using this SDK.
+Download and include the Javascript SDK in the head section of your page:
+<script src="telegram-passport.js"></script>
+Simply call Telegram.Passport.createAuthButton to create the default Telegram Passport button:
+<div id="telegram_passport_auth"></div>
+<script>
+ Telegram.Passport.createAuthButton('telegram_passport_auth', {
+ bot_id: 123456, // place id of your bot here
+ scope: {data: [{type: 'id_document', selfie: true}, 'address_document', 'phone_number', 'email'], v: 1},
+ public_key: '-----BEGIN PUBLIC KEY----- ...', // place public key of your bot here
+ nonce: 'ab2df83746a87d2f3bd6...', // place nonce here
+ callback_url: 'https://example.com/callback/' // place callback url here
+ });
+</script>
+++Note that if you use a Content-Security-Policy (CSP) header with the frame-src/child-src directive you should allow
+tg:
source to prevent errors in some browsers (e.g. Firefox)
Parameters | +Type | +Required | +
element | +String or DOMNode | +Yes | +
auth_params | +AuthParameters or Function | +Yes | +
options | +AuthButtonOptions | +Optional | +
Field | +Type | +Description | +
bot_id | +Integer | +Unique identifier for the bot | +
scope | +PassportScope | +A JSON-serialized object describing the data you want to request | +
public_key | +String | +Public key of your bot | +
nonce | +String | +Bot-specified nonce | +
callback_url | +String | +Optional. URL to which the user will be redirected. | +
Field | +Type | +Description | +
text | +String | +Optional. Text on the button. Defaults to "Log In With Telegram". | +
radius | +Integer | +Optional. Radius of the button and tooltip in px. Defaults to 23. | +
tooltip_text | +String | +Optional. Text on the tooltip. Defaults to "Please install Telegram to use this option.". | +
tooltip_position | +String | +Optional. Tooltip position. One of "top", "bottom", "left", "right". Defaults to "bottom". | +
tooltip_force | +Boolean | +Optional. Always show a tooltip. Defaults to false. | +
You can also create your custom button. Do not forget about the tooltip. You should add an onclick
listener to the button which calls the Telegram.Passport.auth(auth_params, tooltip_toggle);
method:
<button id="telegram_passport_auth">Log In With Telegram</button>
+<script>
+ var auth_button = document.getElementById('telegram_passport_auth');
+ var auth_params = {
+ bot_id: XXXXXX, // place id of your bot here
+ scope: {data: [{type: 'id_document', selfie: true}, 'address_document', 'phone_number', 'email'], v: 1},
+ public_key: '-----BEGIN PUBLIC KEY----- ...', // place public key of your bot here
+ nonce: 'ab2df83746a87d2f3bd6...', // place nonce here
+ callback_url: 'https://example.com/callback/' // place callback url here
+ };
+ auth_button.addEventListener('click', function() {
+ Telegram.Passport.auth(auth_params, function(show) {
+ if (show) {
+ // some code to show tooltip
+ } else {
+ // some code to hide tooltip
+ }
+ });
+ }, false);
+</script>
+When the user confirms your request by pressing the 'Authorize' button, it will be redirected to the URL specified in the callback_url with the parameter tg_passport=success
and the Bot API will send the bot an Update with the field passport_data which contains encrypted Telegram Passport data.
If the user cancels your request, it will be redirected to the URL specified in the callback_url with the parameter tg_passport=cancel
.
If an error occurs during the request, the user will be redirected to the URL specified in the callback_url with the parameter tg_passport=error
. The parameter error
will contain one of the following values: BOT_INVALID, PUBLIC_KEY_REQUIRED, PUBLIC_KEY_INVALID, SCOPE_EMPTY, NONCE_EMPTY.
This page contains instructions for verifying that Telegram's open source code is exactly the same as the code that is used to build the apps that are available in the App Store, Google Play and directly on the Telegram website.
+Warning: Telegram supports reproducible builds as of version 5.13. Bear in mind that, at this stage, the verification process should be considered experimental. We will be updating our apps and these instructions to make this process as straightforward as possible.
+ +++Please read the relevant notes and troubleshooting section carefully.
+
Docker can be obtained here. Once the installation is complete, log into your Docker account > Preferences > Advanced and configure the amount of resources Docker may use:
+ + +We recommend using the maximum amount allowed by your system's hardware, in order to speed up the build time.
+You can find the version/build number at the bottom of the Settings page. Note that Telegram supports reproducible builds starting with version 5.13.
+ + +The commit tag to checkout source code for the example above will be release-5.13.0_1821
.
++Please make sure that you're using the correct version and build number of the version you want to check (and not the one from this example ).
+
The part after the version number will help you know in which folder to look for the correct APK when you've finished building the app (Step 4):
+Open Terminal, run the commands:git clone https://github.com/DrKLO/Telegram.git $HOME/telegram-android
cd $HOME/telegram-android
git checkout release-{VERSION AND BUILD NUMBER FROM STEP 2}
For our example, the command would be:git checkout release-5.13.0_1821
Open Terminal, run the commands:cd $HOME/telegram-android
docker build -t telegram-build .
docker run --rm -v "$PWD":/home/source telegram-build
These commands will produce 9 different APKs for different target SDK versions and CPU ABIs.
+These APKs can be found in:$HOME/telegram-android/TMessagesProj/build/outputs/apk/
Use the folder name from Step 2 to find the correct folder that holds the same APK as installed on your device. For example, for non-universal Android 9.0 arm64-v8a, the path to your APK will be:$HOME/telegram-android/TMessagesProj/build/outputs/apk/arm64_SDK23/release/app.apk
Copy this APK to the root source directory by running this command in Terminal:cp $HOME/telegram-android/TMessagesProj/build/outputs/apk/arm64_SDK23/release/app.apk $HOME/telegram-android/telegram_built.apk
You will need adb for this step.
+ + +++If you downloaded your APK directly from Telegram's website, use the package name
+org.telegram.messenger.web
in this step. To verify the Google Play APK, useorg.telegram.messenger
.
Connect your device to the computer, open Terminal, run the commands:adb shell pm path org.telegram.messenger
The output will look something like this:package:/data/app/org.telegram.messenger-_zOSURFEx2GpHM8UDF_PVg==/base.apk
By using this information, pull the APK from your device to $HOME/telegram-android using command:adb pull /data/app/org.telegram.messenger-_zOSURFEx2GpHM8UDF_PVg==/base.apk $HOME/telegram-android/telegram_store.apk
Open Terminal, run the commands:cd $HOME/telegram-android
python apkdiff.py telegram_store.apk telegram_built.apk
If the APKs are the same, you will seeAPKs are the same!
If your APKs don't match, please make sure that you chose the correct code version and the right SDK.
+Check out the Troubleshooting section first in case you run into trouble.
+The verification process for iOS builds is, unfortunately, a lot more complex than for Android. The two main issues with Apple's current policies and infrastructure are as follows:
+Apple insists on using FairPlay encryption to “protect” even free apps from “app pirates” which makes obtaining the executable code of apps impossible without a jailbroken device. To solve this issue, Apple would simply need to allow submitting unencryptable binaries to the App Store. This would not affect security since the code would still be signed – and would enable anyone to check the integrity of apps supporting reproducible builds without endangering the integrity and security of their devices.
+Building your own reproducible binaries is difficult because macOS doesn't support containers like Docker. If Apple followed in the footsteps of Linux (and even Microsoft!) and added container support, it would eliminate the need for steps 1-3 in the guide below.
+++As things stand now, you'll need a jailbroken device, at least 1,5 hours and approximately 90GB of free space to properly set up a virtual machine for the verification process.
+
To provide a stable and easily reproducible environment, Telegram iOS builds are compiled on a virtual machine. Parallels is used to verify the builds.
+Parallels can be obtained here, it features a fully-functional trial version.
+To download an image that can be installed on the virtual machine, open the App Store, search for “Catalina” and click “View”.
+ + + + +This will open a system pop-up offering to download the OS:
+ +Choose “Download” and wait for the download to finish. + +> If you were not using the latest version of the OS, your system may start updating instead. Please finish updating to download macOS Catalina. + +When done, open Parallels and choose macOS Catalina: + + + + + +Before starting the installation, configure the virtual machine:
+ + +Change the name of the virtual machine to macos10_15_Xcode12_2
Hardware > Processors: 2-4
Memory > 4GB may suffice but 8GB is recommended
You will get something like this:
+ + +Parallels may request access to your microphone and camera, this is not required – just press Close.
+ + +Your Apple ID is also not required, you can choose Set Up Later.
+ + +Use “telegram” for both the account name and password.
+++ + +Do not ever use the password “telegram” for anything else, it's cursed.
+
Now install Parallels tools from the menu bar:
+Install Parallels Tools using menu bar > Parallels icon > Actions > Install (Reinstall) Parallels Tools...
+After the system restarts, log in.
Open Terminal and run:sudo visudo
Enter the password “telegram”
Find this line at the end of the file:%admin ALL=(ALL) ALL
Press “i” on your keyboard, add “NOPASSWD:”%admin ALL=(ALL) NOPASSWD: ALL
Press Escape.
Type in “:wq”
Press Enter
In the terminal, run:sudo systemsetup -setcomputersleep Never
In the virtual machine, open System Settings > Sharing and enable Remote Login.
+ + +In the virtual machine, open Terminal and run:mkdir -p .ssh; nano .ssh/authorized_keys
In your main OS, open Terminal and run:if [ ! -e ~/.ssh/id_rsa.pub ]; then ssh-keygen -t rsa -b 4096; fi && cat ~/.ssh/id_rsa.pub | pbcopy
If you see the line “Enter file in which to save the key (/Users/…/.ssh/id_rsa):”, press Enter
In the virtual machine, press CMD+V
Then Ctrl+O, Ctrl+X
In the virtual machine, open Safari and go to https://developer.apple.com
Sign in to your Account:
Go to Downloads > More
Enter Xcode in the search field and find the version 12.2
Once the installation is complete, open the file Xcode 12.2.xip. The system will unarchive the app into the same folder. Move it to the Applications folder using Finder.
+ + + + +On the virtual machine, run this command from the terminal:sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
Shut down the virtual machine.
+ + +git clone --recursive https://github.com/TelegramMessenger/telegram-ios.git $HOME/telegram-ios
cd $HOME/telegram-ios
git checkout release-${VERSION_NUMBER}
E.g., git checkout release-7.3
. Please note that you need to check out the whole git history as the build version depends on the number of commits in the repository.
mkdir -p $HOME/bazel && cd $HOME/bazel
curl -O -L https://github.com/bazelbuild/bazel/releases/download/3.7.0/bazel-3.7.0-darwin-x86_64
mv bazel-3.7.0-darwin-x86_64 bazel
Check that you have downloaded the correct version:chmod +x bazel
./bazel --version
Open Terminal, run the commands:cd $HOME/telegram-ios
+BAZEL="$HOME/bazel/bazel" sh buildbox/build-telegram.sh verify
If the environment has been set up correctly, this will start the building process. Note that this step can easily take 30-40 minutes. The average build time on a MacBook Pro (i9 6 core) is 35 minutes.
+ + +Once the process is complete the resulting IPA file can be found in build/artifacts/Telegram.ipa
All the following steps will be made via Terminal on your main system.
This step requires a jailbroken device equipped with tools for decrypting apps. We‘d love to make this process more simple but that’s what you get for using Apple tech.
+Install the necessary tools:if ! type brew > /dev/null;
+ then /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"; fi && brew install python3
Runpython3 tools/ipadiff.py build/artifacts/Telegram.ipa PATH-TO-THE-IPA-FILE-FROM-STEP-9
In case of a successful comparison, you will get a text along these lines:
+IPAs are equal, except for the files that can't currently be checked:
+ Excluded files that couldn't be checked due to being encrypted:
+ PlugIns/SiriIntents.appex/SiriIntents
+ PlugIns/Widget.appex/Widget
+ PlugIns/NotificationContent.appex/NotificationContent
+ PlugIns/NotificationService.appex/NotificationService
+ PlugIns/Share.appex/Share
+ IPAs contain Watch directory with a Watch app which can't be checked currently.
+ IPAs contain .car (Asset Catalog) files that are compiled by the App Store and can't currently be checked:
+
+ Frameworks/TelegramUI.framework/Assets.car
+ Assets.car
+ IPAs contain .nib (compiled Interface Builder) files that are compiled by the App Store and can't currently be checked:
+ Base.lproj/LaunchScreen.nib
+
+
+In case of any mismatches, you'll get a detailed report.
+ + +You will get a warning if the archive created in Step 7 contains encrypted files. If all these files are in the PlugIns
subfolder, they represent various system extensions (e.g. external sharing, Siri, 3D touch). Decrypting such files using existing ways of receiving app archives via Jailbreak is non-trivial (but we're working on resolving this issue). If you do manage to decrypt them, e.g. on iOS 8, they will be matched.
You will be notified if the archive includes an Apple Watch app. The watch app will soon no longer be included in the archive.
+Files with the .car
extension are app resource archives (images, sounds) which were compiled and processed specifically for the target device. The App Store processes them in non-trivial ways, we're planning on getting rid of them in future versions.
The LaunchScreen.nib
file is an empty file containing a description of the interface which is displayed by the system before the app is launched. It is processed by the App Store in a non-trivial way but doesn't contain any code and therefore may be ignored.
If you encounter any issues with obtaining the code, building and comparing the apps, please contact us at @botsupport and include the hashtag #reproducibleBuilds
with your message describing the problem.
Make sure that you checkout the correct version of the code.
+Make sure that you build the app using the right SDK.
+If the gradle version used in the Dockerfile is not available anymore and building of the Docker image fails, wait for a Dockerfile update or update manually to lastest available version.
+…
+We will update this section with information on overcoming other common issues.
+++Below you will find the current end-to-end TL-schema. More details on TL »
+
See also: +End-to-end encryption in MTProto, Secret Chats +Detailed schema in JSON
===8===
+decryptedMessage#1f814f1f random_id:long random_bytes:bytes message:string media:DecryptedMessageMedia = DecryptedMessage;
+decryptedMessageService#aa48327d random_id:long random_bytes:bytes action:DecryptedMessageAction = DecryptedMessage;
+decryptedMessageMediaEmpty#89f5c4a = DecryptedMessageMedia;
+decryptedMessageMediaPhoto#32798a8c thumb:bytes thumb_w:int thumb_h:int w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
+decryptedMessageMediaVideo#4cee6ef3 thumb:bytes thumb_w:int thumb_h:int duration:int w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
+decryptedMessageMediaGeoPoint#35480a59 lat:double long:double = DecryptedMessageMedia;
+decryptedMessageMediaContact#588a0a97 phone_number:string first_name:string last_name:string user_id:int = DecryptedMessageMedia;
+decryptedMessageActionSetMessageTTL#a1733aec ttl_seconds:int = DecryptedMessageAction;
+decryptedMessageMediaDocument#b095434b thumb:bytes thumb_w:int thumb_h:int file_name:string mime_type:string size:int key:bytes iv:bytes = DecryptedMessageMedia;
+decryptedMessageMediaAudio#6080758f duration:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
+decryptedMessageActionReadMessages#c4f40be random_ids:Vector<long> = DecryptedMessageAction;
+decryptedMessageActionDeleteMessages#65614304 random_ids:Vector<long> = DecryptedMessageAction;
+decryptedMessageActionScreenshotMessages#8ac1f475 random_ids:Vector<long> = DecryptedMessageAction;
+decryptedMessageActionFlushHistory#6719e45c = DecryptedMessageAction;
+
+===17===
+decryptedMessage#204d3878 random_id:long ttl:int message:string media:DecryptedMessageMedia = DecryptedMessage;
+decryptedMessageService#73164160 random_id:long action:DecryptedMessageAction = DecryptedMessage;
+decryptedMessageMediaVideo#524a415d thumb:bytes thumb_w:int thumb_h:int duration:int mime_type:string w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
+decryptedMessageMediaAudio#57e0a9cb duration:int mime_type:string size:int key:bytes iv:bytes = DecryptedMessageMedia;
+decryptedMessageLayer#1be31789 random_bytes:bytes layer:int in_seq_no:int out_seq_no:int message:DecryptedMessage = DecryptedMessageLayer;
+sendMessageTypingAction#16bf744e = SendMessageAction;
+sendMessageCancelAction#fd5ec8f5 = SendMessageAction;
+sendMessageRecordVideoAction#a187d66f = SendMessageAction;
+sendMessageUploadVideoAction#92042ff7 = SendMessageAction;
+sendMessageRecordAudioAction#d52f73f7 = SendMessageAction;
+sendMessageUploadAudioAction#e6ac8a6f = SendMessageAction;
+sendMessageUploadPhotoAction#990a3c1a = SendMessageAction;
+sendMessageUploadDocumentAction#8faee98e = SendMessageAction;
+sendMessageGeoLocationAction#176f8ba1 = SendMessageAction;
+sendMessageChooseContactAction#628cbc6f = SendMessageAction;
+decryptedMessageActionResend#511110b0 start_seq_no:int end_seq_no:int = DecryptedMessageAction;
+decryptedMessageActionNotifyLayer#f3048883 layer:int = DecryptedMessageAction;
+decryptedMessageActionTyping#ccb27641 action:SendMessageAction = DecryptedMessageAction;
+
+===20===
+decryptedMessageActionRequestKey#f3c9611b exchange_id:long g_a:bytes = DecryptedMessageAction;
+decryptedMessageActionAcceptKey#6fe1735b exchange_id:long g_b:bytes key_fingerprint:long = DecryptedMessageAction;
+decryptedMessageActionAbortKey#dd05ec6b exchange_id:long = DecryptedMessageAction;
+decryptedMessageActionCommitKey#ec2e0b9b exchange_id:long key_fingerprint:long = DecryptedMessageAction;
+decryptedMessageActionNoop#a82fdd63 = DecryptedMessageAction;
+
+===23===
+documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute;
+documentAttributeAnimated#11b58939 = DocumentAttribute;
+documentAttributeSticker#fb0a5727 = DocumentAttribute;
+documentAttributeVideo#5910cccb duration:int w:int h:int = DocumentAttribute;
+documentAttributeAudio#51448e5 duration:int = DocumentAttribute;
+documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
+photoSizeEmpty#e17e23c type:string = PhotoSize;
+photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize;
+photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize;
+fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileLocation;
+fileLocation#53d69076 dc_id:int volume_id:long local_id:int secret:long = FileLocation;
+decryptedMessageMediaExternalDocument#fa95b0dd id:long access_hash:long date:int mime_type:string size:int thumb:PhotoSize dc_id:int attributes:Vector<DocumentAttribute> = DecryptedMessageMedia;
+
+===45===
+decryptedMessage#36b091de flags:# random_id:long ttl:int message:string media:flags.9?DecryptedMessageMedia entities:flags.7?Vector<MessageEntity> via_bot_name:flags.11?string reply_to_random_id:flags.3?long = DecryptedMessage;
+decryptedMessageMediaPhoto#f1fa8d78 thumb:bytes thumb_w:int thumb_h:int w:int h:int size:int key:bytes iv:bytes caption:string = DecryptedMessageMedia;
+decryptedMessageMediaVideo#970c8c0e thumb:bytes thumb_w:int thumb_h:int duration:int mime_type:string w:int h:int size:int key:bytes iv:bytes caption:string = DecryptedMessageMedia;
+decryptedMessageMediaDocument#7afe8ae2 thumb:bytes thumb_w:int thumb_h:int mime_type:string size:int key:bytes iv:bytes attributes:Vector<DocumentAttribute> caption:string = DecryptedMessageMedia;
+documentAttributeSticker#3a556302 alt:string stickerset:InputStickerSet = DocumentAttribute;
+documentAttributeAudio#ded218e0 duration:int title:string performer:string = DocumentAttribute;
+messageEntityUnknown#bb92ba95 offset:int length:int = MessageEntity;
+messageEntityMention#fa04579d offset:int length:int = MessageEntity;
+messageEntityHashtag#6f635b0d offset:int length:int = MessageEntity;
+messageEntityBotCommand#6cef8ac7 offset:int length:int = MessageEntity;
+messageEntityUrl#6ed02538 offset:int length:int = MessageEntity;
+messageEntityEmail#64e475c2 offset:int length:int = MessageEntity;
+messageEntityBold#bd610bc9 offset:int length:int = MessageEntity;
+messageEntityItalic#826f8b60 offset:int length:int = MessageEntity;
+messageEntityCode#28a20571 offset:int length:int = MessageEntity;
+messageEntityPre#73924be0 offset:int length:int language:string = MessageEntity;
+messageEntityTextUrl#76a6d327 offset:int length:int url:string = MessageEntity;
+inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
+inputStickerSetEmpty#ffb62b95 = InputStickerSet;
+decryptedMessageMediaVenue#8a0df56f lat:double long:double title:string address:string provider:string venue_id:string = DecryptedMessageMedia;
+decryptedMessageMediaWebPage#e50511d8 url:string = DecryptedMessageMedia;
+
+===46===
+documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute;
+
+===66===
+documentAttributeVideo#ef02ce6 flags:# round_message:flags.0?true duration:int w:int h:int = DocumentAttribute;
+sendMessageRecordRoundAction#88f27fbc = SendMessageAction;
+sendMessageUploadRoundAction#bb718624 = SendMessageAction;
+
+===73===
+decryptedMessage#91cc4674 flags:# no_webpage:flags.1?true silent:flags.5?true random_id:long ttl:int message:string media:flags.9?DecryptedMessageMedia entities:flags.7?Vector<MessageEntity> via_bot_name:flags.11?string reply_to_random_id:flags.3?long grouped_id:flags.17?long = DecryptedMessage;
+