--- description: For tasks requiring sending Telegram server API requests or working with generated API types. globs: alwaysApply: false --- # Telegram Desktop API Usage ## API Schema The API definitions are described using [TL Language]\(https:/core.telegram.org/mtproto/TL) in two main schema files: 1. **`Telegram/SourceFiles/mtproto/scheme/mtproto.tl`** * Defines the core MTProto protocol types and methods used for basic communication, encryption, authorization, service messages, etc. * Some fundamental types and methods from this schema (like basic types, RPC calls, containers) are often implemented directly in the C++ MTProto core (`SourceFiles/mtproto/`) and may be skipped during the C++ code generation phase. * Other parts of `mtproto.tl` might still be processed by the code generator. 2. **`Telegram/SourceFiles/mtproto/scheme/api.tl`** * Defines the higher-level Telegram API layer, including all the methods and types related to chat functionality, user profiles, messages, channels, stickers, etc. * This is the primary schema used when making functional API requests within the application. Both files use the same TL syntax to describe API methods (functions) and types (constructors). ## Code Generation A custom code generation tool processes `api.tl` (and parts of `mtproto.tl`) to create corresponding C++ classes and types. These generated headers are typically included via the Precompiled Header (PCH) for the main `Telegram` project. Generated types often follow the pattern `MTP[Type]` (e.g., `MTPUser`, `MTPMessage`) and methods correspond to functions within the `MTP` namespace or related classes (e.g., `MTPmessages_SendMessage`). ## Making API Requests API requests are made using a standard pattern involving the `api()` object (providing access to the `MTP::Instance`), the generated `MTP...` request object, callback handlers for success (`.done()`) and failure (`.fail()`), and the `.send()` method. Here's the general structure: ```cpp // Include necessary headers if not already in PCH // Obtain the API instance (usually via api() or MTP::Instance::Get()) api().request(MTPnamespace_MethodName( // Constructor arguments based on the api.tl definition for the method MTP_flags(flags_value), // Use MTP_flags if the method has flags MTP_inputPeer(peer), // Use MTP_... types for parameters MTP_string(messageText), MTP_long(randomId), // ... other arguments matching the TL definition MTP_vector() // Example for a vector argument )).done([=]\(const MTPResponseType &result) { // Handle the successful response (result). // 'result' will be of the C++ type corresponding to the TL type // specified after the '=' in the api.tl method definition. // How to access data depends on whether the TL type has one or multiple constructors: // 1. Multiple Constructors (e.g., User = User | UserEmpty): // Use .match() with lambdas for each constructor: result.match([&]\(const MTPDuser &data) { /* use data.vfirst_name().v, etc. */ }, [&]\(const MTPDuserEmpty &data) { /* handle empty user */ }); // Alternatively, check the type explicitly and use the constructor getter: if (result.type() == mtpc_user) { const auto &data = result.c_user(); // Asserts if type is not mtpc_user! // use data.vfirst_name().v } else if (result.type() == mtpc_userEmpty) { const auto &data = result.c_userEmpty(); // handle empty user } // 2. Single Constructor (e.g., Messages = messages { msgs: vector }): // Use .match() with a single lambda: result.match([&]\(const MTPDmessages &data) { /* use data.vmessages().v */ }); // Or check the type explicitly and use the constructor getter: if (result.type() == mtpc_messages) { const auto &data = result.c_messages(); // Asserts if type is not mtpc_messages! // use data.vmessages().v } // Or use the shortcut .data() for single-constructor types: const auto &data = result.data(); // Only works for single-constructor types! // use data.vmessages().v }).fail([=]\(const MTP::Error &error) { // Handle the API error (error). // 'error' is an MTP::Error object containing the error code (error.type()) // and description (error.description()). Check for specific error strings. if (error.type() == u"FLOOD_WAIT_X"_q) { // Handle flood wait } else { Ui::show(Box(Lang::Hard::ServerError())); // Example generic error handling } }).handleFloodErrors().send(); // handleFloodErrors() is common, then send() ``` **Key Points:** * Always refer to `Telegram/SourceFiles/mtproto/scheme/api.tl` for the correct method names, parameters (names and types), and response types. * Use the generated `MTP...` types/classes for request parameters (e.g., `MTP_int`, `MTP_string`, `MTP_bool`, `MTP_vector`, `MTPInputUser`, etc.) and response handling. * The `.done()` lambda receives the specific C++ `MTP...` type corresponding to the TL return type. * For types with **multiple constructors** (e.g., `User = User | UserEmpty`), use `result.match([&]\(const MTPDuser &d){ ... }, [&]\(const MTPDuserEmpty &d){ ... })` to handle each case, or check `result.type() == mtpc_user` / `mtpc_userEmpty` and call the specific `result.c_user()` / `result.c_userEmpty()` getter (which asserts on type mismatch). * For types with a **single constructor** (e.g., `Messages = messages{...}`), you can use `result.match([&]\(const MTPDmessages &d){ ... })` with one lambda, or check `type()` and call `c_messages()`, or use the shortcut `result.data()` to access the fields directly. * The `.fail()` lambda receives an `MTP::Error` object. Check `error.type()` against known error strings (often defined as constants or using `u"..."_q` literals). * Directly construct the `MTPnamespace_MethodName(...)` object inside `request()`. * Include `.handleFloodErrors()` before `.send()` for standard flood wait handling.