💻 سیستم دستورات
این راهنما اصول اولیه دستورات، ساختار دستورات، و نحوه مدیریت دستورات شامل ثبت و نوشتن دستور را پوشش میدهد.
🤖 اصول دستورات ربات بله
-
در رباتهای بله، دستورات پیامهای خاصی هستند که با یک اسلش (/) شروع میشوند و برای انجام اقدامات خاص در ربات شما استفاده میشوند. به عنوان مثال: /start.
-
زمانی که یک کاربر یک دستور ارسال میکند، ربات شما یک شیء Update دریافت خواهد کرد که شامل خود دستور و هرگونه آرگومان همراه است.
👨💻 مدیریت دستورات در SDK
-
SDK شامل یک سیستم دستورات است که به شما این امکان را میدهد که به راحتی و به طور مؤثر تمام دستورات دریافتی را مدیریت کنید.
-
سیستم مدیریت دستورات به طور خودکار دستور صحیح را شناسایی و زمانی که در یک پیام دریافتی از بله شناسایی میشود، آن را فعال میکند.
-
دستورات به صورت Lazy Load شده و به صورت تقاضا پردازش میشوند، بنابراین ثبت آنها تأثیری بر عملکرد برنامه شما نخواهد داشت.
بیایید با نوشتن و ثبت دستور خود شروع کنیم.
📝 نوشتن دستورات
قبل از اینکه سیستم مدیریت دستورات بتواند دستورات دریافتی شما را مدیریت کند، باید آنها را بنویسید. برای این کار، شما باید کلاس EFive\Bale\Commands\Command
را گسترش دهید که رابط EFive\Bale\Commands\CommandInterface
را پیادهسازی میکند.
شما میتوانید دستورات سفارشی خود را در هر دایرکتوری ذخیره کنید، به شرطی که بتوانند طبق تنظیمات composer.json
شما بارگذاری شوند و بهدرستی در سیستم مدیریت دستورات ثبت شوند.
در این راهنما، ما با ایجاد یک دستور ساده StartCommand
شروع خواهیم کرد که زمانی که یک کاربر /start
را ارسال کند یا برای اولین بار با ربات شما تعامل برقرار کند، فعال خواهد شد. از آنجا، شما همچنین درک بهتری از قابلیتهای پیشرفته سیستم دستورات پیدا خواهید کرد.
🔨 دستور پایه
هر دستور با یک نام منحصر به فرد، یک توضیح، و یک تابع handler که هنگام فعال شدن دستور اجرا میشود، مرتبط است.
در اینجا یک ساختار دستور پایه آورده شده است ت ا شما نحوه نوشتن یک کلاس دستور ساده برای ربات بله خود را درک کنید. بیایید نگاهی دقیقتر به ویژگیها و روش handler که این کلاس را تشکیل میدهند بیندازیم.
ویژگیها و متدهای مورد نیاز:
-
$name
: این ویژگی یک رشته است که نام دستور را نمایش میدهد. از این نام برای شناسایی دستور زمانی که کاربر پیامی به ربات ارسال میکند استفاده میشود. در این مورد، نام به 'start' تنظیم شده است، بنابراین دستور زمانی فعال میشود که کاربر/start
را ارسال کند. -
$description
: این ویژگی یک رشته است که توضیح مختصری در مورد هدف دستور فراهم میکند. معمولاً زمانی که کاربر برای دریافت فهرستی از دستورات موجود/help
را تایپ میکند، نمایش داده میشود. -
handle()
: این متد قلب دستور است و عملی را که باید هنگام فعال شدن دستور اجرا شود، تعریف میکند. در این مورد، متدhandle()
به سادگی یک پیام خوشآمدگویی به کاربر ارسال میکند زمانی که دستور/start
صادر شود.
<?php
namespace App\Bale\Commands;
use EFive\Bale\Commands\Command;
class StartCommand extends Command
{
protected string $name = 'start';
protected string $description = 'Start Command to get you started';
public function handle()
{
$this->replyWithMessage([
'text' => 'Hey, there! Welcome to our bot!',
]);
}
}
replyWithMessage()
یک میانبر راحت برای متد sendMessage()
است. این متد بهطور خودکار پارامتر chat_id را مدیریت میکند، بنابراین نیازی به ارائه آن ندارید. با این حال، همچنان میتوانید سایر پارامترهای پشتیبانیشده توسط sendMessage()
را ارسال کنید. در بخشهای بعدی این موضوع را بهطور دقیقتر بررسی خواهیم کرد.
👥 نامهای مستعار دستورها
علاوه بر نامهای اصلی خود، دستورات میتوانند یک یا چند $aliases
داشته باشند که میتوانند آنها را به صورت داخلی یا توسط کاربر فعال کنند. این ویژگی اجازه میدهد تا یک دستور چندین عملکرد داشته باشد و به روشهای مختلف استفاده شود. به عنوان مثال، یک دستور میتواند هم دستور /start
و هم دستور /subscribe
باشد.
💡 مثال از نامهای مستعار دستور
در اینجا یک مثال از یک دستور با پشتیبانی از نامهای مستعار آورده شده است.
استفاده
- کاربر:
/start
یا/subscribe
- ربات: Hey, there! Welcome to our bot!
<?php
namespace App\Bale\Commands;
use EFive\Bale\Commands\Command;
class StartCommand extends Command
{
protected string $name = 'start';
protected array $aliases = ['subscribe'];
protected string $description = 'Start Command to get you started';
public function handle()
{
$this->replyWithMessage([
'text' => 'Hey, there! Welcome to our bot!',
]);
}
}
🧮 آرگومانهای دستور
همچنین امکان دارد که دستورات دارای آرگومانهایی باشند که توسط کاربر ارسال میشوند. برای انجام این کار، دستور باید از ویژگی $pattern
برای تعیین یک الگو برای آرگومانها استفاده کند.
الگو میتواند یک عبارت ساده یا یک عبارت منظم (Regular Expression) برای آرگومانهای پیچیدهتر باشد. ما هرکدام از این انواع را با مثالهایی برای درک بهتر توضیح خواهیم داد.
🔠 آرگومان ساده
در این مثال، ما یک آرگومان که توسط ویژگی $pattern
مشخص شده است را شامل میشویم. الگو {username}
است که مشخص میکند دستور میتواند یک آرگومان username
بپذیرد. اگر آرگومان ارائه نشود، دستور به استفاده از نام کاربری از شیء Update
باز میگردد.
ما سعی میکنیم آرگومان را با استفاده از متد argument($name, $default)
دریافت کنیم. خود آرگومان در صورتی که توسط کاربر ارسال نشود یا هیچ تطابقی نداشته باشد، null
باز میگرداند. میتوانید از این برای اعتبارسنجی و پردازش بیشتر (مثال: اطلاع دادن به کاربر که باید نام کاربری را وارد کند) استفاده کنید.
در نهایت، ما از متد replyWithMessage()
برای ارسال یک پیام خوشآمدگویی شخصیسازیشده به کاربر استفاده میکنیم و با استفاده از نام کاربری او به او خوشآمد میگوییم.
💡 مثال آرگومان ساده
استفاده
- کاربر: /start johndoe
- ربات: Hello johndoe! Welcome to our bot :)
<?php
namespace App\Bale\Commands;
use EFive\Bale\Commands\Command;
class StartCommand extends Command
{
protected string $name = 'start';
protected string $pattern = '{username}';
protected string $description = 'Start Command to get you started';
public function handle()
{
# username from Update object to be used as fallback.
$fallbackUsername = $this->getUpdate()->getMessage()->from->username;
# Get the username argument if the user provides,
# (optional) fallback to username from Update object as the default.
$username = $this->argument(
'username',
$fallbackUsername
);
$this->replyWithMessage([
'text' => "Hello {$username}! Welcome to our bot :)"
]);
}
}
🧩 آرگومان با استفاده از Regex
در این مثال، ما هم از الگوی ساده و هم از الگوی عبارات منظم (regex) استفاده کردهایم. برای الگوی regex، ما {age: \d+ }
را مشخص کردهایم. این عبارت منظم هر رشتهای که شامل یک یا بیشتر عدد باشد را تطبیق داده و آن را به آرگومان age
اختصاص میدهد.
ما مقادیر آرگومانها را با استفاده از متد argument()
بازیابی میکنیم. سپس میتوانیم به کاربر اطلاع دهیم که باید آرگومان را ارسال کند اگر این مقدار ارائه نشده باشد.
💡 مثال آرگومان با استفاده از Regex
استفاده
- کاربر: /start johndoe 24
- ربات: Hello johndoe! Welcome to our bot :)
<?php
namespace App\Bale\Commands;
use EFive\Bale\Commands\Command;
class StartCommand extends Command
{
protected string $name = 'start';
protected string $pattern = '{username}
{age: \d+}';
protected string $description = 'Start Command to get you started';
public function handle()
{
$username = $this->argument('username');
$age = $this->argument('age');
if(!$username) {
$this->replyWithMessage([
'text' => "Please provide your username! Ex: /start jasondoe"
]);
return;
}
if(!$age) {
$this->replyWithMessage([
'text' => "Please provide your age with the username! Ex: /start jasondoe 24"
]);
return;
}
$this->replyWithMessage([
'text' => "Hello {$username}! Welcome to our bot :)"
]);
}
}
برای استفاده از کلاس StartCommand،
شما نیاز به ثبت آن در ربات خود دارید. اطلاعات بیشتر در مورد این موضوع در بخش ثبت دستورات آورده شده است.
🛠️ مثال جامع
در اینجا یک مثال جامع آورده شده است که تمام مثالهای قبلی را همراه با برخی از متدهای مفید اضافی ترکیب میکند.
<?php
namespace App\Bale\Commands;
use EFive\Bale\Actions;
use EFive\Bale\Commands\Command;
/**
* This command can be triggered in two ways:
* /start and /join due to the alias.
*/
class StartCommand extends Command
{
protected string $name = 'start';
protected array $aliases = ['join'];
protected string $description = 'Start Command to get you started';
protected string $pattern = '{username}
{age: \d+}';
public function handle()
{
# username from Update object to be used as fallback.
$fallbackUsername = $this->getUpdate()->getMessage()->from->username;
# Get the username argument if the user provides,
# (optional) fallback to username from Update object as the default.
$username = $this->argument(
'username',
$fallbackUsername
);
$this->replyWithMessage([
'text' => "Hello {$username}! Welcome to our bot, Here are our available commands:"
]);
# This will update the chat status to "typing..."
$this->replyWithChatAction(['action' => Actions::TYPING]);
# Get all the registered commands.
$commands = $this->getBale()->getCommands();
$response = '';
foreach ($commands as $name => $command) {
$response .= sprintf('/%s - %s' . PHP_EOL, $name, $command->getDescription());
}
$this->replyWithMessage(['text' => $response]);
if($this->argument('age', 0) >= 18) {
$this->replyWithMessage(['text' => 'Congrats, You are eligible to buy premimum access to our membership!']);
} else {
$this->replyWithMessage(['text' => 'Sorry, you are not eligible to access premium membership yet!']);
}
}
}
📌 ثبت دستورات
حالا که یاد گرفتیم چگونه یک دستور ایجاد کنیم، اولین قدم ثبت آن است. دو روش برای انجام این کار وجود دارد: ثبت از طریق مدیر ربات در پیکربندی شما یا ثبت به صورت آنی.
به دلایل مربوط به نگهداری، توصیه میشود که تمام دستورات را در فایل پیکربندی ثبت کنید.
برای یادگیری نحوه ثبت دستورات با استفاده از فایل پیکربندی، لطفاً به بخشهای ثبت چندین ربات و ثبت دستورات جهانی در راهنمای پیکربندی مراجعه کنید. این بخشها دستورالعملهای گام به گام برای ثبت دستورات در فایل پیکربندی شما برای رباتهای متعدد یا به صورت جهانی ارائه میدهند.
در این راهنما، ما بر روی ثبت دستورات به صورت آنی تمرکز خواهیم کرد.
💬 دستور تکی
برای ثبت یک دستور تکی، میتوانید از متد addCommand()
استفاده کنید که یا شیء دستور یا مسیر کامل دستور را میپذیرد. این متد به طور خودکار دستور را در پشت صحنه مقداردهی میکند و به شما اجازه میدهد تا آن را به صورت یکپارچه ثبت کنید.
- Standalone
- Laravel
$bale->addCommand(EFive\Bale\Commands\HelpCommand::class);
# OR
$command = new EFive\Bale\Commands\HelpCommand();
$bale->addCommand($command);
ثبت دستورات در لاراول در واقع بسیار ساده است. کافی است فایل پیکربندی bale.php
را باز کنید و تمام مسیر کامل دستورات خود را به آرایه دستورات اضافه کنید، سپس SDK باقی کارها را انجام خواهد داد.
مثال:
[
'commands' => [
EFive\Bale\Commands\HelpCommand::class,
Vendor\Project\Commands\StartCommand::class,
Vendor\Project\Commands\SettingsCommand::class,
]
// ...
]
به طور پیشفرض، SDK یک دستور جهانی Help را در لاراول ثبت میکند، اما شما میتوانید با کامنت کردن خط HelpCommand
یا حذف کامل آن، این دستور را غیرفعال کنید یا آن را با دستور Help خود جایگزین کنید.
همچنین میتوانید دستورات را بهصورت آنی با استفاده از متد addCommand()
مانند مثال Standalone ثبت کنید.
Bale::addCommand(EFive\Bale\Commands\HelpCommand::class);
# OR
$command = new EFive\Bale\Commands\HelpCommand();
Bale::addCommand($command);
🔢 دستورات چندگانه
برای ثبت دستورات چندگانه، میتوانید آرایهای شامل تمامی دستورات را به متد addCommands()
ارسال کنید. این متد به شما امکان میدهد تمامی دستورات را به صورت یکجا ثبت کنید و در زمان و تلاش صرفهجویی کنید.
مثال:
- Standalone
- Laravel
$bale->addCommands([
EFive\Bale\Commands\HelpCommand::class,
Vendor\Project\TestCommand::class,
Vendor\Project\StartCommand::class,
]);
Bale::addCommands([
EFive\Bale\Commands\HelpCommand::class,
Vendor\Project\TestCommand::class,
Vendor\Project\StartCommand::class,
]);
تمام دستورات به صورت Lazy Loaded بارگذاری میشوند.
اگر کاربری دستوری وارد کند که ثبت نشده باشد، سیستم به طور خودکار به دنبال دستور help
ثبت شده میگردد. اگر دستور help
یافت شود، اجرا خواهد شد و کلاس پیشفرض دستور کمک به کاربر پاسخ خواهد داد و لیستی از دستورات موجود و توضیحات مربوط به آنها را نمایش میدهد (اگر از دستور کمک همراه با SDK استفاده کنید). این میتواند تجربه کاربری را بهبود بخشد و به کاربر کمک کند تا دستورات موجود را بهتر پیدا کند.
🗑️ حذف دستورات
اگر بخواهید دستوری را به صورت آنی حذف کنید، میتوانید از متد removeCommand('command_name')
یا متد removeCommands(array $commands)
برای حذف چندین دستور استفاده کنید.
👌 مدیریت دستورات
برای پردازش دستورات ورودی، میتوانید از متد commandsHandler()
استفاده کنید. این متد به شما این امکان را میدهد که دستورات ورودی را پردازش کرده و منطق لازم برای پاسخدهی به آنها را پیادهسازی کنید.
به طور پیشفرض، این متد مقدار بولی false
را میپذیرد، به این معنی که دستورات باید به صورت دستی با استفاده از متد getUpdates()
پردازش شوند. اگر آن را روی true
تنظیم کنید، بهطور خودکار بهروزرسانیهای ورودی که از سوی بله به Webhook شما ارسال میشود را با استفاده از متد getWebhookUpdate()
پردازش میکند.
سیستم مدیریت دستورات از طریق شیء Update
ورودی اسکن میکند تا بررسی کند که آیا دستورات ثبت شده مطابقت دارند یا نه و آنها را بهطور مناسب پردازش میکند. این سیستم همیشه شیء Update
را باز میگرداند، خواه دستور پردازش شود یا نشود، که میتوان از آن برای پردازشهای بعدی استفاده کرد.
علاوه بر این، همان شیء Update
میتواند برای پردازش درخواستهای callback نیز استفاده شود.
در اینجا دو مثال آورده شده است که نحوه مدیریت هر دو سناریو را نشان میدهد.
📡 استفاده از Webhook
در اینجا مثالی از استفاده از commandsHandler()
با Webhook ثبتشده آورده شده است.
این روش توصیهشده برای مدیریت بهروزرسانیهای ورودی از بله است.
- Standalone
- Laravel
$update = $bale->commandsHandler(true);
Route::post('/<token>/webhook', function () {
$update = Bale::commandsHandler(true);
// Commands handler method returns the Update object.
// So you can further process $update object
// to however you want.
return 'ok';
});
هنگام استفاده از متد commandsHandler()
با Webhook در یک برنامه لاراول، ممکن است با خطاهای تأیید توکن CSRF مواجه شوید. این به این دلیل است که میانهافزار CSRF لاراول به طور پیشفرض توکن CSRF را برای تمام درخواستهای HTTP POST ورودی، از جمله درخواستهای Webhook، تأیید میکند.
برای دور زدن این تأیید برای مسیر Webhook خود، میتوانید مسیر مورد نظر را به آرایه $except
در میانهافزار VerifyCsrfToken
که در دایرکتوری app/Http/Middleware
قرار دارد اضافه کنید. این کار به لاراول میگوید که تأیید توکن CSRF را برای آن مسیر خاص نادیده بگیرد.
🔍 استفاده از Long-Polling
در اینجا مثالی از استفاده از commandsHandler()
با بروزرسانیهای Long-Polling آورده شده است.
- Standalone
- Laravel
$update = $bale->commandsHandler(false, ['timeout' => 30]);
$update = Bale::commandsHandler(false, ['timeout' => 30]);
🔫 فعالسازی دستور به صورت دستی
متد triggerCommand('command_name')
یک متد کمکی است که به شما امکان میدهد یک دستور دیگر را در داخل یک دستور فعال کنید. این ویژگی بهویژه زمانی مفید است که بخواهید یک سری از دستورات را بهطور زنجیرهای اجرا ک نید یا زمانی که بخواهید اجرای یک دستور ثبتشده را در داخل دستور خود شبیهسازی کنید.
متد triggerCommand
کد شما را سادهتر میکند با خودکار کردن برخی از وظایف و بهبود تجربه کاربری. زمانی که این متد فراخوانی میشود، هرگونه منطق مرتبط با دستور را بهگونهای اجرا میکند که گویی کاربر آن را فعال کرده است. به عنوان مثال، میتوانید به طور خودکار یک کاربر را برای دریافت هشدارها با فعال کردن دستور /subscribe
در داخل دستور /start
مشترک کنید. در این مثال، شما triggerCommand('subscribe')
را فراخوانی میکنید.
به طور خلاصه، triggerCommand
یک ابزار قدرتمند برای خودکارسازی جریانهای کاری پیچیده و بهبود عملکرد ربات شما است.
📚 متدهای موجود
نام متد | توضیحات |
---|---|
getName(): string | دریافت نام دستور بله |
setName(string $name): self | تنظیم نام دستور بله |
getAliases(): array | دریافت مستعارهای دستور |
setAliases(array | string $aliases): self |
getDescription(): string | دریافت توضیحات دستور بله |
setDescription(string $description): self | تنظیم توضیحات دستور بله |
argument(string $name, mixed $default = null): mixed | دریافت آرگومان دستور یا مقدار پیشفرض |
getArguments(): array | دریافت تمامی آرگومانهای دستور |
setArguments(array $arguments): self | تنظیم آرگومانهای دستور |
getPattern(): string | دریافت الگوی آرگومانهای دستور |
setPattern(string $pattern): self | تنظیم الگوی آرگومانهای دستور |
handle() | منطق اصلی دستور برای پردازش |
getBale(): EFive\Bale\Api | دریافت نمونه API بله |
getUpdate(): EFive\Bale\Objects\Update | دریافت نمونه API بله |
triggerCommand(string $command): mixedvHelper | برای فعال کردن دستورات دیگر |
getCommandBus(): CommandBus | برگرداندن یک نمونه از CommandBus |
🛠️ متدهای کمکی
سیستم دستورات شامل متدهای کمکی اختیاری است که میتوانند روند کاری شما را ساده کرده و به شما کمک کنند تا به کاربری که دستور شما را فعال کرده پاسخ دهید. این متدها فقط برای بهبود تجربه توسعه است.
متدهای replyWith*
از تمامی پارامترهای send<API Method>
پشتیبانی میکنند و از پیش با chat_id
فرستنده دستور پر شدهاند. شما به راحتی میتوانید هر پارامتر دیگری را طبق مستندات ارسال کنید.
نام متد | توضیحات |
---|---|
replyWithMessage(array $params): mixed | پاسخ دادن با یک پیام |
replyWithPhoto(array $params): mixed | پاسخ دادن با یک عکس |
replyWithAudio(array $params): mixed | پاسخ دادن با یک پیام صوتی |
replyWithVideo(array $params): mixed | پاسخ دادن با یک ویدیو |
replyWithVoice(array $params): mixed | پاسخ دادن با یک پیام صوتی |
replyWithDocument(array $params): mixed | پاسخ دادن با یک سند |
replyWithSticker(array $params): mixed | پاسخ دادن با یک استیکر |
replyWithLocation(array $params): mixed | پاسخ دادن با یک موقعیت مکانی |