Skip to content

MTProto: Requests ​

Introduction ​

Sending requests to Telegram is done through the same high-level API whether you are inside an update handler (via the ClientRequest) or outside one (via Client::session()). Both expose expressive methods — sendMessage, sendPhoto, editMessage, forwardMessages, and so on — that hide the underlying TL method calls. You never assemble a raw TL request yourself.

php
use LaraGram\MTProto\Facades\Client;

Client::session()->sendMessage(peer: '@durov', message: 'Hello!');

Inside a handler, the same methods are available directly on the request, and replies route back to the originating session automatically:

php
Client::onText('ping', function (\LaraGram\MTProto\Foundation\ClientRequest $request) {
    $request->sendMessage(peer: $request->chatId(), message: 'pong');
});

Addressing Peers ​

Almost every method takes a peer (the chat, channel, or user to act on). LaraGram resolves peers for you from several convenient forms:

  • Username — '@durov'
  • Numeric id — a user id 123456789, or a channel/supergroup id like -1001234567890
  • 'me' — the logged-in account (Saved Messages)
  • A peer object — e.g. $request->message->peer_id
php
$client->sendMessage(peer: '@durov', message: 'By username');
$client->sendMessage(peer: 123456789, message: 'By id');
$client->sendMessage(peer: 'me', message: 'Note to self');

NOTE

The first time you address a peer by username or id, LaraGram resolves it and caches the access hash, so subsequent calls are instant. Peer caching is backed by the configurable peer store.

Sending Messages ​

Plain Text ​

php
$client->sendMessage(
    peer: '@durov',
    message: 'A plain text message.',
);

Formatting with parse_mode ​

Pass parse_mode as markdown or html and LaraGram converts the formatting into Telegram message entities for you:

php
$client->sendMessage(
    peer: $chatId,
    message: 'Hello *world*, visit [our site](https://laragram.dev).',
    parse_mode: 'markdown',
);

$client->sendMessage(
    peer: $chatId,
    message: '<b>bold</b> <i>italic</i> <code>code</code> '
        . '<a href="tg://user?id=123456789">mention</a>',
    parse_mode: 'html',
);

The HTML mode supports the full Telegram entity set — b, i, u, s, code, pre (with language), blockquote (and expandable), spoilers via <span class="tg-spoiler">, text_link, text_mention, and custom emoji via <tg-emoji emoji-id="...">. If you supply your own entities, they take precedence and parse_mode is ignored.

Rich Messages ​

Beyond parse_mode, MTProto supports Telegram's rich message format (headings, fenced code blocks, blockquotes) through sendRichMessage. This uses Telegram's native InputRichMessage, not parse_mode:

php
$client->sendRichMessage(
    peer: $chatId,
    content: "# Rich heading\n\n"
        . "**bold**, *italic*, `code`, and a [link](https://telegram.org).\n\n"
        . "> a blockquote\n\n"
        . "```php\necho 'fenced block';\n```",
    format: 'markdown',   // or 'html'
);

Replies and Silent Messages ​

Additional parameters map straight to Telegram's options — reply to a message, send silently, disable the link preview, and so on:

php
$client->sendMessage(
    peer: $chatId,
    message: 'This is a reply, sent silently.',
    reply_to_msg_id: $request->messageId(),
    silent: true,
    no_webpage: true,
);

Keyboards ​

Attach a keyboard by passing reply_markup. It accepts the exact same array shape produced by LaraGram's Keyboard Builder, so you build keyboards the way you already do for bots:

php
use LaraGram\Support\Facades\Keyboard;
use LaraGram\Keyboard\Make;

$keyboard = Keyboard::inlineKeyboardMarkup(
    Make::row(
        Make::callbackData('👍 Yes', 'vote_yes'),
        Make::callbackData('👎 No', 'vote_no'),
    ),
    Make::row(
        Make::url('Open site', 'https://laragram.dev'),
    ),
)->get();

$client->sendMessage(
    peer: $chatId,
    message: 'Do you agree?',
    reply_markup: $keyboard,
);

Reply keyboards, force-reply, and remove-keyboard markups are all supported the same way — build them with the Keyboard Builder and pass the result as reply_markup. Handle button presses with onCallbackQuery / onCallbackQueryData and answer them with answerCallback.

Editing, Forwarding & Copying ​

php
// Edit a message you sent
$client->editMessage(peer: $chatId, id: $messageId, message: 'Updated text', parse_mode: 'markdown');

// Forward (keeps the "forwarded from" header)
$client->forwardMessages(fromPeer: $sourceChat, toPeer: $targetChat, ids: [101, 102, 103]);

// Copy (no forward header — re-sends the content)
$client->copyMessages(fromPeer: $sourceChat, toPeer: $targetChat, ids: 101);

// Fetch specific messages
$messages = $client->getMessages(peer: $chatId, ids: [101, 102]);

// Delete
$client->deleteChatMessages(peer: $chatId, ids: [101, 102], revoke: true);

Reading & Housekeeping ​

php
$client->markAsRead(peer: $chatId);                 // Mark history read
$client->readMessageContents(peer: $chatId, ids: 101); // Mark media/voice as consumed
$client->sendChatAction(peer: $chatId, action: 'typing');
$client->sendTyping(peer: $chatId);                 // Shortcut for the typing action
$client->deleteHistory(peer: $chatId, forEveryone: false);

From inside a handler you can also use the request shortcuts:

php
$request->read();   // Mark the incoming message's chat as read
$request->seen();   // Mark its contents as consumed

Searching Messages ​

php
// Within a chat
$results = $client->searchMessages(peer: $chatId, query: 'invoice');

// Across all chats
$results = $client->searchGlobal(query: 'meeting notes');

For scheduled messages, see getScheduledMessages, sendScheduledMessages, and deleteScheduledMessages — each takes a peer and message ids.

Answering Queries ​

Bots answer callback and inline queries through the high-level API:

php
Client::onCallbackQueryData('vote_yes', function (ClientRequest $request) {
    $request->answerCallback(
        queryId: $request->query_id,
        text: 'Thanks for voting!',
        alert: false,
    );
});

Client::onInlineQuery(function (ClientRequest $request) {
    $request->answerInlineQuery(
        queryId: $request->query_id,
        results: [ /* inline result objects */ ],
    );
});

Bot management (setBotCommands, setBotMenuButton, setBotInfo, setDefaultAdminRights) is available on the same client — see the Chats & Channels and Features references for related surfaces.

The Namespaced API ​

The high-level helpers cover the common cases, but Telegram's API is organized into namespaces (messages, channels, contacts, account, photos, stories, payments, …), and every method in them is reachable — with named parameters and IDE autocompletion — through the matching property on the client or request:

php
// On the request (inside a handler)
$request->messages->sendMessage(
    peer: $request->message->peer_id->user_id,
    message: 'Via the messages namespace',
);

// On a live client (outside a handler)
Client::session()->contacts->getContacts();
Client::session()->channels->getFullChannel(channel: '@laragram');
Client::session()->account->updateProfile(about: 'Building bots with LaraGram');

Peers, parse_mode, and reply_markup are pre-processed for namespaced calls exactly as they are for the high-level helpers, so '@username' peers and Bot-API keyboards work everywhere.

Calling Any Method ​

For anything not surfaced elsewhere, invoke() calls any TL method by name with an associative array of parameters, while still resolving peers and formatting for you:

php
$result = Client::session()->invoke('help.getConfig');

$result = Client::session()->invoke('messages.sendMessage', [
    'peer'    => '@durov',
    'message' => 'Low-level, still framework-managed.',
]);

This is the escape hatch for brand-new API methods — you stay inside LaraGram's peer resolution, rate limiting, and pacing, without touching the raw protocol.

Released under the MIT License.