توجه: این پروژه را ابتدا برای رباتهای خودم توسعه دادم و تصمیم گرفتم آن را منتشر کنم تا اگر برای شما هم کاربردی بود، بتوانید از آن استفاده کنید.
در صورت مشکل در کار با پروژه از هوش مصنوعی جیمنای با لینک زیر استفاده کنید. اکثر موارد مورد نیاز به gem داده شده است.
https://gemini.google.com/gem/13NSCMFOipq6HMvSRPZ2NvhKGzuYVrTDS?usp=sharing
هدف از توسعه این بیس، سادگی در کدنویسی و بهینهسازی برای سرویسدهی به تعداد بالای کاربر است.
سعی کردهام در حد امکان مصرف منابع (CPU / RAM / I/O) پایین باشد و ساختار پروژه قابل توسعه و انعطافپذیر باقی بماند.
در بخشهای مختلف پروژه از کمک هوش مصنوعی هم استفاده شده است. کمک گرفتن هیچ ایرادی ندارد — خروجی نهایی حاصل چند سال تجربه در ساخت رباتهای تلگرامی است و با کمک AI روی بهینگی و انتخاب بهترین ساختار تمرکز شده است.
- پشتیبانی از وبهوک, اجرای cli و کرون جاب
- پشتیبانی از صف بندی آپدیت ها
- پشتیبانی از منطق جدا سازی صب بندی و اجرای اسکریپت
- امکان توسعه چند زبانه
- امکان افزودن چندین دیتابیس
- سیستم کشینگ
- پشتیبانی از سیستم پلاگین
- امکان پوشه بندی پلاگین ها برای توسعه راحت ت
- لاگ گیری و بررسی راحت لاگ ها
- پشتیبانی از چندین درایور کشینگ, صف بندی و دیتابیس
- امکان استفاده از متغییر های داینامیک در زبان ها و دکمه
git clone https://github.com/alirezax5/baleBotBase.git
نیازه که git روی سیستم نصب باشه.
پس از کلون دستور زیر رو در پوشه ی سورس بزنید :
composer install
دانلود از بخش releases
پس از دانلود دستور زیر رو در پوشه ی سورس بزنید :
composer install
فرقی نمیکنه از کدوم روش استفاده کنید.
تمامی تنطیمات و کانفیگ سورس در فایل .env قرار داره
در حال حاضر سورس حالت های زیر رو داره :
- update_direct
در این حالت سورس با استفاده از polling آپدیت هارا دریافت میکنه و بدون صف بندی اجرا میکنه
- update_queue
در این حالت سورس با استفاده از polling آپدیت هارا دریافت میکنه و به صف آپدیت ها اضافه میکنه
- webhook_direct
در این حالت آپدیت هایی که از وبهوک میان به صورت مستقیم اجرا میشه
- webhook_queue
در این حالت آپدیت هایی که از وبهوک میان به صف برای اجرای به ترتیب اضافه میشن
- cronjob_update
در این حالت شما می توانید کرون جاب ست کنید و سورس با استفاده از polling و حلقه بی نهایت آپدیت هارو اجرا میکنه
- cronjob_queue
در این حالت کرون جاب با استفاده از polling اپدیت ها وارد صف می شن.
پیشنهاد اول راه اندازی اگر سرور دارید نصب rabbitmq, redis,Supervisor می باشد تا هم از قابلیت صف بندی استفاده کنید و هم از Supervisor برای مدیریت اجرا استفاده کنید:
در env :
BOT_MODE="update_queue"QUEUE_SAVE_TYPE="rabbitmq"CACHE_DRIVER="redis"و کانفیگ برای Supervisor :
[program:name]
command=php /www/wwwroot/domain/newBase/bot.php
directory=/www/wwwroot/domain/newBase
autorestart=true
startsecs=3
startretries=3
stdout_logfile=/www/server/panel/plugin/supervisor/log/test.out.log
stderr_logfile=/www/server/panel/plugin/supervisor/log/test.err.log
stdout_logfile_maxbytes=2MB
stderr_logfile_maxbytes=2MB
user=root
priority=999
numprocs=2
stopsignal=QUIT
process_name=name
توجه داشته باشید که مقدار های کانفیگ نمونه رو تغییر بدید.
در صورتی که از هاستینگی استفاده می کنید که از Supervisor استفاده نمیکنه:
BOT_MODE="webhook_direct"QUEUE_SAVE_TYPE="json"CACHE_DRIVER="array"در صورتی که دامنه ندارید میتونید از روش کرون جاب استفاده کنید.
اگر از صف بندی استفاده نمی کنید :
usealirezax5\BaleBase\App\Core;
include'./vendor/autoload.php';
$core = newCore();
$core->run();اما اگر میخواید استفاده کنید به 2 فایل نیاز دارید :
usealirezax5\BaleBase\App\Core;
usealirezax5\BaleBase\App\Enum\CoreMode;
include'./vendor/autoload.php';
$core = newCore(CoreMode::UPDATES_ONLY);
$core->runFetchQueueUpdate();usealirezax5\BaleBase\App\Core;
include'./vendor/autoload.php';
$core = newCore();
$core->run();نکته : شما حتی می توانید دریافت و اجرا آپدیت رو در سرور ها مختلف قرار بدید.
سورس از پوشه بندی پلاگین ها پشتیبانی میکنه برای همین میتونید پلاگین هارو دسته بندی کنید فقط باید دقت داشته باشید که نام پوشه را در namespace کلاس قرار داده باشید
برای تنظیم کانفیگ ها میتوانید به فایل .env مراجعه کنید.
نمونه کد پلاگین :
<?phpnamespacealirezax5\BaleBase\Plugin;
usealirezax5\BaleBase\App\Plugin\Contract\PluginInterface;
usebaleBotPhp\bale;
class Start implements PluginInterface
{
publicfunctiongetPriority(): int
{
return1;
}
publicfunctiononMessage($update, bale$bale)
{
SharedManagement::set('findCommand', 'na');
$bale->sendMessage($update->from->id, $update->text,['reply_markup'=>btn('start')]);
}
publicfunctiononEditedMessage($update, bale$bale)
{
}
publicfunctiononCallbackQuery($update, bale$bale)
{
}
publicfunctiononInlineQuery($update, bale$bale)
{
}
publicfunctiononPollAnswer($update, bale$bale)
{
}
#.. سایر آپدیت هاpublicfunctionbefore($update, bale$bale)
{
}
publicfunctionafter($update, bale$bale)
{
}
}نام متدها در ساختار PascalCase تعریف میشوند و هر نوع آپدیت، به متد مخصوص خودش ارسال خواهد شد. پارامترهای $update و $bale در تمام متدها بهصورت ثابت ارسال میشوند.
متد getPriority
این متد برای تعیین اولویت اجرای پلاگین استفاده میشود.
پیشنهاد میشود هنگام دریافت اطلاعات اولیهی کاربر، مقدار اولویت را روی 0 قرار دهید. اگر ترتیب اجرای پلاگین برای شما اهمیتی ندارد، مقدار 999 را تنظیم کنید.
متدهای before و after
before قبل از اجرای متد مربوط به آپدیت فراخوانی میشود.
after پس از اجرای متد آپدیت اجرا خواهد شد.
این ساختار به شما اجازه میدهد منطق پیشپردازش و پسپردازش را بهصورت مجزا و تمیز مدیریت کنید.
بیس به شما این امکان رو میده یک ربات چندزبانه رو توسعه بدید در فایل .env شما می توانید زبان پیشفرض,مسیر زبان ها و مدت زمان کش زبان ها را تنظیم نمایید. پیشفرض در پوشه Language و با پسوند php باید متون خود رو ذخیره کنید.
برای فرخوانی متن و تغییر زبان می توانید از مثال زیر استفاده کنید :
<?php# دریافت ترجمه براساس نام کلید در فایل json__('name');
# برای تنظیم زبان
Language::getInstance()->setLanguageDir( 'fa');
# برای جایگزین کردن مقدار خاص در متن ترجمه__('name',['id'=>1]);return [
"key" => "value",
"key2" => "value #id",
];دکمهها بهصورت پیشفرض در فایل btn.php در ریشهی سورس قرار دارند. در صورت نیاز، میتوانید مسیر این فایل را از طریق تنظیمات .env تغییر دهید.
نمونهی فایل تعریف دکمهها:
<?phpreturn [
"remove" => ['KeyboardRemove' => [], 'remove_keyboard' => true],
"start" => [
'keyboard' => [
[
['text' => __('btn_name')],
],
],
'resize_keyboard' => true
]
];<?php<?phpnamespacealirezax5\BaleBase\Plugin;
usealirezax5\BaleBase\App\Plugin\Contract\PluginInterface;
usebaleBotPhp\bale;
class Start implements PluginInterface
{
publicfunctiongetPriority(): int
{
return1;
}
publicfunctiononMessage($update, bale$bale)
{
SharedManagement::set('findCommand', 'na');
$bale->sendMessage($update->from->id, $update->text,['reply_markup'=>btn('start')]);
}
}تنظیمات مربوط به دیتابیس را میتوانید در فایل .env ویرایش کنید. از آنجایی که این بیس از illuminate/database استفاده میکند، میتوانید برای نحوهی کار با کوئریها و مدلها از مستندات لاراول کمک بگیرید:
🔗 https://laravel.com/docs/12.x/queries
بهطور کلی، فایلهای مربوط به دیتابیس را در پوشهی Database قرار دهید. نمونهی ساختار و تعریف دیتابیس:
<?phpnamespaceDatabase;
useCarbon\Carbon;
useIlluminate\Database\Eloquent\Model;
useIlluminate\Database\Capsule\ManagerasDB;
class Users extends Model
{
protected$table = 'users';
protected$primaryKey = 'chatid';
protected$casts = [
'active' => 'boolean',
'status' => 'boolean'
];
public$timestamps = false;
protected$connection = 'main';
publicstaticfunctioncheckAndInsert(int$chatid): bool
{
return !self::check($chatid) && self::insertOrIgnore(['chatid' => $chatid]);
}
publicstaticfunctioncheck(int$chatid): bool
{
returnself::where('chatid', $chatid)->exists();
}
publicstaticfunctiongetAllStatusActiveUser(bool$limit = true, int$page = 1, int$per = 20)
{
$query = self::where('status', true)->orderBy('id');
return$limit ? $query->paginate($per, ['*'], 'page', $page) : $query->get();
}
publicstaticfunctiongetAllStatusActiveUserByLang($lang = 'all',bool$limit = true, int$page = 1, int$per = 20)
{
if ($lang == 'all')
$query = self::where('status', true)->orderBy('id');
else$query = self::where('status', true)->where('lang', $lang)->orderBy('id');
return$limit ? $query->paginate($per, ['*'], 'page', $page) : $query->get();
}
publicstaticfunctiongetAllActiveUser(bool$limit = true, int$page = 1, int$per = 20)
{
$query = self::where('active', true)->orderBy('id', 'DESC');
return$limit ? $query->paginate($per, ['*'], 'page', $page) : $query->get();
}
publicstaticfunctiongetAll(bool$limit = true, int$page = 1, int$per = 20)
{
$query = self::orderBy('id');
return$limit ? $query->paginate($per, ['*'], 'page', $page) : $query->get();
}
publicstaticfunctiongetByRole(string$role)
{
returnself::where('role', $role)->get(['id', 'chatid']);
}
publicstaticfunctiongetAdmins()
{
returnself::getByRole('admin');
}
publicstaticfunctiongetCountByField(string$field, $value): int
{
returnself::where($field, $value)->count();
}
publicstaticfunctiongetCountAll(): int
{
returnself::count();
}
publicstaticfunctiongetCountAdmin(): int
{
returnself::getCountByField('role', 'admin');
}
publicstaticfunctiongetUser(int$chatid)
{
returnself::where('chatid', $chatid)->first();
}
publicstaticfunctiongetUserById($id)
{
returnself::where('id', $id)->first();
}
publicstaticfunctionupdateFieldByChatId(int$chatid, string$field, $value): bool
{
returnself::where('chatid', $chatid)->update([$field => $value]);
}
publicstaticfunctiongetRecentUsers()
{
$twentyFourHoursAgo = Carbon::today();
$recentUsers = DB::table('users')
->where('create_at', '>=', $twentyFourHoursAgo)
->count();
return$recentUsers;
}
}<?phpusealirezax5\TelegramBase\App\Shared\SharedManagement;
#ثبت داده
SharedManagement::set('findCommand', 'na');
#دریافت داده
SharedManagement::get('findCommand', 'default');
#ثبت داده ای که بعد از انجام آپدیت پاک نشود.
SharedManagement::set('findCommand', 'na',true);برای تنظیم مسیر فایل لاگ، نام فایل، و همچنین فعال یا غیرفعال کردن سیستم لاگ، میتوانید مقادیر مربوطه را در فایل .env تغییر دهید.
نمونهای از ثبت لاگ:
<?phpusealirezax5\TelegramBase\App\Logger\LogHandler;
LogHandler::warning("warning");
LogHandler::info('info');برای تنظیم تنظیمات کش میتونید به .env مراجعه کنید نمونه کد:
<?phpnamespaceFunctions;
usealirezax5\BaleBase\App\Cache\CacheManager;
usealirezax5\BaleBase\App\Environment\EnvHandler;
usebaleBotPhp\bale;
class CacheData
{
staticpublicfunctiongetchat(bale$bale, $chatid)
{
$cacheKey = EnvHandler::get('CACHE_PREFIX') . 'getchat_' . $chatid;
if (!CacheManager::isInitialized())
return$bale->getchat($chatid);
if (CacheManager::has($cacheKey)) {
return CacheManager::get($cacheKey);
}
$getchat = $bale->getchat($chatid);
CacheManager::put($cacheKey, $getchat, 300);
return$getchat;
}