Search by

A simple and flexible PHP package for building Telegram bots, featuring route handling, user state management, logging, path helpers, and an Illuminate-based container.

Package info

github.com/natilosir/BOT

pkg:composer/natilosir/bot

Statistics

Installs: 49

Dependents: 1

Suggesters: 0

Stars: 1

Open Issues: 0

1.4.2 2026-09-23 12:48 UTC

This package is auto-updated.

Last update: 2026-09-23 12:54:40 UTC


README

Driver-based PHP bot package for Telegram and Bale

composer require natilosir/BOT
git clone https://github.com/natilosir/BOT/
  • ORM

    • select
    • insert
    • table
    • update
    • delete
    • eloquent
    • search
  • Error log Advanced error management and storing logs in a separate file for every request to the server.

  • Route Class

  • Object Method api telegram

    • HTTP request
    • answerCallbackQuery
    • sendChatAction
    • deleteMessage
    • forwardMessage
    • sendMessage
    • copyMessage
      • inline
      • keyboard
        • editMessageReplyMarkup
        • column
        • row

Bootstrap

The application must be bootstrapped once before using bot, paths(), models, routes or other container-backed services. Keep this in the project entry point (or require a dedicated bootstrap.php from the entry point):

<?php

use natilosir\bot\Bootstrap;

require __DIR__ . '/vendor/autoload.php';

$paths = [
    'base_path'    => __DIR__,
    'app_path'     => __DIR__ . '/app',
    'route_path'   => __DIR__ . '/Router',
    'config_path'  => __DIR__ . '/config.php',
    'storage_path' => __DIR__ . '/storage',
    'log_path'     => __DIR__ . '/log.html',
];

$app = new Bootstrap($paths);

Bootstrap is intentionally platform-agnostic. It only owns application paths, configuration, the Illuminate container and service registration. Telegram/Bale selection remains the responsibility of DriverManager and the webhook resolver.

The old bootstrap contract is therefore preserved: existing project code using $app = new Bootstrap($paths); continues to work unchanged.

Bot drivers (Telegram / Bale)

The bot layer is fully driver-based. BotManager is platform-agnostic and only delegates calls to the active driver. Telegram and Bale own separate drivers and separate trait trees.

Directory structure

src/bot/
├── BotManager.php
├── DriverManager.php
├── Contracts/
│   ├── BotDriver.php
│   └── WebhookAwareDriver.php
├── Webhook/
│   ├── WebhookRequest.php
│   └── WebhookDriverResolver.php
└── Drivers/
    ├── AbstractBotDriver.php
    ├── Telegram/
    │   ├── TelegramDriver.php
    │   └── Traits/...
    └── Bale/
        ├── BaleDriver.php
        └── Traits/...

Configuration

Keep the real tokens only under their own drivers. paths()->config('bot.token') is a virtual compatibility alias: it reads bot.default and returns bot.drivers.<default>.token, so the token is never duplicated in configuration.

$bot = [
    'default' => 'bale', // telegram | bale

    'drivers' => [
        'telegram' => [
            'token' => 'YOUR_TELEGRAM_TOKEN',
            'base_url' => 'https://api.telegram.org',
            'webhook' => [
                'url' => 'http://1.bot.borzan.ir/webhook/telegram',
                'secret_token' => 'YOUR_TELEGRAM_WEBHOOK_SECRET',
            ],
        ],

        'bale' => [
            'token' => 'YOUR_BALE_TOKEN',
            'base_url' => 'https://tapi.bale.ai',
            'webhook' => [
                'url' => 'http://1.bot.borzan.ir/webhook/bale',
            ],
        ],
    ],
];


return [
    'bot' => $bot,
];

Changing only:

$bot['default'] = 'telegram';

makes both generic bot calls and paths()->config('bot.token') resolve to Telegram. Setting it to bale makes both resolve to Bale.

Default outgoing driver

bot::sendMessage($chatId, 'سلام');

The call is delegated to the driver selected by bot.default, unless the current request was recognized as a webhook from another configured driver.

Complete API coverage

The Telegram driver exposes explicit wrappers for every method documented in Telegram Bot API 10.3, including getUserProfileAudios, setUserEmojiStatus, Rich Messages, Ephemeral Messages, Guest Mode, Managed Bots, Business, Gifts, Stories and Join Request Query methods. getUpdates is also exposed even though webhook mode and long polling are mutually exclusive.

The Bale driver exposes all 50 methods currently documented by Bale, plus the 7 methods supported by Bale Business API through business* wrappers.

Each driver also keeps a low-level api($method, $data) escape hatch for forward compatibility when a platform adds a method before this package is updated.

Automatic webhook driver detection

Use a unique webhook endpoint for each platform:

http://1.bot.borzan.ir/webhook/telegram
http://1.bot.borzan.ir/webhook/bale

When a request arrives, DriverManager resolves the driver before route/controller code runs. Therefore this controller code automatically answers through the same platform that delivered the update:

bot::sendMessage($request->chatID, 'پاسخ');

You can inspect the detected source through:

$request->getDriverName(); // telegram | bale
$request->platform;        // telegram | bale
bot::driverName();         // active driver

For Telegram, the path is derived from webhook.url (or an explicit webhook.path override) and webhook.secret_token, when configured, must also match. setWebhook() automatically sends the secret and incoming requests are validated using the official X-Telegram-Bot-Api-Secret-Token header. Bale does not currently document an equivalent secret header, so it is detected by its dedicated webhook URL/path. For extra protection behind your own reverse proxy, Bale may also use the generic webhook.header + webhook.header_value pair; when configured, URL/path and custom header must all match.

Because Bale and Telegram update payloads can have the same shape, do not point both bots at one indistinguishable URL without a platform-specific path/header. The resolver deliberately throws on ambiguous matches. In a multi-driver installation it also rejects a POST bot update when no driver matches, instead of silently falling back to bot.default.

Runtime override

bot::useDriver('bale');
bot::sendMessage($chatId, 'سلام از بله');

bot::useDriver('telegram');
bot::sendMessage($chatId, 'Hello from Telegram');

Direct driver access

$bale = bot::driver('bale');
$bale->sendMessage($chatId, 'سلام');
$bale->askReview($userId, 2);
$bale->inquireTransaction($transactionId);

$telegram = bot::driver('telegram');
$telegram->sendMessage($chatId, 'Hello');

Telegram-only methods are not composed into Bale and Bale-specific methods are not mixed into Telegram.

Low-level API

bot::driver('bale')->api('METHOD_NAME', [
    'key' => 'value',
]);

Bale Business API

$bale = bot::driver('bale');

$bale->businessSendMessage([
    'chat_id' => $chatId,
    'text' => 'پیام کسب‌وکاری',
]);

Official Bale Bot API documentation: https://docs.bale.ai/

PhpStorm / IDE navigation

The package ships with .phpstorm.meta.php and strongly typed driver accessors. For exact autocomplete, signatures and Ctrl+Click navigation to the trait that implements a method, use one of these forms:

bot::telegram()->sendMessage($chatId, 'Telegram');
// Ctrl+Click sendMessage -> Drivers/Telegram/Traits/MessageTrait.php

bot::bale()->sendMessage($chatId, 'Bale');
// Ctrl+Click sendMessage -> Drivers/Bale/Traits/MessageTrait.php

bot::driver('telegram')->sendPhoto(...);
bot::driver('bale')->askReview(...);

bot::driver('telegram') and bot::driver('bale') are mapped to their concrete return types by PhpStorm metadata. The old dynamic form remains valid:

bot::sendMessage($chatId, 'runtime selected driver');

That form is intentionally runtime-dynamic because an incoming webhook can change the active driver. An IDE cannot know the source of a future HTTP request, so exact trait navigation is provided by the typed accessors above.

Borzan webhook endpoints

The default package configuration uses these endpoints:

Telegram: http://1.bot.borzan.ir/webhook/telegram
Bale:     http://1.bot.borzan.ir/webhook/bale

Only the full webhook.url needs to be configured. The resolver derives the path from that URL, so there is no duplicated path value that can become stale. An explicit webhook.path is still supported as an override for reverse proxies.

Incoming webhook resolution works in this order:

  1. Request must be POST and contain a bot update_id.
  2. The resolver checks all configured drivers.
  3. The configured webhook URL determines the expected host and path.
  4. Telegram additionally validates X-Telegram-Bot-Api-Secret-Token when a secret_token is configured.
  5. Exactly one driver must match. Zero matches in a multi-driver bot or more than one match fails fast; the code never falls back to the wrong token.
  6. The detected driver becomes the request context, so ordinary calls such as bot::sendMessage(...) answer through the same platform.

Calling these methods registers the configured URL automatically:

bot::telegram()->setWebhook();
bot::bale()->setWebhook();

Important: Telegram's hosted Bot API requires an HTTPS webhook URL. The HTTP Telegram URL above is kept exactly as requested and is usable with a local Telegram Bot API server; when using api.telegram.org, put TLS in front of 1.bot.borzan.ir and change the Telegram webhook URL to https://... before calling setWebhook().