پرش به مطلب اصلی

💻 سیستم دستورات

این راهنما اصول اولیه دستورات، ساختار دستورات، و نحوه مدیریت دستورات شامل ثبت و نوشتن دستور را پوشش می‌دهد.

🤖 اصول دستورات ربات بله

  • در ربات‌های بله، دستورات پیام‌های خاصی هستند که با یک اسلش (/) شروع می‌شوند و برای انجام اقدامات خاص در ربات شما استفاده می‌شوند. به عنوان مثال: /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 صادر شود.

StartCommand.php
<?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!
StartCommand.php
<?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 :)
StartCommand.php
<?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 :)
StartCommand.php
<?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، شما نیاز به ثبت آن در ربات خود دارید. اطلاعات بیشتر در مورد این موضوع در بخش ثبت دستورات آورده شده است.

🛠️ مثال جامع

در اینجا یک مثال جامع آورده شده است که تمام مثال‌های قبلی را همراه با برخی از متدهای مفید اضافی ترکیب می‌کند.

StartCommand.php
<?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() استفاده کنید که یا شیء دستور یا مسیر کامل دستور را می‌پذیرد. این متد به طور خودکار دستور را در پشت صحنه مقداردهی می‌کند و به شما اجازه می‌دهد تا آن را به صورت یکپارچه ثبت کنید.

$bale->addCommand(EFive\Bale\Commands\HelpCommand::class);

# OR

$command = new EFive\Bale\Commands\HelpCommand();
$bale->addCommand($command);

🔢 دستورات چندگانه

برای ثبت دستورات چندگانه، می‌توانید آرایه‌ای شامل تمامی دستورات را به متد addCommands() ارسال کنید. این متد به شما امکان می‌دهد تمامی دستورات را به صورت یکجا ثبت کنید و در زمان و تلاش صرفه‌جویی کنید.

مثال:

$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 ثبت‌شده آورده شده است.

این روش توصیه‌شده برای مدیریت به‌روزرسانی‌های ورودی از بله است.

webhook.php
$update = $bale->commandsHandler(true);

🔍 استفاده از Long-Polling

در اینجا مثالی از استفاده از commandsHandler() با بروزرسانی‌های Long-Polling آورده شده است.

$update = $bale->commandsHandler(false, ['timeout' => 30]);

🔫 فعال‌سازی دستور به صورت دستی

متد triggerCommand('command_name') یک متد کمکی است که به شما امکان می‌دهد یک دستور دیگر را در داخل یک دستور فعال کنید. این ویژگی به‌ویژه زمانی مفید است که بخواهید یک سری از دستورات را به‌طور زنجیره‌ای اجرا کنید یا زمانی که بخواهید اجرای یک دستور ثبت‌شده را در داخل دستور خود شبیه‌سازی کنید.

متد triggerCommand کد شما را ساده‌تر می‌کند با خودکار کردن برخی از وظایف و بهبود تجربه کاربری. زمانی که این متد فراخوانی می‌شود، هرگونه منطق مرتبط با دستور را به‌گونه‌ای اجرا می‌کند که گویی کاربر آن را فعال کرده است. به عنوان مثال، می‌توانید به طور خودکار یک کاربر را برای دریافت هشدارها با فعال کردن دستور /subscribe در داخل دستور /start مشترک کنید. در این مثال، شما triggerCommand('subscribe') را فراخوانی می‌کنید.

به طور خلاصه، triggerCommand یک ابزار قدرتمند برای خودکارسازی جریان‌های کاری پیچیده و بهبود عملکرد ربات شما است.

📚 متدهای موجود

نام متدتوضیحات
getName(): stringدریافت نام دستور بله
setName(string $name): selfتنظیم نام دستور بله
getAliases(): arrayدریافت مستعارهای دستور
setAliases(arraystring $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پاسخ دادن با یک موقعیت مکانی