Proto Brick is just starting its journey. Your stars βοΈ are crucial for the project's visibility!
Modern async pure PHP client library for Telegram MTProto. Build userbots with direct API access β no TDLib dependencies.
Note
π§ Work in Progress: This library is currently under active development. Features are being added rapidly, and breaking changes may occur before the v1.0 release.
- Full Async: Built on top of AMPHP and Revolt for high-performance non-blocking I/O.
- Pure PHP Implementation: No requirement for the official
tdlibbinary extension. Works anywhere PHP runs. - Strictly Typed: 100% of the API is auto-generated from the official TL-Schema.
- Direct API Access: 1:1 mapping of Telegram API methods (e.g.,
$client->messages->sendMessage). - Smart Peer Management: Automatically handles
access_hashand resolves@usernamesorIDsvia local cache. - Advanced Logging: Native colorized console output with channel filtering, fully compatible with PSR-3
- PHP 8.2+ (64-bit)
- Required Extensions:
ext-zlib,ext-json - Highly Recommended Extensions:
ext-gmp(Speeds up crypto/auth operations).ext-openssl(Speeds up crypto operations).ext-uv(Better event loop performance).
composer require "protobrick/mtproto-client:^0.1-beta"Run once to log in. It prompts for credentials (phone, code, 2FA) and saves the session locally.
<?php //auth.php
require __DIR__ . '/vendor/autoload.php';
use ProtoBrick\MTProtoClient\Client;
use ProtoBrick\MTProtoClient\Settings;
// Get your api_id and api_hash from https://my.telegram.org
$settings = new Settings(
api_id: 12345,
api_hash: 'your_api_hash'
);
// Session files will be stored in 'session_name' folder
$client = Client::create($settings, __DIR__ . '/session_name');
$auth = $client->login();
$client->logger->info("Logged in as {$auth->user->firstName}\n");If you need to handle login programmatically (e.g., from database, API, or custom input), pass callables
$auth = $client->login(
phoneNumber: '+1234567890',
codeProvider: fn() => readline("Code: "), // Or fetch from DB/API
passwordProvider: fn() => readline("2FA: "), // Or fetch from DB/API
signupProvider: fn() => [readline("Name: "), readline("Last Name: ")]
);Use this for simple scripts (e.g., cron jobs) where you need to perform an action and exit.
<?php
require __DIR__ . '/vendor/autoload.php';
use ProtoBrick\MTProtoClient\Client;
use ProtoBrick\MTProtoClient\Settings;
$settings = new Settings(api_id: 12345, api_hash: 'hash');
$client = Client::create($settings, __DIR__. '/session_name');
$client->channels->joinChannel(channel: "@ProtoBrickChat");
$client->messages->sendMessage(
peer: "@ProtoBrickChat", //auto-resolve peer
message: 'Hello from ProtoBrick!'
);Use the Event Loop to build bots or tools that react to events in real-time.
<?php
require __DIR__ . '/vendor/autoload.php';
use ProtoBrick\MTProtoClient\Client;
use ProtoBrick\MTProtoClient\Settings;
use ProtoBrick\MTProtoClient\Event\MessageContext;
use ProtoBrick\MTProtoClient\Event\ServiceMessageContext;
// Import specific TL types if you need raw checks
use ProtoBrick\MTProtoClient\Generated\Types\Base\MessageActionChatCreate;
use ProtoBrick\MTProtoClient\Generated\Types\Base\MessageMediaPhoto;
use ProtoBrick\MTProtoClient\Generated\Types\Base\UpdateUserTyping;
use ProtoBrick\MTProtoClient\TL\TlObject;
$settings = new Settings(api_id: 12345, api_hash: 'hash');
$client = Client::create($settings, __DIR__. '/session_name');
// Handle New Messages
$client->onMessage(function (MessageContext $ctx) {
// Ignore own messages
if ($ctx->isOutgoing()) return;
// High-level helpers
// (See src/Event/MessageContext.php & AbstractMessageContext.php)
if ($ctx->getText() === '/ping') {
$ctx->reply("Pong! π");
$ctx->react('π');
return;
}
// Low-level access
// Check for media types, via_bot_id, reply_markup, etc. directly
if ($ctx->message->media instanceof MessageMediaPhoto) {
echo "Received a photo with caption: " . $ctx->getText();
}
});
// Handle Edited Messages
$client->onMessageEdit(function (MessageContext $ctx) {
echo "EDIT in chat {$ctx->getChatId()}: {$ctx->getText()}\n";
});
// Handle Service Messages (Joins, Leaves, Title Changes)
$client->onServiceMessage(function (ServiceMessageContext $ctx) {
// High-level helpers
// (See src/Event/ServiceMessageContext.php & AbstractMessageContext.php)
if ($ctx->isTitleChanged()) {
echo "Chat {$ctx->getChatId()} renamed to: {$ctx->getNewTitle()}\n";
}
// Low-level access
// Handle specific actions that don't have wrappers yet
if ($ctx->message->action instanceof MessageActionChatCreate) {
echo "Group created with title: " . $ctx->message->action->title;
}
});
// Handle Raw Updates (Typing, Status, Read Receipts)
$client->onUpdate(function (TlObject $update) {
// Catches ALL updates flowing from the server
if ($update instanceof UpdateUserTyping) {
echo "User {$update->userId} is typing...\n";
}
});
// Start the Event Loop (Blocks execution)
$client->start();The client provides generated *Async methods for every API call.
Use synchronous calls for simple logic, and concurrent execution for performance.
use function Amp\Future\await;
// π’ Synchronous (Blocking)
// Execution stops here until the channel is joined.
$client->channels->joinChannel('@ProtoBrickChat');
// π Concurrent (Parallel)
// Send multiple requests at once. Total time = time of the slowest request.
$futures = [];
foreach (['@Chat1', '@Chat2', '@Chat3'] as $chat) {
// Returns Amp\Future immediately without waiting
$futures[] = $client->channels->joinChannelAsync($chat);
}
// Wait for ALL requests to complete
await($futures);
echo "Joined all chats!";Use ignore() for tasks where you don't care about the result or errors.
// The code continues immediately, the request is sent in the background
$client->messages->sendMessageAsync(
peer: 'me', message: 'Background log entry',
)->ignore();Warning
ignore() pushes the task to the Event Loop.
- Short Scripts: If your script ends immediately (reaches the end of the file), the Event Loop stops, and the request will not be sent.
- Daemons/Bots: This works perfectly inside
$client->start()or long-running processes, as the loop keeps turning.
You can customize the connection settings via the Settings object:
use ProtoBrick\MTProtoClient\Transport\TransportProtocol;
$settings = new Settings(
api_id: ...,
api_hash: ...,
server_address: '149.154.167.50', // Custom DC IP
transport: TransportProtocol::PaddedIntermediate, // Obfuscation
connect_timeout_seconds: 10
);The core of this library is generated automatically:
src/Generated/Api/*.php: Methods grouped by namespace (messages,users, etc.).src/Generated/Types/**/*.php: DTOs for every TL object (InputPeer,Message,User, etc.).
You can regenerate the library API for a newer or older API layer yourself, without waiting for a release.
- Download the new
.jsonschema file (e.g., from here). - Ensure the filename contains the version number (e.g.,
TL_telegram_v220.json). - Place the file in the
schema/directory (you can keep the old ones; the builder automatically picks the highest version). - Run the builder:
php bin/build.php
Downgrading: To build using a specific schema file, pass the path as an argument:
php bin/build.php schema/TL_old_v199.jsonThis library is intended for personal automation and educational use. We are not responsible for any account limitations caused by misuse, spamming, or violating Telegram's Terms of Service. Please automate responsibly.
ProtoBrick is a young library, and we are building a community of developers interested in MTProto.
- Telegram Chat: Join the Discussion β Ask questions, suggest features, or share your projects.
- GitHub Issues: Found a bug? Open an issue here.
- Stars: If you find this library useful, please give it a star βοΈ! It helps us grow and keeps the updates coming.
Contributions (PRs) are always welcome!
MIT License. See LICENSE for details.
