arefshojaei / tgram
PHP Telegram Bot
Requires
- arefshojaei/kitdash: ^1.4
- guzzlehttp/guzzle: ^7.10
Requires (Dev)
- phpunit/phpunit: ^10
This package is auto-updated.
Last update: 2026-05-29 11:18:41 UTC
README
A Powerful and Easy-to-Use PHP Library for Building Telegram Bots
Table of Contents
- Introduction
- Features
- Requirements
- Installation
- Quick Start
- Core Concepts
- Basic Usage
- Handling Messages
- Sending Messages
- Keyboards
- Advanced Features
- Configuration
- Examples
- Testing
- Troubleshooting
- Contributing
- License
Introduction
TGram is a modern, user-friendly PHP framework for building Telegram bots. Whether you're creating a simple bot that responds to messages or building a complex interactive application, TGram provides all the tools you need with an intuitive API.
With TGram, you can:
- Easily create Telegram bots in PHP
- Handle messages and commands with simple syntax
- Build interactive keyboards and menus
- Manage user interactions effortlessly
- Deploy using polling or webhook methods
Features
✨ Key Features:
- Simple API: Clean, intuitive syntax for developers of all levels
- Command Handling: Built-in support for bot commands (
/start,/help, etc.) - Message Listeners: Listen for specific messages or patterns
- Keyboard Support: Create inline and reply keyboards with buttons
- Message Types: Handle text, photos, documents, and more
- Chat Management: Manage chat information and user interactions
- Multiple Modes: Support for both Polling and Webhook processing
- Error Handling: Robust exception handling and error reporting
- Modern PHP: Built with PHP 8.0+ features (attributes, typed properties, etc.)
- Well-Structured: Organized codebase with clear separation of concerns
- Fully Tested: Comprehensive test suite with 105+ tests
Requirements
Before you start, make sure you have:
- PHP 8.2 or higher
- Composer (dependency manager for PHP)
- Active Telegram Bot Token (create one via BotFather)
Installation
Option 1: Using Composer (Recommended)
The easiest way to install TGram is through Composer:
composer require arefshojaei/tgram
This will download TGram and all its dependencies automatically.
Option 2: Manual Clone
If you prefer to clone the repository directly:
git clone https://github.com/ArefShojaei/TGram.git
cd TGram
composer install
Quick Start
Here's the simplest way to get your first bot running:
<?php require __DIR__ . "/vendor/autoload.php"; use TGram\{Telegram, Context}; // Create a new bot instance with your token $app = new Telegram("YOUR_BOT_TOKEN_HERE"); // Handle /start command $app->start(function(Context $ctx) { $ctx->sendMessage("Hello! 👋 I'm your bot!"); }); // Run the bot $app->run();
That's it! Your bot will now:
- Listen for incoming messages
- Respond to
/startcommand with a greeting - Keep running and polling for new messages
Getting Your Bot Token
- Open Telegram and search for @BotFather
- Start the conversation and type
/newbot - Follow the prompts to create your bot
- Copy your token and replace
"YOUR_BOT_TOKEN_HERE"in the code above
Core Concepts
The Telegram Class
The Telegram class is your main application. It's the entry point for all bot functionality:
$bot = new Telegram("TOKEN");
The Context Object
The Context object is passed to your handlers and contains:
- Update Information: Details about the incoming message
- User Information: Who sent the message
- Methods: Functions to send responses
$app->start(function(Context $context) { // Access the user $user = $context->update->user; echo $user->first_name; // User's first name // Send a response $context->sendMessage("Hello!"); });
Processing Modes
TGram supports two ways to receive updates:
Polling (Default - Long Polling):
- Your bot continuously asks Telegram for new messages
- Simple to set up, no server required
- Good for development and small deployments
Webhook (Server-based):
- Telegram sends updates directly to your server
- More efficient for production
- Requires a public URL and SSL certificate
use TGram\Enums\ProcessMode; // Start with polling (default) $app->run(ProcessMode::POLLING); // Or use webhook $app->run(ProcessMode::WEBHOOK);
Basic Usage
1. Handling the /start Command
The /start command is special - it's sent when a user first interacts with your bot:
$app->start(function(Context $ctx) { $user = $ctx->update->user; $message = "Welcome, {$user->first_name}! 👋"; $ctx->sendMessage($message); });
2. Listening for Specific Commands
Handle custom commands with the command() method:
$app->command("/help", function(Context $ctx) { $message = " 📖 **Available Commands:** /start - Start the bot /help - Show this help message /about - Learn about me "; $ctx->sendMessage($message); }); $app->command("/about", function(Context $ctx) { $ctx->sendMessage("I'm a TGram bot! 🤖"); });
3. Listening for Specific Messages
Use the hears() method to respond to exact messages:
$app->hears("Hello", function(Context $ctx) { $ctx->sendMessage("Hello to you too! 👋"); }); $app->hears("How are you?", function(Context $ctx) { $ctx->sendMessage("I'm doing great, thanks for asking! 😊"); });
4. Handling Any Message
Catch all messages that don't match other handlers:
$app->fallback(function(Context $ctx) { $message = $ctx->update->message; $ctx->sendMessage("I didn't understand that. Try /help"); });
Handling Messages
Accessing Message Information
When a message arrives, you can access detailed information:
$app->start(function(Context $ctx) { $message = $ctx->update->message; $user = $ctx->update->user; // Message details echo $message->message_id; // Unique message ID echo $message->text; // Message text echo $message->chat->id; // Chat ID // User details echo $user->id; // User's numeric ID echo $user->first_name; // First name echo $user->username; // Username (if set) echo $user->is_bot; // Is this user a bot? });
Different Message Types
Handle different types of messages:
// Text messages (default) $app->hears("text", function(Context $ctx) { // Handle text }); // Photo messages $app->onPhoto(function(Context $ctx) { $ctx->sendMessage("Nice photo! 📸"); }); // Document messages $app->onDocument(function(Context $ctx) { $ctx->sendMessage("Document received! 📄"); }); // Voice messages $app->onVoice(function(Context $ctx) { $ctx->sendMessage("Voice received! 🎙️"); });
Sending Messages
Simple Text Messages
$ctx->sendMessage("Hello, world!");
Formatted Messages
Use HTML or Markdown formatting:
// HTML formatting $message = "This is <b>bold</b> and <i>italic</i> text"; $ctx->sendMessage($message); // Multiple lines $message = " <b>Title</b> Hello <i>world</i> Line 3 "; $ctx->sendMessage($message);
Sending with Options
Add additional parameters to customize your message:
$ctx->sendMessage( "Hello!", parse_mode: "HTML", // or "Markdown" disable_web_page_preview: true // Don't show link previews );
Sending Media
// Send a photo $ctx->sendPhoto( "https://example.com/photo.jpg", caption: "Beautiful photo! 📸" ); // Send a document $ctx->sendDocument( "https://example.com/file.pdf", caption: "Important document" ); // Send voice $ctx->sendVoice("https://example.com/audio.ogg");
Editing Messages
Update previously sent messages:
$ctx->editMessageText( text: "Updated message", message_id: $messageId, chat_id: $chatId );
Keyboards
Reply Keyboards (Bottom Menu)
Reply keyboards appear as a menu at the bottom of the chat:
use TGram\Utils\Keyboard\{Keyboard, Button}; $keyboard = Keyboard::reply() ->row(Button::text("Option 1"), Button::text("Option 2")) ->row(Button::text("Option 3")) ->toArray(); $ctx->sendMessage("Choose an option:", reply_markup: $keyboard);
Inline Keyboards (Above Message)
Inline keyboards appear directly above or below messages:
$keyboard = Keyboard::inline() ->row( Button::url("GitHub", "https://github.com/ArefShojaei/TGram"), Button::url("Docs", "https://example.com/docs") ) ->row(Button::callback("Delete", "action_delete")) ->toArray(); $ctx->sendMessage("Check these links:", reply_markup: $keyboard);
Button Types
Text Button:
Button::text("Click me")
URL Button:
Button::url("Visit GitHub", "https://github.com")
Callback Button:
Button::callback("Vote 👍", "vote_yes")
Web App Button:
Button::webApp("Open App", "https://example.com/app")
Keyboard Examples
Simple Row:
$keyboard = Keyboard::reply() ->row(Button::text("Yes"), Button::text("No")) ->toArray();
Multiple Rows:
$keyboard = Keyboard::reply() ->row(Button::text("Option 1")) ->row(Button::text("Option 2")) ->row(Button::text("Option 3")) ->row(Button::text("Option 4")) ->toArray();
Hide Keyboard:
$ctx->sendMessage("Keyboard hidden", reply_markup: ["remove_keyboard" => true] );
Advanced Features
Custom Configuration
Configure your bot behavior:
$bot = new Telegram("TOKEN"); $bot->configure([ "polling_interval" => 2, // Check for messages every 2 seconds "max_attempts" => 3, // Retry failed requests 3 times "timeout" => 30, // Request timeout in seconds "debug_mode" => true, // Enable debug logging ]); $bot->run();
Handling Button Callbacks
When users click inline buttons, handle the callback:
$app->onCallbackQuery(function(Context $ctx) { $callbackData = $ctx->update->callback_query->data; if ($callbackData === "vote_yes") { $ctx->answerCallbackQuery("Thanks for voting! ✅"); // Update message or send new one } });
Chat Management
Get and manage chat information:
$app->start(function(Context $ctx) { $chat = $ctx->update->message->chat; echo $chat->id; // Chat ID echo $chat->title; // Chat title (for groups) echo $chat->type; // "private", "group", "supergroup", etc. // Get chat info from API $chatInfo = $ctx->getChatInfo(); });
User Information
Access detailed user data:
$user = $ctx->update->user; echo $user->id; // Unique user ID echo $user->is_bot; // Is a bot? echo $user->first_name; // First name echo $user->last_name; // Last name (optional) echo $user->username; // Username (optional) echo $user->language_code; // User's language
Middleware & Filters
Create custom filters for advanced message handling:
$app->command("/admin", function(Context $ctx) { // Check if user is admin if ($ctx->update->user->id !== ADMIN_ID) { $ctx->sendMessage("❌ You don't have permission"); return; } $ctx->sendMessage("✅ Admin panel"); });
Configuration
Environment Variables
Store your token securely using environment variables:
// .env file TELEGRAM_BOT_TOKEN=your_token_here // In your code $token = getenv("TELEGRAM_BOT_TOKEN"); $app = new Telegram($token);
Webhook Configuration
To use webhooks instead of polling:
$app = new Telegram("TOKEN"); $app->configure([ "webhook_url" => "https://your-domain.com/webhook.php", "webhook_port" => 443, // HTTPS port ]); $app->run(ProcessMode::WEBHOOK);
Examples
Example 1: Echo Bot
A bot that repeats everything the user sends:
<?php require __DIR__ . "/vendor/autoload.php"; use TGram\{Telegram, Context}; $app = new Telegram("TOKEN"); $app->start(function(Context $ctx) { $ctx->sendMessage("Send me anything and I'll repeat it!"); }); $app->fallback(function(Context $ctx) { $text = $ctx->update->message->text; $ctx->sendMessage("You said: " . $text); }); $app->run();
Example 2: Calculator Bot
A bot that performs simple calculations:
<?php require __DIR__ . "/vendor/autoload.php"; use TGram\{Telegram, Context}; $app = new Telegram("TOKEN"); $app->start(function(Context $ctx) { $ctx->sendMessage("Send me math like: 2 + 2"); }); $app->hears("/calculate", function(Context $ctx) { $ctx->sendMessage("Type your calculation (e.g., 5 * 3)"); }); $app->fallback(function(Context $ctx) { $text = $ctx->update->message->text; // Simple calculation if (preg_match('/(\d+)\s*([\+\-\*\/])\s*(\d+)/', $text, $matches)) { $a = (int)$matches[1]; $op = $matches[2]; $b = (int)$matches[3]; $result = match($op) { '+' => $a + $b, '-' => $a - $b, '*' => $a * $b, '/' => $b !== 0 ? $a / $b : "Cannot divide by zero", }; $ctx->sendMessage("Result: $result"); } }); $app->run();
Example 3: Menu-Based Bot
A bot with navigation buttons:
<?php require __DIR__ . "/vendor/autoload.php"; use TGram\{Telegram, Context}; use TGram\Utils\Keyboard\{Keyboard, Button}; $app = new Telegram("TOKEN"); $app->start(function(Context $ctx) { $keyboard = Keyboard::reply() ->row(Button::text("📚 Learn")) ->row(Button::text("🎮 Games")) ->row(Button::text("⚙️ Settings")) ->toArray(); $ctx->sendMessage("Welcome! Choose an option:", reply_markup: $keyboard); }); $app->hears("📚 Learn", function(Context $ctx) { $ctx->sendMessage("Learning resources coming soon!"); }); $app->hears("🎮 Games", function(Context $ctx) { $keyboard = Keyboard::inline() ->row( Button::callback("🎲 Dice", "game_dice"), Button::callback("🃏 Card", "game_card") ) ->toArray(); $ctx->sendMessage("Pick a game:", reply_markup: $keyboard); }); $app->hears("⚙️ Settings", function(Context $ctx) { $ctx->sendMessage("Settings panel will be here"); }); $app->run();
Testing
TGram includes a comprehensive test suite with 105+ tests covering all major features and components.
Test Structure
The test suite is organized into two main categories:
Unit Tests - Test individual components in isolation:
- Core classes (Bot, Telegram, Context)
- Exceptions and error handling
- Enums and data types
- Abilities and traits
- Utilities and helpers
Feature Tests - Test complete workflows:
- Bot initialization flow
- Command handling
- Message listening
- Keyboard interactions
- Exception handling
Running Tests
Install Development Dependencies
composer install
This automatically installs PHPUnit and other testing dependencies defined in composer.json.
Run All Tests
Execute the complete test suite:
vendor/bin/phpunit
This runs all 105+ tests and generates a summary report.
Run Specific Test Categories
Run only Unit Tests:
vendor/bin/phpunit tests/Unit/
Run only Feature Tests:
vendor/bin/phpunit tests/Feature/
Run Individual Test Classes
Test Telegram class:
vendor/bin/phpunit tests/Unit/Core/TelegramTest.php
Test Exception handling:
vendor/bin/phpunit tests/Unit/Exceptions/InvalidTokenExceptionTest.php
Test Keyboard utilities:
vendor/bin/phpunit tests/Unit/Utils/KeyboardTest.php
Test listener abilities:
vendor/bin/phpunit tests/Unit/Abilities/CanProvideListenerTest.php
Run Tests with Filters
Run tests matching a pattern:
vendor/bin/phpunit --filter testConstructor
Run tests for a specific method:
vendor/bin/phpunit --filter CanProvideListener
Generate Coverage Reports
Generate HTML code coverage report:
vendor/bin/phpunit --coverage-html coverage/
The report will be saved in the coverage/ directory. Open coverage/index.html in a browser to view detailed coverage information.
Generate text coverage summary:
vendor/bin/phpunit --coverage-text
Generate clover format for CI/CD integration:
vendor/bin/phpunit --coverage-clover coverage.xml
Verbose and Detailed Output
Run tests with verbose output:
vendor/bin/phpunit -v
Run tests with very verbose output:
vendor/bin/phpunit -vv
Run tests and generate test documentation (testdox):
vendor/bin/phpunit --testdox
Save testdox output to file:
vendor/bin/phpunit --testdox > testdox.txt
Parallel Test Execution
Run tests in parallel (if supported):
vendor/bin/phpunit -p
Custom Test Configuration
All test settings are configured in phpunit.xml:
<?xml version="1.0" encoding="UTF-8"?> <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" bootstrap="vendor/autoload.php" cacheDirectory=".phpunit.cache" executionOrder="depends,defects" beStrictAboutCoverageMetadata="true" beStrictAboutOutputDuringTests="true" failOnRisky="true" colors="true" testdox="true" failOnWarning="true"> <testsuites> <testsuite name="default"> <directory>tests</directory> </testsuite> </testsuites> <source restrictDeprecations="true" restrictNotices="true" restrictWarnings="true"> <include> <directory>src</directory> </include> </source> </phpunit>
Common Test Commands
# Quick test run vendor/bin/phpunit # Full test suite with coverage vendor/bin/phpunit --coverage-html coverage/ # Generate test documentation vendor/bin/phpunit --testdox > tests.txt # Test a specific component vendor/bin/phpunit tests/Unit/Core/ # Stop after first failure vendor/bin/phpunit --stop-on-failure # Stop after N failures vendor/bin/phpunit --stop-on-defect=5 # Run tests in reverse order vendor/bin/phpunit --reverse-order # Show only skipped and incomplete tests vendor/bin/phpunit --list-tests # Display interactive menu vendor/bin/phpunit --interactive
Test Files Location
All tests are located in the tests/ directory:
tests/
├── Helpers/
│ └── TestHelper.php # Test utility functions
├── Fixtures/
│ ├── FakeUpdateData.php # Mock Telegram data
│ └── FakeTelegramResponse.php # Mock API responses
├── Unit/
│ ├── Core/
│ │ ├── BotTest.php
│ │ ├── TelegramTest.php
│ │ └── ContextTest.php
│ ├── Exceptions/
│ │ ├── InvalidTokenExceptionTest.php
│ │ └── ValidationExceptionTest.php
│ ├── Enums/
│ │ ├── ProcessModeTest.php
│ │ └── HttpMethodTest.php
│ ├── Abilities/
│ │ └── CanProvideListenerTest.php
│ └── Utils/
│ ├── KeyboardTest.php
│ ├── ButtonTest.php
│ └── ConsoleTest.php
└── Feature/
├── BotInitializationTest.php
├── CommandHandlingTest.php
├── MessageListeningTest.php
├── KeyboardInteractionTest.php
└── ExceptionHandlingTest.php
Test Coverage Goals
- Overall: 85%+ coverage
- Core Classes: 90%+ coverage
- Exceptions: 100% coverage
- Enums: 100% coverage
- Utilities: 85%+ coverage
Writing New Tests
To add new tests:
- Create test file in appropriate directory under
tests/ - Extend
PHPUnit\Framework\TestCase - Name test methods with
testprefix - Use descriptive names:
testConstructorWithValidToken() - Follow Arrange-Act-Assert pattern
Example test:
<?php namespace Tests\Unit\Core; use PHPUnit\Framework\TestCase; use TGram\Telegram; class MyNewTest extends TestCase { public function testSomethingWorks(): void { // Arrange $bot = new Telegram('token'); // Act $result = $bot->doSomething(); // Assert $this->assertTrue($result); } }
Troubleshooting
Bot Not Responding
Problem: Bot doesn't respond to messages
Solutions:
- Verify your bot token is correct
- Check that your bot is actually running
- Make sure you've started a conversation with
/start - Check your bot's privacy settings with @BotFather
Connection Errors
Problem: "Connection refused" or timeout errors
Solutions:
- Check your internet connection
- Verify Telegram API is accessible from your server
- Increase timeout in configuration
- Check firewall rules if on a server
Messages Not Sending
Problem: sendMessage() doesn't work
Solutions:
- Verify chat ID is correct
- Ensure the context object is valid
- Check error messages in logs
- Test with a simple text message first
Memory Issues
Problem: Bot crashes or uses too much memory
Solutions:
- Reduce
polling_intervalto check less frequently - Avoid storing large data in memory
- Clear temporary files regularly
- Monitor memory usage with
memory_get_usage()
Test Failures
Problem: Tests are failing
Solutions:
- Ensure PHPUnit is installed:
composer install - Check PHP version is 8.0+:
php -v - Verify all dependencies are installed:
composer update - Run tests with verbose output:
vendor/bin/phpunit -v - Check test configuration in
phpunit.xml
Contributing
We welcome contributions! To contribute:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Write tests for your changes
- Ensure all tests pass:
vendor/bin/phpunit - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Before Submitting a PR
- ✅ Run the full test suite
- ✅ Ensure code coverage is maintained
- ✅ Follow PSR-4 autoloading standards
- ✅ Use English for code and comments
- ✅ Update documentation if needed
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
For more information:
- 📖 Documentation
- 🐛 Report Issues
- 💬 Discussions
- 📧 Email: arefshojaei82@gmail.com
Acknowledgments
- Built with ❤️ by Aref Shojaei
- Powered by the Telegram Bot API
- Uses Guzzle HTTP Client
Happy Bot Building! 🚀