A Flysystem 3 storage adapter backed by the Telegram Bot API.
This package treats files attached to Telegram messages as object storage and keeps local metadata for Flysystem paths, Telegram file_id values, MIME types, visibility, and chunk information. SQLite is the default metadata store, and the public MetadataStore interface allows replacing it with Redis, MySQL, or another backend later.
composer require aahl/flysystem-telegram- PHP 8.1+
ext-pdo- One metadata driver extension:
ext-pdo_sqlitefor the default SQLite metadata storeext-pdo_mysqlforMysqlMetadataStore
- A Telegram Bot token
- A Telegram chat id where the bot can send messages
<?php
use Aahl\FlysystemTelegram\Config\TelegramAdapterConfig;
use Aahl\FlysystemTelegram\TelegramAdapter;
use League\Flysystem\Filesystem;
$adapter = new TelegramAdapter(new TelegramAdapterConfig(
botToken: '123456:telegram-bot-token',
chatId: '-1001234567890',
databasePath: __DIR__ . '/storage/flysystem-telegram.sqlite',
));
$filesystem = new Filesystem($adapter);
$filesystem->write('docs/hello.txt', 'Hello Telegram');
$contents = $filesystem->read('docs/hello.txt');botToken and chatId can be passed explicitly or read from environment variables.
export FLYSYSTEM_TELEGRAM_BOT_TOKEN="123456:telegram-bot-token"
export FLYSYSTEM_TELEGRAM_CHAT_ID="-1001234567890"The bot token also supports the generic environment variable:
export TELEGRAM_BOT_TOKEN="123456:telegram-bot-token"databasePath is only used by the default SQLite metadata store. It is not read from environment variables. If omitted, it defaults to .flysystem-telegram.sqlite in the current working directory.
$adapter = new TelegramAdapter(new TelegramAdapterConfig());use Aahl\FlysystemTelegram\Config\TelegramAdapterConfig;
use Aahl\FlysystemTelegram\Config\UploadTypeStrategy;
use Aahl\FlysystemTelegram\Telegram\TelegramType;
use League\Flysystem\Visibility;
$config = new TelegramAdapterConfig(
botToken: null,
chatId: null,
databasePath: __DIR__ . '/flysystem-telegram.sqlite',
defaultVisibility: Visibility::PRIVATE,
enableChunking: true,
maxFileSize: 50 * 1024 * 1024,
chunkSize: 20 * 1024 * 1024,
chunkStreamProtocol: 'flysystem-telegram',
uploadTypeStrategy: UploadTypeStrategy::Auto,
typeSizeLimits: [
TelegramType::PHOTO => 10 * 1024 * 1024,
TelegramType::VIDEO => 20 * 1024 * 1024,
TelegramType::AUDIO => 20 * 1024 * 1024,
TelegramType::ANIMATION => 20 * 1024 * 1024,
TelegramType::DOCUMENT => 20 * 1024 * 1024,
],
apiBaseUri: 'https://api.telegram.org',
timeout: 30.0,
);The default strategy chooses a Telegram file type from the MIME type or file extension:
photo: imagesvideo: videosaudio: audio filesanimation: GIF filesdocument: everything else
Resolution order:
mime_typefrom the Flysystem write config- File extension inference
document
$filesystem->write('video.bin', $contents, [
'mime_type' => 'video/mp4',
]);By default, non-photo typed uploads and documents use a 20 MB limit even though the hosted Bot API accepts uploads up to 50 MB. This keeps stored files readable through the hosted Bot API, whose getFile download path is limited to 20 MB. If a typed upload exceeds its configured size limit, the adapter falls back to document. If the file also exceeds the document limit and chunking is enabled, it is uploaded as multiple Telegram documents.
You can raise typeSizeLimits and chunkSize when using a local Bot API server, which supports larger uploads and unrestricted downloads.
You can also force all uploads to use document:
$config = new TelegramAdapterConfig(
uploadTypeStrategy: UploadTypeStrategy::DocumentOnly,
);When a file exceeds the document limit and enableChunking is true, the adapter splits it into multiple Telegram documents and stores the chunk order in metadata.
Chunked reads use a lazy stream: each chunk is downloaded from Telegram only when the stream reaches it. The default chunk size is 20 MB so each chunk remains downloadable through the hosted Bot API.
$stream = $filesystem->readStream('large/archive.zip');
while (!feof($stream)) {
$chunk = fread($stream, 8192);
}
fclose($stream);If your process already has a stream wrapper with the same name, configure a different chunkStreamProtocol.
SQLite is used by default and requires ext-pdo_sqlite:
use Aahl\FlysystemTelegram\Metadata\SqliteMetadataStore;
$metadataStore = new SqliteMetadataStore(__DIR__ . '/flysystem-telegram.sqlite');If your application cannot install SQLite support, pass a custom MetadataStore or use MysqlMetadataStore.
Use MySQL/MariaDB when you do not want the default local SQLite metadata file. This store requires ext-pdo_mysql.
use Aahl\FlysystemTelegram\Config\TelegramAdapterConfig;
use Aahl\FlysystemTelegram\Metadata\MysqlMetadataStore;
use Aahl\FlysystemTelegram\TelegramAdapter;
$config = new TelegramAdapterConfig(
botToken: 'token',
chatId: '-1001234567890',
);
$metadataStore = new MysqlMetadataStore(
dsn: 'mysql:host=127.0.0.1;dbname=app;charset=utf8mb4',
username: 'app',
password: 'secret',
tablePrefix: 'flysystem_',
);
$adapter = new TelegramAdapter($config, metadataStore: $metadataStore);You can also pass an existing PDO connection:
$pdo = new PDO(
'mysql:host=127.0.0.1;dbname=app;charset=utf8mb4',
'app',
'secret',
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION],
);
$metadataStore = new MysqlMetadataStore(
pdo: $pdo,
tablePrefix: 'flysystem_',
);MysqlMetadataStore creates its tables by default. Disable automatic initialization when your framework owns database migrations:
$metadataStore = new MysqlMetadataStore(
pdo: $pdo,
tablePrefix: 'flysystem_',
autoInitialize: false,
);When explicit DSN credentials are omitted, MysqlMetadataStore can read them from environment variables:
export FLYSYSTEM_TELEGRAM_DB_DSN="mysql:host=127.0.0.1;dbname=app;charset=utf8mb4"
export FLYSYSTEM_TELEGRAM_DB_USER="app"
export FLYSYSTEM_TELEGRAM_DB_PASS="secret"MySQL/MariaDB metadata paths support up to 1023 bytes.
You may pass any custom MetadataStore implementation to TelegramAdapter:
use Aahl\FlysystemTelegram\Config\TelegramAdapterConfig;
use Aahl\FlysystemTelegram\Metadata\MetadataStore;
use Aahl\FlysystemTelegram\TelegramAdapter;
/** @var MetadataStore $metadataStore */
$adapter = new TelegramAdapter(
new TelegramAdapterConfig(botToken: 'token', chatId: '-1001234567890'),
metadataStore: $metadataStore,
);Custom stores must implement Aahl\FlysystemTelegram\Metadata\MetadataStore.
delete()anddeleteDirectory()remove metadata only. They do not delete Telegram messages or files.- Flysystem visibility is stored only as metadata. It does not change Telegram-side access control.
copy()duplicates metadata and reuses Telegramfile_idvalues. It does not upload the file again.move()updates the metadata path. It does not move the Telegram-side file.createDirectory()is a no-op. Directories are virtual and derived from file path prefixes.
composer install
composer test
composer analyse
composer csRun MySQL/MariaDB metadata store integration tests by providing a disposable database:
export FLYSYSTEM_TELEGRAM_DB_DSN="mysql:host=127.0.0.1;dbname=test;charset=utf8mb4"
export FLYSYSTEM_TELEGRAM_DB_USER="root"
export FLYSYSTEM_TELEGRAM_DB_PASS=""
composer testFix code style automatically:
composer cs:fixMIT