hojjatjh / bot-builder
A PHP tool to scaffold ready-to-run Telegram bots from reusable templates.
Requires
- php: >=8.1
- ext-json: *
Requires (Dev)
- phpunit/phpunit: ^10.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
🤖 BotBuilder
A modern, zero-dependency PHP scaffolding engine that builds ready-to-run projects (like Telegram bots) from reusable templates.
English | فارسی
Table of Contents
- Why BotBuilder?
- Features
- How It Works
- Requirements
- Installation
- Quick Start
- Creating a Template
- Usage
- API Reference
- Full Example: Telegram Bot
- Testing
- Contributing
- License
Why BotBuilder?
Building many similar projects by hand is repetitive and error-prone. BotBuilder lets you keep a single base template and generate as many customized copies as you want — each one filled with its own values. Perfect for spinning up new Telegram bots, micro-services, or any boilerplate project in seconds, while your original template stays untouched and reusable forever.
Features
- 🧩 Template-based scaffolding — generate a whole project from a base folder.
- 🔖
{{PLACEHOLDER}}replacement in both file contents and file/folder names. - 🗂 Works on any file — not just
config.php. Every file in the template is scanned. - 🧵 Multiple files at once — fill
config.php,server-key.php, and more in a single build. - 🌳 Nested folders supported out of the box (recursive).
- ✅ Required values & defaults with built-in validation.
- ♻️ Read-only base — the template is never modified and can be reused infinitely.
- 🪶 Zero dependencies — pure filesystem, fully unit-tested.
- ⚙️ Configurable delimiters (
{{ }}by default).
How It Works
BotBuilder reads a base/template directory, replaces every {{KEY}} token with the values you
provide, and writes the result into a new output directory — leaving the original template
untouched.
base/telegram-bot/ -> bots/my-new-bot/
config.php ({{...}}) -> config.php (filled in)
server-key.php ({{...}}) -> server-key.php (filled in)
bot.php -> bot.php
Requirements
- PHP >= 8.1
- ext-json
Installation
composer require hojjatjh/bot-builder
Quick Start
<?php require 'vendor/autoload.php'; use BotBuilder\BotBuilder; $builder = new BotBuilder(__DIR__ . '/base/telegram-bot'); $builder ->define('BOT_NAME', required: true) ->define('BOT_TOKEN', required: true) ->define('ADMIN_ID', default: '0'); $files = $builder->build(__DIR__ . '/bots/my-new-bot', [ 'BOT_NAME' => 'MyCoolBot', 'BOT_TOKEN' => '123456:ABC-DEF', ]); print_r($files);
Creating a Template
A template is nothing more than a normal folder of files. Wherever you want a value to be filled
in later, you write a placeholder like {{BOT_NAME}}. That's the whole idea — there is nothing
complicated to learn.
💡 Important: BotBuilder does not care about file names, and it is not limited to
config.php. It scans every file in the template, so your placeholders work in any file you like —config.php,server-key.php,.env,README.md, anything.
Step 1 — Put placeholders in your files
Your config file can have any name. Here it is called config.php:
<?php return [ 'name' => '{{BOT_NAME}}', 'token' => '{{BOT_TOKEN}}', ];
Step 2 — Placeholders also work in file & folder names
A file literally named {{BOT_NAME}}.php becomes MyCoolBot.php after building. The same works for
folders, e.g. handlers/{{BOT_NAME}}/.
Step 3 — Use as many files as you want (a real example)
A template can hold any number of files. Here it has two: config.php and server-key.php.
config.php
<?php return [ 'name' => '{{BOT_NAME}}', 'token' => '{{BOT_TOKEN}}', ];
server-key.php
<?php return [ 'secret' => '{{SERVER_KEY}}', 'webhook_url' => '{{WEBHOOK_URL}}', ];
A single build() call fills in both files at once:
$builder ->define('BOT_NAME', required: true) ->define('BOT_TOKEN', required: true) ->define('SERVER_KEY', required: true) ->define('WEBHOOK_URL', default: ''); $builder->build(__DIR__ . '/bots/my-new-bot', [ 'BOT_NAME' => 'MyCoolBot', 'BOT_TOKEN' => '123456:ABC-DEF', 'SERVER_KEY' => 's3cr3t', 'WEBHOOK_URL' => 'https://example.com/hook', ]);
Files without any placeholder are simply copied as-is, so you can freely mix static and
templated files (PHP, JSON, .env, Markdown, images — anything).
Usage
Defining config (required & defaults)
$builder ->define('BOT_TOKEN', required: true) // must be provided ->define('ADMIN_ID', default: '0'); // optional, falls back to '0'
If a required value is missing, a BotBuilderException is thrown before anything is written to
disk — so you never end up with a half-generated project.
Overwriting an existing target
$builder->build($target, $values, overwrite: true);
Without overwrite: true, building into a directory that already exists throws an exception (this
protects you from accidentally clobbering previous work).
Using the lower-level pieces
The BotBuilder facade is optional. You can compose the parts directly:
use BotBuilder\Template; use BotBuilder\BotGenerator; $generator = new BotGenerator(new Template(__DIR__ . '/base/telegram-bot')); $files = $generator->generate(__DIR__ . '/bots/my-new-bot', [ 'BOT_NAME' => 'MyCoolBot', ]);
Custom delimiters
use BotBuilder\Template; use BotBuilder\BotGenerator; use BotBuilder\PlaceholderReplacer; $generator = new BotGenerator( new Template(__DIR__ . '/base/telegram-bot'), new PlaceholderReplacer('%%', '%%'), // use %%KEY%% instead of {{KEY}} );
API Reference
BotBuilder (facade)
| Method | Description |
|---|---|
__construct(string $templatePath) |
Point at the base template folder. |
define(string $key, ?string $default = null, bool $required = false): self |
Register an expected placeholder. |
build(string $target, array $values, bool $overwrite = false): array |
Validate values and generate the project. Returns the generated file paths. |
BotGenerator
| Method | Description |
|---|---|
__construct(Template $template, PlaceholderReplacer $replacer = new PlaceholderReplacer()) |
Build from a template with an optional custom replacer. |
generate(string $target, array $values, bool $overwrite = false): array |
Generate files (content + names) into the target directory. |
Template
| Method | Description |
|---|---|
__construct(string $path) |
Wrap a base directory (must exist). |
path(): string |
The template root path. |
files(): array |
All files as relative paths (recursive). |
read(string $relativePath): string |
Read a single file's content. |
Config
| Method | Description |
|---|---|
define(string $key, ?string $default = null, bool $required = false): self |
Declare an expected placeholder. |
resolve(array $values): array |
Merge given values with defaults and validate required keys. |
PlaceholderReplacer
| Method | Description |
|---|---|
__construct(string $open = '{{', string $close = '}}') |
Configure the delimiters. |
replace(string $content, array $values): string |
Replace known tokens (unknown ones are left untouched). |
placeholders(string $content): array |
List the unique tokens found in a string. |
Full Example: Telegram Bot
base/telegram-bot/config.php
<?php return [ 'name' => '{{BOT_NAME}}', 'token' => '{{BOT_TOKEN}}', 'admin_id' => {{ADMIN_ID}}, ];
base/telegram-bot/bot.php
<?php $config = require __DIR__ . '/config.php'; $api = 'https://api.telegram.org/bot' . $config['token'] . '/'; $offset = 0; echo $config['name'] . ' is running...' . PHP_EOL; while (true) { $response = file_get_contents($api . 'getUpdates?timeout=30&offset=' . $offset); $updates = json_decode($response, true); foreach ($updates['result'] ?? [] as $update) { $offset = $update['update_id'] + 1; $chatId = $update['message']['chat']['id'] ?? null; $text = $update['message']['text'] ?? ''; if ($chatId !== null) { file_get_contents($api . 'sendMessage?' . http_build_query([ 'chat_id' => $chatId, 'text' => 'You said: ' . $text, ])); } } }
Generate it:
use BotBuilder\BotBuilder; $builder = new BotBuilder(__DIR__ . '/base/telegram-bot'); $builder ->define('BOT_NAME', required: true) ->define('BOT_TOKEN', required: true) ->define('ADMIN_ID', default: '0'); $builder->build(__DIR__ . '/bots/my-new-bot', [ 'BOT_NAME' => 'MyCoolBot', 'BOT_TOKEN' => '123456:ABC-DEF', 'ADMIN_ID' => '55555', ]);
Now run your brand-new bot:
php bots/my-new-bot/bot.php
Testing
composer install ./vendor/bin/phpunit
Contributing
Contributions are welcome! Feel free to open an issue or submit a pull request on GitHub.
License
Released under the MIT License. See LICENSE for details.
📖 راهنمای فارسی
BotBuilder یک موتور scaffolding مدرن و بدون هیچ وابستگی برای PHP است که پروژههای آمادهی اجرا (مثل رباتهای تلگرام) را از روی قالبهای قابلاستفادهی مجدد میسازد.
فهرست
- چرا BotBuilder؟
- ویژگیها
- چطور کار میکند
- پیشنیازها
- نصب
- شروع سریع
- ساخت یک قالب
- استفاده
- مرجع API
- مثال کامل: ربات تلگرام
- تست
- مشارکت
- لایسنس
چرا BotBuilder؟
ساختن دستیِ چندین پروژهی مشابه، تکراری و مستعد خطاست. با BotBuilder فقط یک قالب پایه (base) نگه میداری و هر تعداد که خواستی نسخهی سفارشیشده از رویش میسازی — هرکدام با مقادیر مخصوص خودش. عالی برای ساختِ رباتهای تلگرام جدید یا هر پروژهی boilerplate در چند ثانیه، درحالیکه قالب اصلیات دستنخورده و برای همیشه قابل استفاده میماند.
ویژگیها
- 🧩 scaffolding مبتنی بر قالب — کل پروژه از روی یک پوشهی base ساخته میشود.
- 🔖 جایگزینی
{{PLACEHOLDER}}هم در محتوای فایلها و هم در نام فایل/پوشهها. - 🗂 روی هر فایلی کار میکند — فقط
config.phpنیست؛ همهی فایلهای قالب اسکن میشوند. - 🧵 چند فایل همزمان — در یک build هم
config.phpو همserver-key.phpو بیشتر را پر میکند. - 🌳 پوشههای تودرتو بهصورت بازگشتی پشتیبانی میشوند.
- ✅ مقادیر اجباری و پیشفرض با اعتبارسنجی داخلی.
- ♻️ قالب فقطخواندنی — قالب هیچوقت تغییر نمیکند و بینهایت بار قابل استفاده است.
- 🪶 بدون هیچ وابستگی — فقط فایلسیستم، کاملاً تستشده.
- ⚙️ جداکنندههای قابل تنظیم (پیشفرض
{{ }}).
چطور کار میکند
BotBuilder یک پوشهی قالب را میخواند، همهی {{KEY}}ها را با مقادیر تو جایگزین میکند و نتیجه را در یک پوشهی خروجی جدید مینویسد — بدون اینکه به قالب اصلی دست بزند.
base/telegram-bot/ -> bots/my-new-bot/
config.php ({{...}}) -> config.php (filled in)
server-key.php ({{...}}) -> server-key.php (filled in)
bot.php -> bot.php
پیشنیازها
- PHP نسخهی 8.1 یا بالاتر
- افزونهی ext-json
نصب
composer require hojjatjh/bot-builder
شروع سریع
<?php require 'vendor/autoload.php'; use BotBuilder\BotBuilder; $builder = new BotBuilder(__DIR__ . '/base/telegram-bot'); $builder ->define('BOT_NAME', required: true) ->define('BOT_TOKEN', required: true) ->define('ADMIN_ID', default: '0'); $files = $builder->build(__DIR__ . '/bots/my-new-bot', [ 'BOT_NAME' => 'MyCoolBot', 'BOT_TOKEN' => '123456:ABC-DEF', ]); print_r($files);
ساخت یک قالب
یک قالب چیزی نیست جز یک پوشهی معمولی از فایلها. هر جا خواستی بعداً یک مقدار جایگزین شود، یک placeholder مثل {{BOT_NAME}} مینویسی. کل ایده همین است — هیچ چیز پیچیدهای برای یادگرفتن نیست.
💡 مهم: BotBuilder به اسم فایلها کاری ندارد و فقط محدود به
config.phpنیست. همهی فایلهای قالب را میخواند، پس placeholderهایت در هر فایلی کار میکنند —config.php،server-key.php،.envیا هر چیزی.
قدم ۱ — placeholder در فایلهایت بگذار
فایل کانفیگت میتواند هر اسمی داشته باشد. اینجا اسمش config.php است:
<?php return [ 'name' => '{{BOT_NAME}}', 'token' => '{{BOT_TOKEN}}', ];
قدم ۲ — placeholder در نام فایل و پوشه هم کار میکند
فایلی به اسمِ {{BOT_NAME}}.php بعد از ساخت میشود MyCoolBot.php. برای پوشهها هم همینطور، مثل handlers/{{BOT_NAME}}/.
قدم ۳ — هر تعداد فایل که خواستی (یک مثال واقعی)
یک قالب میتواند هر تعداد فایل داشته باشد. اینجا دو فایل دارد: config.php و server-key.php.
فایل config.php:
<?php return [ 'name' => '{{BOT_NAME}}', 'token' => '{{BOT_TOKEN}}', ];
فایل server-key.php:
<?php return [ 'secret' => '{{SERVER_KEY}}', 'webhook_url' => '{{WEBHOOK_URL}}', ];
فقط یک فراخوانی build() هر دو فایل را یکجا پر میکند:
$builder ->define('BOT_NAME', required: true) ->define('BOT_TOKEN', required: true) ->define('SERVER_KEY', required: true) ->define('WEBHOOK_URL', default: ''); $builder->build(__DIR__ . '/bots/my-new-bot', [ 'BOT_NAME' => 'MyCoolBot', 'BOT_TOKEN' => '123456:ABC-DEF', 'SERVER_KEY' => 's3cr3t', 'WEBHOOK_URL' => 'https://example.com/hook', ]);
فایلهای بدون placeholder همانطور که هستند کپی میشوند، پس میتوانی فایلهای ثابت و قالبی را آزادانه کنار هم بگذاری (PHP، JSON، .env، Markdown، تصویر و هر چیز دیگر).
استفاده
تعریف مقادیر (اجباری و پیشفرض):
$builder ->define('BOT_TOKEN', required: true) // must be provided ->define('ADMIN_ID', default: '0'); // optional, falls back to '0'
اگر یک مقدار اجباری داده نشود، پیش از نوشتن هر فایلی یک BotBuilderException پرتاب میشود — پس هیچوقت پروژهی نصفهکاره تولید نمیشود.
بازنویسی مقصد موجود:
$builder->build($target, $values, overwrite: true);
بدون overwrite: true، ساختن در پوشهای که از قبل وجود دارد خطا میدهد (این جلوی خرابکردن کارِ قبلیات را میگیرد).
استفاده از اجزای سطحپایین: نمای BotBuilder اختیاری است؛ میتوانی مستقیم از اجزا استفاده کنی:
use BotBuilder\Template; use BotBuilder\BotGenerator; $generator = new BotGenerator(new Template(__DIR__ . '/base/telegram-bot')); $files = $generator->generate(__DIR__ . '/bots/my-new-bot', [ 'BOT_NAME' => 'MyCoolBot', ]);
جداکنندههای سفارشی:
use BotBuilder\Template; use BotBuilder\BotGenerator; use BotBuilder\PlaceholderReplacer; $generator = new BotGenerator( new Template(__DIR__ . '/base/telegram-bot'), new PlaceholderReplacer('%%', '%%'), // use %%KEY%% instead of {{KEY}} );
مرجع API
BotBuilder (facade)
| Method | Description |
|---|---|
__construct(string $templatePath) |
Point at the base template folder. |
define(string $key, ?string $default = null, bool $required = false): self |
Register an expected placeholder. |
build(string $target, array $values, bool $overwrite = false): array |
Validate values and generate the project. Returns the generated file paths. |
BotGenerator
| Method | Description |
|---|---|
generate(string $target, array $values, bool $overwrite = false): array |
Generate files (content + names) into the target directory. |
Template
| Method | Description |
|---|---|
path(): string |
The template root path. |
files(): array |
All files as relative paths (recursive). |
read(string $relativePath): string |
Read a single file's content. |
Config
| Method | Description |
|---|---|
define(string $key, ?string $default = null, bool $required = false): self |
Declare an expected placeholder. |
resolve(array $values): array |
Merge given values with defaults and validate required keys. |
PlaceholderReplacer
| Method | Description |
|---|---|
replace(string $content, array $values): string |
Replace known tokens (unknown ones are left untouched). |
placeholders(string $content): array |
List the unique tokens found in a string. |
مثال کامل: ربات تلگرام
فایل base/telegram-bot/config.php
<?php return [ 'name' => '{{BOT_NAME}}', 'token' => '{{BOT_TOKEN}}', 'admin_id' => {{ADMIN_ID}}, ];
فایل base/telegram-bot/bot.php
<?php $config = require __DIR__ . '/config.php'; $api = 'https://api.telegram.org/bot' . $config['token'] . '/'; $offset = 0; echo $config['name'] . ' is running...' . PHP_EOL; while (true) { $response = file_get_contents($api . 'getUpdates?timeout=30&offset=' . $offset); $updates = json_decode($response, true); foreach ($updates['result'] ?? [] as $update) { $offset = $update['update_id'] + 1; $chatId = $update['message']['chat']['id'] ?? null; $text = $update['message']['text'] ?? ''; if ($chatId !== null) { file_get_contents($api . 'sendMessage?' . http_build_query([ 'chat_id' => $chatId, 'text' => 'You said: ' . $text, ])); } } }
ساختنش:
use BotBuilder\BotBuilder; $builder = new BotBuilder(__DIR__ . '/base/telegram-bot'); $builder ->define('BOT_NAME', required: true) ->define('BOT_TOKEN', required: true) ->define('ADMIN_ID', default: '0'); $builder->build(__DIR__ . '/bots/my-new-bot', [ 'BOT_NAME' => 'MyCoolBot', 'BOT_TOKEN' => '123456:ABC-DEF', 'ADMIN_ID' => '55555', ]);
حالا رباتِ تازهساختهشدهات را اجرا کن:
php bots/my-new-bot/bot.php
تست
composer install ./vendor/bin/phpunit
مشارکت
از مشارکت استقبال میشود! توی گیتهاب بهراحتی issue باز کن یا pull request بفرست.
لایسنس
تحت لایسنس MIT منتشر شده است. برای جزئیات فایل LICENSE را ببین.
Made with ❤️ by hojjatjh